summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ports_qemu.yml11
-rw-r--r--.github/workflows/ports_unix.yml72
-rw-r--r--.github/workflows/ports_windows.yml17
-rw-r--r--docs/develop/natmod.rst9
-rw-r--r--docs/esp32/quickref.rst65
-rw-r--r--docs/esp32/tutorial/pwm.rst220
-rw-r--r--docs/library/esp32.rst31
-rw-r--r--docs/library/framebuf.rst12
-rw-r--r--docs/library/machine.PWM.rst14
-rw-r--r--docs/library/machine.UART.rst3
-rw-r--r--docs/library/socket.rst10
-rw-r--r--docs/library/time.rst11
-rw-r--r--docs/library/wm8960.rst8
-rw-r--r--docs/reference/mpremote.rst8
-rw-r--r--docs/rp2/quickref.rst6
-rw-r--r--docs/samd/pinout.rst120
-rw-r--r--drivers/bus/qspi.h1
-rw-r--r--drivers/esp-hosted/esp_hosted_bthci_uart.c4
-rw-r--r--drivers/esp-hosted/esp_hosted_wifi.c4
-rw-r--r--drivers/memory/spiflash.c8
-rw-r--r--examples/natmod/btree/Makefile6
-rw-r--r--examples/natmod/deflate/Makefile8
-rw-r--r--examples/natmod/framebuf/Makefile6
-rw-r--r--examples/natmod/framebuf/framebuf.c3
-rw-r--r--examples/natmod/random/Makefile11
-rw-r--r--extmod/asyncio/core.py36
-rw-r--r--extmod/btstack/modbluetooth_btstack.c7
-rw-r--r--extmod/cyw43_config_common.h126
-rw-r--r--extmod/extmod.mk2
-rw-r--r--extmod/littlefs-include/lfs2_defines.h12
-rw-r--r--extmod/machine_pulse.c37
-rw-r--r--extmod/modbluetooth.c7
-rw-r--r--extmod/modbluetooth.h2
-rw-r--r--extmod/modframebuf.c57
-rw-r--r--extmod/modjson.c3
-rw-r--r--extmod/modlwip.c303
-rw-r--r--extmod/modmachine.c12
-rw-r--r--extmod/modmachine.h2
-rw-r--r--extmod/modnetwork.c39
-rw-r--r--extmod/modnetwork.h5
-rw-r--r--extmod/modre.c3
-rw-r--r--extmod/modtime.c4
-rw-r--r--extmod/modtls_axtls.c2
-rw-r--r--extmod/modtls_mbedtls.c2
-rw-r--r--extmod/moductypes.c9
-rw-r--r--extmod/mpbthci.h4
-rw-r--r--extmod/network_cyw43.c3
-rw-r--r--extmod/network_esp_hosted.c2
-rw-r--r--extmod/network_ppp_lwip.c9
-rw-r--r--extmod/network_wiznet5k.c2
-rw-r--r--extmod/nimble/modbluetooth_nimble.c21
-rw-r--r--extmod/nimble/modbluetooth_nimble.h2
-rw-r--r--extmod/vfs_fat.c8
-rw-r--r--extmod/vfs_lfsx.c10
m---------lib/berkeley-db-1.xx0
m---------lib/libhydrogen0
-rw-r--r--lib/libm_dbl/rint.c2
-rw-r--r--lib/littlefs/lfs1.c2
-rw-r--r--lib/littlefs/lfs2.c859
-rw-r--r--lib/littlefs/lfs2.h100
-rw-r--r--lib/littlefs/lfs2_util.c3
-rw-r--r--lib/littlefs/lfs2_util.h56
m---------lib/stm32lib0
-rw-r--r--mpy-cross/main.c11
-rw-r--r--mpy-cross/mpconfigport.h2
-rw-r--r--ports/alif/Makefile15
-rw-r--r--ports/alif/README.md58
-rw-r--r--ports/alif/alif.mk5
-rw-r--r--ports/alif/boards/ALIF_ENSEMBLE/board.json17
-rw-r--r--ports/alif/boards/ALIF_ENSEMBLE/deploy.md37
-rw-r--r--ports/alif/cyw43_configport.h54
-rw-r--r--ports/alif/irq.h1
-rw-r--r--ports/alif/machine_pin.c197
-rw-r--r--ports/alif/machine_spi.c60
-rw-r--r--ports/alif/main.c4
-rw-r--r--ports/alif/modmachine.c6
-rw-r--r--ports/alif/mpconfigport.h8
-rw-r--r--ports/alif/mphalport.h3
-rw-r--r--ports/cc3200/misc/mperror.c2
-rw-r--r--ports/cc3200/misc/mperror.h2
-rw-r--r--ports/cc3200/mods/modmachine.c4
-rw-r--r--ports/cc3200/mods/modtime.c2
-rw-r--r--ports/cc3200/mods/pybsleep.c4
-rw-r--r--ports/esp32/README.md32
-rw-r--r--ports/esp32/adc.c94
-rw-r--r--ports/esp32/adc.h47
-rw-r--r--ports/esp32/boards/ARDUINO_NANO_ESP32/board.json4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC/board.md2
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_C3/board.md4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S2/board.md2
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/board.json3
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/board.md4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake4
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board6
-rw-r--r--ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m4
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json23
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake7
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h10
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv13
-rw-r--r--ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board4
-rw-r--r--ports/esp32/boards/LILYGO_TTGO_LORA32/board.json4
-rw-r--r--ports/esp32/boards/M5STACK_ATOM/board.json6
-rw-r--r--ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json2
-rw-r--r--ports/esp32/boards/M5STACK_NANOC6/board.json2
-rw-r--r--ports/esp32/boards/OLIMEX_ESP32_EVB/board.json6
-rw-r--r--ports/esp32/boards/OLIMEX_ESP32_POE/board.json6
-rw-r--r--ports/esp32/boards/SIL_WESP32/board.json2
-rw-r--r--ports/esp32/boards/SIL_WESP32/sdkconfig.board9
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json23
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py2
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake7
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h14
-rw-r--r--ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv22
-rw-r--r--ports/esp32/boards/UM_FEATHERS2/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_FEATHERS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_NANOS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_OMGS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_PROS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_TINYC6/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_TINYS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board5
-rw-r--r--ports/esp32/boards/sdkconfig.base4
-rw-r--r--ports/esp32/esp32_common.cmake19
-rw-r--r--ports/esp32/machine_adc.c95
-rw-r--r--ports/esp32/machine_adc_block.c14
-rw-r--r--ports/esp32/machine_i2c.c5
-rw-r--r--ports/esp32/machine_pin.c6
-rw-r--r--ports/esp32/machine_pwm.c974
-rw-r--r--ports/esp32/machine_rtc.c4
-rw-r--r--ports/esp32/machine_rtc.h12
-rw-r--r--ports/esp32/machine_timer.c49
-rw-r--r--ports/esp32/machine_timer.h3
-rw-r--r--ports/esp32/machine_uart.c74
-rw-r--r--ports/esp32/main.c34
-rw-r--r--ports/esp32/main/idf_component.yml5
-rw-r--r--ports/esp32/modesp32.c70
-rw-r--r--ports/esp32/modmachine.c8
-rw-r--r--ports/esp32/modnetwork.h31
-rw-r--r--ports/esp32/modnetwork_globals.h8
-rw-r--r--ports/esp32/modsocket.c31
-rw-r--r--ports/esp32/modtime.c2
-rw-r--r--ports/esp32/mpconfigport.h9
-rw-r--r--ports/esp32/mpnimbleport.c4
-rw-r--r--ports/esp32/mpthreadport.c96
-rw-r--r--ports/esp32/network_common.c4
-rw-r--r--ports/esp32/network_lan.c20
-rw-r--r--ports/esp32/network_ppp.c511
-rw-r--r--ports/esp32/panichandler.c11
-rw-r--r--ports/esp32/partitions-16MiB-ota.csv10
-rw-r--r--ports/esp32/partitions-16MiB.csv7
-rw-r--r--ports/esp32/partitions-2MiB.csv1
-rw-r--r--ports/esp32/partitions-32MiB.csv7
-rw-r--r--ports/esp32/partitions-4MiB-ota.csv1
-rw-r--r--ports/esp32/partitions-4MiB-romfs.csv1
-rw-r--r--ports/esp32/partitions-4MiBplus.csv (renamed from ports/esp32/partitions-4MiB.csv)5
-rw-r--r--ports/esp32/partitions-8MiB.csv7
-rw-r--r--ports/esp32/partitions-8MiBplus-ota.csv (renamed from ports/esp32/partitions-32MiB-ota.csv)6
-rwxr-xr-xports/esp32/tools/metrics_esp32.py2
-rw-r--r--ports/esp8266/esp_init_data.c2
-rw-r--r--ports/esp8266/esp_mphal.h9
-rw-r--r--ports/esp8266/main.c62
-rw-r--r--ports/esp8266/modmachine.c34
-rw-r--r--ports/esp8266/modtime.c4
-rw-r--r--ports/esp8266/mpconfigport.h3
-rw-r--r--ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json2
-rw-r--r--ports/mimxrt/boards/OLIMEX_RT1010/board.json6
-rw-r--r--ports/mimxrt/cyw43_configport.h50
-rw-r--r--ports/mimxrt/modmachine.c6
-rw-r--r--ports/mimxrt/modtime.c9
-rw-r--r--ports/mimxrt/mpconfigport.h8
-rw-r--r--ports/minimal/main.c2
-rw-r--r--ports/nrf/Makefile8
-rw-r--r--ports/nrf/boards/ACTINIUS_ICARUS/board.json2
-rw-r--r--ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json2
-rw-r--r--ports/nrf/boards/ARDUINO_PRIMO/board.json4
-rw-r--r--ports/nrf/boards/BLUEIO_TAG_EVIM/board.json2
-rw-r--r--ports/nrf/boards/DVK_BL652/board.json6
-rw-r--r--ports/nrf/boards/EVK_NINA_B1/board.json2
-rw-r--r--ports/nrf/boards/EVK_NINA_B3/board.json2
-rw-r--r--ports/nrf/boards/IBK_BLYST_NANO/board.json2
-rw-r--r--ports/nrf/boards/IDK_BLYST_NANO/board.json2
-rw-r--r--ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json2
-rw-r--r--ports/nrf/boards/PCA10028/mpconfigboard.h5
-rw-r--r--ports/nrf/boards/PCA10040/mpconfigboard.h5
-rw-r--r--ports/nrf/boards/SEEED_XIAO_NRF52/board.json4
-rw-r--r--ports/nrf/boards/WT51822_S4AT/board.json4
-rw-r--r--ports/nrf/main.c22
-rw-r--r--ports/nrf/modules/machine/modmachine.c29
-rw-r--r--ports/nrf/modules/machine/uart.c57
-rw-r--r--ports/nrf/modules/machine/uart.h3
-rw-r--r--ports/nrf/modules/os/modos.c14
-rw-r--r--ports/nrf/mpconfigport.h13
-rw-r--r--ports/nrf/mphalport.c2
-rw-r--r--ports/nrf/mphalport.h18
-rw-r--r--ports/pic16bit/main.c2
-rw-r--r--ports/powerpc/main.c2
-rw-r--r--ports/qemu/mcu/arm/errorhandler.c20
-rw-r--r--ports/qemu/mcu/arm/startup.c2
-rw-r--r--ports/renesas-ra/Makefile3
-rw-r--r--ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json2
-rw-r--r--ports/renesas-ra/machine_uart.c2
-rw-r--r--ports/renesas-ra/main.c2
-rw-r--r--ports/renesas-ra/modmachine.c6
-rw-r--r--ports/renesas-ra/modtime.c2
-rw-r--r--ports/renesas-ra/mpconfigport.h21
-rw-r--r--ports/renesas-ra/mphalport.c4
-rw-r--r--ports/renesas-ra/mphalport.h10
-rw-r--r--ports/renesas-ra/powerctrl.c6
-rw-r--r--ports/renesas-ra/powerctrl.h6
-rw-r--r--ports/renesas-ra/systick.c10
-rw-r--r--ports/renesas-ra/uart.c6
-rw-r--r--ports/rp2/CMakeLists.txt38
-rw-r--r--ports/rp2/Makefile3
-rw-r--r--ports/rp2/README.md6
-rw-r--r--ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json2
-rw-r--r--ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json2
-rw-r--r--ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json2
-rw-r--r--ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json2
-rw-r--r--ports/rp2/boards/SPARKFUN_PROMICRO/board.json2
-rw-r--r--ports/rp2/boards/SPARKFUN_THINGPLUS/board.json2
-rw-r--r--ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h4
-rw-r--r--ports/rp2/boards/W5100S_EVB_PICO/board.json6
-rw-r--r--ports/rp2/boards/W5500_EVB_PICO/board.json4
-rw-r--r--ports/rp2/boards/WEACTSTUDIO/board.json2
-rw-r--r--ports/rp2/cyw43_configport.h108
-rw-r--r--ports/rp2/machine_pin.c25
-rw-r--r--ports/rp2/machine_timer.c30
-rw-r--r--ports/rp2/memmap_mp_rp2040.ld2
-rw-r--r--ports/rp2/memmap_mp_rp2350.ld2
-rw-r--r--ports/rp2/modmachine.c88
-rw-r--r--ports/rp2/mpconfigport.h38
-rw-r--r--ports/rp2/mphalport.c12
-rw-r--r--ports/rp2/mphalport.h17
-rw-r--r--ports/rp2/mpnetworkport.c39
-rw-r--r--ports/rp2/rp2_dma.c2
-rw-r--r--ports/rp2/rp2_flash.c18
-rw-r--r--ports/rp2/tools_patch/Findpioasm.cmake58
-rw-r--r--ports/samd/Makefile2
-rw-r--r--ports/samd/boards/MINISAM_M4/mpconfigboard.h2
-rw-r--r--ports/samd/boards/SAMD_GENERIC_D21X18/board.json1
-rw-r--r--ports/samd/boards/SAMD_GENERIC_D51X19/board.json1
-rw-r--r--ports/samd/boards/SAMD_GENERIC_D51X20/board.json1
-rw-r--r--ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk6
-rw-r--r--ports/samd/boards/SEEED_XIAO_SAMD21/board.json2
-rw-r--r--ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json21
-rw-r--r--ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h11
-rw-r--r--ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk8
-rw-r--r--ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv47
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json19
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h8
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk4
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv45
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json4
-rw-r--r--ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h2
-rw-r--r--ports/samd/machine_i2c.c35
-rw-r--r--ports/samd/machine_rtc.c1
-rw-r--r--ports/samd/main.c2
-rw-r--r--ports/samd/mcu/samd51/mpconfigmcu.h2
-rw-r--r--ports/samd/modmachine.c8
-rw-r--r--ports/samd/modtime.c14
-rw-r--r--ports/samd/mphalport.h5
-rw-r--r--ports/samd/samd_qspiflash.c32
-rw-r--r--ports/samd/samd_spiflash.c65
-rw-r--r--ports/stm32/Makefile64
-rw-r--r--ports/stm32/adc.c93
-rw-r--r--ports/stm32/adc.h2
-rw-r--r--ports/stm32/boardctrl.c2
-rw-r--r--ports/stm32/boardctrl.h3
-rw-r--r--ports/stm32/boards/ARDUINO_GIGA/board.json2
-rw-r--r--ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h1
-rw-r--r--ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld25
-rw-r--r--ports/stm32/boards/ARDUINO_NICLA_VISION/board.json2
-rw-r--r--ports/stm32/boards/ARDUINO_OPTA/board.json2
-rw-r--r--ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json2
-rw-r--r--ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h1
-rw-r--r--ports/stm32/boards/HYDRABUS/board.json2
-rw-r--r--ports/stm32/boards/LIMIFROG/board.json2
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/bdev.c42
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/board.c122
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/board.md17
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h103
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk26
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h5
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/pins.csv62
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h18
-rw-r--r--ports/stm32/boards/OLIMEX_E407/board.json6
-rw-r--r--ports/stm32/boards/OLIMEX_H407/board.json6
-rw-r--r--ports/stm32/boards/OPENMV_N6/bdev.c41
-rw-r--r--ports/stm32/boards/OPENMV_N6/board.c131
-rw-r--r--ports/stm32/boards/OPENMV_N6/board.ld39
-rw-r--r--ports/stm32/boards/OPENMV_N6/manifest.py3
-rw-r--r--ports/stm32/boards/OPENMV_N6/mpconfigboard.h167
-rw-r--r--ports/stm32/boards/OPENMV_N6/mpconfigboard.mk30
-rw-r--r--ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h5
-rw-r--r--ports/stm32/boards/OPENMV_N6/pins.csv142
-rw-r--r--ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h18
-rw-r--r--ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json6
-rw-r--r--ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h2
-rw-r--r--ports/stm32/boards/common_n6_flash.ld57
-rw-r--r--ports/stm32/boards/common_text.ld8
-rwxr-xr-xports/stm32/boards/make-pins.py2
-rw-r--r--ports/stm32/boards/pllvalues.py2
-rw-r--r--ports/stm32/boards/stm32g474_af.csv58
-rw-r--r--ports/stm32/boards/stm32h7xx_hal_conf_base.h1
-rw-r--r--ports/stm32/boards/stm32n657_af.csv42
-rw-r--r--ports/stm32/boards/stm32n657x0.ld34
-rw-r--r--ports/stm32/boards/stm32n6xx_hal_conf_base.h215
-rw-r--r--ports/stm32/cyw43_configport.h67
-rw-r--r--ports/stm32/dma.c91
-rw-r--r--ports/stm32/dma.h13
-rw-r--r--ports/stm32/extint.c39
-rw-r--r--ports/stm32/extint.h4
-rw-r--r--ports/stm32/flash.c4
-rw-r--r--ports/stm32/i2cslave.h10
-rw-r--r--ports/stm32/irq.h5
-rw-r--r--ports/stm32/lwip_inc/lwipopts.h9
-rw-r--r--ports/stm32/machine_adc.c79
-rw-r--r--ports/stm32/machine_uart.c2
-rw-r--r--ports/stm32/main.c68
-rwxr-xr-xports/stm32/mboot/Makefile43
-rw-r--r--ports/stm32/mboot/adc.c2
-rw-r--r--ports/stm32/mboot/main.c41
-rw-r--r--ports/stm32/mboot/mboot.h2
-rw-r--r--ports/stm32/mboot/mphalport.h17
-rw-r--r--ports/stm32/mboot/stm32_memory_n6.ld18
-rw-r--r--ports/stm32/mboot/stm32_sections.ld8
-rw-r--r--ports/stm32/modmachine.c24
-rw-r--r--ports/stm32/modtime.c2
-rw-r--r--ports/stm32/mpconfigboard_common.h31
-rw-r--r--ports/stm32/mpconfigport.h19
-rw-r--r--ports/stm32/mphalport.c4
-rw-r--r--ports/stm32/mphalport.h2
-rw-r--r--ports/stm32/mpu.h22
-rw-r--r--ports/stm32/pin_defs_stm32.h4
-rw-r--r--ports/stm32/powerctrl.c88
-rw-r--r--ports/stm32/powerctrl.h12
-rw-r--r--ports/stm32/powerctrlboot.c126
-rw-r--r--ports/stm32/qspi.c2
-rw-r--r--ports/stm32/resethandler_iram.s82
-rw-r--r--ports/stm32/rtc.c55
-rw-r--r--ports/stm32/sdcard.c8
-rw-r--r--ports/stm32/sdio.c30
-rw-r--r--ports/stm32/spi.c37
-rw-r--r--ports/stm32/spibdev.c39
-rw-r--r--ports/stm32/stm32.mk22
-rw-r--r--ports/stm32/stm32_it.c27
-rw-r--r--ports/stm32/storage.c15
-rw-r--r--ports/stm32/storage.h4
-rw-r--r--ports/stm32/timer.c32
-rw-r--r--ports/stm32/uart.c77
-rw-r--r--ports/stm32/uart.h4
-rw-r--r--ports/stm32/usb.c2
-rw-r--r--ports/stm32/usbd_conf.c35
-rw-r--r--ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h6
-rw-r--r--ports/stm32/vfs_rom_ioctl.c21
-rw-r--r--ports/stm32/xspi.c599
-rw-r--r--ports/stm32/xspi.h43
-rw-r--r--ports/unix/Makefile15
-rw-r--r--ports/unix/README.md18
-rw-r--r--ports/unix/coverage.c90
-rw-r--r--ports/unix/main.c2
-rw-r--r--ports/unix/modsocket.c1
-rw-r--r--ports/unix/modtime.c10
-rw-r--r--ports/unix/mpconfigport.h3
-rw-r--r--ports/unix/variants/coverage/mpconfigvariant.h2
-rw-r--r--ports/webassembly/main.c2
-rw-r--r--ports/webassembly/objpyproxy.js6
-rw-r--r--ports/windows/Makefile4
-rw-r--r--ports/windows/mpconfigport.h7
-rw-r--r--ports/zephyr/CMakeLists.txt11
-rw-r--r--ports/zephyr/README.md2
-rw-r--r--ports/zephyr/boards/bbc_microbit_v2.conf1
-rw-r--r--ports/zephyr/boards/beagleconnect_freedom.conf10
-rw-r--r--ports/zephyr/boards/beagleconnect_freedom.overlay41
-rw-r--r--ports/zephyr/boards/beagleplay_cc1352p7.conf13
-rw-r--r--ports/zephyr/boards/frdm_k64f.conf4
-rw-r--r--ports/zephyr/boards/nrf52840dk_nrf52840.conf5
-rw-r--r--ports/zephyr/boards/nucleo_h743zi.conf3
-rw-r--r--ports/zephyr/boards/nucleo_wb55rg.conf17
-rw-r--r--ports/zephyr/boards/nucleo_wb55rg.overlay23
-rw-r--r--ports/zephyr/boards/qemu_cortex_m3.conf11
-rw-r--r--ports/zephyr/boards/qemu_x86.conf8
-rw-r--r--ports/zephyr/boards/rpi_pico.conf19
-rw-r--r--ports/zephyr/boards/rpi_pico.overlay48
-rw-r--r--ports/zephyr/machine_pin.c37
-rw-r--r--ports/zephyr/machine_timer.c15
-rw-r--r--ports/zephyr/machine_uart.c194
-rw-r--r--ports/zephyr/main.c24
-rw-r--r--ports/zephyr/modbluetooth_zephyr.c616
-rw-r--r--ports/zephyr/modsocket.c2
-rw-r--r--ports/zephyr/mpconfigport.h21
-rw-r--r--ports/zephyr/mpconfigport_minimal.h2
-rw-r--r--ports/zephyr/mphalport.c8
-rw-r--r--ports/zephyr/mphalport.h46
-rw-r--r--ports/zephyr/prj.conf12
-rw-r--r--ports/zephyr/prj_minimal.conf5
-rw-r--r--ports/zephyr/qstrdefsport.h4
-rw-r--r--ports/zephyr/src/usbd.c14
-rw-r--r--ports/zephyr/src/zephyr_getchar.c17
-rw-r--r--ports/zephyr/src/zephyr_getchar.h1
-rw-r--r--ports/zephyr/uart_core.c19
-rw-r--r--py/argcheck.c4
-rw-r--r--py/asmarm.c114
-rw-r--r--py/asmarm.h61
-rw-r--r--py/asmbase.c2
-rw-r--r--py/asmrv32.c73
-rw-r--r--py/asmrv32.h66
-rw-r--r--py/asmthumb.c115
-rw-r--r--py/asmthumb.h128
-rw-r--r--py/asmx64.h37
-rw-r--r--py/asmx86.h37
-rw-r--r--py/asmxtensa.c178
-rw-r--r--py/asmxtensa.h82
-rw-r--r--py/bc.c2
-rw-r--r--py/bc.h42
-rw-r--r--py/binary.c2
-rw-r--r--py/dynruntime.h4
-rw-r--r--py/dynruntime.mk7
-rw-r--r--py/emitinlinethumb.c108
-rw-r--r--py/emitinlinextensa.c211
-rw-r--r--py/emitnative.c236
-rw-r--r--py/emitndebug.c12
-rw-r--r--py/makeversionhdr.py4
-rw-r--r--py/misc.h9
-rw-r--r--py/modio.c9
-rw-r--r--py/modmath.c7
-rw-r--r--py/mpconfig.h83
-rw-r--r--py/mpprint.c13
-rw-r--r--py/mpprint.h17
-rw-r--r--py/mpz.c16
-rw-r--r--py/nativeglue.h2
-rw-r--r--py/nlr.c2
-rw-r--r--py/nlr.h6
-rw-r--r--py/nlraarch64.c2
-rw-r--r--py/nlrmips.c2
-rw-r--r--py/nlrpowerpc.c4
-rw-r--r--py/nlrrv32.c2
-rw-r--r--py/nlrrv64.c2
-rw-r--r--py/nlrthumb.c2
-rw-r--r--py/nlrx64.c2
-rw-r--r--py/nlrx86.c2
-rw-r--r--py/nlrxtensa.c2
-rw-r--r--py/obj.c30
-rw-r--r--py/obj.h22
-rw-r--r--py/objarray.c31
-rw-r--r--py/objcode.c65
-rw-r--r--py/objcode.h2
-rw-r--r--py/objfloat.c12
-rw-r--r--py/objint.c5
-rw-r--r--py/objint_longlong.c16
-rw-r--r--py/objlist.c101
-rw-r--r--py/objstr.c13
-rw-r--r--py/parsenum.c34
-rw-r--r--py/persistentcode.c18
-rw-r--r--py/profile.c2
-rw-r--r--py/repl.c4
-rw-r--r--py/runtime.c69
-rw-r--r--py/runtime.h43
-rw-r--r--py/scheduler.c26
-rw-r--r--py/showbc.c14
-rw-r--r--pyproject.toml2
-rw-r--r--shared/libc/abort_.c4
-rw-r--r--shared/timeutils/timeutils.c134
-rw-r--r--shared/timeutils/timeutils.h118
-rw-r--r--shared/tinyusb/mp_usbd.c13
-rw-r--r--tests/basics/array_add.py6
-rw-r--r--tests/basics/fun_code_colines.py81
-rw-r--r--tests/basics/fun_code_colines.py.exp20
-rw-r--r--tests/basics/fun_code_full.py47
-rw-r--r--tests/basics/io_buffered_writer.py24
-rw-r--r--tests/basics/io_buffered_writer.py.exp5
-rw-r--r--tests/basics/string_format.py10
-rw-r--r--tests/cmdline/repl_autocomplete_underscore.py33
-rw-r--r--tests/cmdline/repl_autocomplete_underscore.py.exp41
-rw-r--r--tests/cmdline/repl_paste.py90
-rw-r--r--tests/cmdline/repl_paste.py.exp133
-rw-r--r--tests/cpydiff/core_fstring_concat.py2
-rw-r--r--tests/cpydiff/core_fstring_parser.py2
-rw-r--r--tests/cpydiff/core_fstring_repr.py2
-rw-r--r--tests/cpydiff/core_import_all.py10
-rw-r--r--tests/cpydiff/modules3/__init__.py1
-rw-r--r--tests/cpydiff/modules3/foo.py2
-rw-r--r--tests/cpydiff/syntax_arg_unpacking.py2
-rw-r--r--tests/cpydiff/syntax_literal_underscore.py19
-rw-r--r--tests/cpydiff/syntax_spaces.py19
-rw-r--r--tests/cpydiff/types_complex_parser.py14
-rw-r--r--tests/cpydiff/types_str_formatsep.py19
-rw-r--r--tests/extmod/asyncio_event_queue.py64
-rw-r--r--tests/extmod/asyncio_event_queue.py.exp5
-rw-r--r--tests/extmod/asyncio_iterator_event.py86
-rw-r--r--tests/extmod/asyncio_iterator_event.py.exp5
-rw-r--r--tests/extmod/asyncio_wait_for_linked_task.py66
-rw-r--r--tests/extmod/framebuf_blit.py68
-rw-r--r--tests/extmod/framebuf_blit.py.exp45
-rw-r--r--tests/extmod/json_loads.py24
-rw-r--r--tests/extmod/machine_uart_tx.py5
-rw-r--r--tests/extmod/platform_basic.py8
-rw-r--r--tests/extmod/random_extra_float.py8
-rw-r--r--tests/extmod/select_poll_eintr.py16
-rw-r--r--tests/extmod/time_mktime.py120
-rw-r--r--tests/extmod/tls_noleak.py (renamed from tests/extmod/ssl_noleak.py)0
-rw-r--r--tests/extmod/tls_threads.py (renamed from tests/extmod/ssl_threads.py)0
-rw-r--r--tests/extmod/vfs_lfs.py2
-rw-r--r--tests/extmod/vfs_lfs_error.py46
-rw-r--r--tests/extmod/vfs_lfs_error.py.exp44
-rw-r--r--tests/extmod/vfs_rom.py1
-rw-r--r--tests/extmod_hardware/machine_uart_irq_rx.py11
-rw-r--r--tests/extmod_hardware/machine_uart_irq_rxidle.py40
-rw-r--r--tests/extmod_hardware/machine_uart_irq_rxidle.py.exp24
-rw-r--r--tests/float/complex1.py2
-rw-r--r--tests/float/float_array.py8
-rw-r--r--tests/float/math_constants.py27
-rw-r--r--tests/float/math_constants_extra.py3
-rw-r--r--tests/import/import_star.py59
-rw-r--r--tests/import/pkgstar_all_array/__init__.py49
-rw-r--r--tests/import/pkgstar_all_array/funcs.py2
-rw-r--r--tests/import/pkgstar_all_inval/__init__.py1
-rw-r--r--tests/import/pkgstar_all_miss/__init__.py8
-rw-r--r--tests/import/pkgstar_all_tuple/__init__.py22
-rw-r--r--tests/import/pkgstar_default/__init__.py20
-rw-r--r--tests/inlineasm/xtensa/asmargs.py44
-rw-r--r--tests/inlineasm/xtensa/asmargs.py.exp5
-rw-r--r--tests/inlineasm/xtensa/asmarith.py119
-rw-r--r--tests/inlineasm/xtensa/asmarith.py.exp40
-rw-r--r--tests/inlineasm/xtensa/asmbranch.py299
-rw-r--r--tests/inlineasm/xtensa/asmbranch.py.exp66
-rw-r--r--tests/inlineasm/xtensa/asmjump.py26
-rw-r--r--tests/inlineasm/xtensa/asmjump.py.exp2
-rw-r--r--tests/inlineasm/xtensa/asmloadstore.py98
-rw-r--r--tests/inlineasm/xtensa/asmloadstore.py.exp9
-rw-r--r--tests/inlineasm/xtensa/asmmisc.py25
-rw-r--r--tests/inlineasm/xtensa/asmmisc.py.exp3
-rw-r--r--tests/inlineasm/xtensa/asmshift.py137
-rw-r--r--tests/inlineasm/xtensa/asmshift.py.exp17
-rw-r--r--tests/micropython/viper_ptr16_load_boundary.py39
-rw-r--r--tests/micropython/viper_ptr16_load_boundary.py.exp20
-rw-r--r--tests/micropython/viper_ptr16_store_boundary.py53
-rw-r--r--tests/micropython/viper_ptr16_store_boundary.py.exp20
-rw-r--r--tests/micropython/viper_ptr32_load_boundary.py39
-rw-r--r--tests/micropython/viper_ptr32_load_boundary.py.exp20
-rw-r--r--tests/micropython/viper_ptr32_store_boundary.py58
-rw-r--r--tests/micropython/viper_ptr32_store_boundary.py.exp20
-rw-r--r--tests/micropython/viper_ptr8_load_boundary.py38
-rw-r--r--tests/micropython/viper_ptr8_load_boundary.py.exp20
-rw-r--r--tests/micropython/viper_ptr8_store_boundary.py49
-rw-r--r--tests/micropython/viper_ptr8_store_boundary.py.exp20
-rw-r--r--tests/misc/print_exception.py2
-rw-r--r--tests/misc/sys_settrace_cov.py23
-rw-r--r--tests/misc/sys_settrace_cov.py.exp2
-rw-r--r--tests/multi_net/tcp_accept_recv.py73
-rw-r--r--tests/multi_net/tcp_recv_peek.py46
-rw-r--r--tests/multi_net/udp_data_multi.py69
-rw-r--r--tests/multi_net/udp_data_multi.py.exp15
-rw-r--r--tests/multi_net/udp_recv_dontwait.py59
-rw-r--r--tests/multi_net/udp_recv_dontwait.py.exp7
-rw-r--r--tests/multi_net/udp_recv_peek.py36
-rw-r--r--tests/net_inet/mpycert.derbin1290 -> 1289 bytes
-rw-r--r--tests/net_inet/ssl_cert.py79
-rw-r--r--tests/net_inet/test_sslcontext_client.py10
-rw-r--r--tests/ports/rp2/rp2_lightsleep_thread.py67
-rw-r--r--tests/ports/rp2/rp2_machine_idle.py6
-rw-r--r--tests/ports/rp2/rp2_machine_timer.py20
-rw-r--r--tests/ports/rp2/rp2_machine_timer.py.exp16
-rw-r--r--tests/ports/stm32/adc.py5
-rw-r--r--tests/ports/stm32/adcall.py16
-rw-r--r--tests/ports/stm32/extint.py3
-rw-r--r--tests/ports/stm32/i2c.py7
-rw-r--r--tests/ports/stm32/i2c_accel.py9
-rw-r--r--tests/ports/stm32/i2c_error.py3
-rw-r--r--tests/ports/stm32/irq.py5
-rw-r--r--tests/ports/stm32/modstm.py4
-rw-r--r--tests/ports/stm32/pin.py14
-rw-r--r--tests/ports/stm32/pyb1.py4
-rw-r--r--tests/ports/stm32/rtc.py14
-rw-r--r--tests/ports/stm32/servo.py6
-rw-r--r--tests/ports/stm32/timer.py11
-rw-r--r--tests/ports/stm32/timer_callback.py26
-rw-r--r--tests/ports/stm32/uart.py6
-rw-r--r--tests/ports/unix/extra_coverage.py10
-rw-r--r--tests/ports/unix/extra_coverage.py.exp44
-rw-r--r--tests/ports/webassembly/py_proxy_has.mjs2
-rw-r--r--tests/ports/webassembly/py_proxy_has.mjs.exp2
-rwxr-xr-xtests/run-multitests.py32
-rwxr-xr-xtests/run-natmodtests.py52
-rwxr-xr-xtests/run-perfbench.py29
-rwxr-xr-xtests/run-tests.py185
-rwxr-xr-xtools/autobuild/autobuild.sh4
-rwxr-xr-xtools/autobuild/build-boards.sh4
-rw-r--r--tools/boardgen.py8
-rwxr-xr-xtools/ci.sh141
-rw-r--r--tools/gen-cpydiff.py32
-rw-r--r--tools/mpremote/mpremote/commands.py49
-rw-r--r--tools/mpremote/mpremote/main.py62
-rw-r--r--tools/mpremote/mpremote/mp_errno.py53
-rw-r--r--tools/mpremote/mpremote/repl.py107
-rw-r--r--tools/mpremote/mpremote/transport.py13
-rwxr-xr-xtools/mpremote/tests/test_filesystem.sh3
-rw-r--r--tools/mpremote/tests/test_filesystem.sh.exp2
-rwxr-xr-xtools/mpremote/tests/test_fs_tree.sh114
-rw-r--r--tools/mpremote/tests/test_fs_tree.sh.exp225
-rwxr-xr-xtools/mpy_ld.py91
-rwxr-xr-xtools/pyboard.py66
-rwxr-xr-xtools/verifygitlog.py31
605 files changed, 14988 insertions, 4158 deletions
diff --git a/.github/workflows/ports_qemu.yml b/.github/workflows/ports_qemu.yml
index 57192c4393..ac09dde864 100644
--- a/.github/workflows/ports_qemu.yml
+++ b/.github/workflows/ports_qemu.yml
@@ -20,13 +20,20 @@ concurrency:
jobs:
build_and_test_arm:
+ strategy:
+ fail-fast: false
+ matrix:
+ ci_func: # names are functions in ci.sh
+ - bigendian
+ - sabrelite
+ - thumb
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install packages
run: source tools/ci.sh && ci_qemu_setup_arm
- - name: Build and run test suite
- run: source tools/ci.sh && ci_qemu_build_arm
+ - name: Build and run test suite ci_qemu_build_arm_${{ matrix.ci_func }}
+ run: source tools/ci.sh && ci_qemu_build_arm_${{ matrix.ci_func }}
- name: Print failures
if: failure()
run: tests/run-tests.py --print-failures
diff --git a/.github/workflows/ports_unix.yml b/.github/workflows/ports_unix.yml
index 2547015038..4b22926eaf 100644
--- a/.github/workflows/ports_unix.yml
+++ b/.github/workflows/ports_unix.yml
@@ -71,6 +71,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
+ # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
+ with:
+ python-version: '3.11'
- name: Install packages
run: source tools/ci.sh && ci_unix_coverage_setup
- name: Build
@@ -169,23 +174,6 @@ jobs:
if: failure()
run: tests/run-tests.py --print-failures
- settrace:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-python@v5
- # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
- # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
- with:
- python-version: '3.11'
- - name: Build
- run: source tools/ci.sh && ci_unix_settrace_build
- - name: Run main test suite
- run: source tools/ci.sh && ci_unix_settrace_run_tests
- - name: Print failures
- if: failure()
- run: tests/run-tests.py --print-failures
-
settrace_stackless:
runs-on: ubuntu-latest
steps:
@@ -262,3 +250,53 @@ jobs:
- name: Print failures
if: failure()
run: tests/run-tests.py --print-failures
+
+ sanitize_address:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
+ # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
+ with:
+ python-version: '3.11'
+ - name: Install packages
+ run: source tools/ci.sh && ci_unix_coverage_setup
+ - name: Build
+ run: source tools/ci.sh && ci_unix_sanitize_address_build
+ - name: Run main test suite
+ run: source tools/ci.sh && ci_unix_sanitize_address_run_tests
+ - name: Test merging .mpy files
+ run: source tools/ci.sh && ci_unix_coverage_run_mpy_merge_tests
+ - name: Build native mpy modules
+ run: source tools/ci.sh && ci_native_mpy_modules_build
+ - name: Test importing .mpy generated by mpy_ld.py
+ run: source tools/ci.sh && ci_unix_coverage_run_native_mpy_tests
+ - name: Print failures
+ if: failure()
+ run: tests/run-tests.py --print-failures
+
+ sanitize_undefined:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ # Python 3.12 is the default for ubuntu-24.04, but that has compatibility issues with settrace tests.
+ # Can remove this step when ubuntu-latest uses a more recent Python 3.x as the default.
+ with:
+ python-version: '3.11'
+ - name: Install packages
+ run: source tools/ci.sh && ci_unix_coverage_setup
+ - name: Build
+ run: source tools/ci.sh && ci_unix_sanitize_undefined_build
+ - name: Run main test suite
+ run: source tools/ci.sh && ci_unix_sanitize_undefined_run_tests
+ - name: Test merging .mpy files
+ run: source tools/ci.sh && ci_unix_coverage_run_mpy_merge_tests
+ - name: Build native mpy modules
+ run: source tools/ci.sh && ci_native_mpy_modules_build
+ - name: Test importing .mpy generated by mpy_ld.py
+ run: source tools/ci.sh && ci_unix_coverage_run_native_mpy_tests
+ - name: Print failures
+ if: failure()
+ run: tests/run-tests.py --print-failures
diff --git a/.github/workflows/ports_windows.yml b/.github/workflows/ports_windows.yml
index 84e018ba15..f33277d471 100644
--- a/.github/workflows/ports_windows.yml
+++ b/.github/workflows/ports_windows.yml
@@ -28,13 +28,10 @@ jobs:
visualstudio: ['2017', '2019', '2022']
include:
- visualstudio: '2017'
- runner: windows-latest
vs_version: '[15, 16)'
- visualstudio: '2019'
- runner: windows-2019
vs_version: '[16, 17)'
- visualstudio: '2022'
- runner: windows-2022
vs_version: '[17, 18)'
# trim down the number of jobs in the matrix
exclude:
@@ -42,9 +39,9 @@ jobs:
configuration: Debug
- visualstudio: '2019'
configuration: Debug
+ runs-on: windows-latest
env:
CI_BUILD_CONFIGURATION: ${{ matrix.configuration }}
- runs-on: ${{ matrix.runner }}
steps:
- name: Install Visual Studio 2017
if: matrix.visualstudio == '2017'
@@ -52,13 +49,15 @@ jobs:
choco install visualstudio2017buildtools
choco install visualstudio2017-workload-vctools
choco install windows-sdk-8.1
+ - name: Install Visual Studio 2019
+ if: matrix.visualstudio == '2019'
+ run: |
+ choco install visualstudio2019buildtools
+ choco install visualstudio2019-workload-vctools
+ choco install windows-sdk-8.1
- uses: microsoft/setup-msbuild@v2
with:
vs-version: ${{ matrix.vs_version }}
- - uses: actions/setup-python@v5
- if: matrix.runner == 'windows-2019'
- with:
- python-version: '3.9'
- uses: actions/checkout@v4
- name: Build mpy-cross.exe
run: msbuild mpy-cross\mpy-cross.vcxproj -maxcpucount -property:Configuration=${{ matrix.configuration }} -property:Platform=${{ matrix.platform }}
@@ -103,7 +102,7 @@ jobs:
env: i686
- sys: mingw64
env: x86_64
- runs-on: windows-2022
+ runs-on: windows-latest
env:
CHERE_INVOKING: enabled_from_arguments
defaults:
diff --git a/docs/develop/natmod.rst b/docs/develop/natmod.rst
index 18678eaefb..2ccd832885 100644
--- a/docs/develop/natmod.rst
+++ b/docs/develop/natmod.rst
@@ -81,7 +81,14 @@ Linker limitation: the native module is not linked against the symbol table of t
full MicroPython firmware. Rather, it is linked against an explicit table of exported
symbols found in ``mp_fun_table`` (in ``py/nativeglue.h``), that is fixed at firmware
build time. It is thus not possible to simply call some arbitrary HAL/OS/RTOS/system
-function, for example.
+function, for example, unless that resides at a fixed address. In that case, the path
+of a linkerscript containing a series of symbol names and their fixed address can be
+passed to ``mpy_ld.py`` via the ``--externs`` command line argument. That way symbols
+appearing in the linkerscript will take precedence over what is provided from object
+files, but at the moment the object files' implementation will still reside in the
+final MPY file. The linkerscript parser is limited in its capabilities, and is
+currently used only for parsing the ESP8266 port ROM symbols list (see
+``ports/esp8266/boards/eagle.rom.addr.v6.ld``).
New symbols can be added to the end of the table and the firmware rebuilt.
The symbols also need to be added to ``tools/mpy_ld.py``'s ``fun_table`` dict in the
diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst
index ccc01099d1..c394414a76 100644
--- a/docs/esp32/quickref.rst
+++ b/docs/esp32/quickref.rst
@@ -148,6 +148,7 @@ Required keyword arguments for the constructor:
- ``mdc`` and ``mdio`` - :class:`machine.Pin` objects (or integers) specifying
the MDC and MDIO pins.
- ``phy_type`` - Select the PHY device type. Supported devices are
+ ``PHY_GENERIC``,
``PHY_LAN8710``, ``PHY_LAN8720``, ``PHY_IP101``, ``PHY_RTL8201``,
``PHY_DP83848``, ``PHY_KSZ8041`` and ``PHY_KSZ8081``. These values are all
constants defined in the ``network`` module.
@@ -270,8 +271,10 @@ Use the :mod:`time <time>` module::
Timers
------
-The ESP32 port has four hardware timers. Use the :ref:`machine.Timer <machine.Timer>` class
-with a timer ID from 0 to 3 (inclusive)::
+The ESP32 port has one, two or four hardware timers, depending on the ESP32 device type.
+There is 1 timer for ESP32C2, 2 timers for ESP32C4, ESP32C6 and ESP32H4, and
+4 timers otherwise. Use the :ref:`machine.Timer <machine.Timer>` class
+with a timer ID of 0, 0 and 1, or from 0 to 3 (inclusive)::
from machine import Timer
@@ -281,7 +284,8 @@ with a timer ID from 0 to 3 (inclusive)::
tim1 = Timer(1)
tim1.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(1))
-The period is in milliseconds.
+The period is in milliseconds. When using UART.IRQ_RXIDLE, timer 0 is needed for
+the IRQ_RXIDLE mechanism and must not be used otherwise.
Virtual timers are not currently supported on this port.
@@ -383,7 +387,7 @@ for more details.
Use the :ref:`machine.PWM <machine.PWM>` class::
- from machine import Pin, PWM
+ from machine import Pin, PWM, lightsleep
pwm0 = PWM(Pin(0), freq=5000, duty_u16=32768) # create PWM object from a pin
freq = pwm0.freq() # get current frequency
@@ -393,7 +397,7 @@ Use the :ref:`machine.PWM <machine.PWM>` class::
pwm0.duty(256) # set duty cycle from 0 to 1023 as a ratio duty/1023, (now 25%)
duty_u16 = pwm0.duty_u16() # get current duty cycle, range 0-65535
- pwm0.duty_u16(2**16*3//4) # set duty cycle from 0 to 65535 as a ratio duty_u16/65535, (now 75%)
+ pwm0.duty_u16(65536*3//4) # set duty cycle from 0 to 65535 as a ratio duty_u16/65535, (now 75%)
duty_ns = pwm0.duty_ns() # get current pulse width in ns
pwm0.duty_ns(250_000) # set pulse width in nanoseconds from 0 to 1_000_000_000/freq, (now 25%)
@@ -402,19 +406,35 @@ Use the :ref:`machine.PWM <machine.PWM>` class::
pwm2 = PWM(Pin(2), freq=20000, duty=512) # create and configure in one go
print(pwm2) # view PWM settings
+ pwm2.deinit() # turn off PWM on the pin
+
+ pwm0 = PWM(Pin(0), duty_u16=16384) # The output is at a high level 25% of the time.
+ pwm2 = PWM(Pin(2), duty_u16=16384, invert=1) # The output is at a low level 25% of the time.
+
+ pwm4 = PWM(Pin(4), lightsleep=True) # Allow PWM during light sleep mode
+
+ lightsleep(10*1000) # pwm0, pwm2 goes off, pwm4 stays on during 10s light sleep
+ # pwm0, pwm2, pwm4 on after 10s light sleep
ESP chips have different hardware peripherals:
-===================================================== ======== ======== ========
-Hardware specification ESP32 ESP32-S2 ESP32-C3
------------------------------------------------------ -------- -------- --------
-Number of groups (speed modes) 2 1 1
-Number of timers per group 4 4 4
-Number of channels per group 8 8 6
------------------------------------------------------ -------- -------- --------
-Different PWM frequencies (groups * timers) 8 4 4
-Total PWM channels (Pins, duties) (groups * channels) 16 8 6
-===================================================== ======== ======== ========
+======================================================= ======== ========= ==========
+Hardware specification ESP32 ESP32-S2, ESP32-C2,
+ ESP32-S3, ESP32-C3,
+ ESP32-P4 ESP32-C5,
+ ESP32-C6,
+ ESP32-H2
+------------------------------------------------------- -------- --------- ----------
+Number of groups (speed modes) 2 1 1
+Number of timers per group 4 4 4
+Number of channels per group 8 8 6
+------------------------------------------------------- -------- --------- ----------
+Different PWM frequencies = (groups * timers) 8 4 4
+Total PWM channels (Pins, duties) = (groups * channels) 16 8 6
+======================================================= ======== ========= ==========
+
+In light sleep, the ESP32 PWM can only operate in low speed mode, so only 4 timers and
+8 channels are available.
A maximum number of PWM channels (Pins) are available on the ESP32 - 16 channels,
but only 8 different PWM frequencies are available, the remaining 8 channels must
@@ -524,14 +544,27 @@ Legacy methods:
Equivalent to ``ADC.block().init(bits=bits)``.
+The only chip that can switch resolution to a lower one is the normal esp32.
+The C2 & S3 are stuck at 12 bits, while the S2 is at 13 bits.
+
For compatibility, the ``ADC`` object also provides constants matching the
-supported ADC resolutions:
+supported ADC resolutions, per chip:
+ESP32:
- ``ADC.WIDTH_9BIT`` = 9
- ``ADC.WIDTH_10BIT`` = 10
- ``ADC.WIDTH_11BIT`` = 11
- ``ADC.WIDTH_12BIT`` = 12
+ESP32 C3 & S3:
+ - ``ADC.WIDTH_12BIT`` = 12
+
+ESP32 S2:
+ - ``ADC.WIDTH_13BIT`` = 13
+
+.. method:: ADC.deinit()
+
+ Provided to deinit the adc driver.
Software SPI bus
----------------
diff --git a/docs/esp32/tutorial/pwm.rst b/docs/esp32/tutorial/pwm.rst
index 2650284d35..82d43b36f6 100644
--- a/docs/esp32/tutorial/pwm.rst
+++ b/docs/esp32/tutorial/pwm.rst
@@ -11,16 +11,20 @@ compared with the length of a single period (low plus high time). Maximum
duty cycle is when the pin is high all of the time, and minimum is when it is
low all of the time.
-* More comprehensive example with all 16 PWM channels and 8 timers::
+* More comprehensive example with all **16 PWM channels and 8 timers**::
+ from time import sleep
from machine import Pin, PWM
try:
- f = 100 # Hz
- d = 1024 // 16 # 6.25%
- pins = (15, 2, 4, 16, 18, 19, 22, 23, 25, 26, 27, 14 , 12, 13, 32, 33)
+ F = 10000 # Hz
+ D = 65536 // 16 # 6.25%
+ pins = (2, 4, 12, 13, 14, 15, 16, 18, 19, 22, 23, 25, 26, 27, 32, 33)
pwms = []
for i, pin in enumerate(pins):
- pwms.append(PWM(Pin(pin), freq=f * (i // 2 + 1), duty= 1023 if i==15 else d * (i + 1)))
+ f = F * (i // 2 + 1)
+ d = min(65535, D * (i + 1))
+ pwms.append(PWM(pin, freq=f, duty_u16=d))
+ sleep(2 / f)
print(pwms[i])
finally:
for pwm in pwms:
@@ -31,65 +35,100 @@ low all of the time.
Output is::
- PWM(Pin(15), freq=100, duty=64, resolution=10, mode=0, channel=0, timer=0)
- PWM(Pin(2), freq=100, duty=128, resolution=10, mode=0, channel=1, timer=0)
- PWM(Pin(4), freq=200, duty=192, resolution=10, mode=0, channel=2, timer=1)
- PWM(Pin(16), freq=200, duty=256, resolution=10, mode=0, channel=3, timer=1)
- PWM(Pin(18), freq=300, duty=320, resolution=10, mode=0, channel=4, timer=2)
- PWM(Pin(19), freq=300, duty=384, resolution=10, mode=0, channel=5, timer=2)
- PWM(Pin(22), freq=400, duty=448, resolution=10, mode=0, channel=6, timer=3)
- PWM(Pin(23), freq=400, duty=512, resolution=10, mode=0, channel=7, timer=3)
- PWM(Pin(25), freq=500, duty=576, resolution=10, mode=1, channel=0, timer=0)
- PWM(Pin(26), freq=500, duty=640, resolution=10, mode=1, channel=1, timer=0)
- PWM(Pin(27), freq=600, duty=704, resolution=10, mode=1, channel=2, timer=1)
- PWM(Pin(14), freq=600, duty=768, resolution=10, mode=1, channel=3, timer=1)
- PWM(Pin(12), freq=700, duty=832, resolution=10, mode=1, channel=4, timer=2)
- PWM(Pin(13), freq=700, duty=896, resolution=10, mode=1, channel=5, timer=2)
- PWM(Pin(32), freq=800, duty=960, resolution=10, mode=1, channel=6, timer=3)
- PWM(Pin(33), freq=800, duty=1023, resolution=10, mode=1, channel=7, timer=3)
-
-* Example of a smooth frequency change::
+ PWM(Pin(2), freq=10000, duty_u16=4096)
+ PWM(Pin(4), freq=10000, duty_u16=8192)
+ PWM(Pin(12), freq=20000, duty_u16=12288)
+ PWM(Pin(13), freq=20000, duty_u16=16384)
+ PWM(Pin(14), freq=30030, duty_u16=20480)
+ PWM(Pin(15), freq=30030, duty_u16=24576)
+ PWM(Pin(16), freq=40000, duty_u16=28672)
+ PWM(Pin(18), freq=40000, duty_u16=32768)
+ PWM(Pin(19), freq=50000, duty_u16=36864)
+ PWM(Pin(22), freq=50000, duty_u16=40960)
+ PWM(Pin(23), freq=60060, duty_u16=45056)
+ PWM(Pin(25), freq=60060, duty_u16=49152)
+ PWM(Pin(26), freq=69930, duty_u16=53248)
+ PWM(Pin(27), freq=69930, duty_u16=57344)
+ PWM(Pin(32), freq=80000, duty_u16=61440)
+ PWM(Pin(33), freq=80000, duty_u16=65535)
+
+
+* Example of a **smooth frequency change**::
from time import sleep
from machine import Pin, PWM
- F_MIN = 500
- F_MAX = 1000
+ F_MIN = 1000
+ F_MAX = 10000
f = F_MIN
- delta_f = 1
+ delta_f = F_MAX // 50
- p = PWM(Pin(5), f)
- print(p)
+ pwm = PWM(Pin(27), f)
while True:
- p.freq(f)
-
- sleep(10 / F_MIN)
+ pwm.freq(f)
+ sleep(1 / f)
+ sleep(0.1)
+ print(pwm)
f += delta_f
- if f >= F_MAX or f <= F_MIN:
+ if f > F_MAX or f < F_MIN:
delta_f = -delta_f
+ print()
+ if f > F_MAX:
+ f = F_MAX
+ elif f < F_MIN:
+ f = F_MIN
- See PWM wave at Pin(5) with an oscilloscope.
+ See PWM wave on Pin(27) with an oscilloscope.
+
+ Output is::
-* Example of a smooth duty change::
+ PWM(Pin(27), freq=998, duty_u16=32768)
+ PWM(Pin(27), freq=1202, duty_u16=32768)
+ PWM(Pin(27), freq=1401, duty_u16=32768)
+ PWM(Pin(27), freq=1598, duty_u16=32768)
+ ...
+ PWM(Pin(27), freq=9398, duty_u16=32768)
+ PWM(Pin(27), freq=9615, duty_u16=32768)
+ PWM(Pin(27), freq=9804, duty_u16=32768)
+ PWM(Pin(27), freq=10000, duty_u16=32768)
+
+ PWM(Pin(27), freq=10000, duty_u16=32768)
+ PWM(Pin(27), freq=9804, duty_u16=32768)
+ PWM(Pin(27), freq=9615, duty_u16=32768)
+ PWM(Pin(27), freq=9398, duty_u16=32768)
+ ...
+ PWM(Pin(27), freq=1598, duty_u16=32768)
+ PWM(Pin(27), freq=1401, duty_u16=32768)
+ PWM(Pin(27), freq=1202, duty_u16=32768)
+ PWM(Pin(27), freq=998, duty_u16=32768)
+
+
+* Example of a **smooth duty change**::
from time import sleep
from machine import Pin, PWM
- DUTY_MAX = 2**16 - 1
+ DUTY_MAX = 65535
duty_u16 = 0
- delta_d = 16
+ delta_d = 256
- p = PWM(Pin(5), 1000, duty_u16=duty_u16)
- print(p)
+ pwm = PWM(Pin(27), freq=1000, duty_u16=duty_u16)
while True:
- p.duty_u16(duty_u16)
+ pwm.duty_u16(duty_u16)
+ sleep(2 / pwm.freq())
+ print(pwm)
- sleep(1 / 1000)
+ if duty_u16 >= DUTY_MAX:
+ print()
+ sleep(2)
+ elif duty_u16 <= 0:
+ print()
+ sleep(2)
duty_u16 += delta_d
if duty_u16 >= DUTY_MAX:
@@ -99,9 +138,106 @@ low all of the time.
duty_u16 = 0
delta_d = -delta_d
- See PWM wave at Pin(5) with an oscilloscope.
+ PWM wave on Pin(27) with an oscilloscope.
+
+ Output is::
+
+ PWM(Pin(27), freq=998, duty_u16=0)
+ PWM(Pin(27), freq=998, duty_u16=256)
+ PWM(Pin(27), freq=998, duty_u16=512)
+ PWM(Pin(27), freq=998, duty_u16=768)
+ PWM(Pin(27), freq=998, duty_u16=1024)
+ ...
+ PWM(Pin(27), freq=998, duty_u16=64512)
+ PWM(Pin(27), freq=998, duty_u16=64768)
+ PWM(Pin(27), freq=998, duty_u16=65024)
+ PWM(Pin(27), freq=998, duty_u16=65280)
+ PWM(Pin(27), freq=998, duty_u16=65535)
+
+ PWM(Pin(27), freq=998, duty_u16=65279)
+ PWM(Pin(27), freq=998, duty_u16=65023)
+ PWM(Pin(27), freq=998, duty_u16=64767)
+ PWM(Pin(27), freq=998, duty_u16=64511)
+ ...
+ PWM(Pin(27), freq=998, duty_u16=1023)
+ PWM(Pin(27), freq=998, duty_u16=767)
+ PWM(Pin(27), freq=998, duty_u16=511)
+ PWM(Pin(27), freq=998, duty_u16=255)
+ PWM(Pin(27), freq=998, duty_u16=0)
+
+
+* Example of a **smooth duty change and PWM output inversion**::
+
+ from utime import sleep
+ from machine import Pin, PWM
+
+ try:
+ DUTY_MAX = 65535
+
+ duty_u16 = 0
+ delta_d = 65536 // 32
+
+ pwm = PWM(Pin(27))
+ pwmi = PWM(Pin(32), invert=1)
+
+ while True:
+ pwm.duty_u16(duty_u16)
+ pwmi.duty_u16(duty_u16)
+
+ duty_u16 += delta_d
+ if duty_u16 >= DUTY_MAX:
+ duty_u16 = DUTY_MAX
+ delta_d = -delta_d
+ elif duty_u16 <= 0:
+ duty_u16 = 0
+ delta_d = -delta_d
+
+ sleep(.01)
+ print(pwm)
+ print(pwmi)
+
+ finally:
+ try:
+ pwm.deinit()
+ except:
+ pass
+ try:
+ pwmi.deinit()
+ except:
+ pass
+
+ Output is::
+
+ PWM(Pin(27), freq=5000, duty_u16=0)
+ PWM(Pin(32), freq=5000, duty_u16=32768, invert=1)
+ PWM(Pin(27), freq=5000, duty_u16=2048)
+ PWM(Pin(32), freq=5000, duty_u16=2048, invert=1)
+ PWM(Pin(27), freq=5000, duty_u16=4096)
+ PWM(Pin(32), freq=5000, duty_u16=4096, invert=1)
+ PWM(Pin(27), freq=5000, duty_u16=6144)
+ PWM(Pin(32), freq=5000, duty_u16=6144, invert=1)
+ PWM(Pin(27), freq=5000, duty_u16=8192)
+ PWM(Pin(32), freq=5000, duty_u16=8192, invert=1)
+ ...
+
+
+ See PWM waves on Pin(27) and Pin(32) with an oscilloscope.
+
+Note: New PWM parameters take effect in the next PWM cycle.
+
+ pwm = PWM(2, duty=512)
+ print(pwm)
+ >>> PWM(Pin(2), freq=5000, duty=1023) # the duty is not relevant
+ pwm.init(freq=2, duty=64)
+ print(pwm)
+ >>> PWM(Pin(2), freq=2, duty=16) # the duty is not relevant
+ time.sleep(1 / 2) # wait one PWM period
+ print(pwm)
+ >>> PWM(Pin(2), freq=2, duty=64) # the duty is actual
+
+Note: machine.freq(20_000_000) reduces the highest PWM frequency to 10 MHz.
-Note: the Pin.OUT mode does not need to be specified. The channel is initialized
+Note: the Pin.OUT mode does not need to be specified. The channel is initialized
to PWM mode internally once for each Pin that is passed to the PWM constructor.
The following code is wrong::
diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst
index dc35e7905e..4be6dc2671 100644
--- a/docs/library/esp32.rst
+++ b/docs/library/esp32.rst
@@ -18,23 +18,31 @@ Functions
Configure whether or not a touch will wake the device from sleep.
*wake* should be a boolean value.
+ .. note:: This is only available for boards that have touch sensor support.
+
.. function:: wake_on_ulp(wake)
Configure whether or not the Ultra-Low-Power co-processor can wake the
device from sleep. *wake* should be a boolean value.
+ .. note:: This is only available for boards that have ULP coprocessor support.
+
.. function:: wake_on_ext0(pin, level)
Configure how EXT0 wakes the device from sleep. *pin* can be ``None``
or a valid Pin object. *level* should be ``esp32.WAKEUP_ALL_LOW`` or
``esp32.WAKEUP_ANY_HIGH``.
+ .. note:: This is only available for boards that have ext0 support.
+
.. function:: wake_on_ext1(pins, level)
Configure how EXT1 wakes the device from sleep. *pins* can be ``None``
or a tuple/list of valid Pin objects. *level* should be ``esp32.WAKEUP_ALL_LOW``
or ``esp32.WAKEUP_ANY_HIGH``.
+ .. note:: This is only available for boards that have ext1 support.
+
.. function:: gpio_deep_sleep_hold(enable)
Configure whether non-RTC GPIO pin configuration is retained during
@@ -80,6 +88,29 @@ Functions
The result of :func:`gc.mem_free()` is the total of the current "free"
and "max new split" values printed by :func:`micropython.mem_info()`.
+.. function:: idf_task_info()
+
+ Returns information about running ESP-IDF/FreeRTOS tasks, which include
+ MicroPython threads. This data is useful to gain insight into how much time
+ tasks spend running or if they are blocked for significant parts of time,
+ and to determine if allocated stacks are fully utilized or might be reduced.
+
+ ``CONFIG_FREERTOS_USE_TRACE_FACILITY=y`` must be set in the board
+ configuration to make this method available. Additionally configuring
+ ``CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y`` and
+ ``CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y`` is recommended to be able to
+ retrieve the total and per-task runtime and the core ID respectively.
+
+ The return value is a 2-tuple where the first value is the total runtime,
+ and the second a list of tasks. Each task is a 7-tuple containing: the task
+ ID, name, current state, priority, runtime, stack high water mark, and the
+ ID of the core it is running on. Runtime and core ID will be None when the
+ respective FreeRTOS configuration option is not enabled.
+
+ .. note:: For an easier to use output based on this function you can use the
+ `utop library <https://github.com/micropython/micropython-lib/tree/master/micropython/utop>`_,
+ which implements a live overview similar to the Unix ``top`` command.
+
Flash partitions
----------------
diff --git a/docs/library/framebuf.rst b/docs/library/framebuf.rst
index f22a3613bd..e2a231207d 100644
--- a/docs/library/framebuf.rst
+++ b/docs/library/framebuf.rst
@@ -137,6 +137,18 @@ Other methods
is compared to the value from *palette*, not to the value directly from
*fbuf*.)
+ *fbuf* can be another FrameBuffer instance, or a tuple or list of the form::
+
+ (buffer, width, height, format)
+
+ or::
+
+ (buffer, width, height, format, stride)
+
+ This matches the signature of the FrameBuffer constructor, and the elements
+ of the tuple/list are the same as the arguments to the constructor except that
+ the *buffer* here can be read-only.
+
The *palette* argument enables blitting between FrameBuffers with differing
formats. Typical usage is to render a monochrome or grayscale glyph/icon to
a color display. The *palette* is a FrameBuffer instance whose format is
diff --git a/docs/library/machine.PWM.rst b/docs/library/machine.PWM.rst
index 5f592b8dff..c2b606affd 100644
--- a/docs/library/machine.PWM.rst
+++ b/docs/library/machine.PWM.rst
@@ -11,20 +11,20 @@ Example usage::
from machine import PWM
pwm = PWM(pin, freq=50, duty_u16=8192) # create a PWM object on a pin
- # and set freq and duty
- pwm.duty_u16(32768) # set duty to 50%
+ # and set freq 50 Hz and duty 12.5%
+ pwm.duty_u16(32768) # set duty to 50%
# reinitialise with a period of 200us, duty of 5us
pwm.init(freq=5000, duty_ns=5000)
- pwm.duty_ns(3000) # set pulse width to 3us
+ pwm.duty_ns(3000) # set pulse width to 3us
pwm.deinit()
Constructors
------------
-.. class:: PWM(dest, *, freq, duty_u16, duty_ns, invert)
+.. class:: PWM(dest, *, freq, duty_u16, duty_ns, invert=False)
Construct and return a new PWM object using the following parameters:
@@ -40,7 +40,7 @@ Constructors
Setting *freq* may affect other PWM objects if the objects share the same
underlying PWM generator (this is hardware specific).
Only one of *duty_u16* and *duty_ns* should be specified at a time.
- *invert* is not available at all ports.
+ *invert* is available only on the esp32, mimxrt, nrf, rp2, samd and zephyr ports.
Methods
-------
@@ -116,10 +116,10 @@ Limitations of PWM
resolution of 8 bit, not 16-bit as may be expected. In this case, the lowest
8 bits of *duty_u16* are insignificant. So::
- pwm=PWM(Pin(13), freq=300_000, duty_u16=2**16//2)
+ pwm=PWM(Pin(13), freq=300_000, duty_u16=65536//2)
and::
- pwm=PWM(Pin(13), freq=300_000, duty_u16=2**16//2 + 255)
+ pwm=PWM(Pin(13), freq=300_000, duty_u16=65536//2 + 255)
will generate PWM with the same 50% duty cycle.
diff --git a/docs/library/machine.UART.rst b/docs/library/machine.UART.rst
index 5be79cccce..fbad3fc592 100644
--- a/docs/library/machine.UART.rst
+++ b/docs/library/machine.UART.rst
@@ -224,7 +224,8 @@ Methods
.. note::
- - The ESP32 port does not support the option hard=True.
+ - The ESP32 port does not support the option hard=True. It uses Timer(0)
+ for UART.IRQ_RXIDLE, so this timer cannot be used for other means.
- The rp2 port's UART.IRQ_TXIDLE is only triggered when the message
is longer than 5 characters and the trigger happens when still 5 characters
diff --git a/docs/library/socket.rst b/docs/library/socket.rst
index 944e7e631a..38e0aab704 100644
--- a/docs/library/socket.rst
+++ b/docs/library/socket.rst
@@ -227,22 +227,28 @@ Methods
has the same "no short writes" policy for blocking sockets, and will return
number of bytes sent on non-blocking sockets.
-.. method:: socket.recv(bufsize)
+.. method:: socket.recv(bufsize, [flags])
Receive data from the socket. The return value is a bytes object representing the data
received. The maximum amount of data to be received at once is specified by bufsize.
+ Most ports support the optional *flags* argument. Available *flags* are defined as constants
+ in the socket module and have the same meaning as in CPython. ``MSG_PEEK`` and ``MSG_DONTWAIT``
+ are supported on all ports which accept the *flags* argument.
+
.. method:: socket.sendto(bytes, address)
Send data to the socket. The socket should not be connected to a remote socket, since the
destination socket is specified by *address*.
-.. method:: socket.recvfrom(bufsize)
+.. method:: socket.recvfrom(bufsize, [flags])
Receive data from the socket. The return value is a pair *(bytes, address)* where *bytes* is a
bytes object representing the data received and *address* is the address of the socket sending
the data.
+ See the `recv` function for an explanation of the optional *flags* argument.
+
.. method:: socket.setsockopt(level, optname, value)
Set the value of the given socket option. The needed symbolic constants are defined in the
diff --git a/docs/library/time.rst b/docs/library/time.rst
index 8c1c1d4d6f..b53bb133ec 100644
--- a/docs/library/time.rst
+++ b/docs/library/time.rst
@@ -9,9 +9,10 @@
The ``time`` module provides functions for getting the current time and date,
measuring time intervals, and for delays.
-**Time Epoch**: Unix port uses standard for POSIX systems epoch of
-1970-01-01 00:00:00 UTC. However, some embedded ports use epoch of
-2000-01-01 00:00:00 UTC. Epoch year may be determined with ``gmtime(0)[0]``.
+**Time Epoch**: The unix, windows, webassembly, alif, mimxrt and rp2 ports
+use the standard for POSIX systems epoch of 1970-01-01 00:00:00 UTC.
+The other embedded ports use an epoch of 2000-01-01 00:00:00 UTC.
+Epoch year may be determined with ``gmtime(0)[0]``.
**Maintaining actual calendar date/time**: This requires a
Real Time Clock (RTC). On systems with underlying OS (including some
@@ -57,11 +58,11 @@ Functions
* weekday is 0-6 for Mon-Sun
* yearday is 1-366
-.. function:: mktime()
+.. function:: mktime(date_time_tuple)
This is inverse function of localtime. It's argument is a full 8-tuple
which expresses a time as per localtime. It returns an integer which is
- the number of seconds since Jan 1, 2000.
+ the number of seconds since the time epoch.
.. function:: sleep(seconds)
diff --git a/docs/library/wm8960.rst b/docs/library/wm8960.rst
index 5abfb6a8a0..4115d20758 100644
--- a/docs/library/wm8960.rst
+++ b/docs/library/wm8960.rst
@@ -358,13 +358,13 @@ Run WM8960 on a MIMXRT10xx_DEV board in secondary mode (default)::
sysclk_source=wm8960.SYSCLK_MCLK)
-Record with a Sparkfun WM8960 breakout board with Teensy in secondary mode (default)::
+Record with a SparkFun WM8960 breakout board with Teensy in secondary mode (default)::
# Micro_python WM8960 Codec driver
#
# The breakout board uses a fixed 24MHz MCLK. Therefore the internal
# PLL must be used as sysclk, which is the master audio clock.
- # The Sparkfun board has the WS pins for RX and TX connected on the
+ # The SparkFun board has the WS pins for RX and TX connected on the
# board. Therefore adc_sync must be set to sync_adc, to configure
# it's ADCLRC pin as input.
#
@@ -379,11 +379,11 @@ Record with a Sparkfun WM8960 breakout board with Teensy in secondary mode (defa
right_input=wm8960.INPUT_CLOSED)
-Play with a Sparkfun WM8960 breakout board with Teensy in secondary mode (default)::
+Play with a SparkFun WM8960 breakout board with Teensy in secondary mode (default)::
# The breakout board uses a fixed 24MHz MCLK. Therefore the internal
# PLL must be used as sysclk, which is the master audio clock.
- # The Sparkfun board has the WS pins for RX and TX connected on the
+ # The SparkFun board has the WS pins for RX and TX connected on the
# board. Therefore adc_sync must be set to sync_adc, to configure
# it's ADCLRC pin as input.
diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst
index 32ca5c246a..bee008c637 100644
--- a/docs/reference/mpremote.rst
+++ b/docs/reference/mpremote.rst
@@ -234,6 +234,7 @@ The full list of supported commands are:
- ``rmdir <dirs...>`` to remove directories on the device
- ``touch <file..>`` to create the files (if they don't already exist)
- ``sha256sum <file..>`` to calculate the SHA256 sum of files
+ - ``tree [-vsh] <dirs...>`` to print a tree of the given directories
The ``cp`` command uses a convention where a leading ``:`` represents a remote
path. Without a leading ``:`` means a local path. This is based on the
@@ -264,6 +265,13 @@ The full list of supported commands are:
There is no supported way to undelete files removed by ``mpremote rm -r :``.
Please use with caution.
+ The ``tree`` command will print a tree of the given directories.
+ Using the ``--size/-s`` option will print the size of each file, or use
+ ``--human/-h`` to use a more human readable format.
+ Note: Directory size is only printed when a non-zero size is reported by the device's filesystem.
+ The ``-v`` option can be used to include the name of the serial device in
+ the output.
+
All other commands implicitly assume the path is a remote path, but the ``:``
can be optionally used for clarity.
diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst
index 23071d7721..ec31442990 100644
--- a/docs/rp2/quickref.rst
+++ b/docs/rp2/quickref.rst
@@ -135,6 +135,12 @@ Use the :mod:`machine.Timer` class::
tim = Timer(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print(1))
tim.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(2))
+By default, timer callbacks run as soft IRQs so they can allocate but
+are prone to GC jitter and delays. Pass ``hard=True`` to the ``Timer()``
+constructor or ``init()`` method to run the callback in hard-IRQ context
+instead. This reduces delay and jitter, but see :ref:`isr_rules` for the
+restrictions that apply to hard-IRQ handlers.
+
.. _rp2_Pins_and_GPIO:
diff --git a/docs/samd/pinout.rst b/docs/samd/pinout.rst
index 3945e2bf2f..5731a8fa76 100644
--- a/docs/samd/pinout.rst
+++ b/docs/samd/pinout.rst
@@ -650,6 +650,124 @@ Adafruit ItsyBitsy M0 Express :ref:`samd21_pinout_table`.
The board does not provide access to UART, I2C, SPI or DAC.
+SparkFun Redboard Turbo assignment table
+----------------------------------------
+
+=== ==== ============ ==== ==== ====== ====== ====== ======
+Pin GPIO Pin name IRQ ADC Serial Serial TCC/TC TCC/TC
+=== ==== ============ ==== ==== ====== ====== ====== ======
+ 2 PA02 A0 2 0 - - - -
+ 40 PB08 A1 8 2 - 4/0 4/0 -
+ 41 PB09 A2 9 3 - 4/1 4/1 -
+ 4 PA04 A3 4 4 - 0/0 0/0 -
+ 5 PA05 A4 5 5 - 0/1 0/1 -
+ 34 PB02 A5 2 10 - 5/0 6/0 -
+ 11 PA11 D0 11 19 0/3 2/3 1/1 0/3
+ 10 PA10 D1 10 18 0/2 2/2 1/0 0/2
+ 14 PA14 D2 14 - 2/2 4/2 3/0 0/4
+ 9 PA09 D3 9 17 0/1 2/1 0/1 1/3
+ 8 PA08 D4 - 16 0/0 2/0 0/0 1/2
+ 15 PA15 D5 15 - 2/3 4/3 3/1 0/5
+ 20 PA20 D6 4 - 5/2 3/2 7/0 0/4
+ 21 PA21 D7 5 - 5/3 3/3 7/1 0/7
+ 6 PA06 D8 6 6 - 0/2 1/0 -
+ 7 PA07 D9 7 7 - 0/3 1/1 -
+ 11 PA11 RX 11 19 0/3 2/3 1/1 0/3
+ 10 PA10 TX 10 18 0/2 2/2 1/0 0/2
+ 3 PA03 AREF 3 1 - - - -
+ 18 PA18 D10 2 - 1/2 3/2 3/0 0/2
+ 16 PA16 D11 0 - 1/0 3/0 2/0 0/6
+ 19 PA19 D12 3 - 1/3 3/3 3/1 0/3
+ 17 PA17 D13 1 - 1/1 3/1 2/1 0/7
+ 13 PA13 FLASH_CS 13 - 2/1 4/1 2/0 0/7
+ 35 PB03 FLASH_MISO 3 11 - 5/1 6/1 -
+ 54 PB22 FLASH_MOSI 6 - - 5/2 7/0 -
+ 55 PB23 FLASH_SCK 7 - - 5/3 7/1 -
+ 31 PA31 LED_RX 11 - - 1/3 1/1 -
+ 27 PA27 LED_TX 15 - - - - -
+ 12 PA12 MISO 12 - 2/0 4/0 2/0 0/6
+ 42 PB10 MOSI 10 - - 4/2 5/0 0/4
+ 30 PA30 NEOPIXEL 10 - - 1/2 1/0 -
+ 43 PB11 SCK 11 - - 4/3 5/1 0/5
+ 23 PA23 SCL 7 - 3/1 5/1 4/1 0/5
+ 22 PA22 SDA 6 - 3/0 5/0 4/0 0/4
+ 30 PA30 SWCLK 10 - - 1/2 1/0 -
+ 31 PA31 SWDIO 11 - - 1/3 1/1 -
+ 24 PA24 USB_DM 12 - 3/2 5/2 5/0 1/2
+ 25 PA25 USB_DP 13 - 3/3 5/3 5/1 1/3
+ 0 PA00 0 - - 1/0 2/0 -
+ 1 PA01 1 - - 1/1 2/1 -
+ 28 PA28 8 - - - - -
+=== ==== ============ ==== ==== ====== ====== ====== ======
+
+For the definition of the table columns see the explanation at the table for
+Adafruit ItsyBitsy M0 Express :ref:`samd21_pinout_table`.
+
+The default devices at the board are:
+
+- UART 0 at pins PA11/PA10, labelled RX/TX
+- I2C 3 at pins PA22/PA23, labelled SDA/SCL
+- SPI 4 at pins PB10/PA12/PB11, labelled MISO, MOSI and SCK
+- DAC output on pin PA02, labelled A0
+
+
+SparkFun SAMD21 Dev Breakout assignment table
+---------------------------------------------
+
+=== ==== ============ ==== ==== ====== ====== ====== ======
+Pin GPIO Pin name IRQ ADC Serial Serial TCC/TC TCC/TC
+=== ==== ============ ==== ==== ====== ====== ====== ======
+ 2 PA02 A0 2 0 - - - -
+ 40 PB08 A1 8 2 - 4/0 4/0 -
+ 41 PB09 A2 9 3 - 4/1 4/1 -
+ 4 PA04 A3 4 4 - 0/0 0/0 -
+ 5 PA05 A4 5 5 - 0/1 0/1 -
+ 34 PB02 A5 2 10 - 5/0 6/0 -
+ 11 PA11 D0 11 19 0/3 2/3 1/1 0/3
+ 10 PA10 D1 10 18 0/2 2/2 1/0 0/2
+ 14 PA14 D2 14 - 2/2 4/2 3/0 0/4
+ 9 PA09 D3 9 17 0/1 2/1 0/1 1/3
+ 8 PA08 D4 - 16 0/0 2/0 0/0 1/2
+ 15 PA15 D5 15 - 2/3 4/3 3/1 0/5
+ 20 PA20 D6 4 - 5/2 3/2 7/0 0/4
+ 21 PA21 D7 5 - 5/3 3/3 7/1 0/7
+ 6 PA06 D8 6 6 - 0/2 1/0 -
+ 7 PA07 D9 7 7 - 0/3 1/1 -
+ 11 PA11 RX 11 19 0/3 2/3 1/1 0/3
+ 10 PA10 TX 10 18 0/2 2/2 1/0 0/2
+ 3 PA03 AREF 3 1 - - - -
+ 18 PA18 D10 2 - 1/2 3/2 3/0 0/2
+ 16 PA16 D11 0 - 1/0 3/0 2/0 0/6
+ 19 PA19 D12 3 - 1/3 3/3 3/1 0/3
+ 17 PA17 D13 1 - 1/1 3/1 2/1 0/7
+ 54 PB22 D30 6 - - 5/2 7/0 -
+ 55 PB23 D31 7 - - 5/3 7/1 -
+ 13 PA13 D38 13 - 2/1 4/1 2/0 0/7
+ 35 PB03 LED_RX 3 11 - 5/1 6/1 -
+ 27 PA27 LED_TX 15 - - - - -
+ 12 PA12 MISO 12 - 2/0 4/0 2/0 0/6
+ 42 PB10 MOSI 10 - - 4/2 5/0 0/4
+ 43 PB11 SCK 11 - - 4/3 5/1 0/5
+ 23 PA23 SCL 7 - 3/1 5/1 4/1 0/5
+ 22 PA22 SDA 6 - 3/0 5/0 4/0 0/4
+ 30 PA30 SWCLK 10 - - 1/2 1/0 -
+ 31 PA31 SWDIO 11 - - 1/3 1/1 -
+ 24 PA24 USB_DM 12 - 3/2 5/2 5/0 1/2
+ 25 PA25 USB_DP 13 - 3/3 5/3 5/1 1/3
+ 0 PA00 0 - - 1/0 2/0 -
+ 1 PA01 1 - - 1/1 2/1 -
+ 28 PA28 8 - - - - -
+=== ==== ============ ==== ==== ====== ====== ====== ======
+
+For the definition of the table columns see the explanation at the table for
+Adafruit ItsyBitsy M0 Express :ref:`samd21_pinout_table`.
+
+The default devices at the board are:
+
+- UART 0 at pins PA11/PA10, labelled RX/TX
+- I2C 3 at pins PA22/PA23, labelled SDA/SCL
+- SPI 4 at pins PB10/PA12/PB11, labelled MISO, MOSI and SCK
+- DAC output on pin PA02, labelled A0
SAMD21 Xplained PRO pin assignment table
----------------------------------------
@@ -892,7 +1010,7 @@ Default pin assignments:
There seems to be no default pin assignment for this board.
-Sparkfun SAMD51 Thing Plus pin assignment table
+SparkFun SAMD51 Thing Plus pin assignment table
------------------------------------------------
=== ==== ============ ==== ==== ==== ====== ====== ===== ===== =====
diff --git a/drivers/bus/qspi.h b/drivers/bus/qspi.h
index 32b2890e3f..05d9dd473c 100644
--- a/drivers/bus/qspi.h
+++ b/drivers/bus/qspi.h
@@ -46,6 +46,7 @@ typedef struct _mp_qspi_proto_t {
int (*write_cmd_addr_data)(void *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src);
int (*read_cmd)(void *self, uint8_t cmd, size_t len, uint32_t *dest);
int (*read_cmd_qaddr_qdata)(void *self, uint8_t cmd, uint32_t addr, uint8_t num_dummy, size_t len, uint8_t *dest);
+ int (*direct_read)(void *self, uint32_t addr, size_t len, uint8_t *dest); // can be NULL if direct read not supported
} mp_qspi_proto_t;
typedef struct _mp_soft_qspi_obj_t {
diff --git a/drivers/esp-hosted/esp_hosted_bthci_uart.c b/drivers/esp-hosted/esp_hosted_bthci_uart.c
index 003054460d..069509edde 100644
--- a/drivers/esp-hosted/esp_hosted_bthci_uart.c
+++ b/drivers/esp-hosted/esp_hosted_bthci_uart.c
@@ -72,7 +72,7 @@ int esp_hosted_hci_cmd(int ogf, int ocf, size_t param_len, const uint8_t *param_
// Receive HCI event packet, initially reading 3 bytes (HCI Event, Event code, Plen).
for (mp_uint_t start = mp_hal_ticks_ms(), size = 3, i = 0; i < size;) {
while (!mp_bluetooth_hci_uart_any()) {
- MICROPY_EVENT_POLL_HOOK
+ mp_event_wait_ms(1);
// Timeout.
if ((mp_hal_ticks_ms() - start) > HCI_COMMAND_TIMEOUT) {
error_printf("timeout waiting for HCI packet\n");
@@ -126,7 +126,7 @@ int mp_bluetooth_hci_controller_init(void) {
if (mp_bluetooth_hci_uart_any()) {
mp_bluetooth_hci_uart_readchar();
}
- MICROPY_EVENT_POLL_HOOK
+ mp_event_wait_ms(1);
}
#ifdef MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY
diff --git a/drivers/esp-hosted/esp_hosted_wifi.c b/drivers/esp-hosted/esp_hosted_wifi.c
index d1b6333aa0..763b04db37 100644
--- a/drivers/esp-hosted/esp_hosted_wifi.c
+++ b/drivers/esp-hosted/esp_hosted_wifi.c
@@ -243,7 +243,7 @@ static int esp_hosted_request(CtrlMsgId msg_id, void *ctrl_payload) {
static CtrlMsg *esp_hosted_response(CtrlMsgId msg_id, uint32_t timeout) {
CtrlMsg *ctrl_msg = NULL;
- for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_hal_delay_ms(10)) {
+ for (mp_uint_t start = mp_hal_ticks_ms(); ; mp_event_wait_ms(10)) {
if (!esp_hosted_stack_empty(&esp_state.stack)) {
ctrl_msg = esp_hosted_stack_pop(&esp_state.stack, true);
if (ctrl_msg->msg_id == msg_id) {
@@ -264,8 +264,6 @@ static CtrlMsg *esp_hosted_response(CtrlMsgId msg_id, uint32_t timeout) {
if ((mp_hal_ticks_ms() - start) >= timeout) {
return NULL;
}
-
- MICROPY_EVENT_POLL_HOOK
}
// If message type is a response, check the response struct's return value.
diff --git a/drivers/memory/spiflash.c b/drivers/memory/spiflash.c
index 1ae0bbbc67..7cd1d18a3f 100644
--- a/drivers/memory/spiflash.c
+++ b/drivers/memory/spiflash.c
@@ -197,6 +197,10 @@ void mp_spiflash_init(mp_spiflash_t *self) {
} else {
uint8_t num_dummy = MICROPY_HW_SPIFLASH_QREAD_NUM_DUMMY(self);
self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT, num_dummy);
+ if (self->config->bus.u_qspi.proto->direct_read != NULL) {
+ // A bus with a custom read function should not have any further initialisation done.
+ return;
+ }
}
mp_spiflash_acquire_bus(self);
@@ -318,6 +322,10 @@ int mp_spiflash_read(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *de
if (len == 0) {
return 0;
}
+ const mp_spiflash_config_t *c = self->config;
+ if (c->bus_kind == MP_SPIFLASH_BUS_QSPI && c->bus.u_qspi.proto->direct_read != NULL) {
+ return c->bus.u_qspi.proto->direct_read(c->bus.u_qspi.data, addr, len, dest);
+ }
mp_spiflash_acquire_bus(self);
int ret = mp_spiflash_read_data(self, addr, len, dest);
mp_spiflash_release_bus(self);
diff --git a/examples/natmod/btree/Makefile b/examples/natmod/btree/Makefile
index ff130d61b3..6273ccc657 100644
--- a/examples/natmod/btree/Makefile
+++ b/examples/natmod/btree/Makefile
@@ -8,7 +8,7 @@ MOD = btree_$(ARCH)
SRC = btree_c.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
-ARCH = x64
+ARCH ?= x64
BTREE_DIR = $(MPY_DIR)/lib/berkeley-db-1.xx
BERKELEY_DB_CONFIG_FILE ?= \"extmod/berkeley-db/berkeley_db_config_port.h\"
@@ -32,6 +32,10 @@ SRC += $(addprefix $(realpath $(BTREE_DIR))/,\
mpool/mpool.c \
)
+ifeq ($(ARCH),xtensa)
+MPY_EXTERN_SYM_FILE=$(MPY_DIR)/ports/esp8266/boards/eagle.rom.addr.v6.ld
+endif
+
include $(MPY_DIR)/py/dynruntime.mk
# btree needs gnu99 defined
diff --git a/examples/natmod/deflate/Makefile b/examples/natmod/deflate/Makefile
index 504130d572..1f63de20d2 100644
--- a/examples/natmod/deflate/Makefile
+++ b/examples/natmod/deflate/Makefile
@@ -8,6 +8,12 @@ MOD = deflate_$(ARCH)
SRC = deflate.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
-ARCH = x64
+ARCH ?= x64
+
+ifeq ($(ARCH),xtensa)
+# Link with libm.a and libgcc.a from the toolchain
+LINK_RUNTIME = 1
+MPY_EXTERN_SYM_FILE=$(MPY_DIR)/ports/esp8266/boards/eagle.rom.addr.v6.ld
+endif
include $(MPY_DIR)/py/dynruntime.mk
diff --git a/examples/natmod/framebuf/Makefile b/examples/natmod/framebuf/Makefile
index 2e2b815975..cb821736e7 100644
--- a/examples/natmod/framebuf/Makefile
+++ b/examples/natmod/framebuf/Makefile
@@ -8,6 +8,10 @@ MOD = framebuf_$(ARCH)
SRC = framebuf.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
-ARCH = x64
+ARCH ?= x64
+
+ifeq ($(ARCH),xtensa)
+MPY_EXTERN_SYM_FILE=$(MPY_DIR)/ports/esp8266/boards/eagle.rom.addr.v6.ld
+endif
include $(MPY_DIR)/py/dynruntime.mk
diff --git a/examples/natmod/framebuf/framebuf.c b/examples/natmod/framebuf/framebuf.c
index 1ba702e33d..5fd7c6be3a 100644
--- a/examples/natmod/framebuf/framebuf.c
+++ b/examples/natmod/framebuf/framebuf.c
@@ -4,6 +4,9 @@
#include "py/dynruntime.h"
#if !defined(__linux__)
+void *memcpy(void *dst, const void *src, size_t n) {
+ return mp_fun_table.memmove_(dst, src, n);
+}
void *memset(void *s, int c, size_t n) {
return mp_fun_table.memset_(s, c, n);
}
diff --git a/examples/natmod/random/Makefile b/examples/natmod/random/Makefile
index 8abdb66dc8..bffbb32d60 100644
--- a/examples/natmod/random/Makefile
+++ b/examples/natmod/random/Makefile
@@ -8,6 +8,15 @@ MOD = random_$(ARCH)
SRC = random.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
-ARCH = x64
+ARCH ?= x64
+
+ifeq ($(ARCH),xtensa)
+MPY_EXTERN_SYM_FILE=$(MPY_DIR)/ports/esp8266/boards/eagle.rom.addr.v6.ld
+endif
+
+ifeq ($(ARCH),$(filter $(ARCH),armv6m armv7m))
+# Link with libm.a for soft-float helper functions
+LINK_RUNTIME = 1
+endif
include $(MPY_DIR)/py/dynruntime.mk
diff --git a/extmod/asyncio/core.py b/extmod/asyncio/core.py
index 8aad234514..5d46b4b80e 100644
--- a/extmod/asyncio/core.py
+++ b/extmod/asyncio/core.py
@@ -163,9 +163,16 @@ def run_until_complete(main_task=None):
# A task waiting on _task_queue; "ph_key" is time to schedule task at
dt = max(0, ticks_diff(t.ph_key, ticks()))
elif not _io_queue.map:
- # No tasks can be woken so finished running
+ # No tasks can be woken
cur_task = None
- return
+ if not main_task or not main_task.state:
+ # no main_task, or main_task is done so finished running
+ return
+ # At this point, there is theoretically nothing that could wake the
+ # scheduler, but it is not allowed to exit either. We keep the code
+ # running so that a hypothetical debugger (or other such meta-process)
+ # can get a view of what is happening and possibly abort.
+ dt = 3
# print('(poll {})'.format(dt), len(_io_queue.map))
_io_queue.wait_io_event(dt)
@@ -187,31 +194,33 @@ def run_until_complete(main_task=None):
except excs_all as er:
# Check the task is not on any event queue
assert t.data is None
- # This task is done, check if it's the main task and then loop should stop
- if t is main_task:
+ # If it's the main task, it is considered as awaited by the caller
+ awaited = t is main_task
+ if awaited:
cur_task = None
- if isinstance(er, StopIteration):
- return er.value
- raise er
+ if not isinstance(er, StopIteration):
+ t.state = False
+ raise er
+ if t.state is None:
+ t.state = False
if t.state:
# Task was running but is now finished.
- waiting = False
if t.state is True:
# "None" indicates that the task is complete and not await'ed on (yet).
- t.state = None
+ t.state = False if awaited else None
elif callable(t.state):
# The task has a callback registered to be called on completion.
t.state(t, er)
t.state = False
- waiting = True
+ awaited = True
else:
# Schedule any other tasks waiting on the completion of this task.
while t.state.peek():
_task_queue.push(t.state.pop())
- waiting = True
+ awaited = True
# "False" indicates that the task is complete and has been await'ed on.
t.state = False
- if not waiting and not isinstance(er, excs_stop):
+ if not awaited and not isinstance(er, excs_stop):
# An exception ended this detached task, so queue it for later
# execution to handle the uncaught exception if no other task retrieves
# the exception in the meantime (this is handled by Task.throw).
@@ -229,6 +238,9 @@ def run_until_complete(main_task=None):
_exc_context["exception"] = exc
_exc_context["future"] = t
Loop.call_exception_handler(_exc_context)
+ # If it's the main task then the loop should stop
+ if t is main_task:
+ return er.value
# Create a new task from a coroutine and run it until it finishes
diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c
index b29970842c..7694a1874f 100644
--- a/extmod/btstack/modbluetooth_btstack.c
+++ b/extmod/btstack/modbluetooth_btstack.c
@@ -705,12 +705,12 @@ int mp_bluetooth_init(void) {
return 0;
}
-void mp_bluetooth_deinit(void) {
+int mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit\n");
// Nothing to do if not initialised.
if (!MP_STATE_PORT(bluetooth_btstack_root_pointers)) {
- return;
+ return 0;
}
mp_bluetooth_gap_advertise_stop();
@@ -737,6 +737,9 @@ void mp_bluetooth_deinit(void) {
deinit_stack();
DEBUG_printf("mp_bluetooth_deinit: complete\n");
+
+ bool timeout = mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT;
+ return timeout ? MP_ETIMEDOUT : 0;
}
bool mp_bluetooth_is_active(void) {
diff --git a/extmod/cyw43_config_common.h b/extmod/cyw43_config_common.h
new file mode 100644
index 0000000000..595af37d71
--- /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 b2a0f490b6..997dd3ba98 100644
--- a/extmod/extmod.mk
+++ b/extmod/extmod.mk
@@ -206,7 +206,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 \
diff --git a/extmod/littlefs-include/lfs2_defines.h b/extmod/littlefs-include/lfs2_defines.h
new file mode 100644
index 0000000000..4ae566f508
--- /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/machine_pulse.c b/extmod/machine_pulse.c
index 85dba86d9b..b78a63f182 100644
--- a/extmod/machine_pulse.c
+++ b/extmod/machine_pulse.c
@@ -30,20 +30,35 @@
#if MICROPY_PY_MACHINE_PULSE
-MP_WEAK mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) {
+mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) {
+ mp_uint_t nchanges = 2;
mp_uint_t start = mp_hal_ticks_us();
- while (mp_hal_pin_read(pin) != pulse_level) {
- if ((mp_uint_t)(mp_hal_ticks_us() - start) >= timeout_us) {
- return (mp_uint_t)-2;
- }
- }
- start = mp_hal_ticks_us();
- while (mp_hal_pin_read(pin) == pulse_level) {
- if ((mp_uint_t)(mp_hal_ticks_us() - start) >= timeout_us) {
- return (mp_uint_t)-1;
+ for (;;) {
+ // Sample ticks and pin as close together as possible, and always in the same
+ // order each time around the loop. This gives the most accurate measurement.
+ mp_uint_t t = mp_hal_ticks_us();
+ int pin_value = mp_hal_pin_read(pin);
+
+ if (pin_value == pulse_level) {
+ // Pin is at desired value. Flip desired value and see if we are done.
+ pulse_level = 1 - pulse_level;
+ if (--nchanges == 0) {
+ return t - start;
+ }
+ start = t;
+ } else {
+ // Pin hasn't changed yet, check for timeout.
+ mp_uint_t dt = t - start;
+ if (dt >= timeout_us) {
+ return -nchanges;
+ }
+
+ // Allow a port to perform background task processing if needed.
+ #ifdef MICROPY_PY_MACHINE_TIME_PULSE_US_HOOK
+ MICROPY_PY_MACHINE_TIME_PULSE_US_HOOK(dt);
+ #endif
}
}
- return mp_hal_ticks_us() - start;
}
static mp_obj_t machine_time_pulse_us_(size_t n_args, const mp_obj_t *args) {
diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c
index b95c42a4ee..ffa407809a 100644
--- a/extmod/modbluetooth.c
+++ b/extmod/modbluetooth.c
@@ -290,12 +290,13 @@ static mp_obj_t bluetooth_ble_make_new(const mp_obj_type_t *type, size_t n_args,
static mp_obj_t bluetooth_ble_active(size_t n_args, const mp_obj_t *args) {
if (n_args == 2) {
// Boolean enable/disable argument supplied, set current state.
+ int err;
if (mp_obj_is_true(args[1])) {
- int err = mp_bluetooth_init();
- bluetooth_handle_errno(err);
+ err = mp_bluetooth_init();
} else {
- mp_bluetooth_deinit();
+ err = mp_bluetooth_deinit();
}
+ bluetooth_handle_errno(err);
}
// Return current state.
return mp_obj_new_bool(mp_bluetooth_is_active());
diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h
index 6a087c8e25..24f063fa5d 100644
--- a/extmod/modbluetooth.h
+++ b/extmod/modbluetooth.h
@@ -295,7 +295,7 @@ extern const mp_obj_type_t mp_type_bluetooth_uuid;
int mp_bluetooth_init(void);
// Disables the Bluetooth stack. Is a no-op when not enabled.
-void mp_bluetooth_deinit(void);
+int mp_bluetooth_deinit(void);
// Returns true when the Bluetooth stack is active.
bool mp_bluetooth_is_active(void);
diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c
index b718a66cc6..5c4b9abf0c 100644
--- a/extmod/modframebuf.c
+++ b/extmod/modframebuf.c
@@ -270,8 +270,7 @@ static void fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, u
formats[fb->format].fill_rect(fb, x, y, xend - x, yend - y, col);
}
-static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) {
- mp_arg_check_num(n_args, n_kw, 4, 5, false);
+static mp_obj_t framebuf_make_new_helper(size_t n_args, const mp_obj_t *args_in, unsigned int buf_flags, mp_obj_framebuf_t *o) {
mp_int_t width = mp_obj_get_int(args_in[1]);
mp_int_t height = mp_obj_get_int(args_in[2]);
@@ -318,13 +317,15 @@ static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size
}
mp_buffer_info_t bufinfo;
- mp_get_buffer_raise(args_in[0], &bufinfo, MP_BUFFER_WRITE);
+ mp_get_buffer_raise(args_in[0], &bufinfo, buf_flags);
if ((strides_required * stride + (height_required - strides_required) * width_required) * bpp / 8 > bufinfo.len) {
mp_raise_ValueError(NULL);
}
- mp_obj_framebuf_t *o = mp_obj_malloc(mp_obj_framebuf_t, type);
+ if (o == NULL) {
+ o = mp_obj_malloc(mp_obj_framebuf_t, (const mp_obj_type_t *)&mp_type_framebuf);
+ }
o->buf_obj = args_in[0];
o->buf = bufinfo.buf;
o->width = width;
@@ -335,6 +336,11 @@ static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size
return MP_OBJ_FROM_PTR(o);
}
+static mp_obj_t framebuf_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args_in) {
+ mp_arg_check_num(n_args, n_kw, 4, 5, false);
+ return framebuf_make_new_helper(n_args, args_in, MP_BUFFER_WRITE, NULL);
+}
+
static void framebuf_args(const mp_obj_t *args_in, mp_int_t *args_out, int n) {
for (int i = 0; i < n; ++i) {
args_out[i] = mp_obj_get_int(args_in[i + 1]);
@@ -707,13 +713,27 @@ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(framebuf_poly_obj, 5, 6, framebuf_pol
#endif // MICROPY_PY_ARRAY
+static void get_readonly_framebuffer(mp_obj_t arg, mp_obj_framebuf_t *rofb) {
+ mp_obj_t fb = mp_obj_cast_to_native_base(arg, MP_OBJ_FROM_PTR(&mp_type_framebuf));
+ if (fb != MP_OBJ_NULL) {
+ *rofb = *(mp_obj_framebuf_t *)MP_OBJ_TO_PTR(fb);
+ } else {
+ // A tuple/list of the form: (buffer, width, height, format[, stride]).
+ size_t len;
+ mp_obj_t *items;
+ mp_obj_get_array(arg, &len, &items);
+ if (len < 4 || len > 5) {
+ mp_raise_ValueError(NULL);
+ }
+ framebuf_make_new_helper(len, items, MP_BUFFER_READ, rofb);
+ }
+}
+
static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
mp_obj_framebuf_t *self = MP_OBJ_TO_PTR(args_in[0]);
- mp_obj_t source_in = mp_obj_cast_to_native_base(args_in[1], MP_OBJ_FROM_PTR(&mp_type_framebuf));
- if (source_in == MP_OBJ_NULL) {
- mp_raise_TypeError(NULL);
- }
- mp_obj_framebuf_t *source = MP_OBJ_TO_PTR(source_in);
+
+ mp_obj_framebuf_t source;
+ get_readonly_framebuffer(args_in[1], &source);
mp_int_t x = mp_obj_get_int(args_in[2]);
mp_int_t y = mp_obj_get_int(args_in[3]);
@@ -721,16 +741,17 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
if (n_args > 4) {
key = mp_obj_get_int(args_in[4]);
}
- mp_obj_framebuf_t *palette = NULL;
+ mp_obj_framebuf_t palette;
+ palette.buf = NULL;
if (n_args > 5 && args_in[5] != mp_const_none) {
- palette = MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(args_in[5], MP_OBJ_FROM_PTR(&mp_type_framebuf)));
+ get_readonly_framebuffer(args_in[5], &palette);
}
if (
(x >= self->width) ||
(y >= self->height) ||
- (-x >= source->width) ||
- (-y >= source->height)
+ (-x >= source.width) ||
+ (-y >= source.height)
) {
// Out of bounds, no-op.
return mp_const_none;
@@ -741,15 +762,15 @@ static mp_obj_t framebuf_blit(size_t n_args, const mp_obj_t *args_in) {
int y0 = MAX(0, y);
int x1 = MAX(0, -x);
int y1 = MAX(0, -y);
- int x0end = MIN(self->width, x + source->width);
- int y0end = MIN(self->height, y + source->height);
+ int x0end = MIN(self->width, x + source.width);
+ int y0end = MIN(self->height, y + source.height);
for (; y0 < y0end; ++y0) {
int cx1 = x1;
for (int cx0 = x0; cx0 < x0end; ++cx0) {
- uint32_t col = getpixel(source, cx1, y1);
- if (palette) {
- col = getpixel(palette, col, 0);
+ uint32_t col = getpixel(&source, cx1, y1);
+ if (palette.buf) {
+ col = getpixel(&palette, col, 0);
}
if (col != (uint32_t)key) {
setpixel(self, cx0, y0, col);
diff --git a/extmod/modjson.c b/extmod/modjson.c
index e655a02bc0..11aedd1983 100644
--- a/extmod/modjson.c
+++ b/extmod/modjson.c
@@ -160,7 +160,8 @@ static mp_obj_t mod_json_load(mp_obj_t stream_obj) {
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/modlwip.c b/extmod/modlwip.c
index f109e0029b..b53559ed8c 100644
--- a/extmod/modlwip.c
+++ b/extmod/modlwip.c
@@ -70,6 +70,10 @@
#define TCP_NODELAY TF_NODELAY
+// Socket flags
+#define MSG_PEEK 0x01
+#define MSG_DONTWAIT 0x02
+
// For compatibilily with older lwIP versions.
#ifndef ip_set_option
#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt))
@@ -286,6 +290,15 @@ static const int error_lookup_table[] = {
#define MOD_NETWORK_SOCK_DGRAM (2)
#define MOD_NETWORK_SOCK_RAW (3)
+// Total queue length for buffered UDP/raw incoming packets.
+#define LWIP_INCOMING_PACKET_QUEUE_LEN (4)
+
+typedef struct _lwip_incoming_packet_t {
+ struct pbuf *pbuf;
+ ip_addr_t peer_addr;
+ uint16_t peer_port;
+} lwip_incoming_packet_t;
+
typedef struct _lwip_socket_obj_t {
mp_obj_base_t base;
@@ -294,8 +307,11 @@ typedef struct _lwip_socket_obj_t {
struct udp_pcb *udp;
struct raw_pcb *raw;
} pcb;
+
+ // Data structure that holds incoming pbuf's.
+ // Each socket type has different state that it needs to keep track of.
volatile union {
- struct pbuf *pbuf;
+ // TCP listening sockets have a queue of incoming connections, implemented as a ringbuffer.
struct {
uint8_t alloc;
uint8_t iget;
@@ -305,10 +321,23 @@ typedef struct _lwip_socket_obj_t {
struct tcp_pcb **array; // if alloc != 0
} tcp;
} connection;
+
+ // Connected TCP sockets have a single incoming pbuf that new data is appended to.
+ struct {
+ struct pbuf *pbuf;
+ } tcp;
+
+ // UDP and raw sockets have a queue of incoming pbuf's, implemented as a ringbuffer.
+ struct {
+ uint8_t iget; // ringbuffer read index
+ uint8_t iput; // ringbuffer write index
+ lwip_incoming_packet_t *array;
+ } udp_raw;
} incoming;
+
mp_obj_t callback;
- ip_addr_t peer;
- mp_uint_t peer_port;
+ ip_addr_t tcp_peer_addr;
+ mp_uint_t tcp_peer_port;
mp_uint_t timeout;
uint16_t recv_offset;
@@ -347,9 +376,21 @@ static void lwip_socket_free_incoming(lwip_socket_obj_t *socket) {
&& socket->pcb.tcp->state == LISTEN;
if (!socket_is_listener) {
- if (socket->incoming.pbuf != NULL) {
- pbuf_free(socket->incoming.pbuf);
- socket->incoming.pbuf = NULL;
+ if (socket->type == MOD_NETWORK_SOCK_STREAM) {
+ if (socket->incoming.tcp.pbuf != NULL) {
+ pbuf_free(socket->incoming.tcp.pbuf);
+ socket->incoming.tcp.pbuf = NULL;
+ }
+ } else {
+ for (size_t i = 0; i < LWIP_INCOMING_PACKET_QUEUE_LEN; ++i) {
+ lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[i];
+ if (slot->pbuf != NULL) {
+ pbuf_free(slot->pbuf);
+ slot->pbuf = NULL;
+ }
+ }
+ socket->incoming.udp_raw.iget = 0;
+ socket->incoming.udp_raw.iput = 0;
}
} else {
uint8_t alloc = socket->incoming.connection.alloc;
@@ -407,6 +448,19 @@ static inline void exec_user_callback(lwip_socket_obj_t *socket) {
}
}
+static void udp_raw_incoming(lwip_socket_obj_t *socket, struct pbuf *p, const ip_addr_t *addr, u16_t port) {
+ lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[socket->incoming.udp_raw.iput];
+ if (slot->pbuf != NULL) {
+ // No room in the inn, drop the packet.
+ pbuf_free(p);
+ } else {
+ slot->pbuf = p;
+ slot->peer_addr = *addr;
+ slot->peer_port = port;
+ socket->incoming.udp_raw.iput = (socket->incoming.udp_raw.iput + 1) % LWIP_INCOMING_PACKET_QUEUE_LEN;
+ }
+}
+
#if MICROPY_PY_LWIP_SOCK_RAW
// Callback for incoming raw packets.
#if LWIP_VERSION_MAJOR < 2
@@ -416,13 +470,7 @@ static u8_t _lwip_raw_incoming(void *arg, struct raw_pcb *pcb, struct pbuf *p, c
#endif
{
lwip_socket_obj_t *socket = (lwip_socket_obj_t *)arg;
-
- if (socket->incoming.pbuf != NULL) {
- pbuf_free(p);
- } else {
- socket->incoming.pbuf = p;
- memcpy(&socket->peer, addr, sizeof(socket->peer));
- }
+ udp_raw_incoming(socket, p, addr, 0);
return 1; // we ate the packet
}
#endif
@@ -436,15 +484,7 @@ static void _lwip_udp_incoming(void *arg, struct udp_pcb *upcb, struct pbuf *p,
#endif
{
lwip_socket_obj_t *socket = (lwip_socket_obj_t *)arg;
-
- if (socket->incoming.pbuf != NULL) {
- // That's why they call it "unreliable". No room in the inn, drop the packet.
- pbuf_free(p);
- } else {
- socket->incoming.pbuf = p;
- socket->peer_port = (mp_uint_t)port;
- memcpy(&socket->peer, addr, sizeof(socket->peer));
- }
+ udp_raw_incoming(socket, p, addr, port);
}
// Callback for general tcp errors.
@@ -562,13 +602,13 @@ static err_t _lwip_tcp_recv(void *arg, struct tcp_pcb *tcpb, struct pbuf *p, err
return ERR_OK;
}
- if (socket->incoming.pbuf == NULL) {
- socket->incoming.pbuf = p;
+ if (socket->incoming.tcp.pbuf == NULL) {
+ socket->incoming.tcp.pbuf = p;
} else {
#ifdef SOCKET_SINGLE_PBUF
return ERR_BUF;
#else
- pbuf_cat(socket->incoming.pbuf, p);
+ pbuf_cat(socket->incoming.tcp.pbuf, p);
#endif
}
@@ -637,18 +677,20 @@ static mp_uint_t lwip_raw_udp_send(lwip_socket_obj_t *socket, const byte *buf, m
}
// Helper function for recv/recvfrom to handle raw/UDP packets
-static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, ip_addr_t *ip, mp_uint_t *port, int *_errno) {
+static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, mp_int_t flags, ip_addr_t *ip, mp_uint_t *port, int *_errno) {
- if (socket->incoming.pbuf == NULL) {
- if (socket->timeout == 0) {
- // Non-blocking socket.
+ lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[socket->incoming.udp_raw.iget];
+
+ if (slot->pbuf == NULL) {
+ // Non-blocking socket or flag
+ if (socket->timeout == 0 || (flags & MSG_DONTWAIT)) {
*_errno = MP_EAGAIN;
return -1;
}
// Wait for data to arrive on UDP socket.
mp_uint_t start = mp_hal_ticks_ms();
- while (socket->incoming.pbuf == NULL) {
+ while (slot->pbuf == NULL) {
if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) {
*_errno = MP_ETIMEDOUT;
return -1;
@@ -658,17 +700,20 @@ static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_u
}
if (ip != NULL) {
- memcpy(ip, &socket->peer, sizeof(socket->peer));
- *port = socket->peer_port;
+ *ip = slot->peer_addr;
+ *port = slot->peer_port;
}
- struct pbuf *p = socket->incoming.pbuf;
+ struct pbuf *p = slot->pbuf;
MICROPY_PY_LWIP_ENTER
u16_t result = pbuf_copy_partial(p, buf, ((p->tot_len > len) ? len : p->tot_len), 0);
- pbuf_free(p);
- socket->incoming.pbuf = NULL;
+ if ((flags & MSG_PEEK) == 0) {
+ pbuf_free(p);
+ slot->pbuf = NULL;
+ socket->incoming.udp_raw.iget = (socket->incoming.udp_raw.iget + 1) % LWIP_INCOMING_PACKET_QUEUE_LEN;
+ }
MICROPY_PY_LWIP_EXIT
@@ -776,14 +821,20 @@ static mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui
}
// Helper function for recv/recvfrom to handle TCP packets
-static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, int *_errno) {
+static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_t len, mp_int_t flags, int *_errno) {
// Check for any pending errors
STREAM_ERROR_CHECK(socket);
- if (socket->incoming.pbuf == NULL) {
+ if (socket->state == STATE_LISTENING) {
+ // original socket in listening state, not the accepted connection.
+ *_errno = MP_ENOTCONN;
+ return -1;
+ }
- // Non-blocking socket
- if (socket->timeout == 0) {
+ if (socket->incoming.tcp.pbuf == NULL) {
+
+ // Non-blocking socket or flag
+ if (socket->timeout == 0 || (flags & MSG_DONTWAIT)) {
if (socket->state == STATE_PEER_CLOSED) {
return 0;
}
@@ -792,7 +843,7 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
}
mp_uint_t start = mp_hal_ticks_ms();
- while (socket->state == STATE_CONNECTED && socket->incoming.pbuf == NULL) {
+ while (socket->state == STATE_CONNECTED && socket->incoming.tcp.pbuf == NULL) {
if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) {
*_errno = MP_ETIMEDOUT;
return -1;
@@ -801,7 +852,7 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
}
if (socket->state == STATE_PEER_CLOSED) {
- if (socket->incoming.pbuf == NULL) {
+ if (socket->incoming.tcp.pbuf == NULL) {
// socket closed and no data left in buffer
return 0;
}
@@ -819,7 +870,7 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
assert(socket->pcb.tcp != NULL);
- struct pbuf *p = socket->incoming.pbuf;
+ struct pbuf *p = socket->incoming.tcp.pbuf;
mp_uint_t remaining = p->len - socket->recv_offset;
if (len > remaining) {
@@ -828,19 +879,21 @@ static mp_uint_t lwip_tcp_receive(lwip_socket_obj_t *socket, byte *buf, mp_uint_
memcpy(buf, (byte *)p->payload + socket->recv_offset, len);
- remaining -= len;
- if (remaining == 0) {
- socket->incoming.pbuf = p->next;
- // If we don't ref here, free() will free the entire chain,
- // if we ref, it does what we need: frees 1st buf, and decrements
- // next buf's refcount back to 1.
- pbuf_ref(p->next);
- pbuf_free(p);
- socket->recv_offset = 0;
- } else {
- socket->recv_offset += len;
+ if ((flags & MSG_PEEK) == 0) {
+ remaining -= len;
+ if (remaining == 0) {
+ socket->incoming.tcp.pbuf = p->next;
+ // If we don't ref here, free() will free the entire chain,
+ // if we ref, it does what we need: frees 1st buf, and decrements
+ // next buf's refcount back to 1.
+ pbuf_ref(p->next);
+ pbuf_free(p);
+ socket->recv_offset = 0;
+ } else {
+ socket->recv_offset += len;
+ }
+ tcp_recved(socket->pcb.tcp, len);
}
- tcp_recved(socket->pcb.tcp, len);
MICROPY_PY_LWIP_EXIT
@@ -854,8 +907,18 @@ static const mp_obj_type_t lwip_socket_type;
static void lwip_socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
lwip_socket_obj_t *self = MP_OBJ_TO_PTR(self_in);
- mp_printf(print, "<socket state=%d timeout=%d incoming=%p off=%d>", self->state, self->timeout,
- self->incoming.pbuf, self->recv_offset);
+ mp_printf(print, "<socket state=%d timeout=%d incoming=", self->state, self->timeout);
+ if (self->type == MOD_NETWORK_SOCK_STREAM) {
+ mp_printf(print, "%p off=%d>", self->incoming.tcp.pbuf, self->recv_offset);
+ } else {
+ int num_in_queue = 0;
+ for (size_t i = 0; i < LWIP_INCOMING_PACKET_QUEUE_LEN; ++i) {
+ if (self->incoming.udp_raw.array[i].pbuf != NULL) {
+ ++num_in_queue;
+ }
+ }
+ mp_printf(print, "%d>", num_in_queue);
+ }
}
// FIXME: Only supports two arguments at present
@@ -884,16 +947,22 @@ static mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, size_t n_args, s
socket->incoming.connection.tcp.item = NULL;
break;
case MOD_NETWORK_SOCK_DGRAM:
- socket->pcb.udp = udp_new();
- socket->incoming.pbuf = NULL;
- break;
#if MICROPY_PY_LWIP_SOCK_RAW
- case MOD_NETWORK_SOCK_RAW: {
- mp_int_t proto = n_args <= 2 ? 0 : mp_obj_get_int(args[2]);
- socket->pcb.raw = raw_new(proto);
- break;
- }
+ case MOD_NETWORK_SOCK_RAW:
#endif
+ if (socket->type == MOD_NETWORK_SOCK_DGRAM) {
+ socket->pcb.udp = udp_new();
+ }
+ #if MICROPY_PY_LWIP_SOCK_RAW
+ else {
+ mp_int_t proto = n_args <= 2 ? 0 : mp_obj_get_int(args[2]);
+ socket->pcb.raw = raw_new(proto);
+ }
+ #endif
+ socket->incoming.udp_raw.iget = 0;
+ socket->incoming.udp_raw.iput = 0;
+ socket->incoming.udp_raw.array = m_new0(lwip_incoming_packet_t, LWIP_INCOMING_PACKET_QUEUE_LEN);
+ break;
default:
mp_raise_OSError(MP_EINVAL);
}
@@ -1075,7 +1144,7 @@ static mp_obj_t lwip_socket_accept(mp_obj_t self_in) {
// ...and set up the new socket for it.
socket2->domain = MOD_NETWORK_AF_INET;
socket2->type = MOD_NETWORK_SOCK_STREAM;
- socket2->incoming.pbuf = NULL;
+ socket2->incoming.tcp.pbuf = NULL;
socket2->timeout = socket->timeout;
socket2->state = STATE_CONNECTED;
socket2->recv_offset = 0;
@@ -1130,8 +1199,8 @@ static mp_obj_t lwip_socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
socket->state = STATE_NEW;
mp_raise_OSError(error_lookup_table[-err]);
}
- socket->peer_port = (mp_uint_t)port;
- memcpy(&socket->peer, &dest, sizeof(socket->peer));
+ socket->tcp_peer_addr = dest;
+ socket->tcp_peer_port = (mp_uint_t)port;
MICROPY_PY_LWIP_EXIT
// And now we wait...
@@ -1216,40 +1285,58 @@ static mp_obj_t lwip_socket_send(mp_obj_t self_in, mp_obj_t buf_in) {
}
static MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_send_obj, lwip_socket_send);
-static mp_obj_t lwip_socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
- lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
+// Common implementation for recv & recvfrom
+static mp_obj_t lwip_socket_recv_common(size_t n_args, const mp_obj_t *args, ip_addr_t *ip, mp_uint_t *port) {
+ lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(args[0]);
+ mp_int_t len = mp_obj_get_int(args[1]);
+ mp_int_t flags = n_args > 2 ? mp_obj_get_int(args[2]) : 0;
int _errno;
+ vstr_t vstr;
+ mp_uint_t ret = 0;
lwip_socket_check_connected(socket);
- mp_int_t len = mp_obj_get_int(len_in);
- vstr_t vstr;
vstr_init_len(&vstr, len);
- mp_uint_t ret = 0;
switch (socket->type) {
- case MOD_NETWORK_SOCK_STREAM: {
- ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, &_errno);
+ case MOD_NETWORK_SOCK_STREAM:
+ if (ip != NULL) {
+ *ip = socket->tcp_peer_addr;
+ *port = (mp_uint_t)socket->tcp_peer_port;
+ }
+ ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, flags, &_errno);
break;
- }
case MOD_NETWORK_SOCK_DGRAM:
#if MICROPY_PY_LWIP_SOCK_RAW
case MOD_NETWORK_SOCK_RAW:
#endif
- ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, NULL, NULL, &_errno);
+ ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, flags, ip, port, &_errno);
break;
}
if (ret == -1) {
mp_raise_OSError(_errno);
}
-
if (ret == 0) {
return mp_const_empty_bytes;
}
vstr.len = ret;
return mp_obj_new_bytes_from_vstr(&vstr);
}
-static MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recv_obj, lwip_socket_recv);
+
+static mp_obj_t lwip_socket_recv(size_t n_args, const mp_obj_t *args) {
+ return lwip_socket_recv_common(n_args, args, NULL, NULL);
+}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_recv_obj, 2, 3, lwip_socket_recv);
+
+static mp_obj_t lwip_socket_recvfrom(size_t n_args, const mp_obj_t *args) {
+ ip_addr_t ip;
+ mp_uint_t port;
+ mp_obj_t tuple[2];
+ tuple[0] = lwip_socket_recv_common(n_args, args, &ip, &port);
+ tuple[1] = lwip_format_inet_addr(&ip, port);
+ return mp_obj_new_tuple(2, tuple);
+}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lwip_socket_recvfrom_obj, 2, 3, lwip_socket_recvfrom);
static mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) {
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
@@ -1284,50 +1371,6 @@ static mp_obj_t lwip_socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t
}
static MP_DEFINE_CONST_FUN_OBJ_3(lwip_socket_sendto_obj, lwip_socket_sendto);
-static mp_obj_t lwip_socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
- lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
- int _errno;
-
- lwip_socket_check_connected(socket);
-
- mp_int_t len = mp_obj_get_int(len_in);
- vstr_t vstr;
- vstr_init_len(&vstr, len);
- ip_addr_t ip;
- mp_uint_t port;
-
- mp_uint_t ret = 0;
- switch (socket->type) {
- case MOD_NETWORK_SOCK_STREAM: {
- memcpy(&ip, &socket->peer, sizeof(socket->peer));
- port = (mp_uint_t)socket->peer_port;
- ret = lwip_tcp_receive(socket, (byte *)vstr.buf, len, &_errno);
- break;
- }
- case MOD_NETWORK_SOCK_DGRAM:
- #if MICROPY_PY_LWIP_SOCK_RAW
- case MOD_NETWORK_SOCK_RAW:
- #endif
- ret = lwip_raw_udp_receive(socket, (byte *)vstr.buf, len, &ip, &port, &_errno);
- break;
- }
- if (ret == -1) {
- mp_raise_OSError(_errno);
- }
-
- mp_obj_t tuple[2];
- if (ret == 0) {
- tuple[0] = mp_const_empty_bytes;
- } else {
- vstr.len = ret;
- tuple[0] = mp_obj_new_bytes_from_vstr(&vstr);
- }
- tuple[1] = lwip_format_inet_addr(&ip, port);
-
- return mp_obj_new_tuple(2, tuple);
-}
-static MP_DEFINE_CONST_FUN_OBJ_2(lwip_socket_recvfrom_obj, lwip_socket_recvfrom);
-
static mp_obj_t lwip_socket_sendall(mp_obj_t self_in, mp_obj_t buf_in) {
lwip_socket_obj_t *socket = MP_OBJ_TO_PTR(self_in);
lwip_socket_check_connected(socket);
@@ -1487,12 +1530,12 @@ static mp_uint_t lwip_socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, i
switch (socket->type) {
case MOD_NETWORK_SOCK_STREAM:
- return lwip_tcp_receive(socket, buf, size, errcode);
+ return lwip_tcp_receive(socket, buf, size, 0, errcode);
case MOD_NETWORK_SOCK_DGRAM:
#if MICROPY_PY_LWIP_SOCK_RAW
case MOD_NETWORK_SOCK_RAW:
#endif
- return lwip_raw_udp_receive(socket, buf, size, NULL, NULL, errcode);
+ return lwip_raw_udp_receive(socket, buf, size, 0, NULL, NULL, errcode);
}
// Unreachable
return MP_STREAM_ERROR;
@@ -1537,9 +1580,15 @@ static mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_
if (lwip_socket_incoming_array(socket)[socket->incoming.connection.iget] != NULL) {
ret |= MP_STREAM_POLL_RD;
}
+ } else if (socket->type == MOD_NETWORK_SOCK_STREAM) {
+ // For TCP sockets there is just one slot for incoming data
+ if (socket->incoming.tcp.pbuf != NULL) {
+ ret |= MP_STREAM_POLL_RD;
+ }
} else {
- // Otherwise there is just one slot for incoming data
- if (socket->incoming.pbuf != NULL) {
+ // Otherwise for UDP/raw there is a queue of incoming data
+ lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[socket->incoming.udp_raw.iget];
+ if (slot->pbuf != NULL) {
ret |= MP_STREAM_POLL_RD;
}
}
@@ -1858,6 +1907,8 @@ static const mp_rom_map_elem_t mp_module_lwip_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_IPPROTO_TCP), MP_ROM_INT(IP_PROTO_TCP) },
{ MP_ROM_QSTR(MP_QSTR_TCP_NODELAY), MP_ROM_INT(TCP_NODELAY) },
+ { MP_ROM_QSTR(MP_QSTR_MSG_PEEK), MP_ROM_INT(MSG_PEEK) },
+ { MP_ROM_QSTR(MP_QSTR_MSG_DONTWAIT), MP_ROM_INT(MSG_DONTWAIT) },
};
static MP_DEFINE_CONST_DICT(mp_module_lwip_globals, mp_module_lwip_globals_table);
diff --git a/extmod/modmachine.c b/extmod/modmachine.c
index 5906835949..f2570123e3 100644
--- a/extmod/modmachine.c
+++ b/extmod/modmachine.c
@@ -45,11 +45,11 @@
static void mp_machine_idle(void);
#if MICROPY_PY_MACHINE_BOOTLOADER
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args);
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args);
#endif
#if MICROPY_PY_MACHINE_RESET
-NORETURN static void mp_machine_reset(void);
+MP_NORETURN static void mp_machine_reset(void);
static mp_int_t mp_machine_reset_cause(void);
#endif
@@ -58,7 +58,7 @@ static mp_obj_t mp_machine_unique_id(void);
static mp_obj_t mp_machine_get_freq(void);
static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args);
static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args);
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args);
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args);
#endif
// The port can provide additional machine-module implementation in this file.
@@ -67,7 +67,7 @@ NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args);
#endif
#if MICROPY_PY_MACHINE_BOOTLOADER
-NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args) {
mp_machine_bootloader(n_args, args);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_bootloader_obj, 0, 1, machine_bootloader);
@@ -81,7 +81,7 @@ static MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle);
#if MICROPY_PY_MACHINE_RESET
-NORETURN static mp_obj_t machine_reset(void) {
+MP_NORETURN static mp_obj_t machine_reset(void) {
mp_machine_reset();
}
MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset);
@@ -116,7 +116,7 @@ static mp_obj_t machine_lightsleep(size_t n_args, const mp_obj_t *args) {
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lightsleep_obj, 0, 1, machine_lightsleep);
-NORETURN static mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *args) {
mp_machine_deepsleep(n_args, args);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_deepsleep_obj, 0, 1, machine_deepsleep);
diff --git a/extmod/modmachine.h b/extmod/modmachine.h
index 7c16ed302e..26010be8e1 100644
--- a/extmod/modmachine.h
+++ b/extmod/modmachine.h
@@ -242,7 +242,7 @@ uintptr_t MICROPY_MACHINE_MEM_GET_READ_ADDR(mp_obj_t addr_o, uint align);
uintptr_t MICROPY_MACHINE_MEM_GET_WRITE_ADDR(mp_obj_t addr_o, uint align);
#endif
-NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args);
+MP_NORETURN mp_obj_t machine_bootloader(size_t n_args, const mp_obj_t *args);
void machine_bitstream_high_low(mp_hal_pin_obj_t pin, uint32_t *timing_ns, const uint8_t *buf, size_t len);
mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us);
diff --git a/extmod/modnetwork.c b/extmod/modnetwork.c
index 336836b6b8..b6855fcaaa 100644
--- a/extmod/modnetwork.c
+++ b/extmod/modnetwork.c
@@ -40,6 +40,19 @@
#if MICROPY_PY_NETWORK_CYW43
// So that CYW43_LINK_xxx constants are available to MICROPY_PORT_NETWORK_INTERFACES.
#include "lib/cyw43-driver/src/cyw43.h"
+extern const struct _mp_obj_type_t mp_network_cyw43_type;
+#endif
+
+#if MICROPY_PY_NETWORK_WIZNET5K
+extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k;
+#endif
+
+#if MICROPY_PY_NETWORK_NINAW10
+extern const struct _mp_obj_type_t mod_network_nic_type_nina;
+#endif
+
+#if MICROPY_PY_NETWORK_ESP_HOSTED
+extern const struct _mp_obj_type_t mod_network_esp_hosted_type;
#endif
#ifdef MICROPY_PY_NETWORK_INCLUDEFILE
@@ -166,6 +179,32 @@ static const mp_rom_map_elem_t mp_module_network_globals_table[] = {
MICROPY_PORT_NETWORK_INTERFACES
#endif
+ #if MICROPY_PY_NETWORK_CYW43
+ { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) },
+ // CYW43 status constants, currently for rp2 port only.
+ // TODO move these to WIFI module for all ports.
+ #if defined(PICO_PROGRAM_NAME) && defined(CYW43_LINK_DOWN)
+ { MP_ROM_QSTR(MP_QSTR_STAT_IDLE), MP_ROM_INT(CYW43_LINK_DOWN) },
+ { MP_ROM_QSTR(MP_QSTR_STAT_CONNECTING), MP_ROM_INT(CYW43_LINK_JOIN) },
+ { MP_ROM_QSTR(MP_QSTR_STAT_WRONG_PASSWORD), MP_ROM_INT(CYW43_LINK_BADAUTH) },
+ { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND), MP_ROM_INT(CYW43_LINK_NONET) },
+ { MP_ROM_QSTR(MP_QSTR_STAT_CONNECT_FAIL), MP_ROM_INT(CYW43_LINK_FAIL) },
+ { MP_ROM_QSTR(MP_QSTR_STAT_GOT_IP), MP_ROM_INT(CYW43_LINK_UP) },
+ #endif
+ #endif
+
+ #if MICROPY_PY_NETWORK_WIZNET5K
+ { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) },
+ #endif
+
+ #if MICROPY_PY_NETWORK_NINAW10
+ { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_nic_type_nina) },
+ #endif
+
+ #if MICROPY_PY_NETWORK_ESP_HOSTED
+ { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_esp_hosted_type) },
+ #endif
+
// Allow a port to take mostly full control of the network module.
#ifdef MICROPY_PY_NETWORK_MODULE_GLOBALS_INCLUDEFILE
#include MICROPY_PY_NETWORK_MODULE_GLOBALS_INCLUDEFILE
diff --git a/extmod/modnetwork.h b/extmod/modnetwork.h
index 7e5a283353..d16329f07d 100644
--- a/extmod/modnetwork.h
+++ b/extmod/modnetwork.h
@@ -60,6 +60,11 @@ extern char mod_network_country_code[2];
#define MICROPY_PY_NETWORK_HOSTNAME_MAX_LEN (32)
#endif
+#if MICROPY_PY_NETWORK_NINAW10
+// This Network interface requires the extended socket state.
+#define MICROPY_PY_SOCKET_EXTENDED_STATE (1)
+#endif
+
// This is a null-terminated string.
extern char mod_network_hostname_data[MICROPY_PY_NETWORK_HOSTNAME_MAX_LEN + 1];
diff --git a/extmod/modre.c b/extmod/modre.c
index 1a118009cb..d17ec68d50 100644
--- a/extmod/modre.c
+++ b/extmod/modre.c
@@ -427,6 +427,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);
diff --git a/extmod/modtime.c b/extmod/modtime.c
index deb4bb4c9a..999b81230b 100644
--- a/extmod/modtime.c
+++ b/extmod/modtime.c
@@ -58,7 +58,7 @@ static mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) {
return mp_time_localtime_get();
} else {
// Convert given seconds to tuple.
- mp_int_t seconds = mp_obj_get_int(args[0]);
+ mp_timestamp_t seconds = timeutils_obj_get_timestamp(args[0]);
timeutils_struct_time_t tm;
timeutils_seconds_since_epoch_to_struct_time(seconds, &tm);
mp_obj_t tuple[8] = {
@@ -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/modtls_axtls.c b/extmod/modtls_axtls.c
index 0e49fde2d8..ba28c13fce 100644
--- a/extmod/modtls_axtls.c
+++ b/extmod/modtls_axtls.c
@@ -105,7 +105,7 @@ static const char *const ssl_error_tab2[] = {
"NOT_SUPPORTED",
};
-static NORETURN void ssl_raise_error(int err) {
+static MP_NORETURN void ssl_raise_error(int err) {
MP_STATIC_ASSERT(SSL_NOT_OK - 3 == SSL_EAGAIN);
MP_STATIC_ASSERT(SSL_ERROR_CONN_LOST - 18 == SSL_ERROR_NOT_SUPPORTED);
diff --git a/extmod/modtls_mbedtls.c b/extmod/modtls_mbedtls.c
index 6c34805da4..71a14adcff 100644
--- a/extmod/modtls_mbedtls.c
+++ b/extmod/modtls_mbedtls.c
@@ -147,7 +147,7 @@ static const unsigned char *asn1_get_data(mp_obj_t obj, size_t *out_len) {
return (const unsigned char *)str;
}
-static NORETURN void mbedtls_raise_error(int err) {
+static MP_NORETURN void mbedtls_raise_error(int err) {
// Handle special cases.
if (err == MBEDTLS_ERR_SSL_ALLOC_FAILED) {
mp_raise_OSError(MP_ENOMEM);
diff --git a/extmod/moductypes.c b/extmod/moductypes.c
index bf45797658..eb72f441bb 100644
--- a/extmod/moductypes.c
+++ b/extmod/moductypes.c
@@ -89,7 +89,7 @@ typedef struct _mp_obj_uctypes_struct_t {
uint32_t flags;
} mp_obj_uctypes_struct_t;
-static NORETURN void syntax_error(void) {
+static MP_NORETURN void syntax_error(void) {
mp_raise_TypeError(MP_ERROR_TEXT("syntax error in uctypes descriptor"));
}
@@ -277,15 +277,18 @@ static mp_obj_t uctypes_struct_sizeof(size_t n_args, const mp_obj_t *args) {
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(uctypes_struct_sizeof_obj, 1, 2, uctypes_struct_sizeof);
+static const char type2char[16] = {
+ 'B', 'b', 'H', 'h', 'I', 'i', 'Q', 'q',
+ '-', '-', '-', '-', '-', '-', 'f', 'd'
+};
+
static inline mp_obj_t get_unaligned(uint val_type, byte *p, int big_endian) {
char struct_type = big_endian ? '>' : '<';
- static const char type2char[16] = "BbHhIiQq------fd";
return mp_binary_get_val(struct_type, type2char[val_type], p, &p);
}
static inline void set_unaligned(uint val_type, byte *p, int big_endian, mp_obj_t val) {
char struct_type = big_endian ? '>' : '<';
- static const char type2char[16] = "BbHhIiQq------fd";
mp_binary_set_val(struct_type, type2char[val_type], val, p, &p);
}
diff --git a/extmod/mpbthci.h b/extmod/mpbthci.h
index c10f99c3df..3ff8294c4c 100644
--- a/extmod/mpbthci.h
+++ b/extmod/mpbthci.h
@@ -27,6 +27,10 @@
#ifndef MICROPY_INCLUDED_EXTMOD_MPBTHCI_H
#define MICROPY_INCLUDED_EXTMOD_MPBTHCI_H
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+
#define MICROPY_PY_BLUETOOTH_HCI_READ_MODE_BYTE (0)
#define MICROPY_PY_BLUETOOTH_HCI_READ_MODE_PACKET (1)
diff --git a/extmod/network_cyw43.c b/extmod/network_cyw43.c
index 1b1b10b407..9ebfa904db 100644
--- a/extmod/network_cyw43.c
+++ b/extmod/network_cyw43.c
@@ -143,6 +143,9 @@ static mp_obj_t network_cyw43_active(size_t n_args, const mp_obj_t *args) {
return mp_obj_new_bool(if_active[self->itf]);
} else {
bool value = mp_obj_is_true(args[1]);
+ if (!value && self->itf == CYW43_ITF_STA) {
+ cyw43_wifi_leave(self->cyw, self->itf);
+ }
cyw43_wifi_set_up(self->cyw, self->itf, value, get_country_code());
if_active[self->itf] = value;
return mp_const_none;
diff --git a/extmod/network_esp_hosted.c b/extmod/network_esp_hosted.c
index 04cfc36b32..6ed348450b 100644
--- a/extmod/network_esp_hosted.c
+++ b/extmod/network_esp_hosted.c
@@ -48,6 +48,8 @@
#include "esp_hosted_wifi.h"
#include "esp_hosted_hal.h"
+extern const mp_obj_type_t mod_network_esp_hosted_type;
+
typedef struct _esp_hosted_obj_t {
mp_obj_base_t base;
uint32_t itf;
diff --git a/extmod/network_ppp_lwip.c b/extmod/network_ppp_lwip.c
index 8eb90ea4a6..2c3dac9201 100644
--- a/extmod/network_ppp_lwip.c
+++ b/extmod/network_ppp_lwip.c
@@ -24,6 +24,10 @@
* THE SOFTWARE.
*/
+// This file is intended to closely match ports/esp32/network_ppp.c. Changes can
+// and should probably be applied to both files. Compare them directly by using:
+// git diff --no-index extmod/network_ppp_lwip.c ports/esp32/network_ppp.c
+
#include "py/runtime.h"
#include "py/mphal.h"
#include "py/stream.h"
@@ -80,7 +84,6 @@ static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
break;
case PPPERR_USER:
if (self->state >= STATE_ERROR) {
- network_ppp_stream_uart_irq_disable(self);
// Indicate that we are no longer connected and thus
// only need to free the PPP PCB, not close it.
self->state = STATE_ACTIVE;
@@ -121,6 +124,7 @@ static mp_obj_t network_ppp___del__(mp_obj_t self_in) {
self->state = STATE_INACTIVE;
ppp_close(self->pcb, 1);
}
+ network_ppp_stream_uart_irq_disable(self);
// Free PPP PCB and reset state.
self->state = STATE_INACTIVE;
ppp_free(self->pcb);
@@ -295,7 +299,8 @@ static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_
ppp_set_auth(self->pcb, parsed_args[ARG_security].u_int, user_str, key_str);
}
- netif_set_default(self->pcb->netif);
+ ppp_set_default(self->pcb);
+
ppp_set_usepeerdns(self->pcb, true);
if (ppp_connect(self->pcb, 0) != ERR_OK) {
diff --git a/extmod/network_wiznet5k.c b/extmod/network_wiznet5k.c
index 533d39a187..e065c14866 100644
--- a/extmod/network_wiznet5k.c
+++ b/extmod/network_wiznet5k.c
@@ -78,6 +78,8 @@
#endif
+extern const mp_obj_type_t mod_network_nic_type_wiznet5k;
+
#ifndef printf
#define printf(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__)
#endif
diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c
index 6ca0c17267..5e7030e36f 100644
--- a/extmod/nimble/modbluetooth_nimble.c
+++ b/extmod/nimble/modbluetooth_nimble.c
@@ -60,6 +60,7 @@
static uint8_t nimble_address_mode = BLE_OWN_ADDR_RANDOM;
#define NIMBLE_STARTUP_TIMEOUT 2000
+#define NIMBLE_SHUTDOWN_TIMEOUT 500
// Any BLE_HS_xxx code not in this table will default to MP_EIO.
static int8_t ble_hs_err_to_errno_table[] = {
@@ -554,7 +555,7 @@ static void ble_hs_shutdown_stop_cb(int status, void *arg) {
static struct ble_hs_stop_listener ble_hs_shutdown_stop_listener;
-void mp_bluetooth_nimble_port_shutdown(void) {
+int mp_bluetooth_nimble_port_shutdown(void) {
DEBUG_printf("mp_bluetooth_nimble_port_shutdown (nimble default)\n");
// By default, just call ble_hs_stop directly and wait for the stack to stop.
@@ -562,9 +563,17 @@ void mp_bluetooth_nimble_port_shutdown(void) {
ble_hs_stop(&ble_hs_shutdown_stop_listener, ble_hs_shutdown_stop_cb, NULL);
+ mp_uint_t timeout_start_ticks_ms = mp_hal_ticks_ms();
while (mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) {
- mp_event_wait_indefinite();
+ mp_uint_t elapsed = mp_hal_ticks_ms() - timeout_start_ticks_ms;
+ if (elapsed > NIMBLE_SHUTDOWN_TIMEOUT) {
+ // Stack had not responded (via ble_hs_shutdown_stop_cb)
+ return MP_ETIMEDOUT;
+ }
+
+ mp_event_wait_ms(NIMBLE_SHUTDOWN_TIMEOUT - elapsed);
}
+ return 0;
}
#endif // !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY
@@ -659,10 +668,11 @@ int mp_bluetooth_init(void) {
return 0;
}
-void mp_bluetooth_deinit(void) {
+int mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_nimble_ble_state);
+ int ret = 0;
if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) {
- return;
+ return 0;
}
// Must call ble_hs_stop() in a port-specific way to stop the background
@@ -675,7 +685,7 @@ void mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit: starting port shutdown\n");
- mp_bluetooth_nimble_port_shutdown();
+ ret = mp_bluetooth_nimble_port_shutdown();
assert(mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF);
} else {
mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF;
@@ -692,6 +702,7 @@ void mp_bluetooth_deinit(void) {
#endif
DEBUG_printf("mp_bluetooth_deinit: shut down\n");
+ return ret;
}
bool mp_bluetooth_is_active(void) {
diff --git a/extmod/nimble/modbluetooth_nimble.h b/extmod/nimble/modbluetooth_nimble.h
index d9bef64920..0dd20eb658 100644
--- a/extmod/nimble/modbluetooth_nimble.h
+++ b/extmod/nimble/modbluetooth_nimble.h
@@ -84,7 +84,7 @@ void mp_bluetooth_nimble_port_hci_deinit(void);
void mp_bluetooth_nimble_port_start(void);
// Tell the port to stop its background task.
-void mp_bluetooth_nimble_port_shutdown(void);
+int mp_bluetooth_nimble_port_shutdown(void);
// --- Called by the HCI UART layer to let us know when packets have been sent.
void mp_bluetooth_nimble_sent_hci_packet(void);
diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c
index ee1169b8c3..e832992f46 100644
--- a/extmod/vfs_fat.c
+++ b/extmod/vfs_fat.c
@@ -326,7 +326,7 @@ static mp_obj_t fat_vfs_stat(mp_obj_t vfs_in, mp_obj_t path_in) {
} else {
mode |= MP_S_IFREG;
}
- mp_int_t seconds = timeutils_seconds_since_epoch(
+ mp_timestamp_t seconds = timeutils_seconds_since_epoch(
1980 + ((fno.fdate >> 9) & 0x7f),
(fno.fdate >> 5) & 0x0f,
fno.fdate & 0x1f,
@@ -341,9 +341,9 @@ static mp_obj_t fat_vfs_stat(mp_obj_t vfs_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(fno.fsize); // st_size
- t->items[7] = mp_obj_new_int_from_uint(seconds); // st_atime
- t->items[8] = mp_obj_new_int_from_uint(seconds); // st_mtime
- t->items[9] = mp_obj_new_int_from_uint(seconds); // st_ctime
+ t->items[7] = timeutils_obj_from_timestamp(seconds); // st_atime
+ t->items[8] = timeutils_obj_from_timestamp(seconds); // st_mtime
+ t->items[9] = timeutils_obj_from_timestamp(seconds); // st_ctime
return MP_OBJ_FROM_PTR(t);
}
diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c
index 4b10ca3aa5..372037784b 100644
--- a/extmod/vfs_lfsx.c
+++ b/extmod/vfs_lfsx.c
@@ -300,7 +300,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 +378,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 +400,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/lib/berkeley-db-1.xx b/lib/berkeley-db-1.xx
-Subproject 85373b548f1fb0119a463582570b44189dfb09a
+Subproject 0f3bb6947c2f57233916dccd7bb425d7bf86e5a
diff --git a/lib/libhydrogen b/lib/libhydrogen
-Subproject 5c5d513093075f7245ea522101b17c50aa579af
+Subproject bbca575b62510bfdc6dd927a4bfa7df4a51cb84
diff --git a/lib/libm_dbl/rint.c b/lib/libm_dbl/rint.c
index fbba390e7d..b85dec8f24 100644
--- a/lib/libm_dbl/rint.c
+++ b/lib/libm_dbl/rint.c
@@ -2,7 +2,7 @@
#include <math.h>
#include <stdint.h>
-#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/lfs1.c b/lib/littlefs/lfs1.c
index 6a3fd67001..ec18dc4702 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]},
diff --git a/lib/littlefs/lfs2.c b/lib/littlefs/lfs2.c
index d89c42fd59..abdec19d7c 100644
--- a/lib/littlefs/lfs2.c
+++ b/lib/littlefs/lfs2.c
@@ -282,6 +282,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 +404,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 +562,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 +586,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 +664,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 +722,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)
@@ -1438,32 +1473,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 +1524,9 @@ nextname:
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 +1540,14 @@ nextname:
}
// 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 +1567,7 @@ nextname:
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 +2155,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 +2197,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 +2219,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;
}
}
}
@@ -2312,7 +2366,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 +2388,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 +2556,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 +2621,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 +2632,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 +2717,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 +2761,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 +2813,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 +2850,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 +3061,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 +3086,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 +3106,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 +3229,22 @@ cleanup:
#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 +3335,7 @@ relocate:
#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 +3427,7 @@ relocate:
}
#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 +3442,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 +3549,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 +3574,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 +3603,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 +3646,13 @@ relocate:
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 +3696,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 +3723,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 +3749,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 +3757,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 +3807,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 +3822,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 +3831,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 +3845,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 +3859,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 +3932,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 +3957,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 +3976,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 +3987,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 +4006,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 +4054,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 +4078,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 +4103,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 +4161,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 +4172,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 +4214,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 +4249,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 +4293,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 +4327,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 +4378,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 +4387,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 +4398,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 +4454,31 @@ cleanup:
}
#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 +4486,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 +4544,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 +4552,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 +4590,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 +4633,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 +4689,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 +4708,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 +4797,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 +4857,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 +5139,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 +5175,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 +5185,116 @@ 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 < 8*lfs2->cfg->lookahead_size) {
+ 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
+
+#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
- superblock.block_count = lfs2->block_count;
+static int lfs2_fs_grow_(lfs2_t *lfs2, lfs2_size_t block_count) {
+ int err;
- lfs2_superblock_tole32(&superblock);
- err = lfs2_dir_commit(lfs2, &root, LFS2_MKATTRS(
- {tag, &superblock}));
+ 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 +5666,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 +5720,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 +5974,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 +5987,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 +6004,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 +6017,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 +6031,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 +6046,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 +6062,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 +6077,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 +6093,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 +6110,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 +6126,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 +6141,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 +6161,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 +6180,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 +6196,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 +6214,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 +6232,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 +6250,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 +6267,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 +6283,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 +6297,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 +6312,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 +6327,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 +6343,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 +6357,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 +6372,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 +6387,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 +6401,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 +6415,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 +6429,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 +6443,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 +6458,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 +6466,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 +6505,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 +6523,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 +6536,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 4c426fc4c7..77477aaff8 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
//
@@ -737,10 +745,32 @@ 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 c9850e7886..4fe7e5340c 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 dd2cbcc106..12c82a630b 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 <stddef.h>
+// 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 <stdint.h>
#include <stdbool.h>
@@ -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/stm32lib b/lib/stm32lib
-Subproject 928df866e4d287ebc3c60726151513ebee60912
+Subproject 8b2bb4ef44fbfab5075ed9d09e83ddc3754b925
diff --git a/mpy-cross/main.c b/mpy-cross/main.c
index 7ab95149c7..16f749ae4d 100644
--- a/mpy-cross/main.c
+++ b/mpy-cross/main.c
@@ -130,7 +130,7 @@ static int usage(char **argv) {
"Target specific options:\n"
"-msmall-int-bits=number : set the maximum bits used to encode a small-int\n"
"-march=<arch> : 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, armv7emdp, xtensa, xtensawin, rv32imc, host, debug\n"
"\n"
"Implementation specific options:\n", argv[0]
);
@@ -350,6 +350,15 @@ MP_NOINLINE int main_(int argc, char **argv) {
}
}
+ #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);
diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h
index d3805f0307..94a598c995 100644
--- a/mpy-cross/mpconfigport.h
+++ b/mpy-cross/mpconfigport.h
@@ -137,7 +137,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/ports/alif/Makefile b/ports/alif/Makefile
index 6870667436..d258b27b1d 100644
--- a/ports/alif/Makefile
+++ b/ports/alif/Makefile
@@ -21,6 +21,7 @@ Reset\n\
Exit
ALIF_TOC_CONFIG = alif_cfg.json
+ALIF_TOC_BIN = $(BUILD)/firmware.toc.bin
ALIF_TOC_APPS = $(BUILD)/$(ALIF_TOC_CONFIG)
ALIF_TOC_CFLAGS += -DTOC_CFG_FILE=$(ALIF_TOOLKIT_CFG_FILE)
@@ -69,7 +70,7 @@ include $(TOP)/extmod/extmod.mk
# Main targets
.PHONY: all
-all: $(BUILD)/firmware.toc.bin
+all: $(BUILD)/firmware.zip
# Force make commands to run the targets every time
# regardless of whether firmware.toc.bin already exists
@@ -81,24 +82,28 @@ $(BUILD):
$(MKDIR) -p $@
$(BUILD)/M55_HP/firmware.bin:
- make -f alif.mk MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0
+ make -f alif.mk BUILD=$(BUILD)/M55_HP MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0
$(BUILD)/M55_HE/firmware.bin:
- make -f alif.mk MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1
+ make -f alif.mk BUILD=$(BUILD)/M55_HE MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1
$(BUILD)/$(ALIF_TOC_CONFIG): mcu/$(ALIF_TOC_CONFIG).in | $(BUILD)
$(ECHO) "Preprocess toc config $@"
$(Q)$(CPP) -P -E $(ALIF_TOC_CFLAGS) - < mcu/$(ALIF_TOC_CONFIG).in > $@
-$(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS)
+$(ALIF_TOC_BIN): $(ALIF_TOC_APPS)
$(Q)python $(ALIF_TOOLS)/app-gen-toc.py \
--filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \
--output-dir $(BUILD) \
--firmware-dir $(BUILD) \
--output $@
+$(BUILD)/firmware.zip: $(ALIF_TOC_BIN) $(ALIF_TOC_APPS)
+ $(ECHO) "Create $@"
+ $(Q)$(ZIP) -q - $(BUILD)/application_package.ds $^ > $@
+
.PHONY: deploy
-deploy: $(BUILD)/firmware.toc.bin
+deploy: $(ALIF_TOC_BIN)
$(ECHO) "Writing $< to the board"
$(Q)python $(ALIF_TOOLS)/app-write-mram.py \
--cfg-part $(ALIF_TOOLKIT_CFG_PART) \
diff --git a/ports/alif/README.md b/ports/alif/README.md
index 824a63da18..dcf327060f 100644
--- a/ports/alif/README.md
+++ b/ports/alif/README.md
@@ -19,3 +19,61 @@ The following more advanced features will follow later:
- Ethernet support.
- SDRAM support.
- Other machine modules.
+
+Build instructions
+------------------
+
+Before building the firmware for a given board the MicroPython cross-compiler
+must be built; it will be used to pre-compile some of the built-in scripts to
+bytecode. The cross-compiler is built and run on the host machine, using:
+```bash
+$ make -C mpy-cross
+```
+
+This command should be executed from the root directory of this repository.
+All other commands below should be executed from the ports/alif/ directory.
+
+An ARM compiler is required for the build, along with the associated binary
+utilities. The recommended toolchain version to use with this port is
+Arm GNU toolchain version 13.3.Rel1. The compiler can be changed using the
+`CROSS_COMPILE` variable when invoking `make`.
+
+Next, the board to build must be selected. The default board is `ALIF_ENSEMBLE`
+but any of the names of the subdirectories in the `boards/` directory is valid.
+The board name must be passed as the argument to `BOARD=` when invoking `make`.
+
+All boards require certain submodules to be obtained before they can be built.
+The correct set of submodules can be initialised using (with `ALIF_ENSEMBLE`
+as an example of the selected board):
+```bash
+make BOARD=ALIF_ENSEMBLE submodules
+```
+
+Then to build the board's firmware run:
+```bash
+make BOARD=ALIF_ENSEMBLE
+```
+
+The above command should produce binary images in the `build-ALIF_ENSEMBLE/`
+subdirectory (or the equivalent directory for the board specified).
+
+### Update the SE Firmware
+
+The SE firmware must be updated **before** flashing the main firmware to match
+the version used by MicroPython. This step only needs to be performed once.
+Connect the board to your PC via the **SE UART USB** port (on the Ensemble kit,
+this is labeled **PRG USB**), then run:
+
+```bash
+make update-system-package
+```
+
+**Note:** The board must be power-cycled after this step.
+
+### Deploy MicroPython
+
+To flash the firmware, run:
+
+```bash
+make BOARD=ALIF_ENSEMBLE deploy
+```
diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk
index eee27b3b7d..265418aa07 100644
--- a/ports/alif/alif.mk
+++ b/ports/alif/alif.mk
@@ -3,7 +3,6 @@
BOARD ?= ALIF_ENSEMBLE
BOARD_DIR ?= boards/$(BOARD)
-BUILD ?= build-$(BOARD)/$(MCU_CORE)
ifeq ($(wildcard $(BOARD_DIR)/.),)
$(error Invalid BOARD specified: $(BOARD_DIR))
@@ -103,10 +102,6 @@ CFLAGS += -Wl,-T$(BUILD)/ensemble.ld \
-Wl,--print-memory-usage \
-Wl,--no-warn-rwx-segment
-ifeq ($(MCU_CORE),M55_HP)
-CFLAGS += -Wl,--wrap=dcd_event_handler
-endif
-
################################################################################
# Source files and libraries
diff --git a/ports/alif/boards/ALIF_ENSEMBLE/board.json b/ports/alif/boards/ALIF_ENSEMBLE/board.json
new file mode 100644
index 0000000000..0c63494f7a
--- /dev/null
+++ b/ports/alif/boards/ALIF_ENSEMBLE/board.json
@@ -0,0 +1,17 @@
+{
+ "deploy": [
+ "./deploy.md"
+ ],
+ "docs": "",
+ "features": [
+ "Ethernet"
+ ],
+ "images": [
+ "ensemble-devkit-gen-2.jpg"
+ ],
+ "mcu": "AE722F80F55D5XX",
+ "product": "Ensemble E7 DevKit",
+ "thumbnail": "",
+ "url": "https://alifsemi.com/support/kits/ensemble-devkit/",
+ "vendor": "Alif Semiconductor"
+}
diff --git a/ports/alif/boards/ALIF_ENSEMBLE/deploy.md b/ports/alif/boards/ALIF_ENSEMBLE/deploy.md
new file mode 100644
index 0000000000..acbc85617e
--- /dev/null
+++ b/ports/alif/boards/ALIF_ENSEMBLE/deploy.md
@@ -0,0 +1,37 @@
+### Alif Security Toolkit
+
+This board can be programmed via the SE UART interface using the Alif Security
+Toolkit. MicroPython uses a custom version of the toolkit, which can be downloaded
+from [here](https://github.com/micropython/alif-security-toolkit/) or cloned:
+
+```bash
+git clone https://github.com/micropython/alif-security-toolkit/
+```
+
+---
+
+### Update the SE Firmware (Optional)
+
+If needed, update the SE firmware to match the version used by MicroPython. Ensure
+you have the correct port, part number, and chip revision.
+
+```bash
+python alif-security-toolkit/toolkit/updateSystemPackage.py --port /dev/ttyACM0 --cfg-part AE722F80F55D5LS --cfg-rev B4
+```
+
+**Note:** The board must be power-cycled after this step.
+
+---
+
+### Deploy MicroPython
+
+Download (or build) the firmware package, unzip it, then deploy it:
+
+```bash
+
+python alif-security-toolkit/toolkit/app-write-mram.py --port /dev/ttyACM0 --cfg-part AE722F80F55D5LS --cfg-rev B4 --pad --images file:build-ALIF_ENSEMBLE/application_package.ds
+```
+
+The MicroPython REPL is available on the second USB serial port, eg `/dev/ttyACM1`.
+
+---
diff --git a/ports/alif/cyw43_configport.h b/ports/alif/cyw43_configport.h
index 4b5cce6713..bfd4364ab9 100644
--- a/ports/alif/cyw43_configport.h
+++ b/ports/alif/cyw43_configport.h
@@ -27,13 +27,8 @@
#define MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H
// The board-level config will be included here, so it can set some CYW43 values.
-#include "py/mpconfig.h"
-#include "py/mperrno.h"
-#include "py/mphal.h"
-#include "py/runtime.h"
-#include "extmod/modnetwork.h"
#include "extmod/mpbthci.h"
-#include "pendsv.h"
+#include "extmod/cyw43_config_common.h"
#ifndef static_assert
#define static_assert(expr, msg) typedef int static_assert_##__LINE__[(expr) ? 1 : -1]
@@ -53,51 +48,15 @@
#define CYW43_WARN(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__)
#endif
-#define CYW43_IOCTL_TIMEOUT_US (1000000)
-#define CYW43_SLEEP_MAX (50)
-#define CYW43_NETUTILS (1)
#define CYW43_CLEAR_SDIO_INT (1)
-#define CYW43_EPERM MP_EPERM // Operation not permitted
-#define CYW43_EIO MP_EIO // I/O error
-#define CYW43_EINVAL MP_EINVAL // Invalid argument
-#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out
-
-#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER
-#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT
-#define CYW43_THREAD_LOCK_CHECK
-
-#define CYW43_HOST_NAME mod_network_hostname_data
-
#define CYW43_SDPCM_SEND_COMMON_WAIT __WFE()
#define CYW43_DO_IOCTL_WAIT // __WFE()
-#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait()
-
-#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a)
-
-#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT
-#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT
-#define CYW43_HAL_PIN_PULL_NONE 0
-
-#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0
-#define CYW43_HAL_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_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_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate
#define cyw43_hal_uart_write mp_bluetooth_hci_uart_write
#define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar
-#define cyw43_hal_get_mac mp_hal_get_mac
-#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii
-#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac
-
#define CYW43_PIN_WL_REG_ON pin_WL_REG_ON
#define CYW43_PIN_WL_IRQ pin_WL_IRQ
@@ -110,15 +69,7 @@
void cyw43_post_poll_hook(void);
-static inline void cyw43_delay_us(uint32_t us) {
- uint32_t start = mp_hal_ticks_us();
- while (mp_hal_ticks_us() - start < us) {
- }
-}
-
-static inline void cyw43_delay_ms(uint32_t ms) {
- mp_hal_delay_ms(ms);
-}
+#undef cyw43_hal_pin_config // mp_hal_pin_config on alif port is not API compatible
static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, uint32_t alt) {
if (mode == MP_HAL_PIN_MODE_INPUT) {
@@ -127,7 +78,6 @@ static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uin
mp_hal_pin_output(pin);
}
}
-
static inline void cyw43_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) {
mp_hal_pin_config_irq_falling(pin, enable);
}
diff --git a/ports/alif/irq.h b/ports/alif/irq.h
index 08b8ef8086..02df524a49 100644
--- a/ports/alif/irq.h
+++ b/ports/alif/irq.h
@@ -48,6 +48,7 @@
#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 7, 0)
#define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0)
#define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0)
+#define IRQ_PRI_GPIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 50, 0)
#define IRQ_PRI_RTC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 100, 0)
#define IRQ_PRI_CYW43 NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 126, 0)
#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0)
diff --git a/ports/alif/machine_pin.c b/ports/alif/machine_pin.c
index 02c5e482ad..b8773f103f 100644
--- a/ports/alif/machine_pin.c
+++ b/ports/alif/machine_pin.c
@@ -30,8 +30,76 @@
#include "extmod/virtpin.h"
#include "shared/runtime/mpirq.h"
+#ifndef MACHINE_PIN_NUM_VECTORS
+#define MACHINE_PIN_NUM_PORT_IO (8)
+#define MACHINE_PIN_NUM_VECTORS (16 * MACHINE_PIN_NUM_PORT_IO)
+#endif
+
+typedef struct _machine_pin_irq_obj_t {
+ mp_irq_obj_t base;
+ uint32_t flags;
+ uint32_t trigger;
+ IRQn_Type irq_num;
+ bool reserved; // for use by other drivers
+} machine_pin_irq_obj_t;
+
+#define MACHINE_PIN_IRQ_INDEX(port, pin) \
+ ((port) * MACHINE_PIN_NUM_PORT_IO + (pin))
+
+#define MACHINE_PIN_IRQ_OBJECT(port, pin) \
+ (MP_STATE_PORT(machine_pin_irq_obj[MACHINE_PIN_IRQ_INDEX((port), (pin))]))
+
+// Defines a single GPIO IRQ handler
+#define DEFINE_GPIO_IRQ_HANDLER(pname, port, pin) \
+ void pname##_IRQ##pin##Handler(void) { \
+ machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(port, pin); \
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(irq->base.parent); \
+ gpio_interrupt_eoi(self->gpio, pin); \
+ irq->flags = irq->trigger; \
+ mp_irq_handler(&irq->base); \
+ }
+
+// Defines all 8 pin IRQ handlers for a port
+#define DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(gpio, port) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 0) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 1) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 2) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 3) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 4) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 5) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 6) \
+ DEFINE_GPIO_IRQ_HANDLER(gpio, port, 7)
+
+// Generate handlers for GPIO ports 0 to 14 + LPGPIO
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO0, 0)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO1, 1)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO2, 2)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO3, 3)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO4, 4)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO5, 5)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO6, 6)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO7, 7)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO8, 8)
+
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 0)
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 1)
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 2)
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 3)
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 4)
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 5)
+// DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 6) // Reserved for WiFi
+DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 7)
+
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO10, 10)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO11, 11)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO12, 12)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO13, 13)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO14, 14)
+DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(LPGPIO, 15)
+
extern const mp_obj_dict_t machine_pin_cpu_pins_locals_dict;
extern const mp_obj_dict_t machine_pin_board_pins_locals_dict;
+static const mp_irq_methods_t machine_pin_irq_methods;
static const machine_pin_obj_t *machine_pin_find_named(const mp_obj_dict_t *named_pins, mp_obj_t name) {
const mp_map_t *named_map = &named_pins->map;
@@ -171,6 +239,7 @@ mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args);
}
+
return MP_OBJ_FROM_PTR(self);
}
@@ -225,6 +294,129 @@ static mp_obj_t machine_pin_toggle(mp_obj_t self_in) {
}
static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle);
+static mp_uint_t machine_pin_irq_trigger(mp_obj_t self_in, mp_uint_t trigger) {
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin);
+
+ irq->flags = 0;
+ irq->trigger = trigger;
+
+ // Disable IRQs.
+ gpio_disable_interrupt(self->gpio, self->pin);
+ gpio_mask_interrupt(self->gpio, self->pin);
+
+ NVIC_ClearPendingIRQ(irq->irq_num);
+ NVIC_DisableIRQ(irq->irq_num);
+
+ // Return if the trigger is disabled.
+ if (trigger == 0) {
+ return 0;
+ }
+
+ // Clear and enable GPIO IRQ.
+ gpio_enable_interrupt(self->gpio, self->pin);
+ gpio_unmask_interrupt(self->gpio, self->pin);
+
+ // Clear GPIO config.
+ self->gpio->GPIO_INT_BOTHEDGE &= ~(1 << self->pin);
+ self->gpio->GPIO_INT_POLARITY &= ~(1 << self->pin);
+ self->gpio->GPIO_INTTYPE_LEVEL &= ~(1 << self->pin);
+
+ // Configure GPIO IRQ trigger
+ if (trigger == MP_HAL_PIN_TRIGGER_FALL) {
+ gpio_interrupt_set_edge_trigger(self->gpio, self->pin);
+ gpio_interrupt_set_polarity_low(self->gpio, self->pin);
+ } else if (trigger == MP_HAL_PIN_TRIGGER_RISE) {
+ gpio_interrupt_set_edge_trigger(self->gpio, self->pin);
+ gpio_interrupt_set_polarity_high(self->gpio, self->pin);
+ } else if (trigger == (MP_HAL_PIN_TRIGGER_FALL | MP_HAL_PIN_TRIGGER_RISE)) {
+ gpio_interrupt_set_both_edge_trigger(self->gpio, self->pin);
+ } else {
+ mp_raise_ValueError(MP_ERROR_TEXT("Invalid IRQ trigger"));
+ }
+
+ // Clear GPIO IRQ (must be done after configuring trigger)
+ gpio_interrupt_eoi(self->gpio, self->pin);
+
+ // Clear and enable NVIC GPIO IRQ.
+ NVIC_ClearPendingIRQ(irq->irq_num);
+ NVIC_SetPriority(irq->irq_num, IRQ_PRI_GPIO);
+ NVIC_EnableIRQ(irq->irq_num);
+
+ return 0;
+}
+
+static mp_uint_t machine_pin_irq_info(mp_obj_t self_in, mp_uint_t info_type) {
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin);
+
+ if (info_type == MP_IRQ_INFO_FLAGS) {
+ return irq->flags;
+ } else if (info_type == MP_IRQ_INFO_TRIGGERS) {
+ return irq->trigger;
+ }
+ return 0;
+}
+
+static const mp_irq_methods_t machine_pin_irq_methods = {
+ .trigger = machine_pin_irq_trigger,
+ .info = machine_pin_irq_info,
+};
+
+// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False)
+static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ enum { ARG_handler, ARG_trigger, ARG_hard };
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ { MP_QSTR_trigger, MP_ARG_INT, {.u_int = MP_HAL_PIN_TRIGGER_FALL | MP_HAL_PIN_TRIGGER_RISE} },
+ { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
+ };
+
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[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);
+
+ machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin);
+
+ // Allocate a new IRQ object if it doesn't exist.
+ if (irq == NULL) {
+ irq = m_new_obj(machine_pin_irq_obj_t);
+ uint32_t idx = MACHINE_PIN_IRQ_INDEX(self->port, self->pin);
+
+ irq->base.base.type = &mp_irq_type;
+ irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods;
+ irq->base.parent = MP_OBJ_FROM_PTR(self);
+ irq->base.handler = mp_const_none;
+ irq->base.ishard = false;
+ irq->reserved = false;
+ irq->irq_num = (self->port < 15) ? (GPIO0_IRQ0_IRQn + idx) : (LPGPIO_IRQ0_IRQn + self->pin);
+ MP_STATE_PORT(machine_pin_irq_obj[idx]) = irq;
+ }
+
+ if (n_args > 1 || kw_args->used != 0) {
+ if (irq->reserved) {
+ mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Pin IRQ is reserved"));
+ }
+ irq->base.handler = args[ARG_handler].u_obj;
+ irq->base.ishard = args[ARG_hard].u_bool;
+ machine_pin_irq_trigger(self, args[ARG_trigger].u_int);
+ }
+
+ return MP_OBJ_FROM_PTR(irq);
+}
+static MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq);
+
+void machine_pin_irq_deinit(void) {
+ for (size_t i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(machine_pin_irq_obj)); i++) {
+ machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[i]);
+ if (irq != NULL) {
+ machine_pin_obj_t *self = MP_OBJ_TO_PTR(irq->base.parent);
+ machine_pin_irq_trigger(self, 0);
+ MP_STATE_PORT(machine_pin_irq_obj[i]) = NULL;
+ }
+ }
+}
+
static MP_DEFINE_CONST_OBJ_TYPE(
pin_cpu_pins_obj_type,
MP_QSTR_cpu,
@@ -248,6 +440,7 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_low_obj) },
{ MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_high_obj) },
{ MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) },
+ { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) },
// class attributes
{ MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&pin_board_pins_obj_type) },
@@ -259,6 +452,8 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(MP_HAL_PIN_MODE_OPEN_DRAIN) },
{ MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(MP_HAL_PIN_PULL_UP) },
{ MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(MP_HAL_PIN_PULL_DOWN) },
+ { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(MP_HAL_PIN_TRIGGER_FALL) },
+ { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(MP_HAL_PIN_TRIGGER_RISE) },
};
static MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table);
@@ -296,3 +491,5 @@ MP_DEFINE_CONST_OBJ_TYPE(
mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) {
return machine_pin_find(obj);
}
+
+MP_REGISTER_ROOT_POINTER(void *machine_pin_irq_obj[MACHINE_PIN_NUM_VECTORS]);
diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c
index abb60bc4e8..b2c14745cf 100644
--- a/ports/alif/machine_spi.c
+++ b/ports/alif/machine_spi.c
@@ -39,6 +39,7 @@ typedef struct _machine_spi_obj_t {
uint8_t id;
SPI_Type *inst;
bool is_lp;
+ uint32_t bits;
} machine_spi_obj_t;
static machine_spi_obj_t machine_spi_obj[] = {
@@ -246,6 +247,9 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
// Get static peripheral object.
machine_spi_obj_t *self = &machine_spi_obj[spi_id];
+ // Set args
+ self->bits = args[ARG_bits].u_int;
+
// here we would check the sck/mosi/miso pins and configure them, but it's not implemented
if (args[ARG_sck].u_obj != MP_OBJ_NULL ||
args[ARG_mosi].u_obj != MP_OBJ_NULL ||
@@ -294,22 +298,50 @@ static void machine_spi_deinit(mp_obj_base_t *self_in) {
}
}
+static void machine_spi_poll_flag(SPI_Type *spi, uint32_t flag, uint32_t timeout) {
+ mp_uint_t tick_start = mp_hal_ticks_ms();
+ while (!(spi->SPI_SR & flag)) {
+ if (mp_hal_ticks_ms() - tick_start >= timeout) {
+ mp_raise_OSError(MP_ETIMEDOUT);
+ }
+ mp_event_handle_nowait();
+ }
+}
+
static void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) {
machine_spi_obj_t *self = (machine_spi_obj_t *)self_in;
- spi_transfer_t spi_xfer = {
- .tx_buff = src,
- .tx_total_cnt = len,
- .rx_buff = dest,
- .rx_total_cnt = len,
- .tx_default_val = 0xFF,
- .tx_default_enable = true,
- .mode = SPI_TMOD_TX_AND_RX,
- };
- // TODO redo transfer_blocking to timeout and poll events.
- if (!self->is_lp) {
- spi_transfer_blocking(self->inst, &spi_xfer);
- } else {
- lpspi_transfer_blocking(self->inst, &spi_xfer);
+ volatile uint32_t *dr = self->inst->SPI_DR;
+
+ spi_set_tmod(self->inst, SPI_TMOD_TX_AND_RX);
+
+ for (size_t i = 0; i < len; i++) {
+ // Wait for space in the TX FIFO
+ machine_spi_poll_flag(self->inst, SPI_SR_TFNF, 100);
+
+ // Send data
+ if (src == NULL) {
+ *dr = 0xFFFFFFFFU;
+ } else if (self->bits > 16) {
+ *dr = ((uint32_t *)src)[i];
+ } else if (self->bits > 8) {
+ *dr = ((uint16_t *)src)[i];
+ } else {
+ *dr = ((uint8_t *)src)[i];
+ }
+
+ // Wait for data in the RX FIFO
+ machine_spi_poll_flag(self->inst, SPI_SR_RFNE, 100);
+
+ // Recv data
+ if (dest == NULL) {
+ (void)*dr;
+ } else if (self->bits > 16) {
+ ((uint32_t *)dest)[i] = *dr;
+ } else if (self->bits > 8) {
+ ((uint16_t *)dest)[i] = *dr;
+ } else {
+ ((uint8_t *)dest)[i] = *dr;
+ }
}
}
diff --git a/ports/alif/main.c b/ports/alif/main.c
index fd35604cbc..ab5e85d5b9 100644
--- a/ports/alif/main.c
+++ b/ports/alif/main.c
@@ -55,8 +55,9 @@
extern uint8_t __StackTop, __StackLimit;
extern uint8_t __GcHeapStart, __GcHeapEnd;
+extern void machine_pin_irq_deinit(void);
-NORETURN void panic(const char *msg) {
+MP_NORETURN void panic(const char *msg) {
mp_hal_stdout_tx_strn("\nFATAL ERROR:\n", 14);
mp_hal_stdout_tx_strn(msg, strlen(msg));
for (;;) {
@@ -164,6 +165,7 @@ int main(void) {
mp_bluetooth_deinit();
#endif
soft_timer_deinit();
+ machine_pin_irq_deinit();
gc_sweep_all();
mp_deinit();
}
diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c
index 9868abbeea..71b5be5193 100644
--- a/ports/alif/modmachine.c
+++ b/ports/alif/modmachine.c
@@ -47,11 +47,11 @@ static mp_obj_t mp_machine_unique_id(void) {
return mp_obj_new_bytes(id, sizeof(id));
}
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
se_services_reset_soc();
}
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
__disable_irq();
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
@@ -112,7 +112,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
#endif
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
#if MICROPY_HW_ENABLE_USBDEV
mp_machine_enable_usb(false);
#endif
diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h
index 6c9e6af635..ddfc551bf8 100644
--- a/ports/alif/mpconfigport.h
+++ b/ports/alif/mpconfigport.h
@@ -158,19 +158,11 @@
#define MICROPY_FATFS_MAX_SS (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES)
#endif
-#if MICROPY_PY_NETWORK_CYW43
-extern const struct _mp_obj_type_t mp_network_cyw43_type;
-#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) },
-#else
-#define MICROPY_HW_NIC_CYW43
-#endif
-
#ifndef MICROPY_BOARD_NETWORK_INTERFACES
#define MICROPY_BOARD_NETWORK_INTERFACES
#endif
#define MICROPY_PORT_NETWORK_INTERFACES \
- MICROPY_HW_NIC_CYW43 \
MICROPY_BOARD_NETWORK_INTERFACES \
// Bluetooth code only runs in the scheduler, no locking/mutex required.
diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h
index 4f81439b54..f03dc7c1c1 100644
--- a/ports/alif/mphalport.h
+++ b/ports/alif/mphalport.h
@@ -90,6 +90,9 @@ extern ringbuf_t stdin_ringbuf;
#define MP_HAL_PIN_SPEED_LOW (0)
#define MP_HAL_PIN_SPEED_HIGH (PADCTRL_SLEW_RATE_FAST)
+#define MP_HAL_PIN_TRIGGER_FALL (1)
+#define MP_HAL_PIN_TRIGGER_RISE (2)
+
#define mp_hal_pin_obj_t const machine_pin_obj_t *
#define MP_HAL_PIN_ALT(function, unit) (MP_HAL_PIN_ALT_MAKE((MP_HAL_PIN_ALT_##function), (unit)))
diff --git a/ports/cc3200/misc/mperror.c b/ports/cc3200/misc/mperror.c
index 6d6c0ff0ba..69f0a25371 100644
--- a/ports/cc3200/misc/mperror.c
+++ b/ports/cc3200/misc/mperror.c
@@ -159,7 +159,7 @@ void mperror_heartbeat_signal (void) {
}
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
#ifdef DEBUG
if (msg != NULL) {
// wait for 20ms
diff --git a/ports/cc3200/misc/mperror.h b/ports/cc3200/misc/mperror.h
index 1c3eb62697..30effbbf91 100644
--- a/ports/cc3200/misc/mperror.h
+++ b/ports/cc3200/misc/mperror.h
@@ -27,7 +27,7 @@
#ifndef MICROPY_INCLUDED_CC3200_MISC_MPERROR_H
#define MICROPY_INCLUDED_CC3200_MISC_MPERROR_H
-extern void NORETURN __fatal_error(const char *msg);
+extern void MP_NORETURN __fatal_error(const char *msg);
void mperror_init0 (void);
void mperror_bootloader_check_reset_cause (void);
diff --git a/ports/cc3200/mods/modmachine.c b/ports/cc3200/mods/modmachine.c
index f7184df442..8986635ac6 100644
--- a/ports/cc3200/mods/modmachine.c
+++ b/ports/cc3200/mods/modmachine.c
@@ -93,7 +93,7 @@ extern OsiTaskHandle xSimpleLinkSpawnTaskHndl;
/******************************************************************************/
// MicroPython bindings;
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
// disable wlan
wlan_stop(SL_STOP_TIMEOUT_LONG);
// reset the cpu and it's peripherals
@@ -161,7 +161,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
pyb_sleep_sleep();
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
pyb_sleep_deepsleep();
for (;;) {
}
diff --git a/ports/cc3200/mods/modtime.c b/ports/cc3200/mods/modtime.c
index 21388568ab..254678fb2d 100644
--- a/ports/cc3200/mods/modtime.c
+++ b/ports/cc3200/mods/modtime.c
@@ -50,5 +50,5 @@ static mp_obj_t mp_time_localtime_get(void) {
// Returns the number of seconds, as an integer, since the Epoch.
static mp_obj_t mp_time_time_get(void) {
- return mp_obj_new_int(pyb_rtc_get_seconds());
+ return timeutils_obj_from_timestamp(pyb_rtc_get_seconds());
}
diff --git a/ports/cc3200/mods/pybsleep.c b/ports/cc3200/mods/pybsleep.c
index 9d04dd411d..291860de8d 100644
--- a/ports/cc3200/mods/pybsleep.c
+++ b/ports/cc3200/mods/pybsleep.c
@@ -136,7 +136,7 @@ static MP_DEFINE_CONST_OBJ_TYPE(
******************************************************************************/
static pyb_sleep_obj_t *pyb_sleep_find (mp_obj_t obj);
static void pyb_sleep_flash_powerdown (void);
-static NORETURN void pyb_sleep_suspend_enter (void);
+static MP_NORETURN void pyb_sleep_suspend_enter (void);
void pyb_sleep_suspend_exit (void);
static void pyb_sleep_obj_wakeup (void);
static void PRCMInterruptHandler (void);
@@ -360,7 +360,7 @@ static void pyb_sleep_flash_powerdown (void) {
MAP_SPICSDisable(SSPI_BASE);
}
-static NORETURN void pyb_sleep_suspend_enter (void) {
+static MP_NORETURN void pyb_sleep_suspend_enter (void) {
// enable full RAM retention
MAP_PRCMSRAMRetentionEnable(PRCM_SRAM_COL_1 | PRCM_SRAM_COL_2 | PRCM_SRAM_COL_3 | PRCM_SRAM_COL_4, PRCM_SRAM_LPDS_RET);
diff --git a/ports/esp32/README.md b/ports/esp32/README.md
index d9115072a4..d8b55e45f3 100644
--- a/ports/esp32/README.md
+++ b/ports/esp32/README.md
@@ -31,7 +31,7 @@ manage the ESP32 microcontroller, as well as a way to manage the required
build environment and toolchains needed to build the firmware.
The ESP-IDF changes quickly and MicroPython only supports certain versions.
-Currently MicroPython supports v5.2, v5.2.2, v5.3 and v5.4.
+Currently MicroPython supports v5.2, v5.2.2, v5.3, v5.4 and v5.4.1.
To install the ESP-IDF the full instructions can be found at the
[Espressif Getting Started guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#installation-step-by-step).
@@ -49,10 +49,10 @@ The steps to take are summarised below.
To check out a copy of the IDF use git clone:
```bash
-$ git clone -b v5.2.2 --recursive https://github.com/espressif/esp-idf.git
+$ git clone -b v5.4.1 --recursive https://github.com/espressif/esp-idf.git
```
-You can replace `v5.2.2` with any other supported version.
+You can replace `v5.4.1` with any other supported version.
(You don't need a full recursive clone; see the `ci_esp32_setup` function in
`tools/ci.sh` in this repository for more detailed set-up commands.)
@@ -61,7 +61,7 @@ MicroPython and update the submodules using:
```bash
$ cd esp-idf
-$ git checkout v5.2.2
+$ git checkout v5.4.1
$ git submodule update --init --recursive
```
@@ -221,6 +221,30 @@ import machine
antenna = machine.Pin(16, machine.Pin.OUT, value=0)
```
+Partition table and filesystem size
+-----------------------------------
+
+ESP32 firmware contains a bootloader, partition table and main application
+firmware, which are all stored in (external) SPI flash. The user filesystem
+is also stored in the same SPI flash. By default, MicroPython does not have
+a fixed entry in the ESP32 partition table for the filesystem. Instead it
+will automatically determine the size of the SPI flash upon boot, and then --
+so long as there is no existing partition called "vfs" or "ffat" -- it will
+create a partition for the filesystem called "vfs" which takes all of the
+remaining flash between the end of the last defined partition up until the
+end of flash.
+
+This means that firmware built for, say, a 4MiB flash will work on any
+device that has at least 4MiB flash. The user "vfs" filesystem will then
+take up as much space as possible.
+
+This auto-detection behaviour can be overridden: if the ESP32 partition
+table does contain an entry called "vfs" or "ffat" then these are used for
+the user filesystem and no automatic "vfs" partition is added. This is
+useful in cases where only the MicroPython ESP32 application is flashed to
+the device (and not the bootloader or partition table), eg when deploying
+.uf2 files.
+
Defining a custom ESP32 board
-----------------------------
diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c
index 83af6dce0b..d0e5e81330 100644
--- a/ports/esp32/adc.c
+++ b/ports/esp32/adc.c
@@ -26,83 +26,73 @@
*/
#include "py/mphal.h"
+#include "py/mperrno.h"
#include "adc.h"
-#include "driver/adc.h"
+#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali_scheme.h"
-#define DEFAULT_VREF 1100
+static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten);
-void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits) {
- if (bits < SOC_ADC_RTC_MIN_BITWIDTH && bits > SOC_ADC_RTC_MAX_BITWIDTH) {
- // Invalid value for the current chip, raise exception in the switch below.
- bits = -1;
+void adc_is_init_guard(machine_adc_block_obj_t *self) {
+ if (!self->handle) {
+ mp_raise_OSError(MP_EPERM);
}
- switch (bits) {
- case 9:
- self->width = ADC_BITWIDTH_9;
- break;
- case 10:
- self->width = ADC_BITWIDTH_10;
- break;
- case 11:
- self->width = ADC_BITWIDTH_11;
- break;
- case 12:
- self->width = ADC_BITWIDTH_12;
- break;
- case 13:
- self->width = ADC_BITWIDTH_13;
- break;
- default:
- mp_raise_ValueError(MP_ERROR_TEXT("invalid bits"));
- }
- self->bits = bits;
+}
- if (self->unit_id == ADC_UNIT_1) {
- adc1_config_width(self->width);
- }
+esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten) {
+ adc_is_init_guard(self->block);
+
+ adc_oneshot_chan_cfg_t config = {
+ .atten = atten,
+ .bitwidth = self->block->bitwidth,
+ };
+ esp_err_t ret = adc_oneshot_config_channel(self->block->handle, self->channel_id, &config);
+ return ret;
}
mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id) {
- int raw = 0;
- if (self->unit_id == ADC_UNIT_1) {
- raw = adc1_get_raw(channel_id);
- } else {
- #if (SOC_ADC_PERIPH_NUM >= 2)
- check_esp_err(adc2_get_raw(channel_id, self->width, &raw));
- #endif
- }
- return raw;
+ adc_is_init_guard(self);
+
+ int reading = 0;
+ adc_oneshot_read(self->handle, channel_id, &reading);
+ return reading;
+}
+
+/*
+During testing, it turned out that the function `adc_cali_raw_to_voltage` does not account for the lower resolution,
+instead it expects the full resolution value as an argument, hence the scaling applied here
+*/
+mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) {
+ int raw = madcblock_read_helper(self, channel_id);
+ int uv = 0;
+
+ check_esp_err(ensure_adc_calibration(self, atten));
+ check_esp_err(adc_cali_raw_to_voltage(self->calib[atten], (raw << (ADC_WIDTH_MAX - self->bitwidth)), &uv));
+ return (mp_int_t)uv * 1000;
}
static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten) {
- if (self->handle[atten] != NULL) {
+ if (self->calib[atten] != NULL) {
return ESP_OK;
}
+ esp_err_t ret = ESP_OK;
+
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = self->unit_id,
.atten = atten,
- .bitwidth = self->width,
+ .bitwidth = self->bitwidth,
};
- return adc_cali_create_scheme_curve_fitting(&cali_config, &self->handle[atten]);
+ ret = adc_cali_create_scheme_curve_fitting(&cali_config, &self->calib[atten]);
#else
adc_cali_line_fitting_config_t cali_config = {
.unit_id = self->unit_id,
.atten = atten,
- .bitwidth = self->width,
+ .bitwidth = self->bitwidth,
};
- return adc_cali_create_scheme_line_fitting(&cali_config, &self->handle[atten]);
+ ret = adc_cali_create_scheme_line_fitting(&cali_config, &self->calib[atten]);
#endif
-}
-mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) {
- int raw = madcblock_read_helper(self, channel_id);
- int uv;
-
- check_esp_err(ensure_adc_calibration(self, atten));
- check_esp_err(adc_cali_raw_to_voltage(self->handle[atten], raw, &uv));
-
- return (mp_int_t)uv * 1000;
+ return ret;
}
diff --git a/ports/esp32/adc.h b/ports/esp32/adc.h
index 5688e0a29a..ebf7fcc21a 100644
--- a/ports/esp32/adc.h
+++ b/ports/esp32/adc.h
@@ -29,17 +29,45 @@
#define MICROPY_INCLUDED_ESP32_ADC_H
#include "py/runtime.h"
-#include "esp_adc_cal.h"
+#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali_scheme.h"
-#define ADC_ATTEN_MAX SOC_ADC_ATTEN_NUM
+#define ADC_ATTEN_COUNT SOC_ADC_ATTEN_NUM
+#define ADC_ATTEN_MIN ADC_ATTEN_DB_0
+#define ADC_ATTEN_MAX ADC_ATTEN_DB_11
+
+/*
+https://github.com/espressif/esp-idf/issues/13128
+https://github.com/espressif/esp-idf/blob/release/v5.2/components/soc/esp32s3/include/soc/soc_caps.h
+https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32s2/03-errata-description/index.html
+https://docs.espressif.com/projects/esp-idf/en/v4.2/esp32/api-reference/peripherals/adc.html
+
+Looks like only the original esp32 is capable of bitwidth adjustment, all others are stuck at 12 bits,
+except the S2, which is locked at 13 bits, otherwise attenuation doesn't work.
+*/
+#if CONFIG_IDF_TARGET_ESP32S2
+
+#define ADC_WIDTH_MIN ADC_BITWIDTH_13
+#define ADC_WIDTH_MAX ADC_BITWIDTH_13
+
+#elif CONFIG_IDF_TARGET_ESP32
+
+#define ADC_WIDTH_MIN ADC_BITWIDTH_9
+#define ADC_WIDTH_MAX ADC_BITWIDTH_12
+
+#else
+
+#define ADC_WIDTH_MIN ADC_BITWIDTH_12
+#define ADC_WIDTH_MAX ADC_BITWIDTH_12
+
+#endif
typedef struct _machine_adc_block_obj_t {
mp_obj_base_t base;
adc_unit_t unit_id;
- mp_int_t bits;
- adc_bits_width_t width;
- adc_cali_handle_t handle[ADC_ATTEN_MAX];
+ adc_oneshot_unit_handle_t handle;
+ adc_bitwidth_t bitwidth;
+ adc_cali_handle_t calib[ADC_ATTEN_COUNT];
} machine_adc_block_obj_t;
typedef struct _machine_adc_obj_t {
@@ -51,11 +79,18 @@ typedef struct _machine_adc_obj_t {
extern machine_adc_block_obj_t madcblock_obj[];
-void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits);
+esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten);
+
mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id);
mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten);
const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id);
void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self);
+void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten);
+
+mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self);
+void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width);
+
#endif // MICROPY_INCLUDED_ESP32_ADC_H
diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json
index 9d0016017f..936a498b5e 100644
--- a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json
+++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json
@@ -17,8 +17,8 @@
"ABX00092_01.iso_1000x750.jpg"
],
"mcu": "esp32s3",
- "product": "Arduino Nano ESP32",
+ "product": "Nano ESP32",
"thumbnail": "",
- "url": "https://store.arduino.cc/products/arduino-nano-esp32",
+ "url": "https://docs.arduino.cc/hardware/nano-esp32/",
"vendor": "Arduino"
}
diff --git a/ports/esp32/boards/ESP32_GENERIC/board.md b/ports/esp32/boards/ESP32_GENERIC/board.md
index 9346d18d84..06972e436f 100644
--- a/ports/esp32/boards/ESP32_GENERIC/board.md
+++ b/ports/esp32/boards/ESP32_GENERIC/board.md
@@ -1,5 +1,5 @@
The following files are firmware that should work on most ESP32-based boards
-with 4MiB of flash, including WROOM WROVER, SOLO, PICO, and MINI modules.
+with 4MiB or more of flash, including WROOM WROVER, SOLO, PICO, and MINI modules.
This board has multiple variants available:
diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/board.md b/ports/esp32/boards/ESP32_GENERIC_C3/board.md
index 1604b879c5..6097eb85df 100644
--- a/ports/esp32/boards/ESP32_GENERIC_C3/board.md
+++ b/ports/esp32/boards/ESP32_GENERIC_C3/board.md
@@ -1,6 +1,6 @@
The following files are firmware images that should work on most
-ESP32-C3-based boards with 4MiB of flash, including WROOM and MINI modules,
-that use the revision 3 silicon (or newer).
+ESP32-C3-based boards with 4MiB or more of flash, including WROOM and MINI
+modules, that use the revision 3 silicon (or newer).
USB serial/JTAG support is enabled on pin 18 and 19. Note that this
is not a full USB stack, the C3 just provides a CDC/ACM class serial
diff --git a/ports/esp32/boards/ESP32_GENERIC_S2/board.md b/ports/esp32/boards/ESP32_GENERIC_S2/board.md
index 3e46c02467..d065ecbbb9 100644
--- a/ports/esp32/boards/ESP32_GENERIC_S2/board.md
+++ b/ports/esp32/boards/ESP32_GENERIC_S2/board.md
@@ -1,5 +1,5 @@
The following files are firmware that should work on most ESP32-S2-based
-boards with 4MiB of flash, including WROOM, WROVER, and MINI modules.
+boards with 4MiB or more of flash, including WROOM, WROVER, and MINI modules.
This firmware supports configurations with and without SPIRAM (also known as
PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate
diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.json b/ports/esp32/boards/ESP32_GENERIC_S3/board.json
index fd0c9edce0..7a546d35fc 100644
--- a/ports/esp32/boards/ESP32_GENERIC_S3/board.json
+++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.json
@@ -21,7 +21,6 @@
"url": "https://www.espressif.com/en/products/modules",
"vendor": "Espressif",
"variants": {
- "SPIRAM_OCT": "Support for Octal-SPIRAM",
- "FLASH_4M": "4MiB flash"
+ "SPIRAM_OCT": "Support for Octal-SPIRAM"
}
}
diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.md b/ports/esp32/boards/ESP32_GENERIC_S3/board.md
index 16930c9193..68ce0917e0 100644
--- a/ports/esp32/boards/ESP32_GENERIC_S3/board.md
+++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.md
@@ -1,9 +1,7 @@
The following files are firmware that should work on most ESP32-S3-based
-boards with 4/8MiB of flash, including WROOM and MINI modules.
+boards with 4MiB or more of flash, including WROOM and MINI modules.
This firmware supports configurations with and without SPIRAM (also known as
PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate
the MicroPython heap accordingly. However if your board has Octal SPIRAM, then
use the "spiram-oct" variant.
-
-If your board has 4MiB flash (including ESP32-S3FH4R2 based ones with embedded flash), then use the "flash-4m" build.
diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake
deleted file mode 100644
index e832cdb60f..0000000000
--- a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake
+++ /dev/null
@@ -1,4 +0,0 @@
-set(SDKCONFIG_DEFAULTS
- ${SDKCONFIG_DEFAULTS}
- boards/ESP32_GENERIC_S3/sdkconfig.flash_4m
-)
diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board
index 9839b0d300..369330682f 100644
--- a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board
+++ b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board
@@ -1,8 +1,2 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m
deleted file mode 100644
index c967c46ae6..0000000000
--- a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
-
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv"
diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json
new file mode 100644
index 0000000000..d2ada7cccd
--- /dev/null
+++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json
@@ -0,0 +1,23 @@
+{
+ "deploy": [
+ "../deploy_nativeusb.md"
+ ],
+ "deploy_options": {
+ "flash_offset": "0"
+ },
+ "docs": "",
+ "features": [
+ "BLE",
+ "External Flash",
+ "RGB LED",
+ "WiFi"
+ ],
+ "images": [
+ "GAR-PYBSTICK26-C3-mUSB-00.JPG"
+ ],
+ "mcu": "esp32c3",
+ "product": "PYBSTICK26_ESP32C3",
+ "thumbnail": "",
+ "url": "https://shop.mchobby.be/fr/pybstick/2505-pybstick26-esp32-c3-micropython-et-arduino-3232100025059.html",
+ "vendor": "McHobby"
+}
diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake
new file mode 100644
index 0000000000..81cfff1d77
--- /dev/null
+++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake
@@ -0,0 +1,7 @@
+set(IDF_TARGET esp32c3)
+
+set(SDKCONFIG_DEFAULTS
+ boards/sdkconfig.base
+ boards/sdkconfig.ble
+ boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board
+)
diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h
new file mode 100644
index 0000000000..c9796d1f65
--- /dev/null
+++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h
@@ -0,0 +1,10 @@
+#define MICROPY_HW_BOARD_NAME "PYBSTICK26_ESP32C3"
+#define MICROPY_HW_MCU_NAME "ESP32C3"
+#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "pybstick26_esp32c3"
+
+#define MICROPY_HW_I2C0_SCL (1)
+#define MICROPY_HW_I2C0_SDA (0)
+
+#define MICROPY_HW_SPI1_MOSI (7)
+#define MICROPY_HW_SPI1_MISO (2)
+#define MICROPY_HW_SPI1_SCK (6)
diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv
new file mode 100644
index 0000000000..d786654903
--- /dev/null
+++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv
@@ -0,0 +1,13 @@
+GP0,GPIO0
+GP1,GPIO1
+GP2,GPIO2
+GP3,GPIO3
+GP4,GPIO4
+GP5,GPIO5
+GP6,GPIO6
+GP7,GPIO7
+GP8,GPIO8
+GP9,GPIO9
+GP10,GPIO10
+GP20,GPIO20
+GP21,GPIO21
diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board
new file mode 100644
index 0000000000..70e5f1f0d1
--- /dev/null
+++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board
@@ -0,0 +1,4 @@
+CONFIG_ESP32C3_REV_MIN_3=y
+
+# Workaround for https://github.com/espressif/esp-idf/issues/14456
+CONFIG_ESP_SYSTEM_HW_STACK_GUARD=n
diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json
index d68a9baadd..7521c01a6b 100644
--- a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json
+++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json
@@ -19,8 +19,8 @@
"lilygo-ttgo-lora-32-v1-6.jpg"
],
"mcu": "esp32",
- "product": "LILYGO TTGO LoRa32",
+ "product": "TTGO LoRa32",
"thumbnail": "",
- "url": "http://www.lilygo.cn/prod_view.aspx?TypeId=50060&Id=1270&FId=t3:50060:3",
+ "url": "https://www.lilygo.cc/products/lora3",
"vendor": "LILYGO"
}
diff --git a/ports/esp32/boards/M5STACK_ATOM/board.json b/ports/esp32/boards/M5STACK_ATOM/board.json
index 3a1e7ce359..c3908a4779 100644
--- a/ports/esp32/boards/M5STACK_ATOM/board.json
+++ b/ports/esp32/boards/M5STACK_ATOM/board.json
@@ -18,8 +18,8 @@
"m5stack_atom.jpg"
],
"mcu": "esp32",
- "product": "M5 Stack Atom",
+ "product": "Atom",
"thumbnail": "",
- "url": "https://m5stack.com/",
- "vendor": "M5 Stack"
+ "url": "https://shop.m5stack.com/products/atom-matrix-esp32-development-kit",
+ "vendor": "M5Stack"
}
diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json
index fe0e97f9f9..26b1a4d54a 100644
--- a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json
+++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json
@@ -20,5 +20,5 @@
"product": "AtomS3 Lite",
"thumbnail": "",
"url": "https://shop.m5stack.com/products/atoms3-lite-esp32s3-dev-kit",
- "vendor": "M5 Stack"
+ "vendor": "M5Stack"
}
diff --git a/ports/esp32/boards/M5STACK_NANOC6/board.json b/ports/esp32/boards/M5STACK_NANOC6/board.json
index 087851ae5e..ddf3406c83 100644
--- a/ports/esp32/boards/M5STACK_NANOC6/board.json
+++ b/ports/esp32/boards/M5STACK_NANOC6/board.json
@@ -18,7 +18,7 @@
"m5stack_nanoc6.jpg"
],
"mcu": "esp32c6",
- "product": "M5Stack NanoC6",
+ "product": "NanoC6",
"url": "https://shop.m5stack.com/products/m5stack-nanoc6-dev-kit",
"vendor": "M5Stack"
}
diff --git a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json
index 3eb9a5e0a7..198f1d4b6d 100644
--- a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json
+++ b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json
@@ -18,8 +18,8 @@
"ESP32-EVB_Rev_K1.png"
],
"mcu": "esp32",
- "product": "Olimex ESP32 EVB",
+ "product": "ESP32 EVB",
"thumbnail": "",
- "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB/open-source-hardware",
- "vendor": "OLIMEX"
+ "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB",
+ "vendor": "Olimex"
}
diff --git a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json
index cda26178d7..1b00d213c7 100644
--- a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json
+++ b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json
@@ -19,8 +19,8 @@
"ESP32-POE-ISO-1.jpg"
],
"mcu": "esp32",
- "product": "Olimex ESP32 POE",
+ "product": "ESP32 POE",
"thumbnail": "",
- "url": "https://www.olimex.com/",
- "vendor": "OLIMEX"
+ "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE",
+ "vendor": "Olimex"
}
diff --git a/ports/esp32/boards/SIL_WESP32/board.json b/ports/esp32/boards/SIL_WESP32/board.json
index 53a50f3286..7126fa7ea1 100644
--- a/ports/esp32/boards/SIL_WESP32/board.json
+++ b/ports/esp32/boards/SIL_WESP32/board.json
@@ -18,7 +18,7 @@
"wesp32-top.jpg"
],
"mcu": "esp32",
- "product": "SIL WESP32",
+ "product": "wESP32",
"thumbnail": "",
"url": "https://wesp32.com/",
"vendor": "Silicognition"
diff --git a/ports/esp32/boards/SIL_WESP32/sdkconfig.board b/ports/esp32/boards/SIL_WESP32/sdkconfig.board
index 741a4db439..36155cd5e3 100644
--- a/ports/esp32/boards/SIL_WESP32/sdkconfig.board
+++ b/ports/esp32/boards/SIL_WESP32/sdkconfig.board
@@ -1,9 +1,6 @@
-# 16 MB flash
+# 8 MB+ flash
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE="16MB"
+CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
# Fast flash
@@ -14,7 +11,7 @@ CONFIG_ESP32_REV_MIN_1=y
# OTA
CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB-ota.csv"
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiBplus-ota.csv"
# Network name
diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json
new file mode 100644
index 0000000000..8bce1a0013
--- /dev/null
+++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json
@@ -0,0 +1,23 @@
+{
+ "deploy": [
+ "../deploy.md"
+ ],
+ "deploy_options": {
+ "flash_offset": "0x1000"
+ },
+ "docs": "",
+ "features": [
+ "BLE",
+ "External Flash",
+ "WiFi",
+ "USB-C"
+ ],
+ "images": [
+ "19177-Sparkfun_IoT_Redboard-ESP32.jpg"
+ ],
+ "mcu": "esp32",
+ "product": "ESP32 / WROOM",
+ "thumbnail": "",
+ "url": "https://www.sparkfun.com/sparkfun-iot-redboard-esp32-development-board.html",
+ "vendor": "SparkFun"
+}
diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py
new file mode 100644
index 0000000000..73446ecac9
--- /dev/null
+++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py
@@ -0,0 +1,2 @@
+include("$(PORT_DIR)/boards/manifest.py")
+require("sdcard")
diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake
new file mode 100644
index 0000000000..0ffcc38c87
--- /dev/null
+++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake
@@ -0,0 +1,7 @@
+set(SDKCONFIG_DEFAULTS
+ boards/sdkconfig.base
+ boards/sdkconfig.ble
+ boards/sdkconfig.240mhz
+)
+
+set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h
new file mode 100644
index 0000000000..4fe888ac17
--- /dev/null
+++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h
@@ -0,0 +1,14 @@
+// Board and hardware specific configuration
+
+#define MICROPY_HW_BOARD_NAME "SparkFun IoT RedBoard ESP32"
+#define MICROPY_HW_MCU_NAME "ESP32"
+
+// Enable UART REPL for modules that have an external USB-UART and don't use native USB.
+#define MICROPY_HW_ENABLE_UART_REPL (1)
+
+#define MICROPY_HW_I2C0_SCL (22)
+#define MICROPY_HW_I2C0_SDA (21)
+
+#define MICROPY_HW_SPI1_SCK (18)
+#define MICROPY_HW_SPI1_MOSI (23)
+#define MICROPY_HW_SPI1_MISO (19)
diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv
new file mode 100644
index 0000000000..d37c51af8f
--- /dev/null
+++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv
@@ -0,0 +1,22 @@
+TX,GPIO1
+RX,GPIO3
+ALERT,GPIO4
+TCK,GPIO13
+TMS,GPIO14
+CS,GPIO5
+PICO,GPIO23
+POCI,GPIO19
+SCK,GPIO18
+SDA,GPIO21
+SCL,GPIO22
+A0,GPIO36
+A3,GPIO39
+A4,GPIO32
+A5,GPIO33
+A6,GPIO34
+A7,GPIO35
+LED,GPIO18
+LED_BLUE,GPIO18
+BLUE_LED,GPIO18
+RGB_LED,GPIO2
+NEOPIXEL,GPIO2
diff --git a/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board
index 9ab58f215f..162de0da14 100644
--- a/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board
+++ b/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board
@@ -3,11 +3,6 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_SPIRAM_MEMTEST=
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv"
-
# LWIP
CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS2"
# end of LWIP
diff --git a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board
index 3ca0c4b243..3092e35598 100644
--- a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board
@@ -1,12 +1,7 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3"
diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board
index d0355a94dc..25b8d7689d 100644
--- a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board
+++ b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board
@@ -3,11 +3,6 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_AFTER_NORESET=y
CONFIG_SPIRAM_MEMTEST=
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3Neo"
diff --git a/ports/esp32/boards/UM_NANOS3/sdkconfig.board b/ports/esp32/boards/UM_NANOS3/sdkconfig.board
index 2a39c64337..e06f7a4245 100644
--- a/ports/esp32/boards/UM_NANOS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_NANOS3/sdkconfig.board
@@ -1,12 +1,7 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMNanoS3"
diff --git a/ports/esp32/boards/UM_OMGS3/sdkconfig.board b/ports/esp32/boards/UM_OMGS3/sdkconfig.board
index 84a8ce449e..8a0bf0b13a 100644
--- a/ports/esp32/boards/UM_OMGS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_OMGS3/sdkconfig.board
@@ -2,12 +2,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_AFTER_NORESET=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMOMGS3"
diff --git a/ports/esp32/boards/UM_PROS3/sdkconfig.board b/ports/esp32/boards/UM_PROS3/sdkconfig.board
index 5752e03e60..75ca58d80b 100644
--- a/ports/esp32/boards/UM_PROS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_PROS3/sdkconfig.board
@@ -1,12 +1,7 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMProS3"
diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board
index ef3f38af47..7d244fdc16 100644
--- a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board
+++ b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board
@@ -2,12 +2,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_AFTER_NORESET=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMRGBTouchMini"
diff --git a/ports/esp32/boards/UM_TINYC6/sdkconfig.board b/ports/esp32/boards/UM_TINYC6/sdkconfig.board
index 7917467b12..213d28d8b4 100644
--- a/ports/esp32/boards/UM_TINYC6/sdkconfig.board
+++ b/ports/esp32/boards/UM_TINYC6/sdkconfig.board
@@ -2,9 +2,4 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_AFTER_NORESET=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
diff --git a/ports/esp32/boards/UM_TINYS3/sdkconfig.board b/ports/esp32/boards/UM_TINYS3/sdkconfig.board
index d1d19761a0..2474c5fb27 100644
--- a/ports/esp32/boards/UM_TINYS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_TINYS3/sdkconfig.board
@@ -1,12 +1,7 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyS3"
diff --git a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board
index 1380e15ce8..10121d235d 100644
--- a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board
+++ b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board
@@ -1,12 +1,7 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
-CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
-CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
CONFIG_SPIRAM_MEMTEST=
-CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyWATCHS3"
diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base
index 530db42711..30740af434 100644
--- a/ports/esp32/boards/sdkconfig.base
+++ b/ports/esp32/boards/sdkconfig.base
@@ -50,7 +50,6 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n
CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2
CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=n
-CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK=y
# UDP
CONFIG_LWIP_PPP_SUPPORT=y
@@ -98,7 +97,7 @@ CONFIG_ULP_COPROC_RESERVE_MEM=2040
# For cmake build
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
-CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv"
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiBplus.csv"
# To reduce iRAM usage
CONFIG_ESP32_WIFI_IRAM_OPT=n
@@ -118,7 +117,6 @@ CONFIG_ADC_CAL_LUT_ENABLE=y
CONFIG_UART_ISR_IN_IRAM=y
# IDF 5 deprecated
-CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_ETH_USE_SPI_ETHERNET=y
diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake
index c54f5a5403..09b1203913 100644
--- a/ports/esp32/esp32_common.cmake
+++ b/ports/esp32/esp32_common.cmake
@@ -80,9 +80,7 @@ list(APPEND MICROPY_SOURCE_DRIVERS
${MICROPY_DIR}/drivers/dht/dht.c
)
-list(APPEND GIT_SUBMODULES lib/tinyusb)
if(MICROPY_PY_TINYUSB)
- set(TINYUSB_SRC "${MICROPY_DIR}/lib/tinyusb/src")
string(TOUPPER OPT_MCU_${IDF_TARGET} tusb_mcu)
list(APPEND MICROPY_DEF_TINYUSB
@@ -90,12 +88,6 @@ if(MICROPY_PY_TINYUSB)
)
list(APPEND MICROPY_SOURCE_TINYUSB
- ${TINYUSB_SRC}/tusb.c
- ${TINYUSB_SRC}/common/tusb_fifo.c
- ${TINYUSB_SRC}/device/usbd.c
- ${TINYUSB_SRC}/device/usbd_control.c
- ${TINYUSB_SRC}/class/cdc/cdc_device.c
- ${TINYUSB_SRC}/portable/synopsys/dwc2/dcd_dwc2.c
${MICROPY_DIR}/shared/tinyusb/mp_usbd.c
${MICROPY_DIR}/shared/tinyusb/mp_usbd_cdc.c
${MICROPY_DIR}/shared/tinyusb/mp_usbd_descriptor.c
@@ -103,13 +95,8 @@ if(MICROPY_PY_TINYUSB)
)
list(APPEND MICROPY_INC_TINYUSB
- ${TINYUSB_SRC}
${MICROPY_DIR}/shared/tinyusb/
)
-
- list(APPEND MICROPY_LINK_TINYUSB
- -Wl,--wrap=dcd_event_handler
- )
endif()
list(APPEND MICROPY_SOURCE_PORT
@@ -251,6 +238,7 @@ endif()
# Set compile options for this port.
target_compile_definitions(${MICROPY_TARGET} PUBLIC
+ ${MICROPY_DEF_COMPONENT}
${MICROPY_DEF_CORE}
${MICROPY_DEF_BOARD}
${MICROPY_DEF_TINYUSB}
@@ -263,15 +251,12 @@ target_compile_definitions(${MICROPY_TARGET} PUBLIC
# Disable some warnings to keep the build output clean.
target_compile_options(${MICROPY_TARGET} PUBLIC
+ ${MICROPY_COMPILE_COMPONENT}
-Wno-clobbered
-Wno-deprecated-declarations
-Wno-missing-field-initializers
)
-target_link_options(${MICROPY_TARGET} PUBLIC
- ${MICROPY_LINK_TINYUSB}
-)
-
# Additional include directories needed for private NimBLE headers.
target_include_directories(${MICROPY_TARGET} PUBLIC
${IDF_PATH}/components/bt/host/nimble/nimble
diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c
index be1725c370..02acaa22da 100644
--- a/ports/esp32/machine_adc.c
+++ b/ports/esp32/machine_adc.c
@@ -30,7 +30,7 @@
#include "py/mphal.h"
#include "adc.h"
-#include "driver/adc.h"
+#include "esp_adc/adc_oneshot.h"
#define ADCBLOCK1 (&madcblock_obj[0])
#define ADCBLOCK2 (&madcblock_obj[1])
@@ -126,20 +126,8 @@ static const machine_adc_obj_t madc_obj[] = {
#endif
};
-// These values are initialised to 0, which means the corresponding ADC channel is not initialised.
-// The madc_atten_get/madc_atten_set functions store (atten+1) here so that the uninitialised state
-// can be distinguished from the initialised state.
static uint8_t madc_obj_atten[MP_ARRAY_SIZE(madc_obj)];
-static inline adc_atten_t madc_atten_get(const machine_adc_obj_t *self) {
- uint8_t value = madc_obj_atten[self - &madc_obj[0]];
- return value == 0 ? ADC_ATTEN_MAX : value - 1;
-}
-
-static inline void madc_atten_set(const machine_adc_obj_t *self, adc_atten_t atten) {
- madc_obj_atten[self - &madc_obj[0]] = atten + 1;
-}
-
const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id) {
for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) {
const machine_adc_obj_t *adc = &madc_obj[i];
@@ -152,22 +140,7 @@ const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_
static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
const machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in);
- mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, madc_atten_get(self));
-}
-
-static void madc_atten_helper(const machine_adc_obj_t *self, mp_int_t atten) {
- esp_err_t err = ESP_FAIL;
- if (self->block->unit_id == ADC_UNIT_1) {
- err = adc1_config_channel_atten(self->channel_id, atten);
- } else {
- #if SOC_ADC_PERIPH_NUM >= 2
- err = adc2_config_channel_atten(self->channel_id, atten);
- #endif
- }
- if (err != ESP_OK) {
- mp_raise_ValueError(MP_ERROR_TEXT("invalid atten"));
- }
- madc_atten_set(self, atten);
+ mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, mp_machine_adc_atten_get_helper(self));
}
void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
@@ -182,18 +155,32 @@ void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_pos_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
- mp_int_t atten = args[ARG_atten].u_int;
- if (atten != -1) {
- madc_atten_helper(self, atten);
- } else if (madc_atten_get(self) == ADC_ATTEN_MAX) {
- madc_atten_helper(self, ADC_ATTEN_DB_0);
+
+ if (!self->block->handle) {
+ adc_oneshot_unit_init_cfg_t init_config = {
+ .unit_id = self->block->unit_id
+ };
+ check_esp_err(adc_oneshot_new_unit(&init_config, &self->block->handle));
}
+
+ mp_int_t atten = args[ARG_atten].u_int;
+ mp_machine_adc_atten_set_helper(self, atten != -1 ? atten : ADC_ATTEN_MAX);
+ mp_machine_adc_block_width_set_helper(self->block, ADC_WIDTH_MAX);
+ apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self));
+
}
static void mp_machine_adc_init_helper(machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
madc_init_helper(self, n_pos_args, pos_args, kw_args);
}
+static void mp_machine_adc_deinit(machine_adc_obj_t *self) {
+ if (self->block->handle) {
+ check_esp_err(adc_oneshot_del_unit(self->block->handle));
+ self->block->handle = NULL;
+ }
+}
+
static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_args, size_t n_kw_args, const mp_obj_t *args) {
mp_arg_check_num(n_pos_args, n_kw_args, 1, MP_OBJ_FUN_ARGS_MAX, true);
gpio_num_t gpio_id = machine_pin_get_id(args[0]);
@@ -202,10 +189,6 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_
mp_raise_ValueError(MP_ERROR_TEXT("invalid pin"));
}
- if (self->block->width == -1) {
- madcblock_bits_helper(self->block, self->block->bits);
- }
-
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args);
madc_init_helper(self, n_pos_args - 1, args + 1, &kw_args);
@@ -225,20 +208,46 @@ static mp_int_t mp_machine_adc_read(machine_adc_obj_t *self) {
static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) {
mp_uint_t raw = madcblock_read_helper(self->block, self->channel_id);
// Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16)
- mp_int_t bits = self->block->bits;
+ mp_int_t bits = mp_machine_adc_width_get_helper(self);
mp_uint_t u16 = raw << (16 - bits) | raw >> (2 * bits - 16);
return u16;
}
static mp_int_t mp_machine_adc_read_uv(machine_adc_obj_t *self) {
- adc_atten_t atten = madc_atten_get(self);
- return madcblock_read_uv_helper(self->block, self->channel_id, atten);
+ return madcblock_read_uv_helper(self->block, self->channel_id, mp_machine_adc_atten_get_helper(self));
+}
+
+mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self) {
+ uint8_t value = madc_obj_atten[self - &madc_obj[0]];
+ return value == 0 ? ADC_ATTEN_MAX : value - 1;
+}
+
+void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten) {
+ if (atten < ADC_ATTEN_MIN || atten > ADC_ATTEN_MAX) {
+ mp_raise_ValueError(MP_ERROR_TEXT("invalid attenuation"));
+ }
+
+ madc_obj_atten[self - &madc_obj[0]] = atten + 1;
}
static void mp_machine_adc_atten_set(machine_adc_obj_t *self, mp_int_t atten) {
- madc_atten_helper(self, atten);
+ mp_machine_adc_atten_set_helper(self, atten);
+ apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self));
+}
+
+mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self) {
+ return self->block->bitwidth;
+}
+
+void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width) {
+ if (width < ADC_WIDTH_MIN || width > ADC_WIDTH_MAX) {
+ mp_raise_ValueError(MP_ERROR_TEXT("invalid bit-width"));
+ }
+
+ self->bitwidth = width;
}
static void mp_machine_adc_width_set(machine_adc_obj_t *self, mp_int_t width) {
- madcblock_bits_helper(self->block, width);
+ mp_machine_adc_block_width_set_helper(self->block, width);
+ apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self));
}
diff --git a/ports/esp32/machine_adc_block.c b/ports/esp32/machine_adc_block.c
index a373603b7e..6b06b432c2 100644
--- a/ports/esp32/machine_adc_block.c
+++ b/ports/esp32/machine_adc_block.c
@@ -29,25 +29,21 @@
#include "py/mphal.h"
#include "adc.h"
-#include "driver/adc.h"
+#include "esp_adc/adc_oneshot.h"
machine_adc_block_obj_t madcblock_obj[] = {
- {{&machine_adc_block_type}, ADC_UNIT_1, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}},
+ {{&machine_adc_block_type}, ADC_UNIT_1, NULL, ADC_WIDTH_MAX, {0}},
#if SOC_ADC_PERIPH_NUM > 1
- {{&machine_adc_block_type}, ADC_UNIT_2, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}},
+ {{&machine_adc_block_type}, ADC_UNIT_2, NULL, ADC_WIDTH_MAX, {0}},
#endif
};
static void mp_machine_adc_block_print(const mp_print_t *print, machine_adc_block_obj_t *self) {
- mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bits);
+ mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bitwidth);
}
static void mp_machine_adc_block_bits_set(machine_adc_block_obj_t *self, mp_int_t bits) {
- if (bits != -1) {
- madcblock_bits_helper(self, bits);
- } else if (self->width == -1) {
- madcblock_bits_helper(self, self->bits);
- }
+ mp_machine_adc_block_width_set_helper(self, bits);
}
static machine_adc_block_obj_t *mp_machine_adc_block_get(mp_int_t unit) {
diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c
index 12b86e4aa0..259101ee7e 100644
--- a/ports/esp32/machine_i2c.c
+++ b/ports/esp32/machine_i2c.c
@@ -35,9 +35,14 @@
#if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C
#ifndef MICROPY_HW_I2C0_SCL
+#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3
+#define MICROPY_HW_I2C0_SCL (GPIO_NUM_9)
+#define MICROPY_HW_I2C0_SDA (GPIO_NUM_8)
+#else
#define MICROPY_HW_I2C0_SCL (GPIO_NUM_18)
#define MICROPY_HW_I2C0_SDA (GPIO_NUM_19)
#endif
+#endif
#ifndef MICROPY_HW_I2C1_SCL
#if CONFIG_IDF_TARGET_ESP32
diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c
index d0c4ee1a7e..4ab79f0a26 100644
--- a/ports/esp32/machine_pin.c
+++ b/ports/esp32/machine_pin.c
@@ -325,14 +325,17 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_
mp_raise_ValueError(MP_ERROR_TEXT("bad wake value"));
}
+ #if SOC_TOUCH_SENSOR_SUPPORTED
if (machine_rtc_config.wake_on_touch) { // not compatible
mp_raise_ValueError(MP_ERROR_TEXT("no resources"));
}
+ #endif
if (!RTC_IS_VALID_EXT_PIN(index)) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid pin for wake"));
}
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
if (machine_rtc_config.ext0_pin == -1) {
machine_rtc_config.ext0_pin = index;
} else if (machine_rtc_config.ext0_pin != index) {
@@ -341,10 +344,13 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_
machine_rtc_config.ext0_level = trigger == GPIO_INTR_LOW_LEVEL ? 0 : 1;
machine_rtc_config.ext0_wake_types = wake;
+ #endif
} else {
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
if (machine_rtc_config.ext0_pin == index) {
machine_rtc_config.ext0_pin = -1;
}
+ #endif
if (handler == mp_const_none) {
handler = MP_OBJ_NULL;
diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c
index 7826769556..7cb84640ee 100644
--- a/ports/esp32/machine_pwm.c
+++ b/ports/esp32/machine_pwm.c
@@ -6,7 +6,8 @@
* Copyright (c) 2016-2021 Damien P. George
* Copyright (c) 2018 Alan Dragomirecky
* Copyright (c) 2020 Antoine Aubert
- * Copyright (c) 2021 Ihor Nehrutsa
+ * Copyright (c) 2021, 2023-2025 Ihor Nehrutsa
+ * Copyright (c) 2024 Yoann Darche
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,79 +33,41 @@
#include <math.h>
#include "py/mphal.h"
-#include "driver/ledc.h"
#include "esp_err.h"
+#include "driver/ledc.h"
+#include "soc/ledc_periph.h"
#include "soc/gpio_sig_map.h"
-
-#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
#include "esp_clk_tree.h"
-#endif
-
-#define PWM_DBG(...)
-// #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n");
-
-// Total number of channels
-#define PWM_CHANNEL_MAX (LEDC_SPEED_MODE_MAX * LEDC_CHANNEL_MAX)
-
-typedef struct _chan_t {
- // Which channel has which GPIO pin assigned?
- // (-1 if not assigned)
- gpio_num_t pin;
- // Which channel has which timer assigned?
- // (-1 if not assigned)
- int timer_idx;
-} chan_t;
+#include "py/mpprint.h"
-// List of PWM channels
-static chan_t chans[PWM_CHANNEL_MAX];
+#define debug_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, " | %d at %s\n", __LINE__, __FILE__);
-// channel_idx is an index (end-to-end sequential numbering) for all channels
-// available on the chip and described in chans[]
-#define CHANNEL_IDX(mode, channel) (mode * LEDC_CHANNEL_MAX + channel)
-#define CHANNEL_IDX_TO_MODE(channel_idx) (channel_idx / LEDC_CHANNEL_MAX)
-#define CHANNEL_IDX_TO_CHANNEL(channel_idx) (channel_idx % LEDC_CHANNEL_MAX)
+#define FADE 0
-// Total number of timers
-#define PWM_TIMER_MAX (LEDC_SPEED_MODE_MAX * LEDC_TIMER_MAX)
+// 10-bit user interface resolution compatible with esp8266 PWM.duty()
+#define UI_RES_10_BIT (10)
+#define DUTY_10 UI_RES_10_BIT
+// Maximum duty value on 10-bit resolution is 1024 but reduced to 1023 in UI
+#define MAX_10_DUTY (1U << UI_RES_10_BIT)
-// List of timer configs
-static ledc_timer_config_t timers[PWM_TIMER_MAX];
+// 16-bit user interface resolution used in PWM.duty_u16()
+#define UI_RES_16_BIT (16)
+#define DUTY_16 UI_RES_16_BIT
+// Maximum duty value on 16-bit resolution is 65536 but reduced to 65535 in UI
+#define MAX_16_DUTY (1U << UI_RES_16_BIT)
-// timer_idx is an index (end-to-end sequential numbering) for all timers
-// available on the chip and configured in timers[]
-#define TIMER_IDX(mode, timer) (mode * LEDC_TIMER_MAX + timer)
-#define TIMER_IDX_TO_MODE(timer_idx) (timer_idx / LEDC_TIMER_MAX)
-#define TIMER_IDX_TO_TIMER(timer_idx) (timer_idx % LEDC_TIMER_MAX)
+// ns user interface used in PWM.duty_ns()
+#define DUTY_NS (1)
-// Params for PWM operation
// 5khz is default frequency
-#define PWM_FREQ (5000)
-
-// 10-bit resolution (compatible with esp8266 PWM)
-#define PWM_RES_10_BIT (LEDC_TIMER_10_BIT)
-
-// Maximum duty value on 10-bit resolution
-#define MAX_DUTY_U10 ((1 << PWM_RES_10_BIT) - 1)
-// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html#supported-range-of-frequency-and-duty-resolutions
-// duty() uses 10-bit resolution or less
-// duty_u16() and duty_ns() use 16-bit resolution or less
-
-// Possible highest resolution in device
-#if (LEDC_TIMER_BIT_MAX - 1) < LEDC_TIMER_16_BIT
-#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1)
-#else
-#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used
-#endif
-// Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer
-#define UI_RES_16_BIT (16)
-// Maximum duty value on highest user interface resolution
-#define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1)
-// How much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT
-#define UI_RES_SHIFT (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3
-
-#if SOC_LEDC_SUPPORT_REF_TICK
+#define PWM_FREQ (5000)
+// default duty 50%
+#define PWM_DUTY ((1U << UI_RES_16_BIT) / 2)
+
+// All chips except esp32 and esp32s2 do not have timer-specific clock sources, which means clock source for all timers must be the same one.
+#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
// If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) source is used, else LEDC_APB_CLK_HZ(80 MHz) source is used
-#define EMPIRIC_FREQ (10) // Hz
+#define EMPIRIC_FREQ (10) // Hz
#endif
// Config of timer upon which we run all PWM'ed GPIO pins
@@ -113,490 +76,675 @@ static bool pwm_inited = false;
// MicroPython PWM object struct
typedef struct _machine_pwm_obj_t {
mp_obj_base_t base;
- gpio_num_t pin;
- bool active;
- int mode;
- int channel;
- int timer;
- int duty_x; // PWM_RES_10_BIT if duty(), HIGHEST_PWM_RES if duty_u16(), -HIGHEST_PWM_RES if duty_ns()
- int duty_u10; // stored values from previous duty setters
- int duty_u16; // - / -
- int duty_ns; // - / -
+ int8_t pin;
+ int8_t mode;
+ int8_t channel;
+ int8_t timer;
+ bool lightsleep;
+ int32_t freq;
+ int8_t duty_scale; // DUTY_10 if duty(), DUTY_16 if duty_u16(), DUTY_NS if duty_ns()
+ int duty_ui; // saved values of UI duty
+ int channel_duty; // saved values of UI duty, calculated to raw channel->duty
+ bool output_invert;
+ bool output_invert_prev;
} machine_pwm_obj_t;
-static bool is_timer_in_use(int current_channel_idx, int timer_idx);
-static void set_duty_u16(machine_pwm_obj_t *self, int duty);
-static void set_duty_u10(machine_pwm_obj_t *self, int duty);
-static void set_duty_ns(machine_pwm_obj_t *self, int ns);
+typedef struct _chans_t {
+ int8_t pin; // Which channel has which GPIO pin assigned? (-1 if not assigned)
+ int8_t timer; // Which channel has which timer assigned? (-1 if not assigned)
+ bool lightsleep; // Is light sleep enable has been set for this pin
+} chans_t;
+
+// List of PWM channels
+static chans_t chans[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX];
+
+typedef struct _timers_t {
+ int32_t freq;
+ int8_t duty_resolution;
+ ledc_clk_cfg_t clk_cfg;
+} timers_t;
+
+// List of PWM timers
+static timers_t timers[LEDC_SPEED_MODE_MAX][LEDC_TIMER_MAX];
+
+// register-unregister channel
+static void register_channel(int mode, int channel, int pin, int timer) {
+ chans[mode][channel].pin = pin;
+ chans[mode][channel].timer = timer;
+}
+
+static void unregister_channel(int mode, int channel) {
+ register_channel(mode, channel, -1, -1);
+ chans[mode][channel].lightsleep = false;
+}
+
+static void unregister_timer(int mode, int timer) {
+ timers[mode][timer].freq = -1; // unused timer freq is -1
+ timers[mode][timer].duty_resolution = 0;
+ timers[mode][timer].clk_cfg = LEDC_AUTO_CLK;
+}
static void pwm_init(void) {
- // Initial condition: no channels assigned
- for (int i = 0; i < PWM_CHANNEL_MAX; ++i) {
- chans[i].pin = -1;
- chans[i].timer_idx = -1;
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ // Initial condition: no channels assigned
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ unregister_channel(mode, channel);
+ }
+ // Initial condition: no timers assigned
+ for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
+ unregister_timer(mode, timer);
+ }
}
+}
- // Prepare all timers config
- // Initial condition: no timers assigned
- for (int i = 0; i < PWM_TIMER_MAX; ++i) {
- timers[i].duty_resolution = HIGHEST_PWM_RES;
- // unset timer is -1
- timers[i].freq_hz = -1;
- timers[i].speed_mode = TIMER_IDX_TO_MODE(i);
- timers[i].timer_num = TIMER_IDX_TO_TIMER(i);
- timers[i].clk_cfg = LEDC_AUTO_CLK; // will reinstall later according to the EMPIRIC_FREQ
+// Returns true if the timer is in use in addition to current channel
+static bool is_timer_in_use(int mode, int current_channel, int timer) {
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ if ((channel != current_channel) && (chans[mode][channel].timer == timer) && (chans[mode][channel].pin >= 0)) {
+ return true;
+ }
}
+ return false;
}
// Deinit channel and timer if the timer is unused
-static void pwm_deinit(int channel_idx) {
- // Valid channel?
- if ((channel_idx >= 0) && (channel_idx < PWM_CHANNEL_MAX)) {
+static void pwm_deinit(int mode, int channel, int level) {
+ // Is valid channel?
+ if ((mode >= 0) && (mode < LEDC_SPEED_MODE_MAX) && (channel >= 0) && (channel < LEDC_CHANNEL_MAX)) {
// Clean up timer if necessary
- int timer_idx = chans[channel_idx].timer_idx;
- if (timer_idx != -1) {
- if (!is_timer_in_use(channel_idx, timer_idx)) {
- check_esp_err(ledc_timer_pause(TIMER_IDX_TO_MODE(timer_idx), TIMER_IDX_TO_TIMER(timer_idx)));
- ledc_timer_config_t timer_config = {
+ int timer = chans[mode][channel].timer;
+ if (timer >= 0) {
+ if (!is_timer_in_use(mode, channel, timer)) {
+ check_esp_err(ledc_timer_pause(mode, timer));
+ ledc_timer_config_t ledc_timer = {
.deconfigure = true,
- .speed_mode = TIMER_IDX_TO_MODE(timer_idx),
- .timer_num = TIMER_IDX_TO_TIMER(timer_idx),
+ .speed_mode = mode,
+ .timer_num = timer,
};
- check_esp_err(ledc_timer_config(&timer_config));
- // Flag it unused
- timers[chans[channel_idx].timer_idx].freq_hz = -1;
+ ledc_timer_config(&ledc_timer);
+ unregister_timer(mode, timer);
}
}
- int pin = chans[channel_idx].pin;
- if (pin != -1) {
- int mode = CHANNEL_IDX_TO_MODE(channel_idx);
- int channel = CHANNEL_IDX_TO_CHANNEL(channel_idx);
- // Mark it unused, and tell the hardware to stop routing
- check_esp_err(ledc_stop(mode, channel, 0));
- // Disable ledc signal for the pin
- // esp_rom_gpio_connect_out_signal(pin, SIG_GPIO_OUT_IDX, false, false);
- if (mode == LEDC_LOW_SPEED_MODE) {
- esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + channel, false, true);
- } else {
- #if LEDC_SPEED_MODE_MAX > 1
- #if CONFIG_IDF_TARGET_ESP32
- esp_rom_gpio_connect_out_signal(pin, LEDC_HS_SIG_OUT0_IDX + channel, false, true);
- #else
- #error Add supported CONFIG_IDF_TARGET_ESP32_xxx
- #endif
- #endif
+ int pin = chans[mode][channel].pin;
+ if (pin >= 0) {
+ // Disable LEDC output, and set idle level
+ check_esp_err(ledc_stop(mode, channel, level));
+ if (chans[mode][channel].lightsleep) {
+ // Enable SLP_SEL to change GPIO status automantically in lightsleep.
+ check_esp_err(gpio_sleep_sel_en(pin));
+ chans[mode][channel].lightsleep = false;
}
}
- chans[channel_idx].pin = -1;
- chans[channel_idx].timer_idx = -1;
+ unregister_channel(mode, channel);
}
}
// This called from Ctrl-D soft reboot
void machine_pwm_deinit_all(void) {
if (pwm_inited) {
- for (int channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) {
- pwm_deinit(channel_idx);
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ pwm_deinit(mode, channel, 0);
+ }
}
+ #if FADE
+ ledc_fade_func_uninstall();
+ #endif
pwm_inited = false;
}
}
-static void configure_channel(machine_pwm_obj_t *self) {
- ledc_channel_config_t cfg = {
- .channel = self->channel,
- .duty = (1 << (timers[TIMER_IDX(self->mode, self->timer)].duty_resolution)) / 2,
- .gpio_num = self->pin,
- .intr_type = LEDC_INTR_DISABLE,
- .speed_mode = self->mode,
- .timer_sel = self->timer,
- };
- if (ledc_channel_config(&cfg) != ESP_OK) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM not supported on Pin(%d)"), self->pin);
- }
-}
-
-static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) {
- esp_err_t err;
- if (freq != timer->freq_hz) {
- // Configure the new frequency and resolution
- timer->freq_hz = freq;
-
- #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK
- timer->clk_cfg = LEDC_USE_PLL_DIV_CLK;
- #elif SOC_LEDC_SUPPORT_APB_CLOCK
- timer->clk_cfg = LEDC_USE_APB_CLK;
- #elif SOC_LEDC_SUPPORT_XTAL_CLOCK
- timer->clk_cfg = LEDC_USE_XTAL_CLK;
- #else
- #error No supported PWM / LEDC clocks.
- #endif
- #if SOC_LEDC_SUPPORT_REF_TICK
- if (freq < EMPIRIC_FREQ) {
- timer->clk_cfg = LEDC_USE_REF_TICK;
- }
- #endif
- uint32_t src_clk_freq = 0;
- err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq);
- if (err != ESP_OK) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg);
- }
-
- timer->duty_resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer->freq_hz);
-
- // Set frequency
- err = ledc_timer_config(timer);
- if (err != ESP_OK) {
- if (err == ESP_FAIL) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unreachable frequency %d"), freq);
- } else {
- check_esp_err(err);
- }
- }
- // Reset the timer if low speed
- if (self->mode == LEDC_LOW_SPEED_MODE) {
- check_esp_err(ledc_timer_rst(self->mode, self->timer));
- }
- }
-
- // Save the same duty cycle when frequency is changed
- if (self->duty_x == HIGHEST_PWM_RES) {
- set_duty_u16(self, self->duty_u16);
- } else if (self->duty_x == PWM_RES_10_BIT) {
- set_duty_u10(self, self->duty_u10);
- } else if (self->duty_x == -HIGHEST_PWM_RES) {
- set_duty_ns(self, self->duty_ns);
+static void pwm_is_active(machine_pwm_obj_t *self) {
+ if (self->timer < 0) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("PWM is inactive"));
}
}
// Calculate the duty parameters based on an ns value
static int ns_to_duty(machine_pwm_obj_t *self, int ns) {
- ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)];
- int64_t duty = ((int64_t)ns * UI_MAX_DUTY * timer.freq_hz + 500000000LL) / 1000000000LL;
+ pwm_is_active(self);
+ int64_t duty = ((int64_t)ns * (int64_t)MAX_16_DUTY * self->freq + 500000000LL) / 1000000000LL;
if ((ns > 0) && (duty == 0)) {
duty = 1;
- } else if (duty > UI_MAX_DUTY) {
- duty = UI_MAX_DUTY;
+ } else if (duty > MAX_16_DUTY) {
+ duty = MAX_16_DUTY;
}
return duty;
}
static int duty_to_ns(machine_pwm_obj_t *self, int duty) {
- ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)];
- int64_t ns = ((int64_t)duty * 1000000000LL + (int64_t)timer.freq_hz * UI_MAX_DUTY / 2) / ((int64_t)timer.freq_hz * UI_MAX_DUTY);
- return ns;
+ pwm_is_active(self);
+ return ((int64_t)duty * 1000000000LL + (int64_t)self->freq * (int64_t)(MAX_16_DUTY / 2)) / ((int64_t)self->freq * (int64_t)MAX_16_DUTY);
}
-#define get_duty_raw(self) ledc_get_duty(self->mode, self->channel)
+// Reconfigure PWM pin output as input/output.
+static void reconfigure_pin(machine_pwm_obj_t *self) {
+ #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0)
+ // This allows to read the pin level.
+ gpio_set_direction(self->pin, GPIO_MODE_INPUT_OUTPUT);
+ #endif
+ esp_rom_gpio_connect_out_signal(self->pin, ledc_periph_signal[self->mode].sig_out0_idx + self->channel, self->output_invert, 0);
+}
-static void pwm_is_active(machine_pwm_obj_t *self) {
- if (self->active == false) {
- mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("PWM inactive"));
+static void apply_duty(machine_pwm_obj_t *self) {
+ pwm_is_active(self);
+
+ int duty = 0;
+ if (self->duty_scale == DUTY_16) {
+ duty = self->duty_ui;
+ } else if (self->duty_scale == DUTY_10) {
+ duty = self->duty_ui << (UI_RES_16_BIT - UI_RES_10_BIT);
+ } else if (self->duty_scale == DUTY_NS) {
+ duty = ns_to_duty(self, self->duty_ui);
+ }
+ self->channel_duty = duty >> (UI_RES_16_BIT - timers[self->mode][self->timer].duty_resolution);
+ if ((chans[self->mode][self->channel].pin == -1) || (self->output_invert_prev != self->output_invert)) {
+ self->output_invert_prev = self->output_invert;
+ // New PWM assignment
+ ledc_channel_config_t cfg = {
+ .channel = self->channel,
+ .duty = self->channel_duty,
+ .gpio_num = self->pin,
+ .intr_type = LEDC_INTR_DISABLE,
+ .speed_mode = self->mode,
+ .timer_sel = self->timer,
+ .hpoint = 0,
+ .flags.output_invert = self->output_invert,
+ };
+ check_esp_err(ledc_channel_config(&cfg));
+ reconfigure_pin(self);
+ } else {
+ #if FADE
+ check_esp_err(ledc_set_duty_and_update(self->mode, self->channel, self->channel_duty, 0));
+ #else
+ check_esp_err(ledc_set_duty(self->mode, self->channel, self->channel_duty));
+ check_esp_err(ledc_update_duty(self->mode, self->channel));
+ #endif
+ }
+ if (self->lightsleep) {
+ // Disable SLP_SEL to change GPIO status automantically in lightsleep.
+ check_esp_err(gpio_sleep_sel_dis(self->pin));
+ chans[self->mode][self->channel].lightsleep = true;
}
+ register_channel(self->mode, self->channel, self->pin, self->timer);
+}
+
+static uint32_t find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) {
+ unsigned int resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer_freq);
+ if (resolution > UI_RES_16_BIT) {
+ // limit resolution to user interface
+ resolution = UI_RES_16_BIT;
+ }
+ // Note: On ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32C6, ESP32H2, ESP32P4, due to a hardware bug,
+ // 100% duty cycle (i.e. 2**duty_res) is not reachable when the binded timer selects the maximum duty
+ // resolution. For example, the max duty resolution on ESP32C3 is 14-bit width, then set duty to (2**14)
+ // will mess up the duty calculation in hardware.
+ // Reduce the resolution from 14 to 13 bits to resolve the hardware bug.
+ if (resolution >= SOC_LEDC_TIMER_BIT_WIDTH) {
+ resolution -= 1;
+ }
+ return resolution;
}
static uint32_t get_duty_u16(machine_pwm_obj_t *self) {
pwm_is_active(self);
- int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution;
- int duty = ledc_get_duty(self->mode, self->channel);
- if (resolution <= UI_RES_16_BIT) {
- duty <<= (UI_RES_16_BIT - resolution);
+ int duty = ledc_get_duty(self->mode, self->channel) << (UI_RES_16_BIT - timers[self->mode][self->timer].duty_resolution);
+ if (duty != MAX_16_DUTY) {
+ return duty;
} else {
- duty >>= (resolution - UI_RES_16_BIT);
+ return MAX_16_DUTY - 1;
}
- return duty;
}
static uint32_t get_duty_u10(machine_pwm_obj_t *self) {
- pwm_is_active(self);
- return get_duty_u16(self) >> 6; // Scale down from 16 bit to 10 bit resolution
+ // Scale down from 16 bit to 10 bit resolution
+ return get_duty_u16(self) >> (UI_RES_16_BIT - UI_RES_10_BIT);
}
static uint32_t get_duty_ns(machine_pwm_obj_t *self) {
- pwm_is_active(self);
return duty_to_ns(self, get_duty_u16(self));
}
-static void set_duty_u16(machine_pwm_obj_t *self, int duty) {
- pwm_is_active(self);
- if ((duty < 0) || (duty > UI_MAX_DUTY)) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY);
+static void check_duty_u16(machine_pwm_obj_t *self, int duty) {
+ if ((duty < 0) || (duty > MAX_16_DUTY - 1)) {
+ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), MAX_16_DUTY);
}
- ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)];
- int channel_duty;
- if (timer.duty_resolution <= UI_RES_16_BIT) {
- channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution);
- } else {
- channel_duty = duty << (timer.duty_resolution - UI_RES_16_BIT);
+ if (duty == MAX_16_DUTY - 1) {
+ duty = MAX_16_DUTY;
}
- int max_duty = (1 << timer.duty_resolution) - 1;
- if (channel_duty < 0) {
- channel_duty = 0;
- } else if (channel_duty > max_duty) {
- channel_duty = max_duty;
- }
- check_esp_err(ledc_set_duty(self->mode, self->channel, channel_duty));
- check_esp_err(ledc_update_duty(self->mode, self->channel));
+ self->duty_scale = DUTY_16;
+ self->duty_ui = duty;
+}
- /*
- // Bug: Sometimes duty is not set right now.
- // Not a bug. It's a feature. The duty is applied at the beginning of the next signal period.
- // Bug: It has been experimentally established that the duty is set during 2 signal periods, but 1 period is expected.
- // See https://github.com/espressif/esp-idf/issues/7288
- if (duty != get_duty_u16(self)) {
- PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz);
- esp_rom_delay_us(2 * 1000000 / timer.freq_hz);
- if (duty != get_duty_u16(self)) {
- PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz);
- }
- }
- */
+static void set_duty_u16(machine_pwm_obj_t *self, int duty) {
+ check_duty_u16(self, duty);
+ apply_duty(self);
+}
- self->duty_x = HIGHEST_PWM_RES;
- self->duty_u16 = duty;
+static void check_duty_u10(machine_pwm_obj_t *self, int duty) {
+ if ((duty < 0) || (duty > MAX_10_DUTY - 1)) {
+ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_10_DUTY - 1);
+ }
+ if (duty == MAX_10_DUTY - 1) {
+ duty = MAX_10_DUTY;
+ }
+ self->duty_scale = DUTY_10;
+ self->duty_ui = duty;
}
static void set_duty_u10(machine_pwm_obj_t *self, int duty) {
- pwm_is_active(self);
- if ((duty < 0) || (duty > MAX_DUTY_U10)) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_DUTY_U10);
+ check_duty_u10(self, duty);
+ apply_duty(self);
+}
+
+static void check_duty_ns(machine_pwm_obj_t *self, int ns) {
+ if ((ns < 0) || (ns > duty_to_ns(self, MAX_16_DUTY))) {
+ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, MAX_16_DUTY));
}
- set_duty_u16(self, duty << (UI_RES_16_BIT - PWM_RES_10_BIT));
- self->duty_x = PWM_RES_10_BIT;
- self->duty_u10 = duty;
+ self->duty_scale = DUTY_NS;
+ self->duty_ui = ns;
}
static void set_duty_ns(machine_pwm_obj_t *self, int ns) {
- pwm_is_active(self);
- if ((ns < 0) || (ns > duty_to_ns(self, UI_MAX_DUTY))) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, UI_MAX_DUTY));
+ check_duty_ns(self, ns);
+ apply_duty(self);
+}
+
+#if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2)
+// This check if a clock is already set in the timer list, if yes, return the LEDC_XXX value
+static ledc_clk_cfg_t find_clock_in_use() {
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
+ if (timers[mode][timer].clk_cfg != LEDC_AUTO_CLK) {
+ return timers[mode][timer].clk_cfg;
+ }
+ }
}
- set_duty_u16(self, ns_to_duty(self, ns));
- self->duty_x = -HIGHEST_PWM_RES;
- self->duty_ns = ns;
+ return LEDC_AUTO_CLK;
}
-/******************************************************************************/
+// Check if a timer is already set with a different clock source
+static bool is_timer_with_different_clock(int mode, int current_timer, ledc_clk_cfg_t clk_cfg) {
+ for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
+ if ((timer != current_timer) && (clk_cfg != LEDC_AUTO_CLK) && (timers[mode][timer].clk_cfg != LEDC_AUTO_CLK) && (timers[mode][timer].clk_cfg != clk_cfg)) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
-#define SAME_FREQ_ONLY (true)
-#define SAME_FREQ_OR_FREE (false)
-#define ANY_MODE (-1)
+static void check_freq_ranges(machine_pwm_obj_t *self, int freq, int upper_freq) {
+ if ((freq <= 0) || (freq > upper_freq)) {
+ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("frequency must be from 1Hz to %dMHz"), upper_freq / 1000000);
+ }
+}
-// Return timer_idx. Use TIMER_IDX_TO_MODE(timer_idx) and TIMER_IDX_TO_TIMER(timer_idx) to get mode and timer
-static int find_timer(unsigned int freq, bool same_freq_only, int mode) {
- int free_timer_idx_found = -1;
- // Find a free PWM Timer using the same freq
- for (int timer_idx = 0; timer_idx < PWM_TIMER_MAX; ++timer_idx) {
- if ((mode == ANY_MODE) || (mode == TIMER_IDX_TO_MODE(timer_idx))) {
- if (timers[timer_idx].freq_hz == freq) {
- // A timer already uses the same freq. Use it now.
- return timer_idx;
- }
- if (!same_freq_only && (free_timer_idx_found == -1) && (timers[timer_idx].freq_hz == -1)) {
- free_timer_idx_found = timer_idx;
- // Continue to check if a channel with the same freq is in use.
+// Set timer frequency
+static void set_freq(machine_pwm_obj_t *self, unsigned int freq) {
+ self->freq = freq;
+ if ((timers[self->mode][self->timer].freq != freq) || (self->lightsleep)) {
+ ledc_timer_config_t timer = {};
+ timer.speed_mode = self->mode;
+ timer.timer_num = self->timer;
+ timer.freq_hz = freq;
+ timer.deconfigure = false;
+
+ timer.clk_cfg = LEDC_AUTO_CLK;
+ #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2)
+ ledc_clk_cfg_t clk_cfg = find_clock_in_use();
+ if (clk_cfg != LEDC_AUTO_CLK) {
+ timer.clk_cfg = clk_cfg;
+ } else
+ #endif
+ if (self->lightsleep) {
+ timer.clk_cfg = LEDC_USE_RC_FAST_CLK; // 8 or 20 MHz
+ } else {
+ #if SOC_LEDC_SUPPORT_APB_CLOCK
+ timer.clk_cfg = LEDC_USE_APB_CLK; // 80 MHz
+ #elif SOC_LEDC_SUPPORT_PLL_DIV_CLOCK
+ timer.clk_cfg = LEDC_USE_PLL_DIV_CLK; // 60 or 80 or 96 MHz
+ #elif SOC_LEDC_SUPPORT_XTAL_CLOCK
+ timer.clk_cfg = LEDC_USE_XTAL_CLK; // 40 MHz
+ #else
+ #error No supported PWM / LEDC clocks.
+ #endif
+
+ #ifdef EMPIRIC_FREQ // ESP32 and ESP32S2 only
+ if (freq < EMPIRIC_FREQ) {
+ timer.clk_cfg = LEDC_USE_REF_TICK; // 1 MHz
}
+ #endif
}
- }
+ #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2)
+ // Check for clock source conflict
+ clk_cfg = find_clock_in_use();
+ if ((clk_cfg != LEDC_AUTO_CLK) && (clk_cfg != timer.clk_cfg)) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("one or more active timers use a different clock source, not supported by the current SoC."));
+ }
+ #endif
- return free_timer_idx_found;
+ uint32_t src_clk_freq = 0;
+ check_esp_err(esp_clk_tree_src_get_freq_hz(timer.clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &src_clk_freq));
+ // machine.freq(20_000_000) reduces APB_CLK_FREQ to 20MHz and the highest PWM frequency to 10MHz
+ check_freq_ranges(self, freq, src_clk_freq / 2);
+
+ // Configure the new resolution
+ timer.duty_resolution = find_suitable_duty_resolution(src_clk_freq, self->freq);
+ // Configure timer - Set frequency
+ if ((timers[self->mode][self->timer].duty_resolution == timer.duty_resolution) && (timers[self->mode][self->timer].clk_cfg == timer.clk_cfg)) {
+ check_esp_err(ledc_set_freq(self->mode, self->timer, freq));
+ } else {
+ check_esp_err(ledc_timer_config(&timer));
+ }
+ // Reset the timer if low speed
+ if (self->mode == LEDC_LOW_SPEED_MODE) {
+ check_esp_err(ledc_timer_rst(self->mode, self->timer));
+ }
+ timers[self->mode][self->timer].freq = freq;
+ timers[self->mode][self->timer].duty_resolution = timer.duty_resolution;
+ timers[self->mode][self->timer].clk_cfg = timer.clk_cfg;
+ }
}
-// Return true if the timer is in use in addition to current channel
-static bool is_timer_in_use(int current_channel_idx, int timer_idx) {
- for (int i = 0; i < PWM_CHANNEL_MAX; ++i) {
- if ((i != current_channel_idx) && (chans[i].timer_idx == timer_idx)) {
+static bool is_free_channels(int mode, int pin) {
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ if ((chans[mode][channel].pin < 0) || (chans[mode][channel].pin == pin)) {
return true;
}
}
+ return false;
+}
+static bool is_free_timers(int mode, int32_t freq) {
+ for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
+ if ((timers[mode][timer].freq < 0) || (timers[mode][timer].freq == freq)) {
+ return true;
+ }
+ }
return false;
}
-// Find a free PWM channel, also spot if our pin is already mentioned.
-// Return channel_idx. Use CHANNEL_IDX_TO_MODE(channel_idx) and CHANNEL_IDX_TO_CHANNEL(channel_idx) to get mode and channel
-static int find_channel(int pin, int mode) {
- int avail_idx = -1;
- int channel_idx;
- for (channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) {
- if ((mode == ANY_MODE) || (mode == CHANNEL_IDX_TO_MODE(channel_idx))) {
- if (chans[channel_idx].pin == pin) {
- break;
+// Find self channel or free channel
+static void find_channel(machine_pwm_obj_t *self, int *ret_mode, int *ret_channel, int32_t freq) {
+ // Try to find self channel first
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ #if SOC_LEDC_SUPPORT_HS_MODE
+ if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) {
+ continue;
+ }
+ #endif
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ if (chans[mode][channel].pin == self->pin) {
+ *ret_mode = mode;
+ *ret_channel = channel;
+ return;
}
- if ((avail_idx == -1) && (chans[channel_idx].pin == -1)) {
- avail_idx = channel_idx;
+ }
+ }
+ // Find free channel
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ #if SOC_LEDC_SUPPORT_HS_MODE
+ if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) {
+ continue;
+ }
+ #endif
+ for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) {
+ if ((chans[mode][channel].pin < 0) && is_free_timers(mode, freq)) {
+ *ret_mode = mode;
+ *ret_channel = channel;
+ return;
}
}
}
- if (channel_idx >= PWM_CHANNEL_MAX) {
- channel_idx = avail_idx;
+}
+
+// Returns timer with the same frequency, freq == -1 means free timer
+static void find_timer(machine_pwm_obj_t *self, int freq, int *ret_mode, int *ret_timer) {
+ for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) {
+ #if SOC_LEDC_SUPPORT_HS_MODE
+ if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) {
+ continue;
+ }
+ #endif
+ if (is_free_channels(mode, self->pin)) {
+ for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
+ if (timers[mode][timer].freq == freq) {
+ *ret_mode = mode;
+ *ret_timer = timer;
+ return;
+ }
+ }
+ }
}
- return channel_idx;
}
-/******************************************************************************/
+// Try to find a timer with the same frequency in the current mode, otherwise in the next mode.
+// If no existing timer and channel was found, then try to find free timer in any mode.
+// If the mode or channel is changed, release the channel and register a new channel in the next mode.
+static void select_timer(machine_pwm_obj_t *self, int freq) {
+ // mode, channel, timer may be -1(not defined) or actual values
+ int mode = -1;
+ int timer = -1;
+ // Check if an already running timer with the required frequency is running
+ find_timer(self, freq, &mode, &timer);
+ if (timer < 0) {
+ // Try to reuse self timer
+ if ((self->mode >= 0) && (self->channel >= 0)) {
+ if (!is_timer_in_use(self->mode, self->channel, self->timer)) {
+ mode = self->mode;
+ timer = self->timer;
+ }
+ }
+ // If no existing timer and channel was found, then try to find free timer in any mode
+ if (timer < 0) {
+ find_timer(self, -1, &mode, &timer);
+ }
+ }
+ if (timer < 0) {
+ mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of %sPWM timers:%d"), self->lightsleep ? "light sleep capable " : "", self->lightsleep ? LEDC_TIMER_MAX : LEDC_SPEED_MODE_MAX *LEDC_TIMER_MAX);
+ }
+ // If the timer is found, then register
+ if (self->timer != timer) {
+ unregister_channel(self->mode, self->channel);
+ // Rregister the channel to the timer
+ self->mode = mode;
+ self->timer = timer;
+ register_channel(self->mode, self->channel, -1, self->timer);
+ }
+ #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2)
+ if (is_timer_with_different_clock(self->mode, self->timer, timers[self->mode][self->timer].clk_cfg)) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("one or more active timers use a different clock source, not supported by the current SoC."));
+ }
+ #endif
+}
+
+static void set_freq_duty(machine_pwm_obj_t *self, unsigned int freq) {
+ select_timer(self, freq);
+ set_freq(self, freq);
+ apply_duty(self);
+}
+
+// ******************************************************************************
// MicroPython bindings for PWM
static void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_printf(print, "PWM(Pin(%u)", self->pin);
- if (self->active) {
+ if (self->timer >= 0) {
mp_printf(print, ", freq=%u", ledc_get_freq(self->mode, self->timer));
-
- if (self->duty_x == PWM_RES_10_BIT) {
+ if (self->duty_scale == DUTY_10) {
mp_printf(print, ", duty=%d", get_duty_u10(self));
- } else if (self->duty_x == -HIGHEST_PWM_RES) {
+ } else if (self->duty_scale == DUTY_NS) {
mp_printf(print, ", duty_ns=%d", get_duty_ns(self));
} else {
mp_printf(print, ", duty_u16=%d", get_duty_u16(self));
}
- int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution;
- mp_printf(print, ", resolution=%d", resolution);
-
- mp_printf(print, ", (duty=%.2f%%, resolution=%.3f%%)", 100.0 * get_duty_raw(self) / (1 << resolution), 100.0 * 1 / (1 << resolution)); // percents
-
- mp_printf(print, ", mode=%d, channel=%d, timer=%d", self->mode, self->channel, self->timer);
+ if (self->output_invert) {
+ mp_printf(print, ", invert=True");
+ }
+ if (self->lightsleep) {
+ mp_printf(print, ", lightsleep=True");
+ }
+ mp_printf(print, ")");
+
+ #if MICROPY_ERROR_REPORTING > MICROPY_ERROR_REPORTING_NORMAL
+ mp_printf(print, " # duty=%.2f%%", 100.0 * get_duty_u16(self) / MAX_16_DUTY);
+ mp_printf(print, ", raw_duty=%d, resolution=%d", ledc_get_duty(self->mode, self->channel), timers[self->mode][self->timer].duty_resolution);
+ mp_printf(print, ", mode=%d, timer=%d, channel=%d", self->mode, self->timer, self->channel);
+ int clk_cfg = timers[self->mode][self->timer].clk_cfg;
+ mp_printf(print, ", clk_cfg=%d=", clk_cfg);
+ if (clk_cfg == LEDC_USE_RC_FAST_CLK) {
+ mp_printf(print, "RC_FAST_CLK");
+ }
+ #if SOC_LEDC_SUPPORT_APB_CLOCK
+ else if (clk_cfg == LEDC_USE_APB_CLK) {
+ mp_printf(print, "APB_CLK");
+ }
+ #endif
+ #if SOC_LEDC_SUPPORT_XTAL_CLOCK
+ else if (clk_cfg == LEDC_USE_XTAL_CLK) {
+ mp_printf(print, "XTAL_CLK");
+ }
+ #endif
+ #if SOC_LEDC_SUPPORT_REF_TICK
+ else if (clk_cfg == LEDC_USE_REF_TICK) {
+ mp_printf(print, "REF_TICK");
+ }
+ #endif
+ #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK
+ else if (clk_cfg == LEDC_USE_PLL_DIV_CLK) {
+ mp_printf(print, "PLL_CLK");
+ }
+ #endif
+ else if (clk_cfg == LEDC_AUTO_CLK) {
+ mp_printf(print, "AUTO_CLK");
+ } else {
+ mp_printf(print, "UNKNOWN");
+ }
+ #endif
+ } else {
+ mp_printf(print, ")");
}
- mp_printf(print, ")");
}
// This called from pwm.init() method
+//
+// Check the current mode.
+// If the frequency is changed, try to find a timer with the same frequency
+// in the current mode, otherwise in the new mode.
+// If the mode is changed, release the channel and select a new channel in the new mode.
+// Then set the frequency with the same duty.
static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self,
size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns };
- static const mp_arg_t allowed_args[] = {
+
+ enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_lightsleep };
+ mp_arg_t allowed_args[] = {
{ MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_duty_u16, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_duty_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = self->output_invert} },
+ { MP_QSTR_lightsleep, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = self->lightsleep} },
};
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);
- int channel_idx = find_channel(self->pin, ANY_MODE);
- if (channel_idx == -1) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in all modes
+ self->lightsleep = args[ARG_lightsleep].u_bool;
+
+ int freq = args[ARG_freq].u_int;
+ if (freq != -1) {
+ check_freq_ranges(self, freq, 40000000);
}
int duty = args[ARG_duty].u_int;
int duty_u16 = args[ARG_duty_u16].u_int;
int duty_ns = args[ARG_duty_ns].u_int;
- if (((duty != -1) && (duty_u16 != -1)) || ((duty != -1) && (duty_ns != -1)) || ((duty_u16 != -1) && (duty_ns != -1))) {
- mp_raise_ValueError(MP_ERROR_TEXT("only one of parameters 'duty', 'duty_u16' or 'duty_ns' is allowed"));
+ if (duty_u16 >= 0) {
+ check_duty_u16(self, duty_u16);
+ } else if (duty_ns >= 0) {
+ check_duty_ns(self, duty_ns);
+ } else if (duty >= 0) {
+ check_duty_u10(self, duty);
+ } else if (self->duty_scale == 0) {
+ self->duty_scale = DUTY_16;
+ self->duty_ui = PWM_DUTY;
+ }
+
+ self->output_invert = args[ARG_invert].u_bool;
+
+ // Check the current mode and channel
+ int mode = -1;
+ int channel = -1;
+ find_channel(self, &mode, &channel, freq);
+ if (channel < 0) {
+ mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of %sPWM channels:%d"), self->lightsleep ? "light sleep capable " : "", self->lightsleep ? LEDC_CHANNEL_MAX : LEDC_SPEED_MODE_MAX *LEDC_CHANNEL_MAX);
}
+ self->mode = mode;
+ self->channel = channel;
- int freq = args[ARG_freq].u_int;
// Check if freq wasn't passed as an argument
- if (freq == -1) {
+ if ((freq == -1) && (mode >= 0) && (channel >= 0)) {
// Check if already set, otherwise use the default freq.
// It is possible in case:
// pwm = PWM(pin, freq=1000, duty=256)
// pwm = PWM(pin, duty=128)
- if (chans[channel_idx].timer_idx != -1) {
- freq = timers[chans[channel_idx].timer_idx].freq_hz;
+ if (chans[mode][channel].timer >= 0) {
+ freq = timers[mode][chans[mode][channel].timer].freq;
}
if (freq <= 0) {
freq = PWM_FREQ;
}
}
- if ((freq <= 0) || (freq > 40000000)) {
- mp_raise_ValueError(MP_ERROR_TEXT("frequency must be from 1Hz to 40MHz"));
- }
-
- int timer_idx;
- int current_timer_idx = chans[channel_idx].timer_idx;
- bool current_in_use = is_timer_in_use(channel_idx, current_timer_idx);
- if (current_in_use) {
- timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, CHANNEL_IDX_TO_MODE(channel_idx));
- } else {
- timer_idx = chans[channel_idx].timer_idx;
- }
-
- if (timer_idx == -1) {
- timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, ANY_MODE);
- }
- if (timer_idx == -1) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in all modes
- }
-
- int mode = TIMER_IDX_TO_MODE(timer_idx);
- if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) {
- // unregister old channel
- chans[channel_idx].pin = -1;
- chans[channel_idx].timer_idx = -1;
- // find new channel
- channel_idx = find_channel(self->pin, mode);
- if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in current mode
- }
- }
- self->mode = mode;
- self->timer = TIMER_IDX_TO_TIMER(timer_idx);
- self->channel = CHANNEL_IDX_TO_CHANNEL(channel_idx);
-
- // New PWM assignment
- if ((chans[channel_idx].pin == -1) || (chans[channel_idx].timer_idx != timer_idx)) {
- configure_channel(self);
- chans[channel_idx].pin = self->pin;
- }
- chans[channel_idx].timer_idx = timer_idx;
- self->active = true;
-
- // Set timer frequency
- set_freq(self, freq, &timers[timer_idx]);
+ set_freq_duty(self, freq);
+}
- // Set duty cycle?
- if (duty_u16 != -1) {
- set_duty_u16(self, duty_u16);
- } else if (duty_ns != -1) {
- set_duty_ns(self, duty_ns);
- } else if (duty != -1) {
- set_duty_u10(self, duty);
- } else if (self->duty_x == 0) {
- set_duty_u10(self, (1 << PWM_RES_10_BIT) / 2); // 50%
- }
+static void self_reset(machine_pwm_obj_t *self) {
+ self->mode = -1;
+ self->channel = -1;
+ self->timer = -1;
+ self->freq = -1;
+ self->duty_scale = 0;
+ self->duty_ui = 0;
+ self->channel_duty = -1;
+ self->output_invert = false;
+ self->output_invert_prev = false;
+ self->lightsleep = false;
}
// This called from PWM() constructor
static mp_obj_t mp_machine_pwm_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, 2, true);
- gpio_num_t pin_id = machine_pin_get_id(args[0]);
-
- // create PWM object from the given pin
- machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type);
- self->pin = pin_id;
- self->active = false;
- self->mode = -1;
- self->channel = -1;
- self->timer = -1;
- self->duty_x = 0;
// start the PWM subsystem if it's not already running
if (!pwm_inited) {
pwm_init();
+ #if FADE
+ ledc_fade_func_install(0);
+ #endif
pwm_inited = true;
}
- // start the PWM running for this channel
+ // create PWM object from the given pin
+ machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type);
+ self_reset(self);
+
+ self->pin = machine_pin_get_id(args[0]);
+
+ // Process the remaining parameters.
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
mp_machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args);
-
return MP_OBJ_FROM_PTR(self);
}
// This called from pwm.deinit() method
static void mp_machine_pwm_deinit(machine_pwm_obj_t *self) {
- int channel_idx = CHANNEL_IDX(self->mode, self->channel);
- pwm_deinit(channel_idx);
- self->active = false;
- self->mode = -1;
- self->channel = -1;
- self->timer = -1;
- self->duty_x = 0;
+ pwm_deinit(self->mode, self->channel, self->output_invert);
+ self_reset(self);
}
// Set and get methods of PWM class
@@ -608,57 +756,11 @@ static mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) {
static void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) {
pwm_is_active(self);
- if ((freq <= 0) || (freq > 40000000)) {
- mp_raise_ValueError(MP_ERROR_TEXT("frequency must be from 1Hz to 40MHz"));
- }
- if (freq == timers[TIMER_IDX(self->mode, self->timer)].freq_hz) {
+ check_freq_ranges(self, freq, 40000000);
+ if (freq == timers[self->mode][self->timer].freq) {
return;
}
-
- int current_timer_idx = chans[CHANNEL_IDX(self->mode, self->channel)].timer_idx;
- bool current_in_use = is_timer_in_use(CHANNEL_IDX(self->mode, self->channel), current_timer_idx);
-
- // Check if an already running timer with the same freq is running
- int new_timer_idx = find_timer(freq, SAME_FREQ_ONLY, self->mode);
-
- // If no existing timer was found, and the current one is in use, then find a new one
- if ((new_timer_idx == -1) && current_in_use) {
- // Have to find a new timer
- new_timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, self->mode);
-
- if (new_timer_idx == -1) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in current mode
- }
- }
-
- if ((new_timer_idx != -1) && (new_timer_idx != current_timer_idx)) {
- // Bind the channel to the new timer
- chans[self->channel].timer_idx = new_timer_idx;
-
- if (ledc_bind_channel_timer(self->mode, self->channel, TIMER_IDX_TO_TIMER(new_timer_idx)) != ESP_OK) {
- mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("failed to bind timer to channel"));
- }
-
- if (!current_in_use) {
- // Free the old timer
- check_esp_err(ledc_timer_pause(self->mode, self->timer));
- ledc_timer_config_t timer_config = {
- .deconfigure = true,
- .speed_mode = self->mode,
- .timer_num = self->timer,
- };
- check_esp_err(ledc_timer_config(&timer_config));
- // Flag it unused
- timers[current_timer_idx].freq_hz = -1;
- }
-
- current_timer_idx = new_timer_idx;
- }
- self->mode = TIMER_IDX_TO_MODE(current_timer_idx);
- self->timer = TIMER_IDX_TO_TIMER(current_timer_idx);
-
- // Set the frequency
- set_freq(self, freq, &timers[current_timer_idx]);
+ set_freq_duty(self, freq);
}
static mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) {
diff --git a/ports/esp32/machine_rtc.c b/ports/esp32/machine_rtc.c
index a2f4bb132d..ee6b1ad5cb 100644
--- a/ports/esp32/machine_rtc.c
+++ b/ports/esp32/machine_rtc.c
@@ -82,8 +82,12 @@ _USER_MEM_ATTR uint8_t rtc_user_mem_data[MICROPY_HW_RTC_USER_MEM_MAX];
static const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}};
machine_rtc_config_t machine_rtc_config = {
+ #if SOC_PM_SUPPORT_EXT1_WAKEUP
.ext1_pins = 0,
+ #endif
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
.ext0_pin = -1
+ #endif
};
static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
diff --git a/ports/esp32/machine_rtc.h b/ports/esp32/machine_rtc.h
index ce2a5482a2..72717c5936 100644
--- a/ports/esp32/machine_rtc.h
+++ b/ports/esp32/machine_rtc.h
@@ -31,13 +31,25 @@
#include "modmachine.h"
typedef struct {
+ #if SOC_PM_SUPPORT_EXT1_WAKEUP
uint64_t ext1_pins; // set bit == pin#
+ #endif
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
int8_t ext0_pin; // just the pin#, -1 == None
+ #endif
+ #if SOC_TOUCH_SENSOR_SUPPORTED
bool wake_on_touch : 1;
+ #endif
+ #if SOC_ULP_SUPPORTED
bool wake_on_ulp : 1;
+ #endif
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
bool ext0_level : 1;
wake_type_t ext0_wake_types;
+ #endif
+ #if SOC_PM_SUPPORT_EXT1_WAKEUP
bool ext1_level : 1;
+ #endif
} machine_rtc_config_t;
extern machine_rtc_config_t machine_rtc_config;
diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c
index 82b432bbac..a104288f6e 100644
--- a/ports/esp32/machine_timer.c
+++ b/ports/esp32/machine_timer.c
@@ -50,12 +50,13 @@
const mp_obj_type_t machine_timer_type;
static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
+static mp_obj_t machine_timer_deinit(mp_obj_t self_in);
void machine_timer_deinit_all(void) {
// Disable, deallocate and remove all timers from list
machine_timer_obj_t **t = &MP_STATE_PORT(machine_timer_obj_head);
while (*t != NULL) {
- machine_timer_disable(*t);
+ machine_timer_deinit(*t);
machine_timer_obj_t *next = (*t)->next;
m_del_obj(machine_timer_obj_t, *t);
*t = next;
@@ -96,6 +97,7 @@ machine_timer_obj_t *machine_timer_create(mp_uint_t timer) {
self = mp_obj_malloc(machine_timer_obj_t, &machine_timer_type);
self->group = group;
self->index = index;
+ self->handle = NULL;
// Add the timer to the linked-list of timers
self->next = MP_STATE_PORT(machine_timer_obj_head);
@@ -131,9 +133,8 @@ void machine_timer_disable(machine_timer_obj_t *self) {
}
if (self->handle) {
- // Free the interrupt handler.
- esp_intr_free(self->handle);
- self->handle = NULL;
+ // Disable the interrupt
+ ESP_ERROR_CHECK(esp_intr_disable(self->handle));
}
// We let the disabled timer stay in the list, as it might be
@@ -150,12 +151,16 @@ static void machine_timer_isr(void *self_in) {
if (self->repeat) {
timer_ll_enable_alarm(self->hal_context.dev, self->index, true);
}
- mp_sched_schedule(self->callback, self);
- mp_hal_wake_main_task_from_isr();
+ self->handler(self);
}
}
-void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) {
+static void machine_timer_isr_handler(machine_timer_obj_t *self) {
+ mp_sched_schedule(self->callback, self);
+ mp_hal_wake_main_task_from_isr();
+}
+
+void machine_timer_enable(machine_timer_obj_t *self) {
// Initialise the timer.
timer_hal_init(&self->hal_context, self->group, self->index);
timer_ll_enable_counter(self->hal_context.dev, self->index, false);
@@ -167,10 +172,17 @@ void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) {
// Allocate and enable the alarm interrupt.
timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), false);
timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index));
- ESP_ERROR_CHECK(
- esp_intr_alloc(timer_group_periph_signals.groups[self->group].timer_irq_id[self->index],
- TIMER_FLAGS, timer_isr, self, &self->handle)
- );
+ if (self->handle) {
+ ESP_ERROR_CHECK(esp_intr_enable(self->handle));
+ } else {
+ ESP_ERROR_CHECK(esp_intr_alloc(
+ timer_group_periph_signals.groups[self->group].timer_irq_id[self->index],
+ TIMER_FLAGS,
+ machine_timer_isr,
+ self,
+ &self->handle
+ ));
+ }
timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true);
// Enable the alarm to trigger at the given period.
@@ -224,16 +236,22 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n
}
self->repeat = args[ARG_mode].u_int;
+ self->handler = machine_timer_isr_handler;
self->callback = args[ARG_callback].u_obj;
- self->handle = NULL;
- machine_timer_enable(self, machine_timer_isr);
+ machine_timer_enable(self);
return mp_const_none;
}
static mp_obj_t machine_timer_deinit(mp_obj_t self_in) {
- machine_timer_disable(self_in);
+ machine_timer_obj_t *self = self_in;
+
+ machine_timer_disable(self);
+ if (self->handle) {
+ ESP_ERROR_CHECK(esp_intr_free(self->handle));
+ self->handle = NULL;
+ }
return mp_const_none;
}
@@ -246,6 +264,9 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init)
static mp_obj_t machine_timer_value(mp_obj_t self_in) {
machine_timer_obj_t *self = self_in;
+ if (self->handle == NULL) {
+ mp_raise_ValueError(MP_ERROR_TEXT("timer not set"));
+ }
uint64_t result = timer_ll_get_counter_value(self->hal_context.dev, self->index);
return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result / (TIMER_SCALE / 1000))); // value in ms
}
diff --git a/ports/esp32/machine_timer.h b/ports/esp32/machine_timer.h
index 914bedd86b..10fe2f39c9 100644
--- a/ports/esp32/machine_timer.h
+++ b/ports/esp32/machine_timer.h
@@ -55,12 +55,13 @@ typedef struct _machine_timer_obj_t {
mp_obj_t callback;
intr_handle_t handle;
+ void (*handler)(struct _machine_timer_obj_t *timer);
struct _machine_timer_obj_t *next;
} machine_timer_obj_t;
machine_timer_obj_t *machine_timer_create(mp_uint_t timer);
-void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr));
+void machine_timer_enable(machine_timer_obj_t *self);
void machine_timer_disable(machine_timer_obj_t *self);
#endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H
diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c
index 73089ef463..982d9a7e27 100644
--- a/ports/esp32/machine_uart.c
+++ b/ports/esp32/machine_uart.c
@@ -59,6 +59,7 @@
#define UART_IRQ_BREAK (1 << UART_BREAK)
#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_RXIDLE | UART_IRQ_BREAK)
#define RXIDLE_TIMER_MIN (5000) // 500 us
+#define UART_QUEUE_SIZE (3)
enum {
RXIDLE_INACTIVE,
@@ -109,30 +110,19 @@ static const char *_parity_name[] = {"None", "1", "0"};
{ MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \
{ MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_IRQ_BREAK) }, \
-static void uart_timer_callback(void *self_in) {
- machine_timer_obj_t *self = self_in;
-
- uint32_t intr_status = timer_ll_get_intr_status(self->hal_context.dev);
-
- if (intr_status & TIMER_LL_EVENT_ALARM(self->index)) {
- timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index));
- if (self->repeat) {
- timer_ll_enable_alarm(self->hal_context.dev, self->index, true);
- }
- }
-
+static void uart_timer_callback(machine_timer_obj_t *timer) {
// The UART object is referred here by the callback field.
- machine_uart_obj_t *uart = (machine_uart_obj_t *)self->callback;
- if (uart->rxidle_state == RXIDLE_ALERT) {
+ machine_uart_obj_t *self = (machine_uart_obj_t *)timer->callback;
+ if (self->rxidle_state == RXIDLE_ALERT) {
// At the first call, just switch the state
- uart->rxidle_state = RXIDLE_ARMED;
- } else if (uart->rxidle_state == RXIDLE_ARMED) {
+ self->rxidle_state = RXIDLE_ARMED;
+ } else if (self->rxidle_state == RXIDLE_ARMED) {
// At the second call, run the irq callback and stop the timer
- uart->rxidle_state = RXIDLE_STANDBY;
- uart->mp_irq_flags = UART_IRQ_RXIDLE;
- mp_irq_handler(uart->mp_irq_obj);
+ self->rxidle_state = RXIDLE_STANDBY;
+ self->mp_irq_flags = UART_IRQ_RXIDLE;
+ mp_irq_handler(self->mp_irq_obj);
mp_hal_wake_main_task_from_isr();
- machine_timer_disable(uart->rxidle_timer);
+ machine_timer_disable(self->rxidle_timer);
}
}
@@ -149,9 +139,7 @@ static void uart_event_task(void *self_in) {
if (self->mp_irq_trigger & UART_IRQ_RXIDLE) {
if (self->rxidle_state != RXIDLE_INACTIVE) {
if (self->rxidle_state == RXIDLE_STANDBY) {
- self->rxidle_timer->repeat = true;
- self->rxidle_timer->handle = NULL;
- machine_timer_enable(self->rxidle_timer, uart_timer_callback);
+ machine_timer_enable(self->rxidle_timer);
}
}
self->rxidle_state = RXIDLE_ALERT;
@@ -174,6 +162,13 @@ static void uart_event_task(void *self_in) {
}
}
+static inline void uart_event_task_create(machine_uart_obj_t *self) {
+ if (xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self,
+ ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID) != pdPASS) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create UART event task"));
+ }
+}
+
static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint32_t baudrate;
@@ -250,7 +245,7 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args,
// wait for all data to be transmitted before changing settings
uart_wait_tx_done(self->uart_num, pdMS_TO_TICKS(1000));
- if (args[ARG_txbuf].u_int >= 0 || args[ARG_rxbuf].u_int >= 0) {
+ if ((args[ARG_txbuf].u_int >= 0 && args[ARG_txbuf].u_int != self->txbuf) || (args[ARG_rxbuf].u_int >= 0 && args[ARG_rxbuf].u_int != self->rxbuf)) {
// must reinitialise driver to change the tx/rx buffer size
#if MICROPY_HW_ENABLE_UART_REPL
if (self->uart_num == MICROPY_HW_UART_REPL) {
@@ -275,9 +270,12 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args,
check_esp_err(uart_get_word_length(self->uart_num, &uartcfg.data_bits));
check_esp_err(uart_get_parity(self->uart_num, &uartcfg.parity));
check_esp_err(uart_get_stop_bits(self->uart_num, &uartcfg.stop_bits));
- check_esp_err(uart_driver_delete(self->uart_num));
+ mp_machine_uart_deinit(self);
check_esp_err(uart_param_config(self->uart_num, &uartcfg));
- check_esp_err(uart_driver_install(self->uart_num, self->rxbuf, self->txbuf, 0, NULL, 0));
+ check_esp_err(uart_driver_install(self->uart_num, self->rxbuf, self->txbuf, UART_QUEUE_SIZE, &self->uart_queue, 0));
+ if (self->mp_irq_obj != NULL && self->mp_irq_obj->handler != mp_const_none) {
+ uart_event_task_create(self);
+ }
}
// set baudrate
@@ -437,7 +435,8 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
self->timeout_char = 0;
self->invert = 0;
self->flowcontrol = 0;
- self->uart_event_task = 0;
+ self->uart_event_task = NULL;
+ self->uart_queue = NULL;
self->rxidle_state = RXIDLE_INACTIVE;
switch (uart_num) {
@@ -470,12 +469,13 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
{
// Remove any existing configuration
check_esp_err(uart_driver_delete(self->uart_num));
+ self->uart_queue = NULL;
// init the peripheral
// Setup
check_esp_err(uart_param_config(self->uart_num, &uartcfg));
- check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, 3, &self->uart_queue, 0));
+ check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, UART_QUEUE_SIZE, &self->uart_queue, 0));
}
mp_map_t kw_args;
@@ -489,7 +489,12 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
}
static void mp_machine_uart_deinit(machine_uart_obj_t *self) {
+ if (self->uart_event_task != NULL) {
+ vTaskDelete(self->uart_event_task);
+ self->uart_event_task = NULL;
+ }
check_esp_err(uart_driver_delete(self->uart_num));
+ self->uart_queue = NULL;
}
static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) {
@@ -535,11 +540,11 @@ static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger
}
self->rxidle_period = period;
self->rxidle_timer->period = period;
+ self->rxidle_timer->handler = uart_timer_callback;
// The Python callback is not used. So use this
// data field to hold a reference to the UART object.
self->rxidle_timer->callback = self;
self->rxidle_timer->repeat = true;
- self->rxidle_timer->handle = NULL;
self->rxidle_state = RXIDLE_STANDBY;
}
}
@@ -568,6 +573,12 @@ static const mp_irq_methods_t uart_irq_methods = {
};
static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) {
+ #if MICROPY_HW_ENABLE_UART_REPL
+ if (self->uart_num == MICROPY_HW_UART_REPL) {
+ mp_raise_ValueError(MP_ERROR_TEXT("UART does not support IRQs"));
+ }
+ #endif
+
if (self->mp_irq_obj == NULL) {
self->mp_irq_trigger = 0;
self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self));
@@ -597,9 +608,8 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args
uart_irq_configure_timer(self, trigger);
// Start a task for handling events
- if (handler != mp_const_none && self->uart_event_task == NULL) {
- xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self,
- ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID);
+ if (handler != mp_const_none && self->uart_event_task == NULL && self->uart_queue != NULL) {
+ uart_event_task_create(self);
} else if (handler == mp_const_none && self->uart_event_task != NULL) {
vTaskDelete(self->uart_event_task);
self->uart_event_task = NULL;
diff --git a/ports/esp32/main.c b/ports/esp32/main.c
index 4f0c27ee07..f048aa85f5 100644
--- a/ports/esp32/main.c
+++ b/ports/esp32/main.c
@@ -38,6 +38,7 @@
#include "nvs_flash.h"
#include "esp_task.h"
#include "esp_event.h"
+#include "esp_flash.h"
#include "esp_log.h"
#include "esp_memory_utils.h"
#include "esp_psram.h"
@@ -214,18 +215,45 @@ void boardctrl_startup(void) {
nvs_flash_erase();
nvs_flash_init();
}
+
+ // Query the physical size of the SPI flash and store it in the size
+ // variable of the global, default SPI flash handle.
+ esp_flash_get_physical_size(NULL, &esp_flash_default_chip->size);
+
+ // If there is no filesystem partition (no "vfs" or "ffat"), add a "vfs" partition
+ // that extends from the end of the application partition up to the end of flash.
+ if (esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "vfs") == NULL
+ && esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "ffat") == NULL) {
+ // No "vfs" or "ffat" partition, so try to create one.
+
+ // Find the end of the last partition that exists in the partition table.
+ size_t offset = 0;
+ esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
+ while (iter != NULL) {
+ const esp_partition_t *part = esp_partition_get(iter);
+ offset = MAX(offset, part->address + part->size);
+ iter = esp_partition_next(iter);
+ }
+
+ // If we found the application partition and there is some space between the end of
+ // that and the end of flash, create a "vfs" partition taking up all of that space.
+ if (offset > 0 && esp_flash_default_chip->size > offset) {
+ size_t size = esp_flash_default_chip->size - offset;
+ esp_partition_register_external(esp_flash_default_chip, offset, size, "vfs", ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
+ }
+ }
}
-void app_main(void) {
+void MICROPY_ESP_IDF_ENTRY(void) {
// Hook for a board to run code at start up.
- // This defaults to initialising NVS.
+ // This defaults to initialising NVS and detecting the flash size.
MICROPY_BOARD_STARTUP();
// Create and transfer control to the MicroPython task.
xTaskCreatePinnedToCore(mp_task, "mp_task", MICROPY_TASK_STACK_SIZE / sizeof(StackType_t), NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID);
}
-void nlr_jump_fail(void *val) {
+MP_WEAK void nlr_jump_fail(void *val) {
printf("NLR jump failed, val=%p\n", val);
esp_restart();
}
diff --git a/ports/esp32/main/idf_component.yml b/ports/esp32/main/idf_component.yml
index ccbde1f27e..f7773f4f4e 100644
--- a/ports/esp32/main/idf_component.yml
+++ b/ports/esp32/main/idf_component.yml
@@ -5,5 +5,10 @@ dependencies:
rules:
- if: "target in [esp32s2, esp32s3]"
version: "~1.0.0"
+ espressif/lan867x:
+ version: "~1.0.0"
+ rules:
+ - if: "target == esp32"
+ - if: "idf_version >=5.3"
idf:
version: ">=5.2.0"
diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c
index 164b191826..fcd6ed9fa8 100644
--- a/ports/esp32/modesp32.c
+++ b/ports/esp32/modesp32.c
@@ -30,7 +30,6 @@
#include <time.h>
#include <sys/time.h>
#include "driver/gpio.h"
-#include "driver/adc.h"
#include "esp_heap_caps.h"
#include "multi_heap.h"
@@ -48,22 +47,30 @@
#include "../multi_heap_platform.h"
#include "../heap_private.h"
+#if SOC_TOUCH_SENSOR_SUPPORTED
static mp_obj_t esp32_wake_on_touch(const mp_obj_t wake) {
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
if (machine_rtc_config.ext0_pin != -1) {
mp_raise_ValueError(MP_ERROR_TEXT("no resources"));
}
+ #endif
machine_rtc_config.wake_on_touch = mp_obj_is_true(wake);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_wake_on_touch_obj, esp32_wake_on_touch);
+#endif
+#if SOC_PM_SUPPORT_EXT0_WAKEUP
static mp_obj_t esp32_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ #if SOC_TOUCH_SENSOR_SUPPORTED
if (machine_rtc_config.wake_on_touch) {
mp_raise_ValueError(MP_ERROR_TEXT("no resources"));
}
+ #endif
+
enum {ARG_pin, ARG_level};
const mp_arg_t allowed_args[] = {
{ MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = mp_obj_new_int(machine_rtc_config.ext0_pin)} },
@@ -90,7 +97,9 @@ static mp_obj_t esp32_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_m
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext0_obj, 0, esp32_wake_on_ext0);
+#endif
+#if SOC_PM_SUPPORT_EXT1_WAKEUP
static mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum {ARG_pins, ARG_level};
const mp_arg_t allowed_args[] = {
@@ -126,15 +135,20 @@ static mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_m
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext1_obj, 0, esp32_wake_on_ext1);
+#endif
+#if SOC_ULP_SUPPORTED
static mp_obj_t esp32_wake_on_ulp(const mp_obj_t wake) {
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
if (machine_rtc_config.ext0_pin != -1) {
mp_raise_ValueError(MP_ERROR_TEXT("no resources"));
}
+ #endif
machine_rtc_config.wake_on_ulp = mp_obj_is_true(wake);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_wake_on_ulp_obj, esp32_wake_on_ulp);
+#endif
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
static mp_obj_t esp32_gpio_deep_sleep_hold(const mp_obj_t enable) {
@@ -212,13 +226,64 @@ static mp_obj_t esp32_idf_heap_info(const mp_obj_t cap_in) {
}
static MP_DEFINE_CONST_FUN_OBJ_1(esp32_idf_heap_info_obj, esp32_idf_heap_info);
+#if CONFIG_FREERTOS_USE_TRACE_FACILITY
+static mp_obj_t esp32_idf_task_info(void) {
+ const size_t task_count_max = uxTaskGetNumberOfTasks();
+ TaskStatus_t *task_array = m_new(TaskStatus_t, task_count_max);
+ uint32_t total_time;
+ const size_t task_count = uxTaskGetSystemState(task_array, task_count_max, &total_time);
+
+ mp_obj_list_t *task_list = MP_OBJ_TO_PTR(mp_obj_new_list(task_count, NULL));
+ for (size_t i = 0; i < task_count; i++) {
+ mp_obj_t task_data[] = {
+ mp_obj_new_int_from_uint((mp_uint_t)task_array[i].xHandle),
+ mp_obj_new_str(task_array[i].pcTaskName, strlen(task_array[i].pcTaskName)),
+ MP_OBJ_NEW_SMALL_INT(task_array[i].eCurrentState),
+ MP_OBJ_NEW_SMALL_INT(task_array[i].uxCurrentPriority),
+ #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
+ mp_obj_new_int_from_uint(task_array[i].ulRunTimeCounter),
+ #else
+ mp_const_none,
+ #endif
+ mp_obj_new_int_from_uint(task_array[i].usStackHighWaterMark),
+ #if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID
+ MP_OBJ_NEW_SMALL_INT(task_array[i].xCoreID),
+ #else
+ mp_const_none,
+ #endif
+ };
+ task_list->items[i] = mp_obj_new_tuple(7, task_data);
+ }
+
+ m_del(TaskStatus_t, task_array, task_count_max);
+ mp_obj_t task_stats[] = {
+ #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
+ MP_OBJ_NEW_SMALL_INT(total_time),
+ #else
+ mp_const_none,
+ #endif
+ task_list
+ };
+ return mp_obj_new_tuple(2, task_stats);
+}
+static MP_DEFINE_CONST_FUN_OBJ_0(esp32_idf_task_info_obj, esp32_idf_task_info);
+#endif
+
static const mp_rom_map_elem_t esp32_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp32) },
+ #if SOC_TOUCH_SENSOR_SUPPORTED
{ MP_ROM_QSTR(MP_QSTR_wake_on_touch), MP_ROM_PTR(&esp32_wake_on_touch_obj) },
+ #endif
+ #if SOC_PM_SUPPORT_EXT0_WAKEUP
{ MP_ROM_QSTR(MP_QSTR_wake_on_ext0), MP_ROM_PTR(&esp32_wake_on_ext0_obj) },
+ #endif
+ #if SOC_PM_SUPPORT_EXT1_WAKEUP
{ MP_ROM_QSTR(MP_QSTR_wake_on_ext1), MP_ROM_PTR(&esp32_wake_on_ext1_obj) },
+ #endif
+ #if SOC_ULP_SUPPORTED
{ MP_ROM_QSTR(MP_QSTR_wake_on_ulp), MP_ROM_PTR(&esp32_wake_on_ulp_obj) },
+ #endif
#if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
{ MP_ROM_QSTR(MP_QSTR_gpio_deep_sleep_hold), MP_ROM_PTR(&esp32_gpio_deep_sleep_hold_obj) },
#endif
@@ -228,6 +293,9 @@ static const mp_rom_map_elem_t esp32_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_mcu_temperature), MP_ROM_PTR(&esp32_mcu_temperature_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_idf_heap_info), MP_ROM_PTR(&esp32_idf_heap_info_obj) },
+ #if CONFIG_FREERTOS_USE_TRACE_FACILITY
+ { MP_ROM_QSTR(MP_QSTR_idf_task_info), MP_ROM_PTR(&esp32_idf_task_info_obj) },
+ #endif
{ MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) },
{ MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) },
diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c
index 0c1b94d02d..0d7ea44c66 100644
--- a/ports/esp32/modmachine.c
+++ b/ports/esp32/modmachine.c
@@ -178,7 +178,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
machine_sleep_helper(MACHINE_WAKE_SLEEP, n_args, args);
};
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
machine_sleep_helper(MACHINE_WAKE_DEEPSLEEP, n_args, args);
mp_machine_reset();
};
@@ -221,7 +221,7 @@ static mp_int_t mp_machine_reset_cause(void) {
#include "esp32s3/rom/usb/chip_usb_dw_wrapper.h"
#endif
-NORETURN static void machine_bootloader_rtc(void) {
+MP_NORETURN static void machine_bootloader_rtc(void) {
#if CONFIG_IDF_TARGET_ESP32S3 && MICROPY_HW_USB_CDC
usb_usj_mode();
usb_dc_prepare_persist();
@@ -233,7 +233,7 @@ NORETURN static void machine_bootloader_rtc(void) {
#endif
#ifdef MICROPY_BOARD_ENTER_BOOTLOADER
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
for (;;) {
}
@@ -254,7 +254,7 @@ static mp_obj_t machine_wake_reason(size_t n_args, const mp_obj_t *pos_args, mp_
}
static MP_DEFINE_CONST_FUN_OBJ_KW(machine_wake_reason_obj, 0, machine_wake_reason);
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
esp_restart();
}
diff --git a/ports/esp32/modnetwork.h b/ports/esp32/modnetwork.h
index 387f961976..ba69d5cc0f 100644
--- a/ports/esp32/modnetwork.h
+++ b/ports/esp32/modnetwork.h
@@ -28,7 +28,32 @@
#include "esp_netif.h"
-enum { PHY_LAN8710, PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041, PHY_KSZ8081, PHY_KSZ8851SNL = 100, PHY_DM9051, PHY_W5500 };
+// lan867x component requires newer IDF version
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && CONFIG_IDF_TARGET_ESP32
+#define PHY_LAN867X_ENABLED (1)
+#else
+#define PHY_LAN867X_ENABLED (0)
+#endif
+
+// PHY_GENERIC support requires newer IDF version
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) && CONFIG_IDF_TARGET_ESP32
+#define PHY_GENERIC_ENABLED (1)
+#else
+#define PHY_GENERIC_ENABLED (0)
+#endif
+
+enum {
+ // PHYs supported by the internal Ethernet MAC:
+ PHY_LAN8710, PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041, PHY_KSZ8081,
+ #if PHY_LAN867X_ENABLED
+ PHY_LAN8670,
+ #endif
+ #if PHY_GENERIC_ENABLED
+ PHY_GENERIC,
+ #endif
+ // PHYs which are actually SPI Ethernet MAC+PHY chips:
+ PHY_KSZ8851SNL = 100, PHY_DM9051, PHY_W5500
+};
#define IS_SPI_PHY(NUM) (NUM >= 100)
enum { ETH_INITIALIZED, ETH_STARTED, ETH_STOPPED, ETH_CONNECTED, ETH_DISCONNECTED, ETH_GOT_IP };
@@ -52,7 +77,7 @@ extern const mp_obj_type_t esp_network_wlan_type;
MP_DECLARE_CONST_FUN_OBJ_0(esp_network_initialize_obj);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_get_lan_obj);
-MP_DECLARE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj);
+extern const struct _mp_obj_type_t esp_network_ppp_lwip_type;
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_ifconfig_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj);
MP_DECLARE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj);
@@ -61,7 +86,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj);
mp_obj_t esp_ifname(esp_netif_t *netif);
-NORETURN void esp_exceptions_helper(esp_err_t e);
+MP_NORETURN void esp_exceptions_helper(esp_err_t e);
static inline void esp_exceptions(esp_err_t e) {
if (e != ESP_OK) {
diff --git a/ports/esp32/modnetwork_globals.h b/ports/esp32/modnetwork_globals.h
index 1aad785ee3..12252ddbc5 100644
--- a/ports/esp32/modnetwork_globals.h
+++ b/ports/esp32/modnetwork_globals.h
@@ -8,7 +8,7 @@
{ MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&esp_network_get_lan_obj) },
#endif
#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT)
-{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_make_new_obj) },
+{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_lwip_type) },
#endif
{ MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) },
{ MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_network_ipconfig_obj) },
@@ -47,6 +47,12 @@
{ MP_ROM_QSTR(MP_QSTR_PHY_DP83848), MP_ROM_INT(PHY_DP83848) },
{ MP_ROM_QSTR(MP_QSTR_PHY_KSZ8041), MP_ROM_INT(PHY_KSZ8041) },
{ MP_ROM_QSTR(MP_QSTR_PHY_KSZ8081), MP_ROM_INT(PHY_KSZ8081) },
+#if PHY_LAN867X_ENABLED
+{ MP_ROM_QSTR(MP_QSTR_PHY_LAN8670), MP_ROM_INT(PHY_LAN8670) },
+#endif
+#if PHY_GENERIC_ENABLED
+{ MP_ROM_QSTR(MP_QSTR_PHY_GENERIC), MP_ROM_INT(PHY_GENERIC) },
+#endif
#if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL
{ MP_ROM_QSTR(MP_QSTR_PHY_KSZ8851SNL), MP_ROM_INT(PHY_KSZ8851SNL) },
diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c
index 7ea5e855d3..2050d1d04d 100644
--- a/ports/esp32/modsocket.c
+++ b/ports/esp32/modsocket.c
@@ -611,7 +611,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking);
// XXX this can end up waiting a very long time if the content is dribbled in one character
// at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not
// good behaviour.
-static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size,
+static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, mp_int_t flags,
struct sockaddr *from, socklen_t *from_len, int *errcode) {
socket_obj_t *sock = MP_OBJ_TO_PTR(self_in);
@@ -645,7 +645,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size,
if (release_gil) {
MP_THREAD_GIL_EXIT();
}
- int r = lwip_recvfrom(sock->fd, buf, size, 0, from, from_len);
+ int r = lwip_recvfrom(sock->fd, buf, size, flags, from, from_len);
if (release_gil) {
MP_THREAD_GIL_ENTER();
}
@@ -655,7 +655,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size,
if (r >= 0) {
return r;
}
- if (errno != EWOULDBLOCK) {
+ if (errno != EWOULDBLOCK || (flags & MSG_DONTWAIT)) {
*errcode = errno;
return MP_STREAM_ERROR;
}
@@ -666,14 +666,17 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size,
return MP_STREAM_ERROR;
}
-mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in,
+mp_obj_t _socket_recvfrom(size_t n_args, const mp_obj_t *args,
struct sockaddr *from, socklen_t *from_len) {
- size_t len = mp_obj_get_int(len_in);
+ mp_obj_t self_in = args[0];
+ size_t len = mp_obj_get_int(args[1]);
+ int flags = n_args > 2 ? mp_obj_get_int(args[2]) : 0;
+
vstr_t vstr;
vstr_init_len(&vstr, len);
int errcode;
- mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, from, from_len, &errcode);
+ mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, flags, from, from_len, &errcode);
if (ret == MP_STREAM_ERROR) {
mp_raise_OSError(errcode);
}
@@ -682,17 +685,17 @@ mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in,
return mp_obj_new_bytes_from_vstr(&vstr);
}
-static mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) {
- return _socket_recvfrom(self_in, len_in, NULL, NULL);
+static mp_obj_t socket_recv(size_t n_args, const mp_obj_t *args) {
+ return _socket_recvfrom(n_args, args, NULL, NULL);
}
-static MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv);
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recv_obj, 2, 3, socket_recv);
-static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
+static mp_obj_t socket_recvfrom(size_t n_args, const mp_obj_t *args) {
struct sockaddr from;
socklen_t fromlen = sizeof(from);
mp_obj_t tuple[2];
- tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen);
+ tuple[0] = _socket_recvfrom(n_args, args, &from, &fromlen);
uint8_t *ip = (uint8_t *)&((struct sockaddr_in *)&from)->sin_addr;
mp_uint_t port = lwip_ntohs(((struct sockaddr_in *)&from)->sin_port);
@@ -700,7 +703,7 @@ static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) {
return mp_obj_new_tuple(2, tuple);
}
-static MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom);
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recvfrom_obj, 2, 3, socket_recvfrom);
int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) {
int sentlen = 0;
@@ -789,7 +792,7 @@ static mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) {
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile);
static mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
- return _socket_read_data(self_in, buf, size, NULL, NULL, errcode);
+ return _socket_read_data(self_in, buf, size, 0, NULL, NULL, errcode);
}
static mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
@@ -1010,6 +1013,8 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_SO_BINDTODEVICE), MP_ROM_INT(SO_BINDTODEVICE) },
{ MP_ROM_QSTR(MP_QSTR_IP_ADD_MEMBERSHIP), MP_ROM_INT(IP_ADD_MEMBERSHIP) },
{ MP_ROM_QSTR(MP_QSTR_TCP_NODELAY), MP_ROM_INT(TCP_NODELAY) },
+ { MP_ROM_QSTR(MP_QSTR_MSG_PEEK), MP_ROM_INT(MSG_PEEK) },
+ { MP_ROM_QSTR(MP_QSTR_MSG_DONTWAIT), MP_ROM_INT(MSG_DONTWAIT) },
};
static MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table);
diff --git a/ports/esp32/modtime.c b/ports/esp32/modtime.c
index 4695dd23e7..991f2cf578 100644
--- a/ports/esp32/modtime.c
+++ b/ports/esp32/modtime.c
@@ -54,5 +54,5 @@ static mp_obj_t mp_time_localtime_get(void) {
static mp_obj_t mp_time_time_get(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
- return mp_obj_new_int(tv.tv_sec);
+ return timeutils_obj_from_timestamp(tv.tv_sec);
}
diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h
index b5b7d63a56..a6f103cdef 100644
--- a/ports/esp32/mpconfigport.h
+++ b/ports/esp32/mpconfigport.h
@@ -62,7 +62,8 @@
#define MICROPY_STACK_CHECK_MARGIN (1024)
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
-#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL)
+#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) // Debugging Note: Increase the error reporting level to view
+ // __FUNCTION__, __LINE__, __FILE__ in check_esp_err() exceptions
#define MICROPY_WARNINGS (1)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_STREAMS_POSIX_API (1)
@@ -123,6 +124,7 @@
#define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/esp32/machine_adc.c"
#define MICROPY_PY_MACHINE_ADC_ATTEN_WIDTH (1)
#define MICROPY_PY_MACHINE_ADC_INIT (1)
+#define MICROPY_PY_MACHINE_ADC_DEINIT (1)
#define MICROPY_PY_MACHINE_ADC_READ (1)
#define MICROPY_PY_MACHINE_ADC_READ_UV (1)
#define MICROPY_PY_MACHINE_ADC_BLOCK (1)
@@ -391,3 +393,8 @@ void boardctrl_startup(void);
#ifndef MICROPY_PY_STRING_TX_GIL_THRESHOLD
#define MICROPY_PY_STRING_TX_GIL_THRESHOLD (20)
#endif
+
+// Code can override this to provide a custom ESP-IDF entry point.
+#ifndef MICROPY_ESP_IDF_ENTRY
+#define MICROPY_ESP_IDF_ENTRY app_main
+#endif
diff --git a/ports/esp32/mpnimbleport.c b/ports/esp32/mpnimbleport.c
index ce4b77727a..77185883fd 100644
--- a/ports/esp32/mpnimbleport.c
+++ b/ports/esp32/mpnimbleport.c
@@ -58,7 +58,7 @@ void mp_bluetooth_nimble_port_start(void) {
nimble_port_freertos_init(ble_host_task);
}
-void mp_bluetooth_nimble_port_shutdown(void) {
+int mp_bluetooth_nimble_port_shutdown(void) {
DEBUG_printf("mp_bluetooth_nimble_port_shutdown\n");
#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK
@@ -79,6 +79,8 @@ void mp_bluetooth_nimble_port_shutdown(void) {
// Mark stack as shutdown.
mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF;
+
+ return 0;
}
#endif
diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c
index 962b5780d0..67917e33e5 100644
--- a/ports/esp32/mpthreadport.c
+++ b/ports/esp32/mpthreadport.c
@@ -41,10 +41,16 @@
#define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + MICROPY_STACK_CHECK_MARGIN)
#define MP_THREAD_PRIORITY (ESP_TASK_PRIO_MIN + 1)
+typedef enum {
+ MP_THREAD_RUN_STATE_NEW,
+ MP_THREAD_RUN_STATE_RUNNING,
+ MP_THREAD_RUN_STATE_FINISHED,
+} mp_thread_run_state_t;
+
// this structure forms a linked list, one node per active thread
typedef struct _mp_thread_t {
TaskHandle_t id; // system id of thread
- int ready; // whether the thread is ready and running
+ mp_thread_run_state_t run_state; // current run state of the thread
void *arg; // thread Python args, a GC root pointer
void *stack; // pointer to the stack
size_t stack_len; // number of words in the stack
@@ -60,18 +66,16 @@ void mp_thread_init(void *stack, uint32_t stack_len) {
mp_thread_set_state(&mp_state_ctx.thread);
// create the first entry in the linked list of all threads
thread_entry0.id = xTaskGetCurrentTaskHandle();
- thread_entry0.ready = 1;
+ thread_entry0.run_state = MP_THREAD_RUN_STATE_RUNNING;
thread_entry0.arg = NULL;
thread_entry0.stack = stack;
thread_entry0.stack_len = stack_len;
thread_entry0.next = NULL;
+ thread = &thread_entry0;
mp_thread_mutex_init(&thread_mutex);
// memory barrier to ensure above data is committed
__sync_synchronize();
-
- // vTaskPreDeletionHook needs the thread ready after thread_mutex is ready
- thread = &thread_entry0;
}
void mp_thread_gc_others(void) {
@@ -82,7 +86,7 @@ void mp_thread_gc_others(void) {
if (th->id == xTaskGetCurrentTaskHandle()) {
continue;
}
- if (!th->ready) {
+ if (th->run_state != MP_THREAD_RUN_STATE_RUNNING) {
continue;
}
gc_collect_root(th->stack, th->stack_len);
@@ -106,7 +110,7 @@ void mp_thread_start(void) {
mp_thread_mutex_lock(&thread_mutex, 1);
for (mp_thread_t *th = thread; th != NULL; th = th->next) {
if (th->id == xTaskGetCurrentTaskHandle()) {
- th->ready = 1;
+ th->run_state = MP_THREAD_RUN_STATE_RUNNING;
break;
}
}
@@ -116,12 +120,22 @@ void mp_thread_start(void) {
static void *(*ext_thread_entry)(void *) = NULL;
static void freertos_entry(void *arg) {
+ // Run the Python code.
if (ext_thread_entry) {
ext_thread_entry(arg);
}
- vTaskDelete(NULL);
- for (;;) {;
+
+ // Remove the thread from the linked-list of active threads.
+ mp_thread_mutex_lock(&thread_mutex, 1);
+ for (mp_thread_t **th = &thread; *th != NULL; th = &(*th)->next) {
+ if ((*th)->id == xTaskGetCurrentTaskHandle()) {
+ *th = (*th)->next;
+ }
}
+ mp_thread_mutex_unlock(&thread_mutex);
+
+ // Delete this FreeRTOS task (this call to vTaskDelete will not return).
+ vTaskDelete(NULL);
}
mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_size, int priority, char *name) {
@@ -147,7 +161,7 @@ mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_s
}
// add thread to linked list of all threads
- th->ready = 0;
+ th->run_state = MP_THREAD_RUN_STATE_NEW;
th->arg = arg;
th->stack = pxTaskGetStackStart(th->id);
th->stack_len = *stack_size / sizeof(uintptr_t);
@@ -167,33 +181,7 @@ void mp_thread_finish(void) {
mp_thread_mutex_lock(&thread_mutex, 1);
for (mp_thread_t *th = thread; th != NULL; th = th->next) {
if (th->id == xTaskGetCurrentTaskHandle()) {
- th->ready = 0;
- break;
- }
- }
- mp_thread_mutex_unlock(&thread_mutex);
-}
-
-// This is called either from vTaskDelete() or from the FreeRTOS idle task, so
-// may not be within Python context. Therefore MP_STATE_THREAD may not be valid
-// and it does not have the GIL.
-void vTaskPreDeletionHook(void *tcb) {
- if (thread == NULL) {
- // threading not yet initialised
- return;
- }
- mp_thread_t *prev = NULL;
- mp_thread_mutex_lock(&thread_mutex, 1);
- for (mp_thread_t *th = thread; th != NULL; prev = th, th = th->next) {
- // unlink the node from the list
- if ((void *)th->id == tcb) {
- if (prev != NULL) {
- prev->next = th->next;
- } else {
- // move the start pointer
- thread = th->next;
- }
- // The "th" memory will eventually be reclaimed by the GC.
+ th->run_state = MP_THREAD_RUN_STATE_FINISHED;
break;
}
}
@@ -221,32 +209,22 @@ void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
}
void mp_thread_deinit(void) {
- for (;;) {
- // Find a task to delete
- TaskHandle_t id = NULL;
- mp_thread_mutex_lock(&thread_mutex, 1);
- for (mp_thread_t *th = thread; th != NULL; th = th->next) {
- // Don't delete the current task
- if (th->id != xTaskGetCurrentTaskHandle()) {
- id = th->id;
- break;
- }
- }
- mp_thread_mutex_unlock(&thread_mutex);
+ // The current task should be thread_entry0 and should be the last in the linked list.
+ assert(thread_entry0.id == xTaskGetCurrentTaskHandle());
+ assert(thread_entry0.next == NULL);
- if (id == NULL) {
- // No tasks left to delete
- break;
- } else {
- // Call FreeRTOS to delete the task (it will call vTaskPreDeletionHook)
- vTaskDelete(id);
+ // Delete all tasks except the main one.
+ mp_thread_mutex_lock(&thread_mutex, 1);
+ for (mp_thread_t *th = thread; th != NULL; th = th->next) {
+ if (th != &thread_entry0) {
+ vTaskDelete(th->id);
}
}
-}
-
-#else
+ thread = &thread_entry0;
+ mp_thread_mutex_unlock(&thread_mutex);
-void vTaskPreDeletionHook(void *tcb) {
+ // Give the idle task a chance to run, to clean up any deleted tasks.
+ vTaskDelay(1);
}
#endif // MICROPY_PY_THREAD
diff --git a/ports/esp32/network_common.c b/ports/esp32/network_common.c
index fce8a0304c..bd34f1b41f 100644
--- a/ports/esp32/network_common.c
+++ b/ports/esp32/network_common.c
@@ -45,7 +45,7 @@
#include "lwip/sockets.h"
#include "lwip/dns.h"
-NORETURN void esp_exceptions_helper(esp_err_t e) {
+MP_NORETURN void esp_exceptions_helper(esp_err_t e) {
switch (e) {
case ESP_ERR_WIFI_NOT_INIT:
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Not Initialized"));
@@ -77,6 +77,8 @@ NORETURN void esp_exceptions_helper(esp_err_t e) {
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Would Block"));
case ESP_ERR_WIFI_NOT_CONNECT:
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Not Connected"));
+ case ESP_ERR_NO_MEM:
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("WiFi Out of Memory"));
default:
mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Wifi Unknown Error 0x%04x"), e);
}
diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c
index e1a8c95785..309ee0b14a 100644
--- a/ports/esp32/network_lan.c
+++ b/ports/esp32/network_lan.c
@@ -45,6 +45,10 @@
#include "modnetwork.h"
#include "extmod/modnetwork.h"
+#if PHY_LAN867X_ENABLED
+#include "esp_eth_phy_lan867x.h"
+#endif
+
typedef struct _lan_if_obj_t {
base_if_obj_t base;
bool initialized;
@@ -156,6 +160,12 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
args[ARG_phy_type].u_int != PHY_RTL8201 &&
args[ARG_phy_type].u_int != PHY_KSZ8041 &&
args[ARG_phy_type].u_int != PHY_KSZ8081 &&
+ #if PHY_LAN867X_ENABLED
+ args[ARG_phy_type].u_int != PHY_LAN8670 &&
+ #endif
+ #if PHY_GENERIC_ENABLED
+ args[ARG_phy_type].u_int != PHY_GENERIC &&
+ #endif
#if CONFIG_ETH_USE_SPI_ETHERNET
#if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL
args[ARG_phy_type].u_int != PHY_KSZ8851SNL &&
@@ -231,7 +241,17 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar
case PHY_KSZ8081:
self->phy = esp_eth_phy_new_ksz80xx(&phy_config);
break;
+ #if PHY_LAN867X_ENABLED
+ case PHY_LAN8670:
+ self->phy = esp_eth_phy_new_lan867x(&phy_config);
+ break;
+ #endif
+ #if PHY_GENERIC_ENABLED
+ case PHY_GENERIC:
+ self->phy = esp_eth_phy_new_generic(&phy_config);
+ break;
#endif
+ #endif // CONFIG_IDF_TARGET_ESP32
#if CONFIG_ETH_USE_SPI_ETHERNET
#if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL
case PHY_KSZ8851SNL: {
diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c
index 4dd5a3718c..8b700c98ef 100644
--- a/ports/esp32/network_ppp.c
+++ b/ports/esp32/network_ppp.c
@@ -4,6 +4,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2018 "Eric Poulsen" <eric@zyxod.com>
+ * Copyright (c) 2024 Damien P. George
*
* Based on the ESP IDF example code which is Public Domain / CC0
*
@@ -26,172 +27,265 @@
* THE SOFTWARE.
*/
+// This file is intended to closely match extmod/network_ppp_lwip.c. Changes can
+// and should probably be applied to both files. Compare them directly by using:
+// git diff --no-index extmod/network_ppp_lwip.c ports/esp32/network_ppp.c
+
#include "py/runtime.h"
#include "py/mphal.h"
-#include "py/objtype.h"
#include "py/stream.h"
-#include "shared/netutils/netutils.h"
-#include "modmachine.h"
-#include "ppp_set_auth.h"
-#include "netif/ppp/ppp.h"
-#include "netif/ppp/pppos.h"
-#include "lwip/err.h"
-#include "lwip/sockets.h"
-#include "lwip/sys.h"
-#include "lwip/netdb.h"
+#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT)
+
#include "lwip/dns.h"
+#include "netif/ppp/ppp.h"
#include "netif/ppp/pppapi.h"
+#include "netif/ppp/pppos.h"
-#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT)
+// Includes for port-specific changes compared to network_ppp_lwip.c
+#include "shared/netutils/netutils.h"
+#include "ppp_set_auth.h"
-#define PPP_CLOSE_TIMEOUT_MS (4000)
+// Enable this to see the serial data going between the PPP layer.
+#define PPP_TRACE_IN_OUT (0)
-typedef struct _ppp_if_obj_t {
+typedef enum {
+ STATE_INACTIVE,
+ STATE_ACTIVE,
+ STATE_ERROR,
+ STATE_CONNECTING,
+ STATE_CONNECTED,
+} network_ppp_state_t;
+
+typedef struct _network_ppp_obj_t {
mp_obj_base_t base;
- bool active;
- bool connected;
- volatile bool clean_close;
- ppp_pcb *pcb;
+ network_ppp_state_t state;
+ int error_code;
mp_obj_t stream;
- SemaphoreHandle_t inactiveWaitSem;
- volatile TaskHandle_t client_task_handle;
- struct netif pppif;
-} ppp_if_obj_t;
+ ppp_pcb *pcb;
+ struct netif netif;
+} network_ppp_obj_t;
-const mp_obj_type_t ppp_if_type;
+const mp_obj_type_t esp_network_ppp_lwip_type;
+
+static mp_obj_t network_ppp___del__(mp_obj_t self_in);
+
+static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) {
+ if (self->stream == mp_const_none) {
+ return;
+ }
-static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
- ppp_if_obj_t *self = ctx;
- struct netif *pppif = ppp_netif(self->pcb);
+ // Disable UART IRQ.
+ mp_obj_t dest[3];
+ mp_load_method(self->stream, MP_QSTR_irq, dest);
+ dest[2] = mp_const_none;
+ mp_call_method_n_kw(1, 0, dest);
+}
+static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
+ network_ppp_obj_t *self = ctx;
switch (err_code) {
case PPPERR_NONE:
- #if CONFIG_LWIP_IPV6
- self->connected = (pppif->ip_addr.u_addr.ip4.addr != 0);
- #else
- self->connected = (pppif->ip_addr.addr != 0);
- #endif // CONFIG_LWIP_IPV6
+ self->state = STATE_CONNECTED;
break;
case PPPERR_USER:
- self->clean_close = true;
- break;
- case PPPERR_CONNECT:
- self->connected = false;
+ if (self->state >= STATE_ERROR) {
+ // Indicate that we are no longer connected and thus
+ // only need to free the PPP PCB, not close it.
+ self->state = STATE_ACTIVE;
+ }
+ // Clean up the PPP PCB.
+ network_ppp___del__(MP_OBJ_FROM_PTR(self));
break;
default:
+ self->state = STATE_ERROR;
+ self->error_code = err_code;
break;
}
}
-static mp_obj_t ppp_make_new(mp_obj_t stream) {
+static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
+ mp_arg_check_num(n_args, n_kw, 1, 1, false);
+
+ mp_obj_t stream = all_args[0];
+
if (stream != mp_const_none) {
mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
}
- ppp_if_obj_t *self = mp_obj_malloc_with_finaliser(ppp_if_obj_t, &ppp_if_type);
+ network_ppp_obj_t *self = mp_obj_malloc_with_finaliser(network_ppp_obj_t, type);
+ self->state = STATE_INACTIVE;
self->stream = stream;
- self->active = false;
- self->connected = false;
- self->clean_close = false;
- self->client_task_handle = NULL;
+ self->pcb = NULL;
return MP_OBJ_FROM_PTR(self);
}
-MP_DEFINE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj, ppp_make_new);
-#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
-static u32_t ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx)
-#else
-static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
-#endif
-{
- ppp_if_obj_t *self = ctx;
-
- mp_obj_t stream = self->stream;
- if (stream == mp_const_none) {
- return 0;
+static mp_obj_t network_ppp___del__(mp_obj_t self_in) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ if (self->state >= STATE_ACTIVE) {
+ if (self->state >= STATE_ERROR) {
+ // Still connected over the stream.
+ // Force the connection to close, with nocarrier=1.
+ self->state = STATE_INACTIVE;
+ pppapi_close(self->pcb, 1);
+ }
+ network_ppp_stream_uart_irq_disable(self);
+ // Free PPP PCB and reset state.
+ self->state = STATE_INACTIVE;
+ pppapi_free(self->pcb);
+ self->pcb = NULL;
}
-
- int err;
- return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE);
+ return mp_const_none;
}
+static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp___del___obj, network_ppp___del__);
-static void pppos_client_task(void *self_in) {
- ppp_if_obj_t *self = (ppp_if_obj_t *)self_in;
- uint8_t buf[256];
+static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
- int len = 0;
- while (ulTaskNotifyTake(pdTRUE, len <= 0) == 0) {
- mp_obj_t stream = self->stream;
- if (stream == mp_const_none) {
- len = 0;
- } else {
- int err;
- len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0);
- if (len > 0) {
- pppos_input_tcpip(self->pcb, (u8_t *)buf, len);
- }
- }
+ if (self->state <= STATE_ERROR) {
+ return MP_OBJ_NEW_SMALL_INT(-MP_EPERM);
}
- self->client_task_handle = NULL;
- vTaskDelete(NULL);
- for (;;) {
+ mp_int_t total_len = 0;
+ mp_obj_t stream = self->stream;
+ while (stream != mp_const_none) {
+ uint8_t buf[256];
+ int err;
+ mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0);
+ if (len == 0) {
+ break;
+ }
+ #if PPP_TRACE_IN_OUT
+ mp_printf(&mp_plat_print, "ppp_in(n=%u,data=", len);
+ for (size_t i = 0; i < len; ++i) {
+ mp_printf(&mp_plat_print, "%02x:", buf[i]);
+ }
+ mp_printf(&mp_plat_print, ")\n");
+ #endif
+ pppos_input(self->pcb, (u8_t *)buf, len);
+ total_len += len;
}
+
+ return MP_OBJ_NEW_SMALL_INT(total_len);
}
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll);
-static mp_obj_t ppp_active(size_t n_args, const mp_obj_t *args) {
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+static void network_ppp_stream_uart_irq_enable(network_ppp_obj_t *self) {
+ if (self->stream == mp_const_none) {
+ return;
+ }
- if (n_args > 1) {
- if (mp_obj_is_true(args[1])) {
- if (self->active) {
- return mp_const_true;
- }
+ // Enable UART IRQ to call PPP.poll() when incoming data is ready.
+ mp_obj_t dest[4];
+ mp_load_method(self->stream, MP_QSTR_irq, dest);
+ dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self));
+ dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE);
+ mp_call_method_n_kw(2, 0, dest);
+}
- self->pcb = pppapi_pppos_create(&self->pppif, ppp_output_callback, ppp_status_cb, self);
+static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
+ if (n_args != 1 && kwargs->used != 0) {
+ mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed"));
+ }
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
- if (self->pcb == NULL) {
- mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("init failed"));
- }
- self->active = true;
- } else {
- if (!self->active) {
- return mp_const_false;
+ if (kwargs->used != 0) {
+ for (size_t i = 0; i < kwargs->alloc; i++) {
+ if (mp_map_slot_is_filled(kwargs, i)) {
+ switch (mp_obj_str_get_qstr(kwargs->table[i].key)) {
+ case MP_QSTR_stream: {
+ if (kwargs->table[i].value != mp_const_none) {
+ mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
+ }
+ if (self->state >= STATE_ACTIVE) {
+ network_ppp_stream_uart_irq_disable(self);
+ }
+ self->stream = kwargs->table[i].value;
+ if (self->state >= STATE_ACTIVE) {
+ network_ppp_stream_uart_irq_enable(self);
+ }
+ break;
+ }
+ default:
+ break;
+ }
}
+ }
+ return mp_const_none;
+ }
- if (self->client_task_handle != NULL) { // is connecting or connected?
- // Wait for PPPERR_USER, with timeout
- pppapi_close(self->pcb, 0);
- uint32_t t0 = mp_hal_ticks_ms();
- while (!self->clean_close && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) {
- mp_hal_delay_ms(10);
- }
+ if (n_args != 2) {
+ mp_raise_TypeError(MP_ERROR_TEXT("can query only one param"));
+ }
- // Shutdown task
- xTaskNotifyGive(self->client_task_handle);
- t0 = mp_hal_ticks_ms();
- while (self->client_task_handle != NULL && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) {
- mp_hal_delay_ms(10);
+ mp_obj_t val = mp_const_none;
+
+ switch (mp_obj_str_get_qstr(args[1])) {
+ case MP_QSTR_stream: {
+ val = self->stream;
+ break;
+ }
+ case MP_QSTR_ifname: {
+ if (self->pcb != NULL) {
+ struct netif *pppif = ppp_netif(self->pcb);
+ char ifname[NETIF_NAMESIZE + 1] = {0};
+ netif_index_to_name(netif_get_index(pppif), ifname);
+ if (ifname[0] != 0) {
+ val = mp_obj_new_str_from_cstr((char *)ifname);
}
}
-
- // Release PPP
- pppapi_free(self->pcb);
- self->pcb = NULL;
- self->active = false;
- self->connected = false;
- self->clean_close = false;
+ break;
}
+ default:
+ mp_raise_ValueError(MP_ERROR_TEXT("unknown config param"));
+ }
+
+ return val;
+}
+static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_config_obj, 1, network_ppp_config);
+
+static mp_obj_t network_ppp_status(mp_obj_t self_in) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ if (self->state == STATE_ERROR) {
+ return MP_OBJ_NEW_SMALL_INT(-self->error_code);
+ } else {
+ return MP_OBJ_NEW_SMALL_INT(self->state);
+ }
+}
+static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_status_obj, network_ppp_status);
+
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
+static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx)
+#else
+static u32_t network_ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx)
+#endif
+{
+ network_ppp_obj_t *self = ctx;
+ #if PPP_TRACE_IN_OUT
+ mp_printf(&mp_plat_print, "ppp_out(n=%u,data=", len);
+ for (size_t i = 0; i < len; ++i) {
+ mp_printf(&mp_plat_print, "%02x:", ((const uint8_t *)data)[i]);
+ }
+ mp_printf(&mp_plat_print, ")\n");
+ #endif
+ mp_obj_t stream = self->stream;
+ if (stream == mp_const_none) {
+ return 0;
}
- return mp_obj_new_bool(self->active);
+ int err;
+ // The return value from this output callback is the number of bytes written out.
+ // If it's less than the requested number of bytes then lwIP will propagate out an error.
+ return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE);
}
-static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_active_obj, 1, 2, ppp_active);
-static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
- enum { ARG_authmode, ARG_username, ARG_password };
+static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+ enum { ARG_security, ARG_user, ARG_key, ARG_authmode, ARG_username, ARG_password };
static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
+ { MP_QSTR_user, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ // Deprecated arguments for backwards compatibility
{ MP_QSTR_authmode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PPPAUTHTYPE_NONE} },
{ MP_QSTR_username, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_password, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
@@ -200,17 +294,34 @@ static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw
mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args);
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ // Use deprecated arguments as defaults
+ if (parsed_args[ARG_security].u_int == -1) {
+ parsed_args[ARG_security].u_int = parsed_args[ARG_authmode].u_int;
+ }
+ if (parsed_args[ARG_user].u_obj == mp_const_none) {
+ parsed_args[ARG_user].u_obj = parsed_args[ARG_username].u_obj;
+ }
+ if (parsed_args[ARG_key].u_obj == mp_const_none) {
+ parsed_args[ARG_key].u_obj = parsed_args[ARG_password].u_obj;
+ }
+
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+
+ if (self->state == STATE_INACTIVE) {
+ self->pcb = pppapi_pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self);
+ if (self->pcb == NULL) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed"));
+ }
+ self->state = STATE_ACTIVE;
- if (!self->active) {
- mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("must be active"));
+ network_ppp_stream_uart_irq_enable(self);
}
- if (self->client_task_handle != NULL) {
+ if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) {
mp_raise_OSError(MP_EALREADY);
}
- switch (parsed_args[ARG_authmode].u_int) {
+ switch (parsed_args[ARG_security].u_int) {
case PPPAUTHTYPE_NONE:
case PPPAUTHTYPE_PAP:
case PPPAUTHTYPE_CHAP:
@@ -219,39 +330,49 @@ static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw
mp_raise_ValueError(MP_ERROR_TEXT("invalid auth"));
}
- if (parsed_args[ARG_authmode].u_int != PPPAUTHTYPE_NONE) {
- const char *username_str = mp_obj_str_get_str(parsed_args[ARG_username].u_obj);
- const char *password_str = mp_obj_str_get_str(parsed_args[ARG_password].u_obj);
- pppapi_set_auth(self->pcb, parsed_args[ARG_authmode].u_int, username_str, password_str);
+ if (parsed_args[ARG_security].u_int != PPPAUTHTYPE_NONE) {
+ const char *user_str = mp_obj_str_get_str(parsed_args[ARG_user].u_obj);
+ const char *key_str = mp_obj_str_get_str(parsed_args[ARG_key].u_obj);
+ pppapi_set_auth(self->pcb, parsed_args[ARG_security].u_int, user_str, key_str);
}
- if (pppapi_set_default(self->pcb) != ESP_OK) {
- mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("set default failed"));
+
+ if (pppapi_set_default(self->pcb) != ERR_OK) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_set_default failed"));
}
ppp_set_usepeerdns(self->pcb, true);
- if (pppapi_connect(self->pcb, 0) != ESP_OK) {
- mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("connect failed"));
+ if (pppapi_connect(self->pcb, 0) != ERR_OK) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_connect failed"));
}
- if (xTaskCreatePinnedToCore(pppos_client_task, "ppp", 2048, self, 1, (TaskHandle_t *)&self->client_task_handle, MP_TASK_COREID) != pdPASS) {
- mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create worker task"));
- }
+ self->state = STATE_CONNECTING;
+
+ // Do a poll in case there is data waiting on the input stream.
+ network_ppp_poll(1, args);
return mp_const_none;
}
-MP_DEFINE_CONST_FUN_OBJ_KW(ppp_connect_obj, 1, ppp_connect_py);
+static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_connect_obj, 1, network_ppp_connect);
-static mp_obj_t ppp_delete(mp_obj_t self_in) {
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in);
- mp_obj_t args[] = {self, mp_const_false};
- ppp_active(2, args);
+static mp_obj_t network_ppp_disconnect(mp_obj_t self_in) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) {
+ // Initiate close and wait for PPPERR_USER callback.
+ pppapi_close(self->pcb, 0);
+ }
return mp_const_none;
}
-MP_DEFINE_CONST_FUN_OBJ_1(ppp_delete_obj, ppp_delete);
+static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_disconnect_obj, network_ppp_disconnect);
+
+static mp_obj_t network_ppp_isconnected(mp_obj_t self_in) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ return mp_obj_new_bool(self->state == STATE_CONNECTED);
+}
+static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_isconnected_obj, network_ppp_isconnected);
-static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) {
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+static mp_obj_t network_ppp_ifconfig(size_t n_args, const mp_obj_t *args) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (n_args == 1) {
// get
const ip_addr_t *dns;
@@ -282,11 +403,11 @@ static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) {
return mp_const_none;
}
}
-MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_ifconfig_obj, 1, 2, ppp_ifconfig);
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_ifconfig_obj, 1, 2, network_ppp_ifconfig);
-static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
+static mp_obj_t network_ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (kwargs->used == 0) {
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (self->pcb == NULL) {
mp_raise_ValueError(MP_ERROR_TEXT("PPP not active"));
}
@@ -318,94 +439,64 @@ static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwar
}
return mp_const_none;
}
-static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_ipconfig_obj, 1, ppp_ipconfig);
+static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_ipconfig_obj, 1, network_ppp_ipconfig);
-static mp_obj_t ppp_status(mp_obj_t self_in) {
- return mp_const_none;
-}
-static MP_DEFINE_CONST_FUN_OBJ_1(ppp_status_obj, ppp_status);
-
-static mp_obj_t ppp_isconnected(mp_obj_t self_in) {
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in);
- return mp_obj_new_bool(self->connected);
-}
-static MP_DEFINE_CONST_FUN_OBJ_1(ppp_isconnected_obj, ppp_isconnected);
-
-static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
- if (n_args != 1 && kwargs->used != 0) {
- mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed"));
- }
- ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]);
-
- if (kwargs->used != 0) {
- for (size_t i = 0; i < kwargs->alloc; i++) {
- if (mp_map_slot_is_filled(kwargs, i)) {
- switch (mp_obj_str_get_qstr(kwargs->table[i].key)) {
- case MP_QSTR_stream: {
- if (kwargs->table[i].value != mp_const_none) {
- mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
- }
- self->stream = kwargs->table[i].value;
- break;
- }
- default:
- break;
- }
+static mp_obj_t network_ppp_active(size_t n_args, const mp_obj_t *args) {
+ network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
+ if (n_args > 1) {
+ if (mp_obj_is_true(args[1])) {
+ if (self->state >= STATE_ACTIVE) {
+ return mp_const_true;
}
- }
- return mp_const_none;
- }
- if (n_args != 2) {
- mp_raise_TypeError(MP_ERROR_TEXT("can query only one param"));
- }
-
- mp_obj_t val = mp_const_none;
+ self->pcb = pppapi_pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self);
+ if (self->pcb == NULL) {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed"));
+ }
+ self->state = STATE_ACTIVE;
- switch (mp_obj_str_get_qstr(args[1])) {
- case MP_QSTR_stream: {
- val = self->stream;
- break;
- }
- case MP_QSTR_ifname: {
- if (self->pcb != NULL) {
- struct netif *pppif = ppp_netif(self->pcb);
- char ifname[NETIF_NAMESIZE + 1] = {0};
- netif_index_to_name(netif_get_index(pppif), ifname);
- if (ifname[0] != 0) {
- val = mp_obj_new_str_from_cstr((char *)ifname);
- }
+ network_ppp_stream_uart_irq_enable(self);
+ } else {
+ if (self->state < STATE_ACTIVE) {
+ return mp_const_false;
}
- break;
+
+ network_ppp___del__(MP_OBJ_FROM_PTR(self));
}
- default:
- mp_raise_ValueError(MP_ERROR_TEXT("unknown config param"));
}
-
- return val;
+ return mp_obj_new_bool(self->state >= STATE_ACTIVE);
}
-static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_config_obj, 1, ppp_config);
-
-static const mp_rom_map_elem_t ppp_if_locals_dict_table[] = {
- { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&ppp_active_obj) },
- { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&ppp_connect_obj) },
- { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&ppp_isconnected_obj) },
- { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&ppp_status_obj) },
- { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&ppp_config_obj) },
- { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&ppp_ifconfig_obj) },
- { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&ppp_ipconfig_obj) },
- { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ppp_delete_obj) },
+static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_active_obj, 1, 2, network_ppp_active);
+
+static const mp_rom_map_elem_t network_ppp_locals_dict_table[] = {
+ { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&network_ppp___del___obj) },
+ { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_ppp_config_obj) },
+ { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_ppp_status_obj) },
+ { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&network_ppp_connect_obj) },
+ { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_ppp_disconnect_obj) },
+ { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_ppp_isconnected_obj) },
+ { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_ppp_ifconfig_obj) },
+ { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_ppp_ipconfig_obj) },
+ { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&network_ppp_poll_obj) },
+
+ { MP_ROM_QSTR(MP_QSTR_SEC_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) },
+ { MP_ROM_QSTR(MP_QSTR_SEC_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) },
+ { MP_ROM_QSTR(MP_QSTR_SEC_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) },
+
+ // Deprecated interface for backwards compatibility
+ { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_ppp_active_obj) },
{ MP_ROM_QSTR(MP_QSTR_AUTH_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) },
{ MP_ROM_QSTR(MP_QSTR_AUTH_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) },
{ MP_ROM_QSTR(MP_QSTR_AUTH_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) },
};
-static MP_DEFINE_CONST_DICT(ppp_if_locals_dict, ppp_if_locals_dict_table);
+static MP_DEFINE_CONST_DICT(network_ppp_locals_dict, network_ppp_locals_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
- ppp_if_type,
+ esp_network_ppp_lwip_type,
MP_QSTR_PPP,
MP_TYPE_FLAG_NONE,
- locals_dict, &ppp_if_locals_dict
+ make_new, network_ppp_make_new,
+ locals_dict, &network_ppp_locals_dict
);
#endif
diff --git a/ports/esp32/panichandler.c b/ports/esp32/panichandler.c
index e6ff98ded2..5211286f84 100644
--- a/ports/esp32/panichandler.c
+++ b/ports/esp32/panichandler.c
@@ -42,11 +42,20 @@
#endif
void __real_esp_panic_handler(void *);
-void esp_panic_handler_reconfigure_wdts(uint32_t timeout_ms);
void panic_print_str(const char *str);
+#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2)
+void esp_panic_handler_reconfigure_wdts(uint32_t timeout_ms);
+#else
+void esp_panic_handler_feed_wdts(void);
+#endif
+
void MICROPY_WRAP_PANICHANDLER_FUN(__wrap_esp_panic_handler)(void *info) {
+ #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2)
esp_panic_handler_reconfigure_wdts(1000);
+ #else
+ esp_panic_handler_feed_wdts();
+ #endif
const static char *msg = MICROPY_WRAP_PANICHANDLER_STR(
"\r\n"
diff --git a/ports/esp32/partitions-16MiB-ota.csv b/ports/esp32/partitions-16MiB-ota.csv
deleted file mode 100644
index a6f83bc46b..0000000000
--- a/ports/esp32/partitions-16MiB-ota.csv
+++ /dev/null
@@ -1,10 +0,0 @@
-# Partition table for MicroPython with OTA support using 16MB flash
-# Notes: the offset of the partition table itself is set in
-# $IDF_PATH/components/partition_table/Kconfig.projbuild.
-# Name, Type, SubType, Offset, Size, Flags
-nvs, data, nvs, 0x9000, 0x4000,
-otadata, data, ota, 0xd000, 0x2000,
-phy_init, data, phy, 0xf000, 0x1000,
-ota_0, app, ota_0, 0x10000, 0x270000,
-ota_1, app, ota_1, 0x280000, 0x270000,
-vfs, data, fat, 0x4f0000, 0xb10000,
diff --git a/ports/esp32/partitions-16MiB.csv b/ports/esp32/partitions-16MiB.csv
deleted file mode 100644
index ae926c7b94..0000000000
--- a/ports/esp32/partitions-16MiB.csv
+++ /dev/null
@@ -1,7 +0,0 @@
-# Notes: the offset of the partition table itself is set in
-# $IDF_PATH/components/partition_table/Kconfig.projbuild.
-# Name, Type, SubType, Offset, Size, Flags
-nvs, data, nvs, 0x9000, 0x6000,
-phy_init, data, phy, 0xf000, 0x1000,
-factory, app, factory, 0x10000, 0x1F0000,
-vfs, data, fat, 0x200000, 0xE00000,
diff --git a/ports/esp32/partitions-2MiB.csv b/ports/esp32/partitions-2MiB.csv
index ea6626825c..5449201a7a 100644
--- a/ports/esp32/partitions-2MiB.csv
+++ b/ports/esp32/partitions-2MiB.csv
@@ -4,4 +4,3 @@
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x160000,
-vfs, data, fat, 0x170000, 0x50000,
diff --git a/ports/esp32/partitions-32MiB.csv b/ports/esp32/partitions-32MiB.csv
deleted file mode 100644
index 31591c9949..0000000000
--- a/ports/esp32/partitions-32MiB.csv
+++ /dev/null
@@ -1,7 +0,0 @@
-# Notes: the offset of the partition table itself is set in
-# $IDF_PATH/components/partition_table/Kconfig.projbuild.
-# Name, Type, SubType, Offset, Size, Flags
-nvs, data, nvs, 0x9000, 0x6000,
-phy_init, data, phy, 0xf000, 0x1000,
-factory, app, factory, 0x10000, 0x1F0000,
-vfs, data, fat, 0x200000, 0x1E00000,
diff --git a/ports/esp32/partitions-4MiB-ota.csv b/ports/esp32/partitions-4MiB-ota.csv
index 094ad76660..9cbb422799 100644
--- a/ports/esp32/partitions-4MiB-ota.csv
+++ b/ports/esp32/partitions-4MiB-ota.csv
@@ -7,4 +7,3 @@ otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
ota_0, app, ota_0, 0x10000, 0x180000,
ota_1, app, ota_1, 0x190000, 0x180000,
-vfs, data, fat, 0x310000, 0x0f0000,
diff --git a/ports/esp32/partitions-4MiB-romfs.csv b/ports/esp32/partitions-4MiB-romfs.csv
index dd02506e54..29033ff3ab 100644
--- a/ports/esp32/partitions-4MiB-romfs.csv
+++ b/ports/esp32/partitions-4MiB-romfs.csv
@@ -5,4 +5,3 @@ nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x1D0000,
romfs, data, 0x8f, 0x1E0000, 0x20000,
-vfs, data, fat, 0x200000, 0x200000,
diff --git a/ports/esp32/partitions-4MiB.csv b/ports/esp32/partitions-4MiBplus.csv
index 53f0f16744..460e5cc0e9 100644
--- a/ports/esp32/partitions-4MiB.csv
+++ b/ports/esp32/partitions-4MiBplus.csv
@@ -1,7 +1,10 @@
+# This partition table is for devices with 4MiB or more of flash.
+# The first 2MiB is used for bootloader, nvs, phy_init and firmware.
+# The remaining flash is for the user filesystem(s).
+
# Notes: the offset of the partition table itself is set in
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x1F0000,
-vfs, data, fat, 0x200000, 0x200000,
diff --git a/ports/esp32/partitions-8MiB.csv b/ports/esp32/partitions-8MiB.csv
deleted file mode 100644
index 582d3b50e5..0000000000
--- a/ports/esp32/partitions-8MiB.csv
+++ /dev/null
@@ -1,7 +0,0 @@
-# Notes: the offset of the partition table itself is set in
-# $IDF_PATH/components/partition_table/Kconfig.projbuild.
-# Name, Type, SubType, Offset, Size, Flags
-nvs, data, nvs, 0x9000, 0x6000,
-phy_init, data, phy, 0xf000, 0x1000,
-factory, app, factory, 0x10000, 0x1F0000,
-vfs, data, fat, 0x200000, 0x600000,
diff --git a/ports/esp32/partitions-32MiB-ota.csv b/ports/esp32/partitions-8MiBplus-ota.csv
index 7366a2ad8d..09a8e6d702 100644
--- a/ports/esp32/partitions-32MiB-ota.csv
+++ b/ports/esp32/partitions-8MiBplus-ota.csv
@@ -1,4 +1,7 @@
-# Partition table for MicroPython with OTA support using 32MB flash
+# This partition table is for devices with 8MiB or more of flash and OTA support.
+# The first 5056kiB is used for bootloader, nvs, phy_init and firmware.
+# The remaining flash is for the user filesystem(s).
+
# Notes: the offset of the partition table itself is set in
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
@@ -7,4 +10,3 @@ otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
ota_0, app, ota_0, 0x10000, 0x270000,
ota_1, app, ota_1, 0x280000, 0x270000,
-vfs, data, fat, 0x4f0000, 0x1B10000,
diff --git a/ports/esp32/tools/metrics_esp32.py b/ports/esp32/tools/metrics_esp32.py
index 5e65a78c97..9efaae63a9 100755
--- a/ports/esp32/tools/metrics_esp32.py
+++ b/ports/esp32/tools/metrics_esp32.py
@@ -37,7 +37,7 @@ import sys
import subprocess
from dataclasses import dataclass
-IDF_VERS = ("v5.2.2",)
+IDF_VERS = ("v5.4.1",)
BUILDS = (
("ESP32_GENERIC", ""),
diff --git a/ports/esp8266/esp_init_data.c b/ports/esp8266/esp_init_data.c
index c369ed58f5..a3dd6ffed1 100644
--- a/ports/esp8266/esp_init_data.c
+++ b/ports/esp8266/esp_init_data.c
@@ -30,7 +30,7 @@
#include "user_interface.h"
#include "extmod/misc.h"
-NORETURN void call_user_start(void);
+MP_NORETURN void call_user_start(void);
void ets_printf(const char *fmt, ...);
extern char flashchip;
diff --git a/ports/esp8266/esp_mphal.h b/ports/esp8266/esp_mphal.h
index 0a4f92ac0b..fb2e441c97 100644
--- a/ports/esp8266/esp_mphal.h
+++ b/ports/esp8266/esp_mphal.h
@@ -27,11 +27,20 @@
#include "user_interface.h"
#include "py/ringbuf.h"
#include "shared/runtime/interrupt_char.h"
+#include "ets_alt_task.h"
#include "xtirq.h"
#define MICROPY_BEGIN_ATOMIC_SECTION() esp_disable_irq()
#define MICROPY_END_ATOMIC_SECTION(state) esp_enable_irq(state)
+// During machine.time_pulse_us, feed WDT every now and then.
+#define MICROPY_PY_MACHINE_TIME_PULSE_US_HOOK(dt) \
+ do { \
+ if ((dt & 0xffff) == 0xffff && !ets_loop_dont_feed_sw_wdt) { \
+ system_soft_wdt_feed(); \
+ } \
+ } while (0)
+
void mp_sched_keyboard_interrupt(void);
struct _mp_print_t;
diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c
index 2dd7c1dece..da712fce9b 100644
--- a/ports/esp8266/main.c
+++ b/ports/esp8266/main.c
@@ -49,6 +49,64 @@
static char heap[38 * 1024];
+#if MICROPY_HW_HARD_FAULT_DEBUG
+
+static void format_hex(uint32_t hex, char *buffer) {
+ static const char table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ int offset = 7;
+ uint32_t value = hex;
+ while (offset >= 0) {
+ buffer[offset--] = table[value & 0x0F];
+ value >>= 4;
+ }
+}
+
+static void print_reset_info(void) {
+ struct rst_info *rst_info = system_get_rst_info();
+ if ((rst_info->reason == REASON_WDT_RST) || (rst_info->reason == REASON_EXCEPTION_RST) || (rst_info->reason == REASON_SOFT_WDT_RST)) {
+ char buffer[8];
+ mp_hal_stdout_tx_str("\r\n\r\nThe system restarted due to an error.\r\n\r\nReason: ");
+ switch (rst_info->reason) {
+ case REASON_WDT_RST:
+ mp_hal_stdout_tx_str("WDT");
+ break;
+
+ case REASON_EXCEPTION_RST:
+ mp_hal_stdout_tx_str("EXCEPTION");
+ break;
+
+ case REASON_SOFT_WDT_RST:
+ mp_hal_stdout_tx_str("SOFT_WDT");
+ break;
+
+ default:
+ assert(!"Should not ever get here.");
+ break;
+ }
+ mp_hal_stdout_tx_str(" Cause: ");
+ format_hex(rst_info->exccause, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str(" EPC1: ");
+ format_hex(rst_info->epc1, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str(" EPC2: ");
+ format_hex(rst_info->epc2, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str(" EPC3: ");
+ format_hex(rst_info->epc3, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str(" Exception Vector address: ");
+ format_hex(rst_info->excvaddr, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str(" DEPC: ");
+ format_hex(rst_info->depc, buffer);
+ mp_hal_stdout_tx_strn(buffer, sizeof(buffer));
+ mp_hal_stdout_tx_str("\r\n\r\n");
+ }
+}
+
+#endif
+
static void mp_reset(void) {
mp_stack_set_top((void *)0x40000000);
mp_stack_set_limit(8192);
@@ -114,6 +172,10 @@ void init_done(void) {
pyexec_event_repl_init();
#endif
+ #if MICROPY_HW_HARD_FAULT_DEBUG
+ print_reset_info();
+ #endif
+
#if !MICROPY_REPL_EVENT_DRIVEN
soft_reset:
for (;;) {
diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c
index 23ccf8cebf..d43fe38245 100644
--- a/ports/esp8266/modmachine.c
+++ b/ports/esp8266/modmachine.c
@@ -33,7 +33,6 @@
#include "os_type.h"
#include "osapi.h"
#include "etshal.h"
-#include "ets_alt_task.h"
#include "user_interface.h"
// #define MACHINE_WAKE_IDLE (0x01)
@@ -71,7 +70,7 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
system_update_cpu_freq(freq);
}
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
system_restart();
// we must not return
@@ -114,7 +113,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
}
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
// default to sleep forever
uint32_t sleep_us = 0;
@@ -327,32 +326,3 @@ MP_DEFINE_CONST_OBJ_TYPE(
print, esp_timer_print,
locals_dict, &esp_timer_locals_dict
);
-
-// Custom version of this function that feeds system WDT if necessary
-mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) {
- int nchanges = 2;
- uint32_t start = system_get_time(); // in microseconds
- for (;;) {
- uint32_t dt = system_get_time() - start;
-
- // Check if pin changed to wanted value
- if (mp_hal_pin_read(pin) == pulse_level) {
- if (--nchanges == 0) {
- return dt;
- }
- pulse_level = 1 - pulse_level;
- start = system_get_time();
- continue;
- }
-
- // Check for timeout
- if (dt >= timeout_us) {
- return (mp_uint_t)-nchanges;
- }
-
- // Only feed WDT every now and then, to make sure edge timing is accurate
- if ((dt & 0xffff) == 0xffff && !ets_loop_dont_feed_sw_wdt) {
- system_soft_wdt_feed();
- }
- }
-}
diff --git a/ports/esp8266/modtime.c b/ports/esp8266/modtime.c
index 0903005597..e99d920fde 100644
--- a/ports/esp8266/modtime.c
+++ b/ports/esp8266/modtime.c
@@ -31,7 +31,7 @@
// Return the localtime as an 8-tuple.
static mp_obj_t mp_time_localtime_get(void) {
- mp_int_t seconds = pyb_rtc_get_us_since_epoch() / 1000 / 1000;
+ mp_uint_t seconds = pyb_rtc_get_us_since_epoch() / 1000u / 1000u;
timeutils_struct_time_t tm;
timeutils_seconds_since_epoch_to_struct_time(seconds, &tm);
mp_obj_t tuple[8] = {
@@ -50,5 +50,5 @@ static mp_obj_t mp_time_localtime_get(void) {
// Returns the number of seconds, as an integer, since the Epoch.
static mp_obj_t mp_time_time_get(void) {
// get date and time
- return mp_obj_new_int(pyb_rtc_get_us_since_epoch() / 1000 / 1000);
+ return timeutils_obj_from_timestamp(pyb_rtc_get_us_since_epoch() / 1000 / 1000);
}
diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h
index 83d80a7c96..03f3bb643d 100644
--- a/ports/esp8266/mpconfigport.h
+++ b/ports/esp8266/mpconfigport.h
@@ -117,6 +117,9 @@
#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
#define MICROPY_ESP8266_APA102 (1)
+// Print error information at reboot time if the board crashed.
+#define MICROPY_HW_HARD_FAULT_DEBUG (0)
+
// No blocking wait-for-event on ESP8266, only non-blocking pump of the "OS" event
// loop
//
diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json
index 9f260b3a00..804b4fd734 100644
--- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json
+++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json
@@ -15,7 +15,7 @@
"Metro_M7.jpg"
],
"mcu": "mimxrt",
- "product": "Adafruit Metro M7",
+ "product": "Metro M7",
"thumbnail": "",
"url": "https://www.adafruit.com/product/4950",
"vendor": "Adafruit"
diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/board.json b/ports/mimxrt/boards/OLIMEX_RT1010/board.json
index 882b931f8b..a6f0316bc8 100644
--- a/ports/mimxrt/boards/OLIMEX_RT1010/board.json
+++ b/ports/mimxrt/boards/OLIMEX_RT1010/board.json
@@ -12,8 +12,8 @@
"OLIMEX_RT1010Py.jpg"
],
"mcu": "mimxrt",
- "product": "Olimex_RT1010Py",
+ "product": "RT1010-Py",
"thumbnail": "",
- "url": "https://www.olimex.com/Products/ARM/NXP",
- "vendor": "OLIMEX"
+ "url": "https://www.olimex.com/Products/MicroPython/RT1010-Py",
+ "vendor": "Olimex"
}
diff --git a/ports/mimxrt/cyw43_configport.h b/ports/mimxrt/cyw43_configport.h
index cf2f5d4b9f..b1dd6fbe6a 100644
--- a/ports/mimxrt/cyw43_configport.h
+++ b/ports/mimxrt/cyw43_configport.h
@@ -28,12 +28,8 @@
#define MICROPY_INCLUDED_MIMXRT_CYW43_CONFIGPORT_H
// The board-level config will be included here, so it can set some CYW43 values.
-#include "py/mpconfig.h"
-#include "py/mperrno.h"
-#include "py/mphal.h"
-#include "extmod/modnetwork.h"
#include "extmod/mpbthci.h"
-#include "pendsv.h"
+#include "extmod/cyw43_config_common.h"
#include "sdio.h"
#define CYW43_USE_SPI (0)
@@ -61,55 +57,15 @@
#define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE
#endif
-#define CYW43_IOCTL_TIMEOUT_US (1000000)
-#define CYW43_SLEEP_MAX (50)
-#define CYW43_NETUTILS (1)
#define CYW43_CLEAR_SDIO_INT (1)
-#define CYW43_EPERM MP_EPERM // Operation not permitted
-#define CYW43_EIO MP_EIO // I/O error
-#define CYW43_EINVAL MP_EINVAL // Invalid argument
-#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out
-
-#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER
-#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT
-#define CYW43_THREAD_LOCK_CHECK
-
-#define CYW43_HOST_NAME mod_network_hostname_data
-
#define CYW43_SDPCM_SEND_COMMON_WAIT __WFI();
#define CYW43_DO_IOCTL_WAIT __WFI();
-#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_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_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate
#define cyw43_hal_uart_write mp_bluetooth_hci_uart_write
#define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar
-#define cyw43_delay_us mp_hal_delay_us
-#define cyw43_delay_ms mp_hal_delay_ms
-
#define cyw43_bluetooth_controller_init mp_bluetooth_hci_controller_init
#define cyw43_bluetooth_controller_deinit mp_bluetooth_hci_controller_deinit
#define cyw43_bluetooth_controller_woken mp_bluetooth_hci_controller_woken
@@ -135,7 +91,7 @@
#define CYW43_PIN_RFSW_VDD pin_WL_RFSW_VDD
#endif
-#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func)
+#undef cyw43_hal_pin_config // mp_hal_pin_config not yet implemented on this port
static inline void cyw43_hal_pin_config(cyw43_hal_pin_obj_t pin, uint8_t mode, uint8_t pull, uint8_t alt) {
machine_pin_set_mode(pin, mode);
@@ -175,6 +131,4 @@ static inline int cyw43_sdio_transfer_cmd53(bool write, uint32_t block_size, uin
return sdio_transfer_cmd53(write, block_size, arg, len, buf);
}
-#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK
-
#endif // MICROPY_INCLUDED_MIMXRT_CYW43_CONFIGPORT_H
diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c
index 204699bcc4..ad078ff3ac 100644
--- a/ports/mimxrt/modmachine.c
+++ b/ports/mimxrt/modmachine.c
@@ -87,7 +87,7 @@ static mp_obj_t mp_machine_unique_id(void) {
return mp_obj_new_bytes(id, sizeof(id));
}
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
WDOG_TriggerSystemSoftwareReset(WDOG1);
while (true) {
;
@@ -131,7 +131,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
mp_raise_NotImplementedError(NULL);
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
if (n_args != 0) {
mp_int_t seconds = mp_obj_get_int(args[0]) / 1000;
if (seconds > 0) {
@@ -159,7 +159,7 @@ NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
}
}
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
#if defined(MICROPY_BOARD_ENTER_BOOTLOADER)
// If a board has a custom bootloader, call it first.
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
diff --git a/ports/mimxrt/modtime.c b/ports/mimxrt/modtime.c
index a2ccf1b273..3bcfac411c 100644
--- a/ports/mimxrt/modtime.c
+++ b/ports/mimxrt/modtime.c
@@ -51,14 +51,7 @@ static mp_obj_t mp_time_localtime_get(void) {
static mp_obj_t mp_time_time_get(void) {
snvs_lp_srtc_datetime_t t;
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
- // EPOCH is 1970 for this port, which leads to the following trouble:
- // timeutils_seconds_since_epoch() calls timeutils_seconds_since_2000(), and
- // timeutils_seconds_since_2000() subtracts 2000 from year, but uses
- // an unsigned number for seconds, That causes an underrun, which is not
- // fixed by adding the TIMEUTILS_SECONDS_1970_TO_2000.
- // Masking it to 32 bit for year < 2000 fixes it.
- return mp_obj_new_int_from_ull(
+ return timeutils_obj_from_timestamp(
timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second)
- & (t.year < 2000 ? 0xffffffff : 0xffffffffffff)
);
}
diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h
index 9a7dfc630f..e1c605f452 100644
--- a/ports/mimxrt/mpconfigport.h
+++ b/ports/mimxrt/mpconfigport.h
@@ -178,20 +178,12 @@ extern const struct _mp_obj_type_t network_lan_type;
#define MICROPY_HW_NIC_ETH
#endif
-#if MICROPY_PY_NETWORK_CYW43
-extern const struct _mp_obj_type_t mp_network_cyw43_type;
-#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) },
-#else
-#define MICROPY_HW_NIC_CYW43
-#endif
-
#ifndef MICROPY_BOARD_NETWORK_INTERFACES
#define MICROPY_BOARD_NETWORK_INTERFACES
#endif
#define MICROPY_PORT_NETWORK_INTERFACES \
MICROPY_HW_NIC_ETH \
- MICROPY_HW_NIC_CYW43 \
MICROPY_BOARD_NETWORK_INTERFACES \
#ifndef MICROPY_BOARD_ROOT_POINTERS
diff --git a/ports/minimal/main.c b/ports/minimal/main.c
index 5f472c1afd..b9e9034bf5 100644
--- a/ports/minimal/main.c
+++ b/ports/minimal/main.c
@@ -87,7 +87,7 @@ void nlr_jump_fail(void *val) {
}
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
while (1) {
;
}
diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile
index 59e74dce42..7b16974f97 100644
--- a/ports/nrf/Makefile
+++ b/ports/nrf/Makefile
@@ -129,14 +129,7 @@ CFLAGS_MCU_m4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -mfpu=fpv4-s
CFLAGS_MCU_m0 = $(CFLAGS_CORTEX_M) -fshort-enums -mtune=cortex-m0 -mcpu=cortex-m0 -mfloat-abi=soft
-# linker wrap does not work with lto on older gcc/binutils: https://sourceware.org/bugzilla/show_bug.cgi?id=24406
-GCC_VERSION = $(shell arm-none-eabi-gcc -dumpversion)
-GCC_MAJOR_VERS = $(word 1,$(subst ., ,$(GCC_VERSION)))
-ifeq ($(shell test $(GCC_MAJOR_VERS) -ge 10; echo $$?),0)
LTO ?= 1
-else
-LTO ?= 0
-endif
ifeq ($(LTO),1)
CFLAGS += -flto
@@ -268,7 +261,6 @@ SRC_C += $(addprefix lib/tinyusb/src/,\
portable/nordic/nrf5x/dcd_nrf5x.c \
)
-LDFLAGS += -Wl,--wrap=dcd_event_handler
endif
DRIVERS_SRC_C += $(addprefix modules/,\
diff --git a/ports/nrf/boards/ACTINIUS_ICARUS/board.json b/ports/nrf/boards/ACTINIUS_ICARUS/board.json
index c1d9f1c4ef..09ed3fcba3 100644
--- a/ports/nrf/boards/ACTINIUS_ICARUS/board.json
+++ b/ports/nrf/boards/ACTINIUS_ICARUS/board.json
@@ -8,7 +8,7 @@
"icarus-v1.4-front-shadow-p-800.jpg"
],
"mcu": "nrf91",
- "product": "actinius_icarus",
+ "product": "Icarus",
"thumbnail": "",
"url": "https://www.actinius.com/icarus",
"vendor": "Actinius"
diff --git a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json
index 9079fbd666..72b5135611 100644
--- a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json
+++ b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json
@@ -15,7 +15,7 @@
"ABX00031_01.iso_998x749.jpg"
],
"mcu": "nrf52",
- "product": "Arduino Nano 33 BLE Sense",
+ "product": "Nano 33 BLE Sense",
"thumbnail": "",
"url": "https://store.arduino.cc/products/arduino-nano-33-ble-sense",
"vendor": "Arduino"
diff --git a/ports/nrf/boards/ARDUINO_PRIMO/board.json b/ports/nrf/boards/ARDUINO_PRIMO/board.json
index f7afed0ced..236acfd448 100644
--- a/ports/nrf/boards/ARDUINO_PRIMO/board.json
+++ b/ports/nrf/boards/ARDUINO_PRIMO/board.json
@@ -8,8 +8,8 @@
"arduino_primo.jpg"
],
"mcu": "nrf52",
- "product": "arduino_primo",
+ "product": "Primo",
"thumbnail": "",
- "url": "",
+ "url": "https://docs.arduino.cc/retired/boards/arduino-primo/",
"vendor": "Arduino"
}
diff --git a/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json b/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json
index 5b6e5747d5..e910ea8db2 100644
--- a/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json
+++ b/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json
@@ -8,7 +8,7 @@
"blyst-nano-mod-4_jpg_project-body.jpg"
],
"mcu": "nrf52",
- "product": "blueio_tag_evim",
+ "product": "BLUEIO Tag EVIM",
"thumbnail": "",
"url": "https://www.i-syst.com/index.php/products/blyst-nano",
"vendor": "I-SYST"
diff --git a/ports/nrf/boards/DVK_BL652/board.json b/ports/nrf/boards/DVK_BL652/board.json
index 5c9cfad26e..d0942ca256 100644
--- a/ports/nrf/boards/DVK_BL652/board.json
+++ b/ports/nrf/boards/DVK_BL652/board.json
@@ -8,8 +8,8 @@
"BL652-SA_JPG-500.jpg"
],
"mcu": "nrf52",
- "product": "dvk_bl652",
+ "product": "DVK-BL652",
"thumbnail": "",
- "url": "https://www.lairdconnect.com/wireless-modules/bluetooth-modules/bluetooth-5-modules/bl652-series-bluetooth-v5-nfc-module",
- "vendor": "Laird Connectivity"
+ "url": "https://www.ezurio.com/wireless-modules/bluetooth-modules/bluetooth-5-modules/bl652-series-bluetooth-v5-nfc-module",
+ "vendor": "Ezurio"
}
diff --git a/ports/nrf/boards/EVK_NINA_B1/board.json b/ports/nrf/boards/EVK_NINA_B1/board.json
index 657b0fa06d..ef9c6ae5be 100644
--- a/ports/nrf/boards/EVK_NINA_B1/board.json
+++ b/ports/nrf/boards/EVK_NINA_B1/board.json
@@ -8,7 +8,7 @@
"EVK-NINA-B1_.jpg"
],
"mcu": "nrf52",
- "product": "evk_nina_b1",
+ "product": "EVK-NINA-B1",
"thumbnail": "",
"url": "https://www.u-blox.com/en/product/evk-nina-b1",
"vendor": "u-blox"
diff --git a/ports/nrf/boards/EVK_NINA_B3/board.json b/ports/nrf/boards/EVK_NINA_B3/board.json
index 54e3dc3585..6311b17338 100644
--- a/ports/nrf/boards/EVK_NINA_B3/board.json
+++ b/ports/nrf/boards/EVK_NINA_B3/board.json
@@ -8,7 +8,7 @@
"EVK-NINA-B3-top.jpg"
],
"mcu": "nrf52",
- "product": "evk_nina_b3",
+ "product": "EVK-NINA-B3",
"thumbnail": "",
"url": "https://www.u-blox.com/en/product/evk-nina-b3",
"vendor": "u-blox"
diff --git a/ports/nrf/boards/IBK_BLYST_NANO/board.json b/ports/nrf/boards/IBK_BLYST_NANO/board.json
index 562c33607b..ddbbef66f5 100644
--- a/ports/nrf/boards/IBK_BLYST_NANO/board.json
+++ b/ports/nrf/boards/IBK_BLYST_NANO/board.json
@@ -8,7 +8,7 @@
"blyst-nano-fingertip-close_jpg_content-body-gallery.jpg"
],
"mcu": "nrf52",
- "product": "ibk_blyst_nano",
+ "product": "IBK BLYST Nano",
"thumbnail": "",
"url": "https://www.i-syst.com/products/blyst-nano",
"vendor": "I-SYST"
diff --git a/ports/nrf/boards/IDK_BLYST_NANO/board.json b/ports/nrf/boards/IDK_BLYST_NANO/board.json
index 199721698d..cb692f44cd 100644
--- a/ports/nrf/boards/IDK_BLYST_NANO/board.json
+++ b/ports/nrf/boards/IDK_BLYST_NANO/board.json
@@ -8,7 +8,7 @@
"blyst-nano-fingertip-close_jpg_content-body-gallery.jpg"
],
"mcu": "nrf52",
- "product": "idk_blyst_nano",
+ "product": "IDK BLYST Nano",
"thumbnail": "",
"url": "https://www.i-syst.com/products/blyst-nano",
"vendor": "I-SYST"
diff --git a/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json b/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json
index 99d0caad23..102102b6c2 100644
--- a/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json
+++ b/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json
@@ -8,7 +8,7 @@
"dongle_pcba_case.jpg"
],
"mcu": "nrf52",
- "product": "nrf52840-mdk-usb-dongle",
+ "product": "nrf52840 MDK USB Dongle",
"thumbnail": "",
"url": "https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle",
"vendor": "Makerdiary"
diff --git a/ports/nrf/boards/PCA10028/mpconfigboard.h b/ports/nrf/boards/PCA10028/mpconfigboard.h
index 3dc8ed3459..df2e4e85d9 100644
--- a/ports/nrf/boards/PCA10028/mpconfigboard.h
+++ b/ports/nrf/boards/PCA10028/mpconfigboard.h
@@ -60,3 +60,8 @@
#define MICROPY_HW_SPI0_MISO (28)
#define HELP_TEXT_BOARD_LED "1,2,3,4"
+
+// The JLink CDC on the PCA10028 cannot accept more than 64 incoming bytes at a time.
+// That makes the UART REPL unreliable in general. But it can be improved to some
+// extent by setting the raw-paste buffer size to that limit of 64.
+#define MICROPY_REPL_STDIN_BUFFER_MAX (64)
diff --git a/ports/nrf/boards/PCA10040/mpconfigboard.h b/ports/nrf/boards/PCA10040/mpconfigboard.h
index b965bf319d..2b1c4c7ef5 100644
--- a/ports/nrf/boards/PCA10040/mpconfigboard.h
+++ b/ports/nrf/boards/PCA10040/mpconfigboard.h
@@ -64,3 +64,8 @@
#define MICROPY_HW_PWM2_NAME "PWM2"
#define HELP_TEXT_BOARD_LED "1,2,3,4"
+
+// The JLink CDC on the PCA10040 cannot accept more than 64 incoming bytes at a time.
+// That makes the UART REPL unreliable in general. But it can be improved to some
+// extent by setting the raw-paste buffer size to that limit of 64.
+#define MICROPY_REPL_STDIN_BUFFER_MAX (64)
diff --git a/ports/nrf/boards/SEEED_XIAO_NRF52/board.json b/ports/nrf/boards/SEEED_XIAO_NRF52/board.json
index d40355bb2e..b00044a56a 100644
--- a/ports/nrf/boards/SEEED_XIAO_NRF52/board.json
+++ b/ports/nrf/boards/SEEED_XIAO_NRF52/board.json
@@ -16,8 +16,8 @@
"XIAO_nrf52840_front.jpg"
],
"mcu": "nrf52",
- "product": "SEEED XIAO nRF52840 Sense",
+ "product": "XIAO nRF52840 Sense",
"thumbnail": "",
- "url": "https://www.seeedstudio.com",
+ "url": "https://www.seeedstudio.com/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html",
"vendor": "Seeed Studio"
}
diff --git a/ports/nrf/boards/WT51822_S4AT/board.json b/ports/nrf/boards/WT51822_S4AT/board.json
index 89d008c6f5..2ec42ac414 100644
--- a/ports/nrf/boards/WT51822_S4AT/board.json
+++ b/ports/nrf/boards/WT51822_S4AT/board.json
@@ -8,8 +8,8 @@
"WT51822-S4AT.jpg"
],
"mcu": "nrf51",
- "product": "wt51822_s4at",
+ "product": "WT51822-S4AT",
"thumbnail": "",
- "url": "http://www.wireless-tag.com/portfolio/wt51822-s4at-2/",
+ "url": "https://shop.wireless-tag.com/products/esp32-c3-mini-1-10pcs-espressif-esp32-c3-mini-1-4mb-flash-pcb-antenna-15-gpios-wifi-ble-5-module-esp32-c3-module-on-esp32-c3-chip",
"vendor": "Wireless-Tag"
}
diff --git a/ports/nrf/main.c b/ports/nrf/main.c
index 29550bd77a..21a71c7c9e 100644
--- a/ports/nrf/main.c
+++ b/ports/nrf/main.c
@@ -39,6 +39,8 @@
#include "py/stackctrl.h"
#include "py/gc.h"
#include "py/compile.h"
+#include "py/persistentcode.h"
+#include "extmod/misc.h"
#include "extmod/modmachine.h"
#include "shared/runtime/pyexec.h"
#include "readline.h"
@@ -107,7 +109,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) {
extern uint32_t _heap_start;
extern uint32_t _heap_end;
-void NORETURN _start(void) {
+void MP_NORETURN _start(void) {
// Hook for a board to run code at start up, for example check if a
// bootloader should be entered instead of the main application.
MICROPY_BOARD_STARTUP();
@@ -171,7 +173,8 @@ soft_reset:
MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL),
MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL_BAUD),
};
- MP_STATE_VM(dupterm_objs[0]) = MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, make_new)((mp_obj_t)&machine_uart_type, MP_ARRAY_SIZE(args), 0, args);
+ mp_obj_t uart = MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, make_new)((mp_obj_t)&machine_uart_type, MP_ARRAY_SIZE(args), 0, args);
+ mp_os_dupterm_obj.fun.var(1, &uart);
}
#endif
@@ -353,7 +356,7 @@ void HardFault_Handler(void) {
#endif
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
while (1) {
;
}
@@ -369,3 +372,16 @@ void MP_WEAK __assert_func(const char *file, int line, const char *func, const c
printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line);
__fatal_error("Assertion failed");
}
+
+#if MICROPY_EMIT_MACHINE_CODE
+void *nrf_native_code_commit(void *buf, unsigned int len, void *reloc) {
+ (void)len;
+ if (reloc) {
+ // Native code in RAM must execute from the IRAM region at 0x00800000, and so relocations
+ // to text must also point to this region. The MICROPY_MAKE_POINTER_CALLABLE macro will
+ // adjust the `buf` address from RAM to IRAM.
+ mp_native_relocate(reloc, buf, (uintptr_t)MICROPY_MAKE_POINTER_CALLABLE(buf) & ~1);
+ }
+ return buf;
+}
+#endif
diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c
index de1d0e3124..f543265479 100644
--- a/ports/nrf/modules/machine/modmachine.c
+++ b/ports/nrf/modules/machine/modmachine.c
@@ -85,8 +85,6 @@
#define MICROPY_PY_MACHINE_EXTRA_GLOBALS \
{ MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&machine_info_obj) }, \
- { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, \
- { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, \
{ MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&machine_lightsleep_obj) }, \
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) }, \
\
@@ -181,11 +179,11 @@ static mp_obj_t mp_machine_unique_id(void) {
}
// Resets the board in a manner similar to pushing the external RESET button.
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
NVIC_SystemReset();
}
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
for (;;) {
}
@@ -199,7 +197,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
__WFE();
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
mp_machine_reset();
}
@@ -214,24 +212,3 @@ static mp_obj_t mp_machine_get_freq(void) {
static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
mp_raise_NotImplementedError(NULL);
}
-
-static mp_obj_t machine_enable_irq(void) {
- #ifndef BLUETOOTH_SD
- __enable_irq();
- #else
-
- #endif
- return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_0(machine_enable_irq_obj, machine_enable_irq);
-
-// Resets the board in a manner similar to pushing the external RESET button.
-static mp_obj_t machine_disable_irq(void) {
- #ifndef BLUETOOTH_SD
- __disable_irq();
- #else
-
- #endif
- return mp_const_none;
-}
-MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq);
diff --git a/ports/nrf/modules/machine/uart.c b/ports/nrf/modules/machine/uart.c
index 8d5a73e095..4c75bf8209 100644
--- a/ports/nrf/modules/machine/uart.c
+++ b/ports/nrf/modules/machine/uart.c
@@ -46,6 +46,11 @@
#include "nrfx_uarte.h"
#endif
+#if defined(NRF52832)
+// The nRF52832 cannot write more than 255 bytes at a time.
+#define UART_MAX_TX_CHUNK (255)
+#endif
+
typedef struct _machine_uart_buf_t {
uint8_t tx_buf[1];
uint8_t rx_buf[1];
@@ -104,6 +109,7 @@ typedef struct _machine_uart_obj_t {
uint16_t timeout_char; // timeout waiting between chars (in ms)
uint8_t uart_id;
bool initialized; // static flag. Initialized to False
+ bool attached_to_repl;
#if MICROPY_PY_MACHINE_UART_IRQ
uint16_t mp_irq_trigger; // user IRQ trigger mask
uint16_t mp_irq_flags; // user IRQ active IRQ flags
@@ -118,6 +124,13 @@ static machine_uart_obj_t machine_uart_obj[] = {
};
void uart_init0(void) {
+ for (int i = 0; i < MP_ARRAY_SIZE(machine_uart_obj); i++) {
+ machine_uart_obj[i].attached_to_repl = false;
+ }
+}
+
+void uart_attach_to_repl(machine_uart_obj_t *self, bool attached) {
+ self->attached_to_repl = attached;
}
static int uart_find(mp_obj_t id) {
@@ -137,14 +150,16 @@ static void uart_event_handler(nrfx_uart_event_t const *p_event, void *p_context
if (p_event->type == NRFX_UART_EVT_RX_DONE) {
nrfx_uart_rx(self->p_uart, &self->buf.rx_buf[0], 1);
int chr = self->buf.rx_buf[0];
- #if !MICROPY_PY_BLE_NUS && MICROPY_KBD_EXCEPTION
- if (chr == mp_interrupt_char) {
- self->buf.rx_ringbuf.iget = 0;
- self->buf.rx_ringbuf.iput = 0;
- mp_sched_keyboard_interrupt();
- } else
- #endif
- {
+ if (self->attached_to_repl) {
+ #if MICROPY_KBD_EXCEPTION
+ if (chr == mp_interrupt_char) {
+ mp_sched_keyboard_interrupt();
+ } else
+ #endif
+ {
+ ringbuf_put((ringbuf_t *)&stdin_ringbuf, chr);
+ }
+ } else {
ringbuf_put((ringbuf_t *)&self->buf.rx_ringbuf, chr);
}
#if MICROPY_PY_MACHINE_UART_IRQ
@@ -446,17 +461,29 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf, mp_uin
#endif
machine_uart_obj_t *self = self_in;
- nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf, size);
- if (err == NRFX_SUCCESS) {
+
+ // Send data out, in chunks if needed.
+ mp_uint_t remaining = size;
+ while (remaining) {
+ #ifdef UART_MAX_TX_CHUNK
+ mp_uint_t chunk = MIN(UART_MAX_TX_CHUNK, remaining);
+ #else
+ mp_uint_t chunk = remaining;
+ #endif
+ nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf, chunk);
+ if (err != NRFX_SUCCESS) {
+ *errcode = mp_hal_status_to_errno_table[err];
+ return MP_STREAM_ERROR;
+ }
while (nrfx_uart_tx_in_progress(self->p_uart)) {
MICROPY_EVENT_POLL_HOOK;
}
- // return number of bytes written
- return size;
- } else {
- *errcode = mp_hal_status_to_errno_table[err];
- return MP_STREAM_ERROR;
+ buf += chunk;
+ remaining -= chunk;
}
+
+ // return number of bytes written
+ return size;
}
static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
diff --git a/ports/nrf/modules/machine/uart.h b/ports/nrf/modules/machine/uart.h
index 741473ab7a..85c2092458 100644
--- a/ports/nrf/modules/machine/uart.h
+++ b/ports/nrf/modules/machine/uart.h
@@ -34,8 +34,7 @@
typedef struct _machine_uart_obj_t machine_uart_obj_t;
void uart_init0(void);
-void uart_deinit(void);
-void uart_irq_handler(mp_uint_t uart_id);
+void uart_attach_to_repl(machine_uart_obj_t *self, bool attached);
bool uart_rx_any(machine_uart_obj_t *uart_obj);
int uart_rx_char(machine_uart_obj_t *uart_obj);
diff --git a/ports/nrf/modules/os/modos.c b/ports/nrf/modules/os/modos.c
index f000e1eeb6..97ed1e1bad 100644
--- a/ports/nrf/modules/os/modos.c
+++ b/ports/nrf/modules/os/modos.c
@@ -30,6 +30,7 @@
#include "py/runtime.h"
#include "extmod/modmachine.h"
#include "drivers/rng.h"
+#include "modules/machine/uart.h"
#if MICROPY_PY_OS_URANDOM
// Return a bytes object with n random bytes, generated by the hardware random number generator.
@@ -46,10 +47,17 @@ static MP_DEFINE_CONST_FUN_OBJ_1(mp_os_urandom_obj, mp_os_urandom);
#endif
#if MICROPY_PY_OS_DUPTERM
-// TODO should accept any object with read/write methods.
void mp_os_dupterm_stream_detached_attached(mp_obj_t stream_detached, mp_obj_t stream_attached) {
- if (!(stream_attached == mp_const_none || mp_obj_get_type(stream_attached) == &machine_uart_type)) {
- mp_raise_ValueError(MP_ERROR_TEXT("need a UART object"));
+ #if MICROPY_PY_MACHINE_UART
+ if (mp_obj_get_type(stream_detached) == &machine_uart_type) {
+ uart_attach_to_repl(MP_OBJ_TO_PTR(stream_detached), false);
}
+ #endif
+
+ #if MICROPY_PY_MACHINE_UART
+ if (mp_obj_get_type(stream_attached) == &machine_uart_type) {
+ uart_attach_to_repl(MP_OBJ_TO_PTR(stream_attached), true);
+ }
+ #endif
}
#endif // MICROPY_PY_OS_DUPTERM
diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h
index 7cc8a66d98..d52b5745d4 100644
--- a/ports/nrf/mpconfigport.h
+++ b/ports/nrf/mpconfigport.h
@@ -181,6 +181,7 @@
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
+#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1)
#define MICROPY_PY_MACHINE_PULSE (0)
#define MICROPY_PY_MACHINE_SOFTI2C (MICROPY_PY_MACHINE_I2C)
@@ -320,7 +321,17 @@
// type definitions for the specific machine
-#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1))
+#if defined(NRF52832) || defined(NRF52840)
+// On nRF52, the physical SRAM is mapped to 0x20000000 for data access and 0x00800000
+// for instruction access. So convert addresses to make them executable.
+#define MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA (1)
+#define MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA (0)
+#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)(((uintptr_t)(p) - 0x20000000 + 0x00800000) | 1))
+void *nrf_native_code_commit(void *, unsigned int, void *);
+#define MP_PLAT_COMMIT_EXEC(buf, len, reloc) nrf_native_code_commit(buf, len, reloc)
+#else
+#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((uintptr_t)(p) | 1))
+#endif
#define MP_SSIZE_MAX (0x7fffffff)
diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c
index 9bb51deb12..3a3d3ad7a6 100644
--- a/ports/nrf/mphalport.c
+++ b/ports/nrf/mphalport.c
@@ -202,7 +202,7 @@ const byte mp_hal_status_to_errno_table[4] = {
[HAL_TIMEOUT] = MP_ETIMEDOUT,
};
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
mp_raise_OSError(mp_hal_status_to_errno_table[status]);
}
diff --git a/ports/nrf/mphalport.h b/ports/nrf/mphalport.h
index a0bca58a62..12e881d7b6 100644
--- a/ports/nrf/mphalport.h
+++ b/ports/nrf/mphalport.h
@@ -35,6 +35,22 @@
#include "nrfx_config.h"
#include "shared/runtime/interrupt_char.h"
+// Entering a critical section.
+#ifndef BLUETOOTH_SD
+#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq()
+#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state)
+#endif
+
+static inline void enable_irq(mp_uint_t state) {
+ __set_PRIMASK(state);
+}
+
+static inline mp_uint_t disable_irq(void) {
+ mp_uint_t state = __get_PRIMASK();
+ __disable_irq();
+ return state;
+}
+
typedef enum
{
HAL_OK = 0x00,
@@ -47,7 +63,7 @@ extern const unsigned char mp_hal_status_to_errno_table[4];
extern ringbuf_t stdin_ringbuf;
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
void mp_hal_set_interrupt_char(int c); // -1 to disable
int mp_hal_stdin_rx_chr(void);
diff --git a/ports/pic16bit/main.c b/ports/pic16bit/main.c
index b1fe7321f0..437f91ca7c 100644
--- a/ports/pic16bit/main.c
+++ b/ports/pic16bit/main.c
@@ -121,7 +121,7 @@ void nlr_jump_fail(void *val) {
}
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
while (1) {
;
}
diff --git a/ports/powerpc/main.c b/ports/powerpc/main.c
index 4b668c861c..bbd2082b42 100644
--- a/ports/powerpc/main.c
+++ b/ports/powerpc/main.c
@@ -130,7 +130,7 @@ void nlr_jump_fail(void *val) {
}
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
while (1) {
;
}
diff --git a/ports/qemu/mcu/arm/errorhandler.c b/ports/qemu/mcu/arm/errorhandler.c
index a0e7ef930f..a647c0e153 100644
--- a/ports/qemu/mcu/arm/errorhandler.c
+++ b/ports/qemu/mcu/arm/errorhandler.c
@@ -63,7 +63,7 @@ static const char *EXCEPTION_NAMES_TABLE[] = {
// R0-R15, PSR, Kind
uintptr_t registers_copy[18] = { 0 };
-__attribute__((naked)) NORETURN void exception_handler(uintptr_t kind) {
+__attribute__((naked)) MP_NORETURN void exception_handler(uintptr_t kind) {
// Save registers
__asm volatile (
"ldr r1, =registers_copy \n"
@@ -137,39 +137,39 @@ __attribute__((naked)) NORETURN void exception_handler(uintptr_t kind) {
for (;;) {}
}
-__attribute__((naked)) NORETURN void NMI_Handler(void) {
+__attribute__((naked)) MP_NORETURN void NMI_Handler(void) {
exception_handler(NMI);
}
-__attribute__((naked)) NORETURN void HardFault_Handler(void) {
+__attribute__((naked)) MP_NORETURN void HardFault_Handler(void) {
exception_handler(HARD_FAULT);
}
-__attribute__((naked)) NORETURN void MemManage_Handler(void) {
+__attribute__((naked)) MP_NORETURN void MemManage_Handler(void) {
exception_handler(MEM_MANAGE);
}
-__attribute__((naked)) NORETURN void BusFault_Handler(void) {
+__attribute__((naked)) MP_NORETURN void BusFault_Handler(void) {
exception_handler(BUS_FAULT);
}
-__attribute__((naked)) NORETURN void UsageFault_Handler(void) {
+__attribute__((naked)) MP_NORETURN void UsageFault_Handler(void) {
exception_handler(USAGE_FAULT);
}
-__attribute__((naked)) NORETURN void SVC_Handler(void) {
+__attribute__((naked)) MP_NORETURN void SVC_Handler(void) {
exception_handler(SV_CALL);
}
-__attribute__((naked)) NORETURN void DebugMon_Handler(void) {
+__attribute__((naked)) MP_NORETURN void DebugMon_Handler(void) {
exception_handler(DEBUG_MONITOR);
}
-__attribute__((naked)) NORETURN void PendSV_Handler(void) {
+__attribute__((naked)) MP_NORETURN void PendSV_Handler(void) {
exception_handler(PENDING_SV);
}
-__attribute__((naked)) NORETURN void SysTick_Handler(void) {
+__attribute__((naked)) MP_NORETURN void SysTick_Handler(void) {
exception_handler(SYSTEM_TICK);
}
diff --git a/ports/qemu/mcu/arm/startup.c b/ports/qemu/mcu/arm/startup.c
index dbb75e3392..a7e9efb9fb 100644
--- a/ports/qemu/mcu/arm/startup.c
+++ b/ports/qemu/mcu/arm/startup.c
@@ -52,7 +52,7 @@ __attribute__((naked)) void Reset_Handler(void) {
_start();
}
-NORETURN void Default_Handler(void) {
+MP_NORETURN void Default_Handler(void) {
for (;;) {
}
}
diff --git a/ports/renesas-ra/Makefile b/ports/renesas-ra/Makefile
index ec47510d98..fd74c60a85 100644
--- a/ports/renesas-ra/Makefile
+++ b/ports/renesas-ra/Makefile
@@ -157,9 +157,6 @@ LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)"
LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))"
endif
-# Hook tinyusb USB interrupt if used to service usb task.
-LDFLAGS += --wrap=dcd_event_handler
-
# Options for mpy-cross
MPY_CROSS_FLAGS += -march=armv7m
diff --git a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json
index 4c4d4e4326..70a33b3515 100644
--- a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json
+++ b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json
@@ -15,7 +15,7 @@
"ABX00074_01.iso_1000x750.jpg"
],
"mcu": "RA6M5",
- "product": "Arduino Portenta C33",
+ "product": "Portenta C33",
"thumbnail": "",
"url": "https://store.arduino.cc/pages/portenta-c33",
"vendor": "Arduino"
diff --git a/ports/renesas-ra/machine_uart.c b/ports/renesas-ra/machine_uart.c
index b70978ad7a..196e1ccd6d 100644
--- a/ports/renesas-ra/machine_uart.c
+++ b/ports/renesas-ra/machine_uart.c
@@ -501,7 +501,7 @@ static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uint
if (!uart_tx_busy(self)) {
return 0;
}
- MICROPY_EVENT_POLL_HOOK
+ mp_event_wait_indefinite();
} while (mp_hal_ticks_ms() < timeout);
*errcode = MP_ETIMEDOUT;
ret = MP_STREAM_ERROR;
diff --git a/ports/renesas-ra/main.c b/ports/renesas-ra/main.c
index febb7b6d7a..cfc4611ecb 100644
--- a/ports/renesas-ra/main.c
+++ b/ports/renesas-ra/main.c
@@ -88,7 +88,7 @@ static machine_uart_obj_t machine_uart_repl_obj;
static uint8_t machine_uart_repl_rxbuf[MICROPY_HW_UART_REPL_RXBUF];
#endif
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
for (volatile uint delay = 0; delay < 1000000; delay++) {
}
led_state(1, 1);
diff --git a/ports/renesas-ra/modmachine.c b/ports/renesas-ra/modmachine.c
index dc38d809dd..c23ce7e469 100644
--- a/ports/renesas-ra/modmachine.c
+++ b/ports/renesas-ra/modmachine.c
@@ -184,12 +184,12 @@ static mp_obj_t mp_machine_unique_id(void) {
}
// Resets the pyboard in a manner similar to pushing the external RESET button.
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
powerctrl_mcu_reset();
}
// Activate the bootloader without BOOT* pins.
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
#if MICROPY_HW_ENABLE_STORAGE
storage_flush();
#endif
@@ -232,7 +232,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
powerctrl_enter_stop_mode();
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
if (n_args != 0) {
mp_obj_t args2[2] = {MP_OBJ_NULL, args[0]};
machine_rtc_wakeup(2, args2);
diff --git a/ports/renesas-ra/modtime.c b/ports/renesas-ra/modtime.c
index cbd639721f..e1358f82bc 100644
--- a/ports/renesas-ra/modtime.c
+++ b/ports/renesas-ra/modtime.c
@@ -53,5 +53,5 @@ static mp_obj_t mp_time_time_get(void) {
rtc_init_finalise();
ra_rtc_t time;
ra_rtc_get_time(&time);
- return mp_obj_new_int(timeutils_seconds_since_epoch(time.year, time.month, time.date, time.hour, time.minute, time.second));
+ return timeutils_obj_from_timestamp(timeutils_seconds_since_epoch(time.year, time.month, time.date, time.hour, time.minute, time.second));
}
diff --git a/ports/renesas-ra/mpconfigport.h b/ports/renesas-ra/mpconfigport.h
index bd936061fa..8a116269bb 100644
--- a/ports/renesas-ra/mpconfigport.h
+++ b/ports/renesas-ra/mpconfigport.h
@@ -210,19 +210,11 @@
#define MP_STATE_PORT MP_STATE_VM
-#if MICROPY_PY_NETWORK_ESP_HOSTED
-extern const struct _mp_obj_type_t mod_network_esp_hosted_type;
-#define MICROPY_HW_NIC_ESP_HOSTED { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_esp_hosted_type) },
-#else
-#define MICROPY_HW_NIC_ESP_HOSTED
-#endif
-
#ifndef MICROPY_BOARD_NETWORK_INTERFACES
#define MICROPY_BOARD_NETWORK_INTERFACES
#endif
#define MICROPY_PORT_NETWORK_INTERFACES \
- MICROPY_HW_NIC_ESP_HOSTED \
MICROPY_BOARD_NETWORK_INTERFACES \
// Miscellaneous settings
@@ -251,28 +243,17 @@ typedef unsigned int mp_uint_t; // must be pointer size
typedef long mp_off_t;
#if MICROPY_PY_THREAD
-#define MICROPY_EVENT_POLL_HOOK \
+#define MICROPY_INTERNAL_EVENT_HOOK \
do { \
- extern void mp_handle_pending(bool); \
- mp_handle_pending(true); \
if (pyb_thread_enabled) { \
MP_THREAD_GIL_EXIT(); \
pyb_thread_yield(); \
MP_THREAD_GIL_ENTER(); \
- } else { \
- __WFI(); \
} \
} while (0);
#define MICROPY_THREAD_YIELD() pyb_thread_yield()
#else
-#define MICROPY_EVENT_POLL_HOOK \
- do { \
- extern void mp_handle_pending(bool); \
- mp_handle_pending(true); \
- __WFI(); \
- } while (0);
-
#define MICROPY_THREAD_YIELD()
#endif
diff --git a/ports/renesas-ra/mphalport.c b/ports/renesas-ra/mphalport.c
index 1c62c23dd7..455b28af19 100644
--- a/ports/renesas-ra/mphalport.c
+++ b/ports/renesas-ra/mphalport.c
@@ -61,7 +61,7 @@ const byte mp_hal_status_to_errno_table[4] = {
[HAL_TIMEOUT] = MP_ETIMEDOUT,
};
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
mp_raise_OSError(mp_hal_status_to_errno_table[status]);
}
@@ -104,7 +104,7 @@ int mp_hal_stdin_rx_chr(void) {
return dupterm_c;
}
#endif
- MICROPY_EVENT_POLL_HOOK
+ mp_event_wait_indefinite();
}
}
diff --git a/ports/renesas-ra/mphalport.h b/ports/renesas-ra/mphalport.h
index 0819abeaf6..4f8be705d1 100644
--- a/ports/renesas-ra/mphalport.h
+++ b/ports/renesas-ra/mphalport.h
@@ -35,6 +35,14 @@
#define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV)
#define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state)
+// Port level Wait-for-Event macro
+//
+// Do not use this macro directly, include py/runtime.h and
+// call mp_event_wait_indefinite() or mp_event_wait_ms(timeout).
+// Uses WFI which will wake up from regular systick interrupt if not
+// before from any other source.
+#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) __WFI()
+
#define MICROPY_PY_LWIP_ENTER
#define MICROPY_PY_LWIP_REENTER
#define MICROPY_PY_LWIP_EXIT
@@ -49,7 +57,7 @@ static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) {
return -mp_hal_status_to_errno_table[status];
}
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
void mp_hal_set_interrupt_char(int c); // -1 to disable
static inline void mp_hal_wake_main_task_from_isr(void) {
diff --git a/ports/renesas-ra/powerctrl.c b/ports/renesas-ra/powerctrl.c
index 548d31679a..04c39b3511 100644
--- a/ports/renesas-ra/powerctrl.c
+++ b/ports/renesas-ra/powerctrl.c
@@ -174,7 +174,7 @@ const lpm_instance_t g_lpm_standby = {
#endif
-NORETURN void powerctrl_mcu_reset(void) {
+MP_NORETURN void powerctrl_mcu_reset(void) {
#if BSP_TZ_SECURE_BUILD
R_BSP_NonSecureEnter();
#else
@@ -185,7 +185,7 @@ NORETURN void powerctrl_mcu_reset(void) {
}
}
-NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {
+MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {
while (1) {
;
}
@@ -245,7 +245,7 @@ void powerctrl_enter_stop_mode(void) {
enable_irq(irq_state);
}
-NORETURN void powerctrl_enter_standby_mode(void) {
+MP_NORETURN void powerctrl_enter_standby_mode(void) {
rtc_init_finalise();
#if defined(MICROPY_BOARD_ENTER_STANDBY)
diff --git a/ports/renesas-ra/powerctrl.h b/ports/renesas-ra/powerctrl.h
index 34c40ea1ad..37932b1002 100644
--- a/ports/renesas-ra/powerctrl.h
+++ b/ports/renesas-ra/powerctrl.h
@@ -32,11 +32,11 @@
void SystemClock_Config(void);
-NORETURN void powerctrl_mcu_reset(void);
-NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr);
+MP_NORETURN void powerctrl_mcu_reset(void);
+MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr);
void powerctrl_check_enter_bootloader(void);
void powerctrl_enter_stop_mode(void);
-NORETURN void powerctrl_enter_standby_mode(void);
+MP_NORETURN void powerctrl_enter_standby_mode(void);
#endif // MICROPY_INCLUDED_RA_POWERCTRL_H
diff --git a/ports/renesas-ra/systick.c b/ports/renesas-ra/systick.c
index 6fa02a2b6e..cb528d91d2 100644
--- a/ports/renesas-ra/systick.c
+++ b/ports/renesas-ra/systick.c
@@ -97,22 +97,24 @@ void HAL_Delay(uint32_t Delay) {
// Core delay function that does an efficient sleep and may switch thread context.
// If IRQs are enabled then we must have the GIL.
-void mp_hal_delay_ms(mp_uint_t Delay) {
+void mp_hal_delay_ms(mp_uint_t ms) {
if (query_irq() == IRQ_STATE_ENABLED) {
// IRQs enabled, so can use systick counter to do the delay
uint32_t start = uwTick;
+ mp_uint_t elapsed = 0;
// Wraparound of tick is taken care of by 2's complement arithmetic.
do {
// This macro will execute the necessary idle behaviour. It may
// raise an exception, switch threads or enter sleep mode (waiting for
// (at least) the SysTick interrupt).
- MICROPY_EVENT_POLL_HOOK
- } while (uwTick - start < Delay);
+ mp_event_wait_ms(ms - elapsed);
+ elapsed = uwTick - start;
+ } while (elapsed < ms);
} else {
// IRQs disabled, so need to use a busy loop for the delay.
// To prevent possible overflow of the counter we use a double loop.
volatile uint32_t count_1ms;
- while (Delay-- > 0) {
+ while (ms-- > 0) {
count_1ms = (MICROPY_HW_MCU_PCLK / 1000 / 10);
while (count_1ms-- > 0) {
__asm__ __volatile__ ("nop");
diff --git a/ports/renesas-ra/uart.c b/ports/renesas-ra/uart.c
index c319b4c0a8..91714bf05b 100644
--- a/ports/renesas-ra/uart.c
+++ b/ports/renesas-ra/uart.c
@@ -42,7 +42,7 @@
typedef int (*KEYEX_CB)(uint32_t d);
-extern void NORETURN __fatal_error(const char *msg);
+extern void MP_NORETURN __fatal_error(const char *msg);
#if MICROPY_KBD_EXCEPTION
extern int mp_interrupt_char;
static KEYEX_CB keyex_cb[MICROPY_HW_MAX_UART] = {(KEYEX_CB)NULL};
@@ -477,7 +477,7 @@ bool uart_rx_wait(machine_uart_obj_t *self, uint32_t timeout) {
if (HAL_GetTick() - start >= timeout) {
return false; // timeout
}
- MICROPY_EVENT_POLL_HOOK
+ mp_event_wait_ms(1);
}
}
@@ -498,7 +498,7 @@ bool uart_tx_wait(machine_uart_obj_t *self, uint32_t timeout) {
if (HAL_GetTick() - start >= timeout) {
return false; // timeout
}
- MICROPY_EVENT_POLL_HOOK
+ mp_event_wait_ms(1);
}
}
diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt
index 53d00c7dfd..49ba8d77d6 100644
--- a/ports/rp2/CMakeLists.txt
+++ b/ports/rp2/CMakeLists.txt
@@ -84,6 +84,9 @@ endif()
list(APPEND GIT_SUBMODULES lib/mbedtls)
list(APPEND GIT_SUBMODULES lib/tinyusb)
+# Workaround for pico-sdk host toolchain issue, see directory for details
+list(APPEND CMAKE_MODULE_PATH "${MICROPY_PORT_DIR}/tools_patch")
+
# Include component cmake fragments
include(${MICROPY_DIR}/py/py.cmake)
include(${MICROPY_DIR}/extmod/extmod.cmake)
@@ -522,10 +525,23 @@ target_compile_options(${MICROPY_TARGET} PRIVATE
target_link_options(${MICROPY_TARGET} PRIVATE
-Wl,--defsym=__micropy_c_heap_size__=${MICROPY_C_HEAP_SIZE}
- -Wl,--wrap=dcd_event_handler
-Wl,--wrap=runtime_init_clocks
)
+if(PICO_FLASH_SIZE_BYTES GREATER 0)
+ target_link_options(${MICROPY_TARGET} PRIVATE
+ -Wl,--defsym=__micropy_flash_size__=${PICO_FLASH_SIZE_BYTES}
+ )
+elseif(PICO_RP2040)
+ target_link_options(${MICROPY_TARGET} PRIVATE
+ -Wl,--defsym=__micropy_flash_size__=2048k # Default to 2MB
+ )
+elseif(PICO_RP2350)
+ target_link_options(${MICROPY_TARGET} PRIVATE
+ -Wl,--defsym=__micropy_flash_size__=4096k # Default to 4MB
+ )
+endif()
+
if(PICO_RP2350)
target_link_options(${MICROPY_TARGET} PRIVATE
-Wl,--defsym=__micropy_extra_stack__=4096
@@ -594,14 +610,18 @@ endif()
# todo this is a bit brittle, but we want to move a few source files into RAM (which requires
# a linker script modification) until we explicitly add macro calls around the function
# defs to move them into RAM.
-if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM)
+if (NOT MICROPY_BOARD_LINKER_SCRIPT)
if(PICO_RP2040)
- pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld)
+ set(MICROPY_BOARD_LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld)
elseif(PICO_RP2350)
- pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld)
+ set(MICROPY_BOARD_LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld)
endif()
endif()
+if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM)
+ pico_set_linker_script(${MICROPY_TARGET} ${MICROPY_BOARD_LINKER_SCRIPT})
+endif()
+
pico_add_extra_outputs(${MICROPY_TARGET})
pico_find_compiler_with_triples(PICO_COMPILER_SIZE "${PICO_GCC_TRIPLE}" size)
@@ -638,9 +658,13 @@ if(NOT PICO_NUM_EXT_GPIOS)
set(PICO_NUM_EXT_GPIOS 10)
endif()
-if(EXISTS "${MICROPY_BOARD_DIR}/pins.csv")
- set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_DIR}/pins.csv")
- set(GEN_PINS_CSV_ARG --board-csv "${GEN_PINS_BOARD_CSV}")
+if(NOT MICROPY_BOARD_PINS)
+ set(MICROPY_BOARD_PINS "${MICROPY_BOARD_DIR}/pins.csv")
+endif()
+
+if(EXISTS "${MICROPY_BOARD_PINS}")
+ set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_PINS}")
+ set(GEN_PINS_CSV_ARG --board-csv "${MICROPY_BOARD_PINS}")
endif()
target_sources(${MICROPY_TARGET} PRIVATE
diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile
index 76b5698d2c..2895faaca6 100644
--- a/ports/rp2/Makefile
+++ b/ports/rp2/Makefile
@@ -67,6 +67,9 @@ all:
clean:
$(RM) -rf $(BUILD)
+deploy: all
+ $(Q)picotool load -x $(BUILD)/firmware.elf
+
# First ensure that pico-sdk is initialised, then run CMake with the
# UPDATE_SUBMODULES flag to update necessary submodules for this board.
#
diff --git a/ports/rp2/README.md b/ports/rp2/README.md
index 911d797fe0..41fca97f95 100644
--- a/ports/rp2/README.md
+++ b/ports/rp2/README.md
@@ -47,8 +47,10 @@ pass the board name to the build; e.g. for Raspberry Pi Pico W:
## Deploying firmware to the device
Firmware can be deployed to the device by putting it into bootloader mode
-(hold down BOOTSEL while powering on or resetting) and then copying
-`firmware.uf2` to the USB mass storage device that appears.
+(hold down BOOTSEL while powering on or resetting) and then either copying
+`firmware.uf2` to the USB mass storage device that appears, or using
+`picotool load -x firmware.elf`. The latter command can be accessed
+conveniently via `make deploy`.
If MicroPython is already installed then the bootloader can be entered by
executing `import machine; machine.bootloader()` at the REPL.
diff --git a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json
index 5639aaa2a4..5914445ed8 100644
--- a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json
+++ b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json
@@ -17,7 +17,7 @@
"ABX00052_01.iso_999x750.jpg"
],
"mcu": "rp2040",
- "product": "Arduino Nano RP2040 Connect",
+ "product": "Nano RP2040 Connect",
"thumbnail": "",
"url": "https://store-usa.arduino.cc/products/arduino-nano-rp2040-connect",
"vendor": "Arduino"
diff --git a/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json b/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json
index df9dd5d0de..108d632678 100644
--- a/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json
+++ b/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json
@@ -13,7 +13,7 @@
"pybstick-rp2040-26-broches-micropython-c.jpg"
],
"mcu": "rp2040",
- "product": "PYBSTICK26 RP2040",
+ "product": "RP2040 PYBStick",
"thumbnail": "",
"url": "https://shop.mchobby.be/product.php?id_product=2331",
"vendor": "McHobby"
diff --git a/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json b/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json
index 5358db627e..4b12cfda65 100644
--- a/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json
+++ b/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json
@@ -15,7 +15,7 @@
"pololu_3pi_2040_robot.jpg"
],
"mcu": "rp2040",
- "product": "Pololu 3pi+ 2040 Robot",
+ "product": "3pi+ 2040 Robot",
"thumbnail": "",
"url": "https://www.pololu.com/3pi",
"vendor": "Pololu"
diff --git a/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json b/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json
index 7ef383a9c4..1ec15ad591 100644
--- a/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json
+++ b/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json
@@ -16,7 +16,7 @@
"pololu_zumo_2040_robot.jpg"
],
"mcu": "rp2040",
- "product": "Pololu Zumo 2040 Robot",
+ "product": "Zumo 2040 Robot",
"thumbnail": "",
"url": "https://www.pololu.com/zumo",
"vendor": "Pololu"
diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json
index 66cea79dde..5ee6bcb754 100644
--- a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json
+++ b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json
@@ -14,7 +14,7 @@
"18288-SparkFun_Pro_Micro_-_RP2040-01.jpg"
],
"mcu": "rp2040",
- "product": "Pro Micro RP2040",
+ "product": "Pro Micro - RP2040",
"thumbnail": "",
"url": "https://www.sparkfun.com/products/18288",
"vendor": "SparkFun"
diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json
index e756c9bff4..ebd68bc5d9 100644
--- a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json
+++ b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json
@@ -17,7 +17,7 @@
"17745-SparkFun_Thing_Plus_-_RP2040-01a.jpg"
],
"mcu": "rp2040",
- "product": "Thing Plus RP2040",
+ "product": "Thing Plus - RP2040",
"thumbnail": "",
"url": "https://www.sparkfun.com/products/17745",
"vendor": "SparkFun"
diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h
index 56071e1873..931391d972 100644
--- a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h
+++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h
@@ -24,5 +24,9 @@
int mp_hal_is_pin_reserved(int n);
#define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i)
+// Set the default I2C to I2C1 on pins 18 and 19 which route to the qwiic connector
+#undef PICO_DEFAULT_I2C
+#define PICO_DEFAULT_I2C (1)
+
#define MICROPY_HW_I2C1_SDA (18)
#define MICROPY_HW_I2C1_SCL (19)
diff --git a/ports/rp2/boards/W5100S_EVB_PICO/board.json b/ports/rp2/boards/W5100S_EVB_PICO/board.json
index daaf1cf8ac..3d052de420 100644
--- a/ports/rp2/boards/W5100S_EVB_PICO/board.json
+++ b/ports/rp2/boards/W5100S_EVB_PICO/board.json
@@ -13,8 +13,8 @@
"W5100S-EVB-Pico.jpg"
],
"mcu": "rp2040",
- "product": "Wiznet W5100S-EVB-Pico",
+ "product": "W5100S-EVB-Pico",
"thumbnail": "",
- "url": "https://www.wiznet.io/product-item/w5100s-evb-pico/",
- "vendor": "Wiznet"
+ "url": "https://docs.wiznet.io/Product/iEthernet/W5100S/w5100s-evb-pico",
+ "vendor": "WIZnet"
}
diff --git a/ports/rp2/boards/W5500_EVB_PICO/board.json b/ports/rp2/boards/W5500_EVB_PICO/board.json
index e7625c3436..d4298014c0 100644
--- a/ports/rp2/boards/W5500_EVB_PICO/board.json
+++ b/ports/rp2/boards/W5500_EVB_PICO/board.json
@@ -13,8 +13,8 @@
"W5500-EVB-Pico.jpg"
],
"mcu": "rp2040",
- "product": "Wiznet W5500-EVB-Pico",
+ "product": "W5500-EVB-Pico",
"thumbnail": "",
"url": "https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico",
- "vendor": "Wiznet"
+ "vendor": "WIZnet"
}
diff --git a/ports/rp2/boards/WEACTSTUDIO/board.json b/ports/rp2/boards/WEACTSTUDIO/board.json
index 8881b40e73..58d89ddf2e 100644
--- a/ports/rp2/boards/WEACTSTUDIO/board.json
+++ b/ports/rp2/boards/WEACTSTUDIO/board.json
@@ -12,7 +12,7 @@
"weact_rp2040.jpg"
],
"mcu": "rp2040",
- "product": "WeAct Studio RP2040",
+ "product": "Studio RP2040",
"url": "https://github.com/WeActTC/WeActStudio.RP2040CoreBoard",
"variants": {
"FLASH_2M": "2 MiB Flash",
diff --git a/ports/rp2/cyw43_configport.h b/ports/rp2/cyw43_configport.h
index 5523305742..2da0b1f8a4 100644
--- a/ports/rp2/cyw43_configport.h
+++ b/ports/rp2/cyw43_configport.h
@@ -26,34 +26,27 @@
#ifndef MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H
#define MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H
-// The board-level config will be included here, so it can set some CYW43 values.
-#include <stdio.h>
-#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"
+#include "extmod/cyw43_config_common.h"
#define CYW43_INCLUDE_LEGACY_F1_OVERFLOW_WORKAROUND_VARIABLES (1)
#define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h"
-#define CYW43_IOCTL_TIMEOUT_US (1000000)
-#define CYW43_SLEEP_MAX (10)
-#define CYW43_NETUTILS (1)
+#define CYW43_SLEEP_MAX (10) // Unclear why rp2 port overrides the default here
#define CYW43_USE_OTP_MAC (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
+static inline bool cyw43_poll_is_pending(void) {
+ return pendsv_is_pending(PENDSV_DISPATCH_CYW43);
+}
+
+static inline void cyw43_yield(void) {
+ if (!cyw43_poll_is_pending()) {
+ best_effort_wfe_or_timeout(make_timeout_time_ms(1));
+ }
+}
-#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER
-#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT
-#define CYW43_THREAD_LOCK_CHECK
+#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook();
-#define CYW43_HOST_NAME mod_network_hostname_data
+// set in SDK board header
+#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT
#if CYW43_PIN_WL_DYNAMIC
@@ -100,36 +93,6 @@ uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id);
cyw43_yield(); \
} \
-#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
-
-// set in SDK board header
-#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT
-
-#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook();
-
-#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)
-
// Bluetooth requires dynamic memory allocation to load its firmware (the allocation
// call is made from pico-sdk). This allocation is always done at thread-level, not
// from an IRQ, so is safe to delegate to the MicroPython GC heap.
@@ -140,47 +103,4 @@ uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id);
#define cyw43_free m_tracked_free
#endif
-void cyw43_post_poll_hook(void);
-static inline bool cyw43_poll_is_pending(void) {
- return pendsv_is_pending(PENDSV_DISPATCH_CYW43);
-}
-
-static inline void cyw43_yield(void) {
- if (!cyw43_poll_is_pending()) {
- best_effort_wfe_or_timeout(make_timeout_time_ms(1));
- }
-}
-
-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) {
- mp_event_handle_nowait();
- }
-}
-
-#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait()
-
-#if LWIP_MDNS_RESPONDER == 1
-
-// Hook for any additional TCP/IP initialization than needs to be done.
-// Called after the netif specified by `itf` has been set up.
-#ifndef CYW43_CB_TCPIP_INIT_EXTRA
-#define CYW43_CB_TCPIP_INIT_EXTRA(self, itf) mdns_resp_add_netif(&self->netif[itf], mod_network_hostname_data)
-#endif
-
-// Hook for any additional TCP/IP deinitialization than needs to be done.
-// Called before the netif specified by `itf` is removed.
-#ifndef CYW43_CB_TCPIP_DEINIT_EXTRA
-#define CYW43_CB_TCPIP_DEINIT_EXTRA(self, itf) mdns_resp_remove_netif(&self->netif[itf])
-#endif
-
-#endif
#endif // MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H
diff --git a/ports/rp2/machine_pin.c b/ports/rp2/machine_pin.c
index d9ca3f8ce3..7a2de0c0bc 100644
--- a/ports/rp2/machine_pin.c
+++ b/ports/rp2/machine_pin.c
@@ -46,15 +46,6 @@
#define GPIO_IRQ_ALL (0xf)
-// Macros to access the state of the hardware.
-#define GPIO_GET_FUNCSEL(id) ((iobank0_hw->io[(id)].ctrl & IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS) >> IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB)
-#define GPIO_IS_OUT(id) (sio_hw->gpio_oe & (1 << (id)))
-#define GPIO_IS_PULL_UP(id) (padsbank0_hw->io[(id)] & PADS_BANK0_GPIO0_PUE_BITS)
-#define GPIO_IS_PULL_DOWN(id) (padsbank0_hw->io[(id)] & PADS_BANK0_GPIO0_PDE_BITS)
-
-// Open drain behaviour is simulated.
-#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1 << (id)))
-
#ifndef MICROPY_HW_PIN_RESERVED
#define MICROPY_HW_PIN_RESERVED(i) (0)
#endif
@@ -89,7 +80,11 @@ static const mp_irq_methods_t machine_pin_irq_methods;
static const int num_intr_regs = sizeof(iobank0_hw->intr) / sizeof(iobank0_hw->intr[0]);
// Mask with "1" indicating that the corresponding pin is in simulated open-drain mode.
+#if NUM_BANK0_GPIOS > 32
uint64_t machine_pin_open_drain_mask;
+#else
+uint32_t machine_pin_open_drain_mask;
+#endif
#if MICROPY_HW_PIN_EXT_COUNT
static inline bool is_ext_pin(__unused const machine_pin_obj_t *self) {
@@ -198,13 +193,13 @@ const machine_pin_obj_t *machine_pin_find(mp_obj_t pin) {
static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_pin_obj_t *self = self_in;
- uint funcsel = GPIO_GET_FUNCSEL(self->id);
+ uint funcsel = gpio_get_function(self->id);
qstr mode_qst;
if (!is_ext_pin(self)) {
if (funcsel == GPIO_FUNC_SIO) {
if (GPIO_IS_OPEN_DRAIN(self->id)) {
mode_qst = MP_QSTR_OPEN_DRAIN;
- } else if (GPIO_IS_OUT(self->id)) {
+ } else if (gpio_is_dir_out(self->id)) {
mode_qst = MP_QSTR_OUT;
} else {
mode_qst = MP_QSTR_IN;
@@ -214,11 +209,11 @@ static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin
}
mp_printf(print, "Pin(%q, mode=%q", self->name, mode_qst);
bool pull_up = false;
- if (GPIO_IS_PULL_UP(self->id)) {
+ if (gpio_is_pulled_up(self->id)) {
mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP);
pull_up = true;
}
- if (GPIO_IS_PULL_DOWN(self->id)) {
+ if (gpio_is_pulled_down(self->id)) {
if (pull_up) {
mp_printf(print, "|%q", MP_QSTR_PULL_DOWN);
} else {
@@ -298,7 +293,7 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin af: %d"), af);
}
gpio_set_function(self->id, af);
- machine_pin_open_drain_mask &= ~(1ULL << self->id);
+ GPIO_DISABLE_OPEN_DRAIN(self->id);
}
}
@@ -411,7 +406,7 @@ static mp_obj_t machine_pin_toggle(mp_obj_t self_in) {
machine_pin_ext_set(self, self->last_output_value ^ 1);
#endif
} else if (GPIO_IS_OPEN_DRAIN(self->id)) {
- if (GPIO_IS_OUT(self->id)) {
+ if (gpio_is_dir_out(self->id)) {
gpio_set_dir(self->id, GPIO_IN);
} else {
gpio_set_dir(self->id, GPIO_OUT);
diff --git a/ports/rp2/machine_timer.c b/ports/rp2/machine_timer.c
index 6f8b04f10e..ffb4c70243 100644
--- a/ports/rp2/machine_timer.c
+++ b/ports/rp2/machine_timer.c
@@ -27,6 +27,7 @@
#include "py/runtime.h"
#include "py/mperrno.h"
#include "py/mphal.h"
+#include "py/gc.h"
#include "pico/time.h"
#define ALARM_ID_INVALID (-1)
@@ -40,13 +41,36 @@ typedef struct _machine_timer_obj_t {
uint32_t mode;
uint64_t delta_us; // for periodic mode
mp_obj_t callback;
+ bool ishard;
} machine_timer_obj_t;
const mp_obj_type_t machine_timer_type;
static int64_t alarm_callback(alarm_id_t id, void *user_data) {
machine_timer_obj_t *self = user_data;
- mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self));
+
+ if (self->ishard) {
+ // 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(self->callback, MP_OBJ_FROM_PTR(self));
+ nlr_pop();
+ } else {
+ // Uncaught exception; disable the callback so it doesn't run again.
+ self->mode = TIMER_MODE_ONE_SHOT;
+ mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in timer callback\n");
+ mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val));
+ }
+ gc_unlock();
+ mp_sched_unlock();
+ } else {
+ mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self));
+ }
+
if (self->mode == TIMER_MODE_ONE_SHOT) {
return 0;
} else {
@@ -66,13 +90,14 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr
}
static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, };
+ enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, ARG_hard, };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_MODE_PERIODIC} },
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
{ MP_QSTR_tick_hz, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} },
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ { MP_QSTR_hard, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
};
// Parse args
@@ -96,6 +121,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_ar
}
self->callback = args[ARG_callback].u_obj;
+ self->ishard = args[ARG_hard].u_bool;
self->alarm_id = alarm_pool_add_alarm_in_us(self->pool, self->delta_us, alarm_callback, self, true);
if (self->alarm_id == -1) {
mp_raise_OSError(MP_ENOMEM);
diff --git a/ports/rp2/memmap_mp_rp2040.ld b/ports/rp2/memmap_mp_rp2040.ld
index a5799cd88b..5c8d9f4718 100644
--- a/ports/rp2/memmap_mp_rp2040.ld
+++ b/ports/rp2/memmap_mp_rp2040.ld
@@ -23,7 +23,7 @@
MEMORY
{
- FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
+ FLASH(rx) : ORIGIN = 0x10000000, LENGTH = __micropy_flash_size__
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
diff --git a/ports/rp2/memmap_mp_rp2350.ld b/ports/rp2/memmap_mp_rp2350.ld
index 1e1cbbfd70..1c4770efe1 100644
--- a/ports/rp2/memmap_mp_rp2350.ld
+++ b/ports/rp2/memmap_mp_rp2350.ld
@@ -23,7 +23,7 @@
MEMORY
{
- FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 4096k
+ FLASH(rx) : ORIGIN = 0x10000000, LENGTH = __micropy_flash_size__
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k
SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k
diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c
index 58a3a8ae4d..1f1b0e2f59 100644
--- a/ports/rp2/modmachine.c
+++ b/ports/rp2/modmachine.c
@@ -65,7 +65,7 @@ static mp_obj_t mp_machine_unique_id(void) {
return mp_obj_new_bytes(id.id, sizeof(id.id));
}
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
watchdog_reboot(0, SRAM_END, 0);
for (;;) {
__wfi();
@@ -82,7 +82,7 @@ static mp_int_t mp_machine_reset_cause(void) {
return reset_cause;
}
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args);
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB;
reset_usb_boot(0, 0);
@@ -137,6 +137,12 @@ static void mp_machine_idle(void) {
MICROPY_INTERNAL_WFE(1);
}
+static void alarm_sleep_callback(uint alarm_id) {
+}
+
+// Set this to 1 to enable some debug of the interrupt that woke the device
+#define DEBUG_LIGHTSLEEP 0
+
static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
mp_int_t delay_ms = 0;
bool use_timer_alarm = false;
@@ -167,6 +173,16 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
}
#endif
+ #if MICROPY_PY_THREAD
+ static bool in_lightsleep;
+ if (in_lightsleep) {
+ // The other CPU is also in machine.lightsleep()
+ MICROPY_END_ATOMIC_SECTION(my_interrupts);
+ return;
+ }
+ in_lightsleep = true;
+ #endif
+
#if MICROPY_HW_ENABLE_USBDEV
// Only disable the USB clock if a USB host has not configured the device
// or if going to DORMANT mode.
@@ -206,6 +222,15 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
// Disable ROSC.
rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB;
+ #if DEBUG_LIGHTSLEEP
+ #if PICO_RP2040
+ uint32_t pending_intr = 0;
+ #else
+ uint32_t pending_intr[2] = { 0 };
+ #endif
+ #endif
+
+ bool alarm_armed = false;
if (n_args == 0) {
#if MICROPY_PY_NETWORK_CYW43
gpio_set_dormant_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true);
@@ -214,16 +239,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
} else {
uint32_t save_sleep_en0 = clocks_hw->sleep_en0;
uint32_t save_sleep_en1 = clocks_hw->sleep_en1;
- bool timer3_enabled = irq_is_enabled(3);
-
- const uint32_t alarm_num = 3;
- const uint32_t irq_num = TIMER_ALARM_IRQ_NUM(timer_hw, alarm_num);
if (use_timer_alarm) {
- // Make sure ALARM3/IRQ3 is enabled on _this_ core
- if (!timer3_enabled) {
- irq_set_enabled(irq_num, true);
- }
- hw_set_bits(&timer_hw->inte, 1u << alarm_num);
// Use timer alarm to wake.
clocks_hw->sleep_en0 = 0x0;
#if PICO_RP2040
@@ -233,8 +249,11 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
#else
#error Unknown processor
#endif
- timer_hw->intr = 1u << alarm_num; // clear any IRQ
- timer_hw->alarm[alarm_num] = timer_hw->timerawl + delay_ms * 1000;
+ hardware_alarm_claim(MICROPY_HW_LIGHTSLEEP_ALARM_NUM);
+ hardware_alarm_set_callback(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, alarm_sleep_callback);
+ if (hardware_alarm_set_target(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, make_timeout_time_ms(delay_ms)) == PICO_OK) {
+ alarm_armed = true;
+ }
} else {
// TODO: Use RTC alarm to wake.
clocks_hw->sleep_en0 = 0x0;
@@ -264,10 +283,17 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
#endif
// Go into low-power mode.
- __wfi();
+ if (alarm_armed) {
+ __wfi();
- if (!timer3_enabled) {
- irq_set_enabled(irq_num, false);
+ #if DEBUG_LIGHTSLEEP
+ #if PICO_RP2040
+ pending_intr = nvic_hw->ispr;
+ #else
+ pending_intr[0] = nvic_hw->ispr[0];
+ pending_intr[1] = nvic_hw->ispr[1];
+ #endif
+ #endif
}
clocks_hw->sleep_en0 = save_sleep_en0;
clocks_hw->sleep_en1 = save_sleep_en1;
@@ -282,9 +308,37 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
// Re-sync mp_hal_time_ns() counter with aon timer.
mp_hal_time_ns_set_from_rtc();
+
+ // Note: This must be done after MICROPY_END_ATOMIC_SECTION
+ if (use_timer_alarm) {
+ if (alarm_armed) {
+ hardware_alarm_cancel(MICROPY_HW_LIGHTSLEEP_ALARM_NUM);
+ }
+ hardware_alarm_set_callback(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, NULL);
+ hardware_alarm_unclaim(MICROPY_HW_LIGHTSLEEP_ALARM_NUM);
+
+ #if DEBUG_LIGHTSLEEP
+ // Check irq.h for the list of IRQ's
+ // for rp2040 00000042: TIMER_IRQ_1 woke the device as expected
+ // 00000020: USBCTRL_IRQ woke the device (probably early)
+ // For rp2350 00000000:00000002: TIMER0_IRQ_1 woke the device as expected
+ // 00000000:00004000: USBCTRL_IRQ woke the device (probably early)
+ #if PICO_RP2040
+ mp_printf(MP_PYTHON_PRINTER, "lightsleep: pending_intr=%08lx\n", pending_intr);
+ #else
+ mp_printf(MP_PYTHON_PRINTER, "lightsleep: pending_intr=%08lx:%08lx\n", pending_intr[1], pending_intr[0]);
+ #endif
+ #endif
+ }
+
+ #if MICROPY_PY_THREAD
+ // Clearing the flag here is atomic, and we know we're the ones who set it
+ // (higher up, inside the critical section)
+ in_lightsleep = false;
+ #endif
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
mp_machine_lightsleep(n_args, args);
mp_machine_reset();
}
diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h
index 3d65737266..35afea4fac 100644
--- a/ports/rp2/mpconfigport.h
+++ b/ports/rp2/mpconfigport.h
@@ -198,8 +198,9 @@
#define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP)
// Hardware timer alarm index. Available range 0-3.
-// Number 3 is currently used by pico-sdk (PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM)
+// Number 3 is currently used by pico-sdk alarm pool (PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM)
#define MICROPY_HW_SOFT_TIMER_ALARM_NUM (2)
+#define MICROPY_HW_LIGHTSLEEP_ALARM_NUM (1)
// fatfs configuration
#define MICROPY_FATFS_ENABLE_LFN (2)
@@ -241,46 +242,11 @@
#endif
#endif
-#if MICROPY_PY_NETWORK_CYW43
-extern const struct _mp_obj_type_t mp_network_cyw43_type;
-#define MICROPY_HW_NIC_CYW43 \
- { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_IDLE), MP_ROM_INT(CYW43_LINK_DOWN) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_CONNECTING), MP_ROM_INT(CYW43_LINK_JOIN) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_WRONG_PASSWORD), MP_ROM_INT(CYW43_LINK_BADAUTH) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND), MP_ROM_INT(CYW43_LINK_NONET) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_CONNECT_FAIL), MP_ROM_INT(CYW43_LINK_FAIL) }, \
- { MP_ROM_QSTR(MP_QSTR_STAT_GOT_IP), MP_ROM_INT(CYW43_LINK_UP) },
-#else
-#define MICROPY_HW_NIC_CYW43
-#endif
-
-#if MICROPY_PY_NETWORK_NINAW10
-// This Network interface requires the extended socket state.
-#ifndef MICROPY_PY_SOCKET_EXTENDED_STATE
-#define MICROPY_PY_SOCKET_EXTENDED_STATE (1)
-#endif
-extern const struct _mp_obj_type_t mod_network_nic_type_nina;
-#define MICROPY_HW_NIC_NINAW10 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_nic_type_nina) },
-#else
-#define MICROPY_HW_NIC_NINAW10
-#endif
-
-#if MICROPY_PY_NETWORK_WIZNET5K
-extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k;
-#define MICROPY_HW_NIC_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) },
-#else
-#define MICROPY_HW_NIC_WIZNET5K
-#endif
-
#ifndef MICROPY_BOARD_NETWORK_INTERFACES
#define MICROPY_BOARD_NETWORK_INTERFACES
#endif
#define MICROPY_PORT_NETWORK_INTERFACES \
- MICROPY_HW_NIC_CYW43 \
- MICROPY_HW_NIC_NINAW10 \
- MICROPY_HW_NIC_WIZNET5K \
MICROPY_BOARD_NETWORK_INTERFACES \
// Additional entries for use with pendsv_schedule_dispatch.
diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c
index caecb69509..b581b3b59f 100644
--- a/ports/rp2/mphalport.c
+++ b/ports/rp2/mphalport.c
@@ -266,17 +266,7 @@ void soft_timer_init(void) {
}
void mp_wfe_or_timeout(uint32_t timeout_ms) {
- soft_timer_entry_t timer;
-
- // Note the timer doesn't have an associated callback, it just exists to create a
- // hardware interrupt to wake the CPU
- soft_timer_static_init(&timer, SOFT_TIMER_MODE_ONE_SHOT, 0, NULL);
- soft_timer_insert(&timer, timeout_ms);
-
- __wfe();
-
- // Clean up the timer node if it's not already
- soft_timer_remove(&timer);
+ best_effort_wfe_or_timeout(delayed_by_ms(get_absolute_time(), timeout_ms));
}
int mp_hal_is_pin_reserved(int n) {
diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h
index 956db3ec70..5012b682b9 100644
--- a/ports/rp2/mphalport.h
+++ b/ports/rp2/mphalport.h
@@ -123,7 +123,18 @@ static inline mp_uint_t mp_hal_get_cpu_freq(void) {
#define MP_HAL_PIN_PULL_UP (1)
#define MP_HAL_PIN_PULL_DOWN (2)
+// Open drain behaviour is simulated.
+#if NUM_BANK0_GPIOS > 32
extern uint64_t machine_pin_open_drain_mask;
+#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1ULL << id))
+#define GPIO_ENABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask |= (1ULL << id))
+#define GPIO_DISABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask &= ~(1ULL << id))
+#else
+extern uint32_t machine_pin_open_drain_mask;
+#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1U << id))
+#define GPIO_ENABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask |= (1U << id))
+#define GPIO_DISABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask &= ~(1U << id))
+#endif
mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in);
@@ -133,13 +144,13 @@ static inline unsigned int mp_hal_pin_name(mp_hal_pin_obj_t pin) {
static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) {
gpio_set_dir(pin, GPIO_IN);
- machine_pin_open_drain_mask &= ~(1 << pin);
+ GPIO_DISABLE_OPEN_DRAIN(pin);
gpio_set_function(pin, GPIO_FUNC_SIO);
}
static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) {
gpio_set_dir(pin, GPIO_OUT);
- machine_pin_open_drain_mask &= ~(1 << pin);
+ GPIO_DISABLE_OPEN_DRAIN(pin);
gpio_set_function(pin, GPIO_FUNC_SIO);
}
@@ -151,7 +162,7 @@ static inline void mp_hal_pin_open_drain_with_value(mp_hal_pin_obj_t pin, int v)
gpio_put(pin, 0);
gpio_set_dir(pin, GPIO_OUT);
}
- machine_pin_open_drain_mask |= 1 << pin;
+ GPIO_ENABLE_OPEN_DRAIN(pin);
gpio_set_function(pin, GPIO_FUNC_SIO);
}
diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c
index e1e1567828..fed34be380 100644
--- a/ports/rp2/mpnetworkport.c
+++ b/ports/rp2/mpnetworkport.c
@@ -31,6 +31,7 @@
#if MICROPY_PY_LWIP
#include "shared/runtime/softtimer.h"
+#include "lwip/netif.h"
#include "lwip/timeouts.h"
// Poll lwIP every 64ms by default
@@ -39,6 +40,9 @@
// Soft timer for running lwIP in the background.
static soft_timer_entry_t mp_network_soft_timer;
+// Callback for change of netif state
+NETIF_DECLARE_EXT_CALLBACK(netif_callback)
+
#if MICROPY_PY_NETWORK_CYW43
#include "lib/cyw43-driver/src/cyw43.h"
#include "lib/cyw43-driver/src/cyw43_stats.h"
@@ -137,17 +141,48 @@ static void mp_network_soft_timer_callback(soft_timer_entry_t *self) {
#if MICROPY_PY_NETWORK_WIZNET5K
wiznet5k_poll();
#endif
+
+ // Only keep the timer running if any TCP sockets are active, or any netif is up
+ struct netif *netif;
+ extern void *tcp_active_pcbs;
+ bool keep_running = (tcp_active_pcbs != NULL);
+ if (!keep_running) {
+ NETIF_FOREACH(netif) {
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+ keep_running = true;
+ break;
+ }
+ }
+ }
+
+ // Periodic timer will re-queue as soon as this handler exits,
+ // one shot timer will not
+ mp_network_soft_timer.mode = keep_running ? SOFT_TIMER_MODE_PERIODIC : SOFT_TIMER_MODE_ONE_SHOT;
}
+static void mp_network_netif_status_cb(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args);
+
void mod_network_lwip_init(void) {
soft_timer_static_init(
&mp_network_soft_timer,
- SOFT_TIMER_MODE_PERIODIC,
+ SOFT_TIMER_MODE_ONE_SHOT,
LWIP_TICK_RATE_MS,
mp_network_soft_timer_callback
);
- soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS);
+ if (netif_callback.callback_fn == NULL) {
+ netif_add_ext_callback(&netif_callback, mp_network_netif_status_cb);
+ }
+}
+
+static void mp_network_netif_status_cb(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args) {
+ // Start the network soft timer any time an interface comes up, unless
+ // it's already running
+ if (reason == LWIP_NSC_LINK_CHANGED && args->link_changed.state
+ && mp_network_soft_timer.mode == SOFT_TIMER_MODE_ONE_SHOT) {
+ mp_network_soft_timer.mode = SOFT_TIMER_MODE_PERIODIC;
+ soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS);
+ }
}
#endif // MICROPY_PY_LWIP
diff --git a/ports/rp2/rp2_dma.c b/ports/rp2/rp2_dma.c
index 78f69e6452..94c61e226e 100644
--- a/ports/rp2/rp2_dma.c
+++ b/ports/rp2/rp2_dma.c
@@ -315,7 +315,7 @@ static mp_obj_t rp2_dma_pack_ctrl(size_t n_pos_args, const mp_obj_t *pos_args, m
// Pack keyword settings into a control register value, using either the default for this
// DMA channel or the provided defaults
rp2_dma_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
- mp_uint_t value = DEFAULT_DMA_CONFIG | ((self->channel & 0xf) << 11);
+ mp_uint_t value = DEFAULT_DMA_CONFIG | ((self->channel & 0xf) << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB);
if (n_pos_args > 1) {
mp_raise_TypeError(MP_ERROR_TEXT("pack_ctrl only takes keyword arguments"));
diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c
index a9beabf051..d6b9e13653 100644
--- a/ports/rp2/rp2_flash.c
+++ b/ports/rp2/rp2_flash.c
@@ -49,6 +49,19 @@
static_assert(MICROPY_HW_ROMFS_BYTES % 4096 == 0, "ROMFS size must be a multiple of 4K");
static_assert(MICROPY_HW_FLASH_STORAGE_BYTES % 4096 == 0, "Flash storage size must be a multiple of 4K");
+#ifndef MICROPY_HW_FLASH_MAX_FREQ
+// Emulate Pico SDK's SYS_CLK_HZ / PICO_FLASH_SPI_CLKDIV behaviour by default.
+// On RP2040 if PICO_USE_FASTEST_SUPPORTED_CLOCK is set then SYS_CLK_HZ can be
+// 200MHz, potentially putting timings derived from PICO_FLASH_SPI_CLKDIV
+// out of range.
+#ifdef PICO_FLASH_SPI_CLKDIV
+#define MICROPY_HW_FLASH_MAX_FREQ (SYS_CLK_HZ / PICO_FLASH_SPI_CLKDIV)
+#else
+// A default PICO_FLASH_SPI_CLKDIV of 4 is set in boot2_generic_03h.S
+#define MICROPY_HW_FLASH_MAX_FREQ (SYS_CLK_HZ / 4)
+#endif
+#endif
+
#ifndef MICROPY_HW_FLASH_STORAGE_BASE
#define MICROPY_HW_FLASH_STORAGE_BASE (PICO_FLASH_SIZE_BYTES - MICROPY_HW_FLASH_STORAGE_BYTES)
#endif
@@ -104,9 +117,8 @@ static bool use_multicore_lockout(void) {
// and core1 locked out if relevant.
static void __no_inline_not_in_flash_func(rp2_flash_set_timing_internal)(int clock_hz) {
- // Use the minimum divisor assuming a 133MHz flash.
- const int max_flash_freq = 133000000;
- int divisor = (clock_hz + max_flash_freq - 1) / max_flash_freq;
+ // Use the minimum divisor based upon our target MICROPY_HW_FLASH_MAX_FREQ
+ int divisor = (clock_hz + MICROPY_HW_FLASH_MAX_FREQ - 1) / MICROPY_HW_FLASH_MAX_FREQ;
#if PICO_RP2350
// Make sure flash is deselected - QMI doesn't appear to have a busy flag(!)
diff --git a/ports/rp2/tools_patch/Findpioasm.cmake b/ports/rp2/tools_patch/Findpioasm.cmake
new file mode 100644
index 0000000000..72e6bf6fb6
--- /dev/null
+++ b/ports/rp2/tools_patch/Findpioasm.cmake
@@ -0,0 +1,58 @@
+# Finds (or builds) the pioasm executable
+#
+# This will define the following imported targets
+#
+# pioasm
+#
+
+# This is a temporary patched copy of pico-sdk file Findpioasm.cmake to work around
+# a host toolchain issue with GCC 15.1:
+# https://github.com/raspberrypi/pico-sdk/issues/2448
+
+if (NOT TARGET pioasm)
+ # todo we would like to use pckgconfig to look for it first
+ # see https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/
+
+ include(ExternalProject)
+
+ set(PIOASM_SOURCE_DIR ${PICO_SDK_PATH}/tools/pioasm)
+ set(PIOASM_BINARY_DIR ${CMAKE_BINARY_DIR}/pioasm)
+ set(PIOASM_INSTALL_DIR ${CMAKE_BINARY_DIR}/pioasm-install CACHE PATH "Directory where pioasm has been installed" FORCE)
+
+ set(pioasmBuild_TARGET pioasmBuild)
+ set(pioasm_TARGET pioasm)
+
+ if (NOT TARGET ${pioasmBuild_TARGET})
+ pico_message_debug("PIOASM will need to be built")
+# message("Adding external project ${pioasmBuild_Target} in ${CMAKE_CURRENT_LIST_DIR}}")
+ ExternalProject_Add(${pioasmBuild_TARGET}
+ PREFIX pioasm
+ SOURCE_DIR ${PIOASM_SOURCE_DIR}
+ BINARY_DIR ${PIOASM_BINARY_DIR}
+ INSTALL_DIR ${PIOASM_INSTALL_DIR}
+ CMAKE_ARGS
+ "--no-warn-unused-cli"
+ "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}"
+ "-DPIOASM_FLAT_INSTALL=1"
+ "-DCMAKE_INSTALL_PREFIX=${PIOASM_INSTALL_DIR}"
+ "-DCMAKE_RULE_MESSAGES=OFF" # quieten the build
+ "-DCMAKE_INSTALL_MESSAGE=NEVER" # quieten the install
+ # Toolchain workaround follows
+ "-DCMAKE_CXX_FLAGS=-include cstdint"
+ CMAKE_CACHE_ARGS "-DPIOASM_EXTRA_SOURCE_FILES:STRING=${PIOASM_EXTRA_SOURCE_FILES}"
+ BUILD_ALWAYS 1 # force dependency checking
+ EXCLUDE_FROM_ALL TRUE
+ )
+ endif()
+
+ if (CMAKE_HOST_WIN32)
+ set(pioasm_EXECUTABLE ${PIOASM_INSTALL_DIR}/pioasm/pioasm.exe)
+ else()
+ set(pioasm_EXECUTABLE ${PIOASM_INSTALL_DIR}/pioasm/pioasm)
+ endif()
+ add_executable(${pioasm_TARGET} IMPORTED GLOBAL)
+ set_property(TARGET ${pioasm_TARGET} PROPERTY IMPORTED_LOCATION
+ ${pioasm_EXECUTABLE})
+
+ add_dependencies(${pioasm_TARGET} ${pioasmBuild_TARGET})
+endif()
diff --git a/ports/samd/Makefile b/ports/samd/Makefile
index 005664f178..bec530d803 100644
--- a/ports/samd/Makefile
+++ b/ports/samd/Makefile
@@ -113,8 +113,6 @@ LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)"
LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))"
endif
-LDFLAGS += --wrap=dcd_event_handler
-
MPY_CROSS_FLAGS += -march=$(MPY_CROSS_MCU_ARCH)
SRC_C += \
diff --git a/ports/samd/boards/MINISAM_M4/mpconfigboard.h b/ports/samd/boards/MINISAM_M4/mpconfigboard.h
index 6d908bdcb8..30a7a80454 100644
--- a/ports/samd/boards/MINISAM_M4/mpconfigboard.h
+++ b/ports/samd/boards/MINISAM_M4/mpconfigboard.h
@@ -7,4 +7,4 @@
#define MICROPY_HW_DEFAULT_I2C_ID (2)
#define MICROPY_HW_DEFAULT_SPI_ID (1)
-#define MICROPY_HW_QSPIFLASH GD25Q16C
+#define MICROPY_HW_QSPIFLASH W25Q16JV_IQ
diff --git a/ports/samd/boards/SAMD_GENERIC_D21X18/board.json b/ports/samd/boards/SAMD_GENERIC_D21X18/board.json
index a14730bd1b..c00d78addd 100644
--- a/ports/samd/boards/SAMD_GENERIC_D21X18/board.json
+++ b/ports/samd/boards/SAMD_GENERIC_D21X18/board.json
@@ -11,6 +11,7 @@
],
"mcu": "samd21",
"vendor": "Microchip",
+ "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d",
"product": "Generic SAMD21J18",
"thumbnail": ""
}
diff --git a/ports/samd/boards/SAMD_GENERIC_D51X19/board.json b/ports/samd/boards/SAMD_GENERIC_D51X19/board.json
index 21cb114bf6..2be5f20851 100644
--- a/ports/samd/boards/SAMD_GENERIC_D51X19/board.json
+++ b/ports/samd/boards/SAMD_GENERIC_D51X19/board.json
@@ -11,6 +11,7 @@
],
"mcu": "samd51",
"vendor": "Microchip",
+ "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d",
"product": "Generic SAMD51P19",
"thumbnail": ""
}
diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/board.json b/ports/samd/boards/SAMD_GENERIC_D51X20/board.json
index ae4f83472e..8384484327 100644
--- a/ports/samd/boards/SAMD_GENERIC_D51X20/board.json
+++ b/ports/samd/boards/SAMD_GENERIC_D51X20/board.json
@@ -11,6 +11,7 @@
],
"mcu": "samd51",
"vendor": "Microchip",
+ "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d",
"product": "Generic SAMD51P20",
"thumbnail": ""
}
diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk
index ddba3dcbba..b240c2587f 100644
--- a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk
+++ b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk
@@ -1,13 +1,11 @@
MCU_SERIES = SAMD51
CMSIS_MCU = SAMD51P20A
-LD_FILES = boards/samd51x19a.ld sections.ld
+LD_FILES = boards/samd51x20a.ld sections.ld
TEXT0 = 0x4000
-
# The ?='s allow overriding in mpconfigboard.mk.
# MicroPython settings
# The size of a MCU flash filesystem will be
-# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE
+# 1008k - MICROPY_HW_CODESIZE
# The default for MICROPY_HW_VFSROMSIZE is 64K
MICROPY_HW_CODESIZE ?= 752K
-MICROPY_HW_VFSROMSIZE ?= 128K
diff --git a/ports/samd/boards/SEEED_XIAO_SAMD21/board.json b/ports/samd/boards/SEEED_XIAO_SAMD21/board.json
index 4d19f6b945..0d6b2fc30d 100644
--- a/ports/samd/boards/SEEED_XIAO_SAMD21/board.json
+++ b/ports/samd/boards/SEEED_XIAO_SAMD21/board.json
@@ -10,7 +10,7 @@
"seeeduino-xiao.jpg"
],
"mcu": "samd21",
- "product": "Seeeduino XIAO (SAMD21)",
+ "product": "XIAO SAMD21",
"thumbnail": "",
"url": "https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html",
"vendor": "Seeed Studio"
diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json
new file mode 100644
index 0000000000..789bd97730
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json
@@ -0,0 +1,21 @@
+{
+ "deploy": [
+ "../deploy.md"
+ ],
+ "docs": "",
+ "features": [
+ "Battery Charging",
+ "DAC",
+ "External Flash",
+ "USB",
+ "RGB LED"
+ ],
+ "images": [
+ "sparkfun_readboard_turbo.jpg"
+ ],
+ "mcu": "samd21",
+ "product": "SparkFun RedBoard Turbo",
+ "thumbnail": "",
+ "url": "https://www.sparkfun.com/products/14812",
+ "vendor": "SparkFun"
+}
diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h
new file mode 100644
index 0000000000..cd0ec51acc
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h
@@ -0,0 +1,11 @@
+#define MICROPY_HW_BOARD_NAME "SparkFun RedBoard Turbo"
+#define MICROPY_HW_MCU_NAME "SAMD21G18A"
+
+#define MICROPY_HW_XOSC32K (1)
+
+#define MICROPY_HW_SPIFLASH (1)
+#define MICROPY_HW_SPIFLASH_ID (5)
+
+#define MICROPY_HW_DEFAULT_UART_ID (0)
+#define MICROPY_HW_DEFAULT_I2C_ID (3)
+#define MICROPY_HW_DEFAULT_SPI_ID (4)
diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk
new file mode 100644
index 0000000000..6ea327a650
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk
@@ -0,0 +1,8 @@
+MCU_SERIES = SAMD21
+CMSIS_MCU = SAMD21G18A
+LD_FILES = boards/samd21x18a.ld sections.ld
+TEXT0 = 0x2000
+
+# The ?='s allow overriding in mpconfigboard.mk.
+# MicroPython settings
+MICROPY_HW_CODESIZE ?= 232K
diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv
new file mode 100644
index 0000000000..f065fc8917
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv
@@ -0,0 +1,47 @@
+# The lines contain pairs of Pin name and Pin number.
+# Pin names must be valid Python identifiers.
+# Pin numbers have the form Pxnn, with x being A, B, C or D.
+# Lines starting with # or empty lines are ignored.
+
+USB_DM,PA24
+USB_DP,PA25
+
+SWCLK,PA30
+SWDIO,PA31
+
+D0,PA11
+D1,PA10
+D2,PA14
+D3,PA09
+D4,PA08
+D5,PA15
+D6,PA20
+D7,PA21
+D8,PA06
+D9,PA07
+D10,PA18
+D11,PA16
+D12,PA19
+D13,PA17
+A0,PA02
+A1,PB08
+A2,PB09
+A3,PA04
+A4,PA05
+A5,PB02
+AREF,PA03
+RX,PA11
+TX,PA10
+SCL,PA23
+SDA,PA22
+MOSI,PB10
+MISO,PA12
+SCK,PB11
+FLASH_MOSI,PB22
+FLASH_MISO,PB03
+FLASH_SCK,PB23
+FLASH_CS,PA13
+NEOPIXEL,PA30
+
+LED_TX,PA27
+LED_RX,PA31
diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json
new file mode 100644
index 0000000000..e5c5411a0c
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json
@@ -0,0 +1,19 @@
+{
+ "deploy": [
+ "../deploy.md"
+ ],
+ "docs": "",
+ "features": [
+ "Battery Charging",
+ "DAC",
+ "USB"
+ ],
+ "images": [
+ "sparkfun_sam21_dev_breakout.jpg"
+ ],
+ "mcu": "samd21",
+ "product": "SparkFun SAMD21 Dev Breakout",
+ "thumbnail": "",
+ "url": "https://www.sparkfun.com/products/13672",
+ "vendor": "SparkFun"
+}
diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h
new file mode 100644
index 0000000000..0679a9f4e4
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h
@@ -0,0 +1,8 @@
+#define MICROPY_HW_BOARD_NAME "SparkFun SAMD21 Dev Breakout"
+#define MICROPY_HW_MCU_NAME "SAMD21G18A"
+
+#define MICROPY_HW_XOSC32K (1)
+
+#define MICROPY_HW_DEFAULT_UART_ID (0)
+#define MICROPY_HW_DEFAULT_I2C_ID (3)
+#define MICROPY_HW_DEFAULT_SPI_ID (4)
diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk
new file mode 100644
index 0000000000..8696c966bc
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk
@@ -0,0 +1,4 @@
+MCU_SERIES = SAMD21
+CMSIS_MCU = SAMD21G18A
+LD_FILES = boards/samd21x18a.ld sections.ld
+TEXT0 = 0x2000
diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv
new file mode 100644
index 0000000000..794be31800
--- /dev/null
+++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv
@@ -0,0 +1,45 @@
+# The lines contain pairs of Pin name and Pin number.
+# Pin names must be valid Python identifiers.
+# Pin numbers have the form Pxnn, with x being A, B, C or D.
+# Lines starting with # or empty lines are ignored.
+
+USB_DM,PA24
+USB_DP,PA25
+
+SWCLK,PA30
+SWDIO,PA31
+
+D0,PA11
+D1,PA10
+D2,PA14
+D3,PA09
+D4,PA08
+D5,PA15
+D6,PA20
+D7,PA21
+D8,PA06
+D9,PA07
+D10,PA18
+D11,PA16
+D12,PA19
+D13,PA17
+A0,PA02
+A1,PB08
+A2,PB09
+A3,PA04
+A4,PA05
+A5,PB02
+AREF,PA03
+RX,PA11
+TX,PA10
+SCL,PA23
+SDA,PA22
+MOSI,PB10
+MISO,PA12
+SCK,PB11
+D38,PA13
+D30,PB22
+D31,PB23
+
+LED_TX,PA27
+LED_RX,PB03
diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json
index ee9ca9d368..6e89cebadd 100644
--- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json
+++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json
@@ -13,8 +13,8 @@
"sparkfun_samd51_thing_plus.jpg"
],
"mcu": "samd51",
- "product": "Sparkfun SAMD51 Thing Plus",
+ "product": "SAMD51 Thing Plus",
"thumbnail": "",
"url": "https://www.sparkfun.com/products/14713",
- "vendor": "Sparkfun"
+ "vendor": "SparkFun"
}
diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h
index fe2226a59e..9e0db7875f 100644
--- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h
+++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h
@@ -1,4 +1,4 @@
-#define MICROPY_HW_BOARD_NAME "Sparkfun SAMD51 Thing Plus"
+#define MICROPY_HW_BOARD_NAME "SparkFun SAMD51 Thing Plus"
#define MICROPY_HW_MCU_NAME "SAMD51J20A"
#define MICROPY_HW_XOSC32K (1)
diff --git a/ports/samd/machine_i2c.c b/ports/samd/machine_i2c.c
index 03af3d29ed..172518523d 100644
--- a/ports/samd/machine_i2c.c
+++ b/ports/samd/machine_i2c.c
@@ -37,9 +37,9 @@
#include "genhdr/pins.h"
#include "clock_config.h"
-#define DEFAULT_I2C_FREQ (400000)
-#define RISETIME_NS (200)
-#define I2C_TIMEOUT (100)
+#define DEFAULT_I2C_FREQ (400000)
+#define RISETIME_NS (200)
+#define DEFAULT_I2C_TIMEOUT (50000)
#define IS_BUS_BUSY (i2c->I2CM.STATUS.bit.BUSSTATE == 3)
#define NACK_RECVD (i2c->I2CM.STATUS.bit.RXNACK == 1)
@@ -67,6 +67,7 @@ typedef struct _machine_i2c_obj_t {
uint8_t state;
uint32_t freq;
uint32_t timeout;
+ uint32_t timer;
size_t len;
uint8_t *buf;
} machine_i2c_obj_t;
@@ -88,7 +89,7 @@ void common_i2c_irq_handler(int i2c_id) {
if (self->len > 0) {
*(self->buf)++ = i2c->I2CM.DATA.reg;
self->len--;
- self->timeout = I2C_TIMEOUT;
+ self->timer = self->timeout;
}
if (self->len > 0) { // no ACK at the last byte
PREPARE_ACK; // Send ACK
@@ -105,7 +106,7 @@ void common_i2c_irq_handler(int i2c_id) {
} else if (self->len > 0) { // data to be sent
i2c->I2CM.DATA.bit.DATA = *(self->buf)++;
self->len--;
- self->timeout = I2C_TIMEOUT;
+ self->timer = self->timeout;
} else { // No data left, if there was any.
self->state = state_done;
i2c->I2CM.INTFLAG.reg |= SERCOM_I2CM_INTFLAG_MB;
@@ -120,12 +121,13 @@ void common_i2c_irq_handler(int i2c_id) {
static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
- mp_printf(print, "I2C(%u, freq=%u, scl=\"%q\", sda=\"%q\")",
- self->id, self->freq, pin_find_by_id(self->scl)->name, pin_find_by_id(self->sda)->name);
+ mp_printf(print, "I2C(%u, freq=%u, scl=\"%q\", sda=\"%q\", timeout=%u)",
+ self->id, self->freq, pin_find_by_id(self->scl)->name, pin_find_by_id(self->sda)->name,
+ self->timeout * 1000);
}
mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
- enum { ARG_id, ARG_freq, ARG_scl, ARG_sda };
+ enum { ARG_id, ARG_freq, ARG_scl, ARG_sda, ARG_timeout };
static const mp_arg_t allowed_args[] = {
#if MICROPY_HW_DEFAULT_I2C_ID < 0
{ MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} },
@@ -140,6 +142,7 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
{ MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
#endif
+ { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_TIMEOUT} },
};
// Parse args.
@@ -168,6 +171,8 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
}
MP_STATE_PORT(sercom_table[self->id]) = self;
self->freq = args[ARG_freq].u_int;
+ // The unit for ARG_timeout is us, but the code uses ms.
+ self->timeout = args[ARG_timeout].u_int / 1000;
// Configure the Pin mux.
mp_hal_set_pin_mux(self->scl, scl_pad_config.alt_fct);
@@ -224,13 +229,13 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si
machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in;
Sercom *i2c = self->instance;
- self->timeout = I2C_TIMEOUT;
+ self->timer = self->timeout;
self->len = len;
self->buf = buf;
// Wait a while if the bus is busy
- while (IS_BUS_BUSY && self->timeout) {
+ while (IS_BUS_BUSY && self->timer) {
MICROPY_EVENT_POLL_HOOK
- if (--self->timeout == 0) {
+ if (--self->timer == 0) {
return -MP_ETIMEDOUT;
}
}
@@ -242,9 +247,9 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si
i2c->I2CM.ADDR.bit.ADDR = (addr << 1) | READ_MODE;
// Transfer the data
- self->timeout = I2C_TIMEOUT;
- while (self->state == state_busy && self->timeout) {
- self->timeout--;
+ self->timer = self->timeout;
+ while (self->state == state_busy && self->timer) {
+ self->timer--;
MICROPY_EVENT_POLL_HOOK
}
i2c->I2CM.INTENCLR.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_ERROR;
@@ -256,7 +261,7 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si
} else if (self->state == state_buserr) {
SET_STOP_STATE;
return -MP_EIO;
- } else if (self->timeout == 0) {
+ } else if (self->timer == 0) {
SET_STOP_STATE;
return -MP_ETIMEDOUT;
}
diff --git a/ports/samd/machine_rtc.c b/ports/samd/machine_rtc.c
index 74c2266d60..4b03488b71 100644
--- a/ports/samd/machine_rtc.c
+++ b/ports/samd/machine_rtc.c
@@ -133,6 +133,7 @@ static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args,
}
#endif
+ mp_hal_time_ns_set_from_rtc();
return mp_const_none;
}
}
diff --git a/ports/samd/main.c b/ports/samd/main.c
index 2bbaf63e6e..a7da95582f 100644
--- a/ports/samd/main.c
+++ b/ports/samd/main.c
@@ -28,6 +28,7 @@
#include "py/runtime.h"
#include "py/gc.h"
#include "py/mperrno.h"
+#include "py/mphal.h"
#include "py/stackctrl.h"
#include "shared/readline/readline.h"
#include "shared/runtime/gchelper.h"
@@ -45,6 +46,7 @@ extern void sercom_deinit_all(void);
void samd_main(void) {
mp_stack_set_top(&_estack);
mp_stack_set_limit(&_estack - &_sstack - 1024);
+ mp_hal_time_ns_set_from_rtc();
for (;;) {
gc_init(&_sheap, &_eheap);
diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h
index 831949bab5..8cce90b886 100644
--- a/ports/samd/mcu/samd51/mpconfigmcu.h
+++ b/ports/samd/mcu/samd51/mpconfigmcu.h
@@ -23,7 +23,7 @@ unsigned long trng_random_u32(void);
#define MICROPY_FATFS_MAX_SS (4096)
#define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
-#define VFS_BLOCK_SIZE_BYTES (1536) //
+#define VFS_BLOCK_SIZE_BYTES (2048) //
#ifndef MICROPY_HW_UART_TXBUF
#define MICROPY_HW_UART_TXBUF (1)
diff --git a/ports/samd/modmachine.c b/ports/samd/modmachine.c
index 65dbf8a7f5..c20ce8d641 100644
--- a/ports/samd/modmachine.c
+++ b/ports/samd/modmachine.c
@@ -42,7 +42,7 @@
#define DBL_TAP_ADDR ((volatile uint32_t *)(HSRAM_ADDR + HSRAM_SIZE - 4))
#endif
// A board may define a DPL_TAP_ADDR_ALT, which will be set as well
-// Needed at the moment for Sparkfun SAMD51 Thing Plus
+// Needed at the moment for SparkFun SAMD51 Thing Plus
#define DBL_TAP_MAGIC_LOADER 0xf01669ef
#define DBL_TAP_MAGIC_RESET 0xf02669ef
@@ -65,7 +65,7 @@
extern bool EIC_occured;
extern uint32_t _dbl_tap_addr;
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
*DBL_TAP_ADDR = DBL_TAP_MAGIC_RESET;
#ifdef DBL_TAP_ADDR_ALT
*DBL_TAP_ADDR_ALT = DBL_TAP_MAGIC_RESET;
@@ -73,7 +73,7 @@ NORETURN static void mp_machine_reset(void) {
NVIC_SystemReset();
}
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
*DBL_TAP_ADDR = DBL_TAP_MAGIC_LOADER;
#ifdef DBL_TAP_ADDR_ALT
*DBL_TAP_ADDR_ALT = DBL_TAP_MAGIC_LOADER;
@@ -164,7 +164,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
set_cpu_freq(freq);
}
-NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) {
mp_machine_lightsleep(n_args, args);
mp_machine_reset();
}
diff --git a/ports/samd/modtime.c b/ports/samd/modtime.c
index 83072dd16f..0bed3cb83a 100644
--- a/ports/samd/modtime.c
+++ b/ports/samd/modtime.c
@@ -28,6 +28,8 @@
#include "shared/timeutils/timeutils.h"
#include "modmachine.h"
+static uint64_t time_us_64_offset_from_epoch;
+
// Return the localtime as an 8-tuple.
static mp_obj_t mp_time_localtime_get(void) {
timeutils_struct_time_t tm;
@@ -54,3 +56,15 @@ static mp_obj_t mp_time_time_get(void) {
return mp_obj_new_int_from_uint(timeutils_mktime(
tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec));
}
+
+void mp_hal_time_ns_set_from_rtc(void) {
+ timeutils_struct_time_t tm;
+ rtc_gettime(&tm);
+ uint64_t time_us = (uint64_t)timeutils_mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
+ tm.tm_min, tm.tm_sec) * 1000000ULL;
+ time_us_64_offset_from_epoch = time_us - mp_hal_ticks_us_64();
+}
+
+uint64_t mp_hal_time_ns(void) {
+ return (time_us_64_offset_from_epoch + mp_hal_ticks_us_64()) * 1000ULL;
+}
diff --git a/ports/samd/mphalport.h b/ports/samd/mphalport.h
index 6c9e525bb9..69e2b60640 100644
--- a/ports/samd/mphalport.h
+++ b/ports/samd/mphalport.h
@@ -47,6 +47,7 @@ extern int mp_interrupt_char;
extern ringbuf_t stdin_ringbuf;
extern volatile uint32_t systick_ms;
uint64_t mp_hal_ticks_us_64(void);
+void mp_hal_time_ns_set_from_rtc(void);
void mp_hal_set_interrupt_char(int c);
@@ -94,10 +95,6 @@ static inline mp_uint_t mp_hal_ticks_cpu(void) {
}
#endif
-static inline uint64_t mp_hal_time_ns(void) {
- return mp_hal_ticks_us_64() * 1000;
-}
-
// C-level pin HAL
#include "py/obj.h"
diff --git a/ports/samd/samd_qspiflash.c b/ports/samd/samd_qspiflash.c
index e25ddf21ef..f314a95519 100644
--- a/ports/samd/samd_qspiflash.c
+++ b/ports/samd/samd_qspiflash.c
@@ -105,7 +105,6 @@ static const external_flash_device possible_devices[] = {
#define EXTERNAL_FLASH_DEVICE_COUNT MP_ARRAY_SIZE(possible_devices)
static external_flash_device const *flash_device;
-static external_flash_device generic_config = GENERIC;
extern const mp_obj_type_t samd_qspiflash_type;
// The QSPIflash object is a singleton
@@ -248,15 +247,6 @@ static uint8_t get_baud(int32_t freq_mhz) {
return baud;
}
-int get_sfdp_table(uint8_t *table, int maxlen) {
- uint8_t header[16];
- read_memory_single(QSPI_CMD_READ_SFDP_PARAMETER, 0, header, sizeof(header));
- int len = MIN(header[11] * 4, maxlen);
- int addr = header[12] + (header[13] << 8) + (header[14] << 16);
- read_memory_single(QSPI_CMD_READ_SFDP_PARAMETER, addr, table, len);
- return len;
-}
-
static mp_obj_t samd_qspiflash_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);
@@ -297,19 +287,6 @@ static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args
uint8_t jedec_ids[3];
read_command(QSPI_CMD_READ_JEDEC_ID, jedec_ids, sizeof(jedec_ids));
- // Read the common sfdp table
- // Check the device addr length, support of 1-1-4 mode and get the sector size
- uint8_t sfdp_table[128];
- int len = get_sfdp_table(sfdp_table, sizeof(sfdp_table));
- if (len >= 29) {
- self->sectorsize = 1 << sfdp_table[28];
- bool addr4b = ((sfdp_table[2] >> 1) & 0x03) == 0x02;
- bool supports_qspi_114 = (sfdp_table[2] & 0x40) != 0;
- if (addr4b || !supports_qspi_114) {
- mp_raise_ValueError(MP_ERROR_TEXT("QSPI mode not supported"));
- }
- }
-
// Check, if the flash device is known and get it's properties.
flash_device = NULL;
for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) {
@@ -321,15 +298,8 @@ static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args
break;
}
}
-
- // If the flash device is not known, try generic config options
if (flash_device == NULL) {
- if (jedec_ids[0] == 0xc2) { // Macronix devices
- generic_config.quad_enable_bit_mask = 0x04;
- generic_config.single_status_byte = true;
- }
- generic_config.total_size = 1 << jedec_ids[2];
- flash_device = &generic_config;
+ mp_raise_ValueError(MP_ERROR_TEXT("QSPI device not supported"));
}
self->size = flash_device->total_size;
diff --git a/ports/samd/samd_spiflash.c b/ports/samd/samd_spiflash.c
index 8ada7e9609..63dc0a8304 100644
--- a/ports/samd/samd_spiflash.c
+++ b/ports/samd/samd_spiflash.c
@@ -67,6 +67,19 @@ typedef struct _spiflash_obj_t {
extern const mp_obj_type_t samd_spiflash_type;
+typedef struct _spiflash_jedec_id_t {
+ uint32_t jedec_id;
+ uint32_t mask;
+ uint32_t size;
+} spiflash_jedec_id_t;
+
+spiflash_jedec_id_t jedec_id_table[] = {
+ { 0x1f8401, 0xffff00, 512 * 1024 }, // Adesto/Renesas 4 MBit
+ { 0x1f2400, 0xffff00, 512 * 1024 }, // Adesto 4 MBit
+ { 0x1f4501, 0xffff00, 1024 * 1024 }, // Adesto/Renesas/Atmel 8 MBit
+ { 0xc84013, 0xffffff, 512 * 1024 }, // Gigadevices 4 MBit
+};
+
// The SPIflash object is a singleton
static spiflash_obj_t spiflash_obj = {
{ &samd_spiflash_type }, NULL, 0, false, PAGE_SIZE, SECTOR_SIZE, NULL, 0
@@ -124,6 +137,7 @@ static void write_enable(spiflash_obj_t *self) {
mp_hal_pin_write(self->cs, 1);
}
+#if !defined(MICROPY_HW_SPIFLASH_SIZE)
// Write status register 1
static void write_sr1(spiflash_obj_t *self, uint8_t value) {
uint8_t msg[2];
@@ -134,6 +148,7 @@ static void write_sr1(spiflash_obj_t *self, uint8_t value) {
spi_transfer(self->spi, 2, msg, NULL);
mp_hal_pin_write(self->cs, 1);
}
+#endif
static void get_sfdp(spiflash_obj_t *self, uint32_t addr, uint8_t *buffer, int size) {
uint8_t dummy[1];
@@ -170,32 +185,46 @@ static mp_obj_t spiflash_make_new(const mp_obj_type_t *type, size_t n_args, size
// Get the flash size from the device ID (default)
uint8_t id[3];
get_id(self, id);
- bool read_sfdp = true;
-
- if (id[1] == 0x84 && id[2] == 1) { // Adesto
- self->size = 512 * 1024;
- } else if (id[0] == 0x1f && id[1] == 0x45 && id[2] == 1) { // Adesto/Renesas 8 MBit
- self->size = 1024 * 1024;
- read_sfdp = false;
- self->sectorsize = 4096;
- self->addr_is_32bit = false;
- // Globally unlock the sectors, which are locked after power on.
- write_enable(self);
- write_sr1(self, 0);
- } else {
- self->size = 1 << id[2];
+
+ #if defined(MICROPY_HW_SPIFLASH_SIZE)
+ self->size = MICROPY_HW_SPIFLASH_SIZE;
+ #else
+ // Assume as default that the size is coded into the last JEDEC ID byte.
+ self->size = 1 << id[2];
+ // Look for specific flash devices with different encoding.
+ uint32_t jedec_id = (id[0] << 16) | (id[1] << 8) | id[2];
+ for (int i = 0; i < MP_ARRAY_SIZE(jedec_id_table); i++) {
+ if (jedec_id_table[i].jedec_id == (jedec_id & jedec_id_table[i].mask)) {
+ self->size = jedec_id_table[i].size;
+ // Globally unlock the sectors, which may be locked after power on.
+ write_enable(self);
+ write_sr1(self, 0);
+ break;
+ }
}
+ #endif
- // Get the addr_is_32bit flag and the sector size
- if (read_sfdp) {
- uint8_t buffer[128];
- get_sfdp(self, 0, buffer, 16); // get the header
+ // Get the flash size, addr_is_32bit flag and sector size from SFDP, if present.
+ uint8_t buffer[128];
+ get_sfdp(self, 0, buffer, 16); // get the header
+ if (*(uint32_t *)buffer == 0x50444653) { // Header signature "SFDP"
int len = MIN(buffer[11] * 4, sizeof(buffer));
if (len >= 29) {
int addr = buffer[12] + (buffer[13] << 8) + (buffer[14] << 16);
get_sfdp(self, addr, buffer, len); // Get the JEDEC mandatory table
self->sectorsize = 1 << buffer[28];
self->addr_is_32bit = ((buffer[2] >> 1) & 0x03) != 0;
+ #if !defined(MICROPY_HW_SPIFLASH_SIZE)
+ // Get the bit size from the SFDP data
+ uint32_t size = *(uint32_t *)(buffer + 4);
+ if (size & 0x8000000) {
+ // Byte size is 2 ** lower_31_bits / 8
+ self->size = 1 << ((size & 0x7fffffff) >> 3);
+ } else {
+ // Byte size is lower_31_bits / 8 + 1
+ self->size = ((size & 0x7fffffff) >> 3) + 1;
+ }
+ #endif
}
}
self->commands = self->addr_is_32bit ? _COMMANDS_32BIT : _COMMANDS_24BIT;
diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile
index 8ac9a8af03..37d70dcdbf 100644
--- a/ports/stm32/Makefile
+++ b/ports/stm32/Makefile
@@ -60,6 +60,7 @@ include $(TOP)/extmod/extmod.mk
GIT_SUBMODULES += lib/libhydrogen lib/stm32lib
+CROSS_COMPILE ?= arm-none-eabi-
LD_DIR=boards
USBDEV_DIR=usbdev
#USBHOST_DIR=usbhost
@@ -101,9 +102,6 @@ GEN_STMCONST_HDR = $(HEADER_BUILD)/modstm_const.h
GEN_STMCONST_MPZ = $(HEADER_BUILD)/modstm_mpz.h
CMSIS_MCU_HDR = $(STM32LIB_CMSIS_ABS)/Include/$(CMSIS_MCU_LOWER).h
-# Select the cross compile prefix
-CROSS_COMPILE ?= arm-none-eabi-
-
INC += -I.
INC += -I$(TOP)
INC += -I$(BUILD)
@@ -123,7 +121,15 @@ CFLAGS += -DSTM32_HAL_H='<stm32$(MCU_SERIES)xx_hal.h>'
CFLAGS += -DMBOOT_VTOR=$(MBOOT_TEXT0_ADDR)
CFLAGS += -DMICROPY_HW_VTOR=$(TEXT0_ADDR)
+ifeq ($(MCU_SERIES),n6)
+ifeq ($(USE_MBOOT),1)
+CFLAGS += -DMICROPY_HW_RUNS_FROM_EXT_FLASH=1
+endif
+# as doesn't recognise -mcpu=cortex-m55
+AFLAGS += -march=armv8.1-m.main
+else
AFLAGS += $(filter -mcpu=%,$(CFLAGS_MCU_$(MCU_SERIES)))
+endif
# Configure for nan-boxing object model if requested
ifeq ($(NANBOX),1)
@@ -300,6 +306,7 @@ SRC_C += \
adc.c \
sdio.c \
subghz.c \
+ xspi.c \
$(wildcard $(BOARD_DIR)/*.c)
SRC_O += \
@@ -316,6 +323,13 @@ CFLAGS += -DUSE_HAL_DRIVER
SRC_O += \
resethandler_m3.o \
shared/runtime/gchelper_thumb2.o
+else ifeq ($(MCU_SERIES),n6)
+SRC_O += shared/runtime/gchelper_thumb2.o
+ifeq ($(USE_MBOOT),1)
+SRC_O += resethandler_iram.o
+else
+SRC_O += resethandler.o
+endif
else
SRC_O += \
system_stm32.o \
@@ -329,8 +343,6 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal_adc_ex.c \
hal_cortex.c \
hal_dma.c \
- hal_flash.c \
- hal_flash_ex.c \
hal_gpio.c \
hal_i2c.c \
hal_pwr.c \
@@ -347,7 +359,14 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
ll_utils.c \
)
-ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7 l0 l1 l4 wb))
+ifneq ($(MCU_SERIES),n6)
+HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
+ hal_flash.c \
+ hal_flash_ex.c \
+ )
+endif
+
+ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7 l0 l1 l4 n6 wb))
HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal_pcd.c \
hal_pcd_ex.c \
@@ -355,7 +374,15 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
)
endif
-ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h5 h7 l4))
+ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),n6))
+HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
+ hal_bsec.c \
+ hal_rif.c \
+ hal_xspi.c \
+ )
+endif
+
+ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h5 h7 l4 n6))
HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal_sd.c \
ll_sdmmc.c \
@@ -380,7 +407,7 @@ $(BUILD)/$(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_hal_mmc.o: CFLAGS += -Wno
endif
endif
-ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7))
+ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7 n6))
HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal_dma_ex.c \
)
@@ -417,6 +444,12 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
)
endif
+ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),h7))
+HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
+ hal_uart_ex.c \
+ )
+endif
+
USBDEV_SRC_C += $(addprefix $(USBDEV_DIR)/,\
core/src/usbd_core.c \
core/src/usbd_ctlreq.c \
@@ -490,6 +523,12 @@ all: $(TOP)/lib/stm32lib/README.md all_main $(BUILD)/firmware.hex
ifeq ($(MBOOT_ENABLE_PACKING),1)
all_main: $(BUILD)/firmware.pack.dfu
+else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),n6))
+ifeq ($(USE_MBOOT),1)
+all_main: $(BUILD)/firmware.dfu
+else
+all_main: $(BUILD)/firmware-trusted.bin
+endif
else
all_main: $(BUILD)/firmware.dfu
endif
@@ -550,7 +589,7 @@ define GENERATE_HEX
$(Q)$(OBJCOPY) -O ihex $(2) $(1)
endef
-.PHONY: deploy deploy-stlink deploy-openocd
+.PHONY: deploy deploy-stlink deploy-openocd deploy-trusted
ifeq ($(MBOOT_ENABLE_PACKING),1)
deploy: $(BUILD)/firmware.pack.dfu
@@ -560,6 +599,9 @@ deploy: $(BUILD)/firmware.dfu
$(call RUN_DFU,$^)
endif
+deploy-trusted: $(BUILD)/firmware-trusted.bin
+ $(STM32_CUBE_PROGRAMMER)/bin/STM32_Programmer.sh -c port=SWD mode=HOTPLUG ap=1 -el $(DKEL) -w $^ 0x70000000 -hardRst
+
# A board should specify TEXT0_ADDR if to use a different location than the
# default for the firmware memory location. A board can also optionally define
# TEXT1_ADDR to split the firmware into two sections; see below for details.
@@ -614,6 +656,10 @@ $(BUILD)/firmware.hex: $(BUILD)/firmware.elf
$(BUILD)/firmware.elf: $(OBJ)
$(call GENERATE_ELF,$@,$^)
+$(BUILD)/firmware-trusted.bin: $(BUILD)/firmware.bin
+ /bin/rm -f $@
+ $(STM32_CUBE_PROGRAMMER)/bin/STM32_SigningTool_CLI -bin $^ -nk -of 0x80000000 -t fsbl -o $@ -hv $(STM32_N6_HEADER_VERSION)
+
# List of sources for qstr extraction
SRC_QSTR += $(SRC_C) $(SRC_CXX) $(SHARED_SRC_C) $(GEN_PINS_SRC)
diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c
index 3549fc29a9..f47e9eaad7 100644
--- a/ports/stm32/adc.c
+++ b/ports/stm32/adc.c
@@ -51,7 +51,7 @@
/// val = adc.read_core_vref() # read MCU VREF
/* ADC definitions */
-#if defined(STM32H5)
+#if defined(STM32H5) || defined(STM32N6)
// STM32H5 features two ADC instances, ADCx and pin_adc_table are set dynamically
#define PIN_ADC_MASK (PIN_ADC1 | PIN_ADC2)
#else
@@ -107,12 +107,12 @@
#define ADC_CAL2 ((uint16_t *)(ADC_CAL_ADDRESS + 4))
#define ADC_CAL_BITS (12)
-#elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5)
+#elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L1) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
#define ADC_SCALE_V (((float)VREFINT_CAL_VREF) / 1000.0f)
-#define ADC_CAL_ADDRESS VREFINT_CAL_ADDR
-#define ADC_CAL1 TEMPSENSOR_CAL1_ADDR
-#define ADC_CAL2 TEMPSENSOR_CAL2_ADDR
+#define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR)
+#define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR)
+#define ADC_CAL2 (TEMPSENSOR_CAL2_ADDR)
#define ADC_CAL_BITS (12) // UM2319/UM2570, __HAL_ADC_CALC_TEMPERATURE: 'corresponds to a resolution of 12 bits'
#elif defined(STM32H7)
@@ -123,22 +123,6 @@
#define ADC_CAL2 ((uint16_t *)(0x1FF1E840))
#define ADC_CAL_BITS (16)
-#elif defined(STM32L1)
-
-#define ADC_SCALE_V (VREFINT_CAL_VREF / 1000.0f)
-#define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR)
-#define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR)
-#define ADC_CAL2 (TEMPSENSOR_CAL2_ADDR)
-#define ADC_CAL_BITS (12)
-
-#elif defined(STM32L4) || defined(STM32WB)
-
-#define ADC_SCALE_V (VREFINT_CAL_VREF / 1000.0f)
-#define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR)
-#define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR)
-#define ADC_CAL2 (TEMPSENSOR_CAL2_ADDR)
-#define ADC_CAL_BITS (12)
-
#else
#error Unsupported processor
@@ -182,6 +166,9 @@
#define VBAT_DIV (3)
#elif defined(STM32L152xE)
// STM32L152xE does not have vbat.
+#elif defined(STM32N6)
+// ADC2 VINP 16
+#define VBAT_DIV (4)
#else
#error Unsupported processor
#endif
@@ -263,7 +250,7 @@ static bool is_adcx_channel(int channel) {
handle.Instance = ADCx;
return __HAL_ADC_IS_CHANNEL_INTERNAL(channel)
|| IS_ADC_CHANNEL(&handle, __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel));
- #elif defined(STM32H5)
+ #elif defined(STM32H5) || defined(STM32N6)
// The first argument to the IS_ADC_CHANNEL macro is unused.
return __HAL_ADC_IS_CHANNEL_INTERNAL(channel)
|| IS_ADC_CHANNEL(NULL, __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel));
@@ -276,7 +263,7 @@ static void adc_wait_for_eoc_or_timeout(ADC_HandleTypeDef *adcHandle, int32_t ti
uint32_t tickstart = HAL_GetTick();
#if defined(STM32F4) || defined(STM32F7) || defined(STM32L1)
while ((adcHandle->Instance->SR & ADC_FLAG_EOC) != ADC_FLAG_EOC) {
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
while (READ_BIT(adcHandle->Instance->ISR, ADC_FLAG_EOC) != ADC_FLAG_EOC) {
#else
#error Unsupported processor
@@ -295,7 +282,7 @@ static void adcx_clock_enable(ADC_HandleTypeDef *adch) {
__HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_CLKP);
#elif defined(STM32G0)
__HAL_RCC_ADC_CLK_ENABLE();
- #elif defined(STM32G4)
+ #elif defined(STM32G4) || defined(STM32N6)
__HAL_RCC_ADC12_CLK_ENABLE();
#elif defined(STM32H5)
__HAL_RCC_ADC_CLK_ENABLE();
@@ -368,6 +355,15 @@ static void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution) {
adch->Init.OversamplingMode = DISABLE;
adch->Init.DataAlign = ADC_DATAALIGN_RIGHT;
adch->Init.DMAContinuousRequests = DISABLE;
+ #elif defined(STM32N6)
+ adch->Init.GainCompensation = 0;
+ adch->Init.ScanConvMode = ADC_SCAN_DISABLE;
+ adch->Init.LowPowerAutoWait = DISABLE;
+ adch->Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL;
+ adch->Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
+ adch->Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
+ adch->Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
+ adch->Init.OversamplingMode = DISABLE;
#else
#error Unsupported processor
#endif
@@ -400,7 +396,7 @@ static void adc_init_single(pyb_obj_adc_t *adc_obj, ADC_TypeDef *adc) {
static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) {
ADC_ChannelConfTypeDef sConfig;
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
sConfig.Rank = ADC_REGULAR_RANK_1;
if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel) == 0) {
channel = __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel);
@@ -432,7 +428,7 @@ static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel)
if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) {
sConfig.SamplingTime = ADC_SAMPLETIME_384CYCLES;
} else {
- sConfig.SamplingTime = ADC_SAMPLETIME_384CYCLES;
+ sConfig.SamplingTime = ADC_SAMPLETIME_16CYCLES;
}
#elif defined(STM32G0)
if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) {
@@ -449,6 +445,18 @@ static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel)
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
+ #elif defined(STM32N6)
+ if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) {
+ sConfig.SamplingTime = ADC_SAMPLETIME_246CYCLES_5;
+ } else {
+ sConfig.SamplingTime = ADC_SAMPLETIME_11CYCLES_5;
+ }
+ sConfig.SingleDiff = ADC_SINGLE_ENDED;
+ sConfig.OffsetNumber = ADC_OFFSET_NONE;
+ sConfig.Offset = 0;
+ sConfig.OffsetSignedSaturation = DISABLE;
+ sConfig.OffsetSaturation = DISABLE;
+ sConfig.OffsetSign = ADC_OFFSET_SIGN_POSITIVE;
#else
#error Unsupported processor
#endif
@@ -464,10 +472,13 @@ static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel)
static uint32_t adc_read_channel(ADC_HandleTypeDef *adcHandle) {
uint32_t value;
- #if defined(STM32G4)
- // For STM32G4 there is errata 2.7.7, "Wrong ADC result if conversion done late after
- // calibration or previous conversion". According to the errata, this can be avoided
- // by performing two consecutive ADC conversions and keeping the second result.
+ #if defined(STM32G4) || defined(STM32WB)
+ // For STM32G4 errata 2.7.7 / STM32WB errata 2.7.1:
+ // "Wrong ADC result if conversion done late after calibration or previous conversion"
+ // states an incorrect reading is returned if more than 1ms has elapsed since the last
+ // reading or calibration. According to the errata, this can be avoided by performing
+ // two consecutive ADC conversions and keeping the second result.
+ // Note: On STM32WB55 @ 64Mhz each ADC read takes ~ 3us.
for (uint8_t i = 0; i < 2; i++)
#endif
{
@@ -523,7 +534,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
// 1st argument is the pin name
mp_obj_t pin_obj = args[0];
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
// STM32H5 has two ADC instances where some pins are only available on ADC1 or ADC2 (but not both).
// Assume we're using a channel of ADC1. Can be overridden for ADC2 later in this function.
ADC_TypeDef *adc = ADC1;
@@ -540,7 +551,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
// No ADC function on the given pin.
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Pin(%q) doesn't have ADC capabilities"), pin->name);
}
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
if ((pin->adc_num & PIN_ADC2) == PIN_ADC2) {
adc = ADC2;
pin_adc_table = pin_adc2;
@@ -555,7 +566,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
}
// If this channel corresponds to a pin then configure the pin in ADC mode.
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
if (channel < num_adc_pins) {
const machine_pin_obj_t *pin = pin_adc_table[channel];
if (pin != NULL) {
@@ -576,7 +587,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
o->base.type = &pyb_adc_type;
o->pin_name = pin_obj;
o->channel = channel;
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
adc_init_single(o, adc);
#else
adc_init_single(o, ADCx);
@@ -667,7 +678,7 @@ static mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_
// for subsequent samples we can just set the "start sample" bit
#if defined(STM32F4) || defined(STM32F7) || defined(STM32L1)
self->handle.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART;
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
SET_BIT(self->handle.Instance->CR, ADC_CR_ADSTART);
#else
#error Unsupported processor
@@ -777,7 +788,7 @@ static mp_obj_t adc_read_timed_multi(mp_obj_t adc_array_in, mp_obj_t buf_array_i
// ADC is started: set the "start sample" bit
#if defined(STM32F4) || defined(STM32F7) || defined(STM32L1)
adc->handle.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART;
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
SET_BIT(adc->handle.Instance->CR, ADC_CR_ADSTART);
#else
#error Unsupported processor
@@ -911,6 +922,8 @@ int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) {
} else {
return 0;
}
+ #elif defined(STM32N6)
+ int32_t raw_value = 0; // TODO
#else
int32_t raw_value = adc_config_and_read_ref(adcHandle, ADC_CHANNEL_TEMPSENSOR);
#endif
@@ -922,6 +935,10 @@ int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) {
static volatile float adc_refcor = 1.0f;
float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) {
+ #if defined(STM32N6)
+ return 0.0f; // TODO
+ #else
+
#if defined(STM32G4) || defined(STM32L1) || defined(STM32L4)
// Update the reference correction factor before reading tempsensor
// because TS_CAL1 and TS_CAL2 of STM32G4,L1/L4 are at VDDA=3.0V
@@ -936,7 +953,7 @@ float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) {
return 0;
}
float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 100.0f;
- #elif defined(STM32H5)
+ #elif defined(STM32H5) || defined(STM32WB)
int32_t raw_value = adc_config_and_read_ref(adcHandle, ADC_CHANNEL_TEMPSENSOR);
float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 100.0f;
#else
@@ -944,6 +961,8 @@ float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) {
float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 80.0f;
#endif
return (((float)raw_value * adc_refcor - *ADC_CAL1) / core_temp_avg_slope) + 30.0f;
+
+ #endif
}
float adc_read_core_vbat(ADC_HandleTypeDef *adcHandle) {
diff --git a/ports/stm32/adc.h b/ports/stm32/adc.h
index 0518cdcd9a..0adc9ac2be 100644
--- a/ports/stm32/adc.h
+++ b/ports/stm32/adc.h
@@ -48,7 +48,7 @@ static inline void adc_deselect_vbat(ADC_TypeDef *adc, uint32_t channel) {
adc_common = ADC_COMMON_REGISTER(0);
#elif defined(STM32F7)
adc_common = ADC123_COMMON;
- #elif defined(STM32G4) || defined(STM32H5)
+ #elif defined(STM32G4) || defined(STM32H5) || defined(STM32N6)
adc_common = ADC12_COMMON;
#elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ)
adc_common = ADC12_COMMON;
diff --git a/ports/stm32/boardctrl.c b/ports/stm32/boardctrl.c
index 8f0d066f37..ea95c8d2d5 100644
--- a/ports/stm32/boardctrl.c
+++ b/ports/stm32/boardctrl.c
@@ -35,7 +35,7 @@
#include "led.h"
#include "usrsw.h"
-NORETURN void boardctrl_fatal_error(const char *msg) {
+MP_NORETURN void boardctrl_fatal_error(const char *msg) {
for (volatile uint delay = 0; delay < 10000000; delay++) {
}
led_state(1, 1);
diff --git a/ports/stm32/boardctrl.h b/ports/stm32/boardctrl.h
index 8f4ce30eff..cb5380c298 100644
--- a/ports/stm32/boardctrl.h
+++ b/ports/stm32/boardctrl.h
@@ -114,7 +114,7 @@ typedef struct _boardctrl_state_t {
bool log_soft_reset;
} boardctrl_state_t;
-NORETURN void boardctrl_fatal_error(const char *msg);
+MP_NORETURN void boardctrl_fatal_error(const char *msg);
void boardctrl_maybe_enter_mboot(size_t n_args, const void *args);
void boardctrl_before_soft_reset_loop(boardctrl_state_t *state);
void boardctrl_top_soft_reset_loop(boardctrl_state_t *state);
@@ -122,5 +122,6 @@ int boardctrl_run_boot_py(boardctrl_state_t *state);
int boardctrl_run_main_py(boardctrl_state_t *state);
void boardctrl_start_soft_reset(boardctrl_state_t *state);
void boardctrl_end_soft_reset(boardctrl_state_t *state);
+void boardctrl_enter_standby(void);
#endif // MICROPY_INCLUDED_STM32_BOARDCTRL_H
diff --git a/ports/stm32/boards/ARDUINO_GIGA/board.json b/ports/stm32/boards/ARDUINO_GIGA/board.json
index 53c636c774..a99e747017 100644
--- a/ports/stm32/boards/ARDUINO_GIGA/board.json
+++ b/ports/stm32/boards/ARDUINO_GIGA/board.json
@@ -15,7 +15,7 @@
"ABX00063_01.front_1000x750.jpg"
],
"mcu": "stm32h7",
- "product": "Arduino Giga",
+ "product": "Giga",
"thumbnail": "",
"url": "https://store.arduino.cc/products/giga-r1-wifi",
"vendor": "Arduino"
diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h
index cef45d730c..44f6ce66bc 100644
--- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h
+++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h
@@ -31,6 +31,7 @@ typedef unsigned int mp_uint_t; // must be pointer size
#define MICROPY_HW_ENABLE_MMCARD (0)
#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0)
#define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1)
+#define MICROPY_GC_SPLIT_HEAP (1)
// ROMFS config
#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1)
diff --git a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld
index e7bb950dbe..dceb1a7489 100644
--- a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld
+++ b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld
@@ -12,6 +12,7 @@ MEMORY
SRAM2 (xrw) : ORIGIN = 0x30020000, LENGTH = 128K /* SRAM2 D2 */
SRAM3 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3 D2 */
SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */
+ SDRAM (xrw) : ORIGIN = 0x60000000, LENGTH = 8M /* SDRAM */
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */
FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */
FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */
@@ -53,3 +54,27 @@ _openamp_shm_region_start = ORIGIN(SRAM4);
_openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4);
INCLUDE common_blifs.ld
+
+SECTIONS
+{
+ /* GC blocks addresses and sizes */
+ .gc.blocks.table (READONLY) : {
+ . = ALIGN(4);
+ _gc_blocks_table_start = .;
+
+ LONG (ORIGIN(SRAM1));
+ LONG (128K);
+
+ LONG (ORIGIN(SDRAM) + 0M);
+ LONG (2M);
+
+ LONG (ORIGIN(SDRAM) + 2M);
+ LONG (2M);
+
+ LONG (ORIGIN(SDRAM) + 4M);
+ LONG (4M);
+
+ _gc_blocks_table_end = .;
+ . = ALIGN(4);
+ } > FLASH_TEXT
+}
diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json
index a4c81d69d4..129d9f2fc7 100644
--- a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json
+++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json
@@ -15,7 +15,7 @@
"ABX00051_01.iso_1000x750.jpg"
],
"mcu": "stm32h7",
- "product": "Arduino Nicla Vision",
+ "product": "Nicla Vision",
"thumbnail": "",
"url": "https://store.arduino.cc/products/nicla-vision",
"vendor": "Arduino"
diff --git a/ports/stm32/boards/ARDUINO_OPTA/board.json b/ports/stm32/boards/ARDUINO_OPTA/board.json
index 3955d7df31..dd6c39a191 100644
--- a/ports/stm32/boards/ARDUINO_OPTA/board.json
+++ b/ports/stm32/boards/ARDUINO_OPTA/board.json
@@ -14,7 +14,7 @@
"AFX00002_01.iso_1000x750.jpg"
],
"mcu": "stm32h7",
- "product": "Arduino Opta WiFi",
+ "product": "Opta WiFi",
"thumbnail": "",
"url": "https://store.arduino.cc/products/opta-wifi",
"vendor": "Arduino"
diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json
index f39d7d4c47..c97b9f165c 100644
--- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json
+++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json
@@ -16,7 +16,7 @@
"ABX00042_01.iso_1000x750.jpg"
],
"mcu": "stm32h7",
- "product": "Arduino Portenta H7",
+ "product": "Portenta H7",
"thumbnail": "",
"url": "https://store.arduino.cc/products/portenta-h7",
"vendor": "Arduino"
diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h
index 90a1469056..a9ecf38fbf 100644
--- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h
+++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h
@@ -30,7 +30,6 @@ typedef unsigned int mp_uint_t; // must be pointer size
#define MICROPY_HW_ENABLE_SDCARD (1)
#define MICROPY_HW_ENABLE_MMCARD (0)
#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0)
-#define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1)
// ROMFS config
#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1)
diff --git a/ports/stm32/boards/HYDRABUS/board.json b/ports/stm32/boards/HYDRABUS/board.json
index 20a2ac5eae..24df6ac99f 100644
--- a/ports/stm32/boards/HYDRABUS/board.json
+++ b/ports/stm32/boards/HYDRABUS/board.json
@@ -10,6 +10,6 @@
"mcu": "stm32f4",
"product": "HydraBus v1.0",
"thumbnail": "",
- "url": "",
+ "url": "https://github.com/hydrabus/hydrabus",
"vendor": "HydraBus"
}
diff --git a/ports/stm32/boards/LIMIFROG/board.json b/ports/stm32/boards/LIMIFROG/board.json
index f516963a5b..36c80f8f72 100644
--- a/ports/stm32/boards/LIMIFROG/board.json
+++ b/ports/stm32/boards/LIMIFROG/board.json
@@ -10,6 +10,6 @@
"mcu": "stm32l4",
"product": "LimiFrog",
"thumbnail": "",
- "url": "",
+ "url": "https://github.com/LimiFrog",
"vendor": "LimiFrog"
}
diff --git a/ports/stm32/boards/NUCLEO_N657X0/bdev.c b/ports/stm32/boards/NUCLEO_N657X0/bdev.c
new file mode 100644
index 0000000000..2180d46174
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/bdev.c
@@ -0,0 +1,42 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/obj.h"
+#include "storage.h"
+#include "xspi.h"
+
+#if MICROPY_HW_SPIFLASH_ENABLE_CACHE
+#error "Cannot enable MICROPY_HW_SPIFLASH_ENABLE_CACHE"
+#endif
+
+// External SPI flash uses hardware XSPI interface.
+const mp_spiflash_config_t spiflash_config = {
+ .bus_kind = MP_SPIFLASH_BUS_QSPI,
+ .bus.u_qspi.data = (void *)&xspi_flash2,
+ .bus.u_qspi.proto = &xspi_proto,
+};
+
+spi_bdev_t spi_bdev;
diff --git a/ports/stm32/boards/NUCLEO_N657X0/board.c b/ports/stm32/boards/NUCLEO_N657X0/board.c
new file mode 100644
index 0000000000..fe5f2f1cc8
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/board.c
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024-2025 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mphal.h"
+#include "boardctrl.h"
+#include "xspi.h"
+
+// Values for OTP fuses for VDDIO3, to select low voltage mode (<2.5V).
+// See RM0486, Section 5, Table 18.
+#define BSEC_HW_CONFIG_ID (124U)
+#define BSEC_HWS_HSLV_VDDIO3 (1U << 15)
+
+static void board_config_vdd(void) {
+ // TODO: move some of the below code to a common location for all N6 boards?
+
+ // Enable PWR, BSEC and SYSCFG clocks.
+ LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG);
+
+ // Program high speed IO optimization fuses if they aren't already set.
+ uint32_t fuse;
+ BSEC_HandleTypeDef hbsec = { .Instance = BSEC };
+ const uint32_t mask = BSEC_HWS_HSLV_VDDIO3;
+ if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) {
+ fuse = 0;
+ } else if ((fuse & mask) != mask) {
+ // Program the fuse, and read back the set value.
+ if (HAL_BSEC_OTP_Program(&hbsec, BSEC_HW_CONFIG_ID, fuse | mask, HAL_BSEC_NORMAL_PROG) != HAL_OK) {
+ fuse = 0;
+ } else if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) {
+ fuse = 0;
+ }
+ }
+
+ // Enable Vdd ADC, needed for the ADC to work.
+ LL_PWR_EnableVddADC();
+
+ // Configure VDDIO2.
+ LL_PWR_EnableVddIO2();
+ LL_PWR_SetVddIO2VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO3. Only enable 1.8V mode if the fuse is set.
+ LL_PWR_EnableVddIO3();
+ if (fuse & BSEC_HWS_HSLV_VDDIO3) {
+ LL_PWR_SetVddIO3VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8);
+ }
+ SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO4.
+ LL_PWR_EnableVddIO4();
+ LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation
+
+ // Enable VDD for ADC and USB.
+ LL_PWR_EnableVddADC();
+ LL_PWR_EnableVddUSB();
+}
+
+void mboot_board_early_init(void) {
+ board_config_vdd();
+ xspi_init();
+}
+
+void board_early_init(void) {
+ #if !MICROPY_HW_RUNS_FROM_EXT_FLASH
+ // Firmware runs directly from SRAM, so configure VDD and enable XSPI flash.
+ board_config_vdd();
+ xspi_init();
+ #endif
+}
+
+void board_leave_standby(void) {
+ // TODO: move some of the below code to a common location for all N6 boards?
+
+ // Enable PWR, BSEC and SYSCFG clocks.
+ LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG);
+
+ // Configure VDDIO2.
+ LL_PWR_EnableVddIO2();
+ LL_PWR_SetVddIO2VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO3 (1.8V mode selection is retained).
+ LL_PWR_EnableVddIO3();
+ SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO4.
+ LL_PWR_EnableVddIO4();
+ LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation
+
+ // Enable VDD for ADC and USB.
+ LL_PWR_EnableVddADC();
+ LL_PWR_EnableVddUSB();
+}
diff --git a/ports/stm32/boards/NUCLEO_N657X0/board.md b/ports/stm32/boards/NUCLEO_N657X0/board.md
new file mode 100644
index 0000000000..3360c5db6c
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/board.md
@@ -0,0 +1,17 @@
+The mboot bootloader must first be built and deployed to this board. Make sure that
+CN9 is in position 1-2 to select STLK as the 5V power source, that JP1 is in position
+1-2 (lower position) and JP2 is in position 2-3 (upper position). Then plug in a USB
+cable into the ST-LINK port CN10. This will allow mboot firmware to be programmed to
+the external SPI flash via ST's tools, eg:
+
+ make -C ports/stm32/mboot BOARD=NUCLEO_N657X0 deploy-trusted
+
+Once mboot is installed, change CN9 to position 3-4 to select USB as the 5V power
+source, change JP2 back to position 1-2 (lower position) and change the USB cable to
+CN8. mboot will present a USB DFU device on this USB port, and the red LED2 should be
+blinking at 1Hz to indicate that mboot is active. If it's not active then hold the
+USER button and press NRST, and wait until all three LEDs are on, then release USER.
+Now mboot will be active.
+
+Once the USB DFU port can be seen, the firmware below can be programmed as usual with
+any DFU loader.
diff --git a/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h
new file mode 100644
index 0000000000..ccc3fa051f
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h
@@ -0,0 +1,103 @@
+#define MICROPY_HW_BOARD_NAME "NUCLEO-N657X0"
+#define MICROPY_HW_MCU_NAME "STM32N657X0"
+
+#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t
+#define MICROPY_ALLOC_GC_STACK_SIZE (128)
+#define MICROPY_FATFS_EXFAT (1)
+
+#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)
+#define MICROPY_HW_HAS_SWITCH (1)
+#define MICROPY_HW_HAS_FLASH (1)
+#define MICROPY_HW_ENABLE_RNG (1)
+#define MICROPY_HW_ENABLE_RTC (1)
+#define MICROPY_HW_ENABLE_DAC (0)
+#define MICROPY_HW_ENABLE_USB (1)
+#define MICROPY_PY_PYB_LEGACY (0)
+
+#define MICROPY_BOARD_EARLY_INIT board_early_init
+#define MICROPY_BOARD_LEAVE_STANDBY board_leave_standby()
+
+// HSE is 48MHz, this gives a CPU frequency of 800MHz.
+#define MICROPY_HW_CLK_PLLM (6)
+#define MICROPY_HW_CLK_PLLN (100)
+#define MICROPY_HW_CLK_PLLP1 (1)
+#define MICROPY_HW_CLK_PLLP2 (1)
+#define MICROPY_HW_CLK_PLLFRAC (0)
+
+// The LSE is a 32kHz crystal.
+#define MICROPY_HW_RTC_USE_LSE (1)
+#define MICROPY_HW_RTC_USE_US (1)
+
+// External SPI flash, MX25UM51245GXDI00.
+#define MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2 (29)
+
+// SPI flash, block device config.
+#define MICROPY_HW_BDEV_SPIFLASH (&spi_bdev)
+#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev)
+#define MICROPY_HW_BDEV_SPIFLASH_CONFIG (&spiflash_config)
+#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES (4 * 1024 * 1024)
+#define MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES (60 * 1024 * 1024)
+
+// UART buses
+#define MICROPY_HW_UART1_TX (pyb_pin_UART1_TX)
+#define MICROPY_HW_UART1_RX (pyb_pin_UART1_RX)
+#define MICROPY_HW_UART3_TX (pyb_pin_UART3_TX)
+#define MICROPY_HW_UART3_RX (pyb_pin_UART3_RX)
+#define MICROPY_HW_UART_REPL (PYB_UART_1)
+#define MICROPY_HW_UART_REPL_BAUD (115200)
+
+// I2C buses
+#define MICROPY_HW_I2C1_SCL (pyb_pin_I2C1_SCL)
+#define MICROPY_HW_I2C1_SDA (pyb_pin_I2C1_SDA)
+
+// SPI buses
+#define MICROPY_HW_SPI5_NSS (pyb_pin_SPI5_CS)
+#define MICROPY_HW_SPI5_SCK (pyb_pin_SPI5_SCK)
+#define MICROPY_HW_SPI5_MISO (pyb_pin_SPI5_MISO)
+#define MICROPY_HW_SPI5_MOSI (pyb_pin_SPI5_MOSI)
+
+// USER2 is floating, and pressing the button makes the input go high.
+#define MICROPY_HW_USRSW_PIN (pyb_pin_BUTTON)
+#define MICROPY_HW_USRSW_PULL (GPIO_PULLDOWN)
+#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_RISING)
+#define MICROPY_HW_USRSW_PRESSED (1)
+
+// LEDs
+#define MICROPY_HW_LED1 (pyb_pin_LED_RED)
+#define MICROPY_HW_LED2 (pyb_pin_LED_GREEN)
+#define MICROPY_HW_LED3 (pyb_pin_LED_BLUE)
+#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
+#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin))
+
+// USB config
+#define MICROPY_HW_USB_HS (1)
+#define MICROPY_HW_USB_HS_IN_FS (1)
+#define MICROPY_HW_USB_MAIN_DEV (USB_PHY_HS_ID)
+
+/******************************************************************************/
+// Bootloader configuration
+
+#define MBOOT_BOARD_EARLY_INIT(initial_r0) mboot_board_early_init()
+
+#define MBOOT_SPIFLASH_CS (pyb_pin_XSPIM_P2_CS)
+#define MBOOT_SPIFLASH_SCK (pyb_pin_XSPIM_P2_SCK)
+#define MBOOT_SPIFLASH_MOSI (pyb_pin_XSPIM_P2_IO0)
+#define MBOOT_SPIFLASH_MISO (pyb_pin_XSPIM_P2_IO1)
+#define MBOOT_SPIFLASH_ADDR (0x70000000)
+#define MBOOT_SPIFLASH_BYTE_SIZE (64 * 1024 * 1024)
+#define MBOOT_SPIFLASH_LAYOUT "/0x70000000/16384*4Kg"
+#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (1)
+#define MBOOT_SPIFLASH_SPIFLASH (&spi_bdev.spiflash)
+#define MBOOT_SPIFLASH_CONFIG (&spiflash_config)
+
+/******************************************************************************/
+// Function and variable declarations
+
+extern const struct _mp_spiflash_config_t spiflash_config;
+extern struct _spi_bdev_t spi_bdev;
+
+void mboot_board_early_init(void);
+void mboot_board_entry_init(void);
+
+void board_early_init(void);
+void board_leave_standby(void);
diff --git a/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk
new file mode 100644
index 0000000000..fa64cb1706
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk
@@ -0,0 +1,26 @@
+# Without mboot, the main firmware must fit in 512k flash, will be copied to SRAM by
+# the hardware bootloader, and will run from SRAM. With mboot, the main firmware can
+# be much larger and will run from flash via XSPI in memory-mapped mode.
+USE_MBOOT ?= 1
+
+MCU_SERIES = n6
+CMSIS_MCU = STM32N657xx
+AF_FILE = boards/stm32n657_af.csv
+ifeq ($(BUILDING_MBOOT),1)
+SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_fsbl.o
+else
+SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_s.o
+endif
+STM32_N6_HEADER_VERSION = 2.1
+DKEL = $(STM32_CUBE_PROGRAMMER)/bin/ExternalLoader/MX25UM51245G_STM32N6570-NUCLEO.stldr
+
+ifeq ($(USE_MBOOT),1)
+LD_FILES = boards/stm32n657x0.ld boards/common_n6_flash.ld
+TEXT0_ADDR = 0x70080000
+else
+LD_FILES = boards/stm32n657x0.ld boards/common_basic.ld
+TEXT0_ADDR = 0x34180400
+endif
+
+# MicroPython settings
+MICROPY_FLOAT_IMPL = double
diff --git a/ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h b/ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h
new file mode 100644
index 0000000000..ac38dac748
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h
@@ -0,0 +1,5 @@
+// This board does not use any security settings, so can just stay in secure
+// mode without configuring the SAU.
+
+static inline void TZ_SAU_Setup(void) {
+}
diff --git a/ports/stm32/boards/NUCLEO_N657X0/pins.csv b/ports/stm32/boards/NUCLEO_N657X0/pins.csv
new file mode 100644
index 0000000000..033f0a552e
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/pins.csv
@@ -0,0 +1,62 @@
+D0,PD9
+D1,PD8
+D2,PD0
+D3,PE9
+D4,PE0
+D5,PE10
+D6,PD5
+D7,PE11
+D8,PD12
+D9,PD7
+D10,PA3
+D11,PG2
+D12,PG1
+D13,PE15
+D14,PC1
+D15,PH9
+
+# Ax header pins are connected directly to the following digital IO
+A0D,PF5
+A1D,PC10
+A2D,PF6
+A3D,PA2
+A4D,PC12
+A5D,PH2
+
+# Ax header pins are connected to the following analog IO via an op-amp in voltage-follower mode running at 1.8V
+A0,PA8
+A1,PA9
+A2,PA10
+A3,PA12
+A4,PF3
+A5,PG15
+
+-UART1_TX,PE5
+-UART1_RX,PE6
+-UART3_TX,PD8
+-UART3_RX,PD9
+
+-I2C1_SCL,PH9
+-I2C1_SDA,PC1
+
+-SPI5_CS,PA3
+-SPI5_SCK,PE15
+-SPI5_MISO,PG1
+-SPI5_MOSI,PG2
+
+-BUTTON,PC13
+LED_BLUE,PG8
+LED_RED,PG10
+LED_GREEN,PG0
+
+-XSPIM_P2_DQS,PN0
+-XSPIM_P2_CS,PN1
+-XSPIM_P2_IO0,PN2
+-XSPIM_P2_IO1,PN3
+-XSPIM_P2_IO2,PN4
+-XSPIM_P2_IO3,PN5
+-XSPIM_P2_SCK,PN6
+-XSPIM_P2_IO4,PN8
+-XSPIM_P2_IO5,PN9
+-XSPIM_P2_IO6,PN10
+-XSPIM_P2_IO7,PN11
diff --git a/ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h b/ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h
new file mode 100644
index 0000000000..4012d56e5a
--- /dev/null
+++ b/ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h
@@ -0,0 +1,18 @@
+/* This file is part of the MicroPython project, http://micropython.org/
+ * The MIT License (MIT)
+ * Copyright (c) 2019 Damien P. George
+ */
+#ifndef MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
+#define MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
+
+// Oscillator values in Hz
+#define HSE_VALUE (48000000)
+#define LSE_VALUE (32768)
+
+// Oscillator timeouts in ms
+#define HSE_STARTUP_TIMEOUT (100)
+#define LSE_STARTUP_TIMEOUT (5000)
+
+#include "boards/stm32n6xx_hal_conf_base.h"
+
+#endif // MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
diff --git a/ports/stm32/boards/OLIMEX_E407/board.json b/ports/stm32/boards/OLIMEX_E407/board.json
index c14755c564..408df5a320 100644
--- a/ports/stm32/boards/OLIMEX_E407/board.json
+++ b/ports/stm32/boards/OLIMEX_E407/board.json
@@ -8,8 +8,8 @@
"olimex_e407.jpg"
],
"mcu": "stm32f4",
- "product": "E407",
+ "product": "STM32-E407",
"thumbnail": "",
- "url": "",
- "vendor": "OLIMEX"
+ "url": "https://www.olimex.com/Products/ARM/ST/STM32-E407",
+ "vendor": "Olimex"
}
diff --git a/ports/stm32/boards/OLIMEX_H407/board.json b/ports/stm32/boards/OLIMEX_H407/board.json
index 9ecc860ddc..27d2940513 100644
--- a/ports/stm32/boards/OLIMEX_H407/board.json
+++ b/ports/stm32/boards/OLIMEX_H407/board.json
@@ -8,8 +8,8 @@
"olimex_h407.jpg"
],
"mcu": "stm32f4",
- "product": "H407",
+ "product": "STM32-H407",
"thumbnail": "",
- "url": "",
- "vendor": "OLIMEX"
+ "url": "https://www.olimex.com/Products/ARM/ST/STM32-H407",
+ "vendor": "Olimex"
}
diff --git a/ports/stm32/boards/OPENMV_N6/bdev.c b/ports/stm32/boards/OPENMV_N6/bdev.c
new file mode 100644
index 0000000000..c6c918bd67
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/bdev.c
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "storage.h"
+#include "xspi.h"
+
+#if MICROPY_HW_SPIFLASH_ENABLE_CACHE
+#error "Cannot enable MICROPY_HW_SPIFLASH_ENABLE_CACHE"
+#endif
+
+// External SPI flash uses hardware XSPI interface.
+const mp_spiflash_config_t spiflash_config = {
+ .bus_kind = MP_SPIFLASH_BUS_QSPI,
+ .bus.u_qspi.data = (void *)&xspi_flash2,
+ .bus.u_qspi.proto = &xspi_proto,
+};
+
+spi_bdev_t spi_bdev;
diff --git a/ports/stm32/boards/OPENMV_N6/board.c b/ports/stm32/boards/OPENMV_N6/board.c
new file mode 100644
index 0000000000..1f82d10bac
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/board.c
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024-2025 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/mphal.h"
+#include "boardctrl.h"
+#include "xspi.h"
+
+// Values for OTP fuses for VDDIO2/3, to select low voltage mode (<2.5V).
+// See RM0486, Section 5, Table 18.
+#define BSEC_HW_CONFIG_ID (124U)
+#define BSEC_HWS_HSLV_VDDIO3 (1U << 15)
+#define BSEC_HWS_HSLV_VDDIO2 (1U << 16)
+
+#define OMV_BOOT_MAGIC_ADDR (0x3401FFFCU)
+#define OMV_BOOT_MAGIC_VALUE (0xB00710ADU)
+
+void mboot_board_early_init(void) {
+ // TODO: move some of the below code to a common location for all N6 boards?
+
+ // Enable PWR, BSEC and SYSCFG clocks.
+ LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG);
+
+ // Program high speed IO optimization fuses if they aren't already set.
+ uint32_t fuse;
+ BSEC_HandleTypeDef hbsec = { .Instance = BSEC };
+ const uint32_t mask = BSEC_HWS_HSLV_VDDIO2 | BSEC_HWS_HSLV_VDDIO3;
+ if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) {
+ fuse = 0;
+ } else if ((fuse & mask) != mask) {
+ // Program the fuse, and read back the set value.
+ if (HAL_BSEC_OTP_Program(&hbsec, BSEC_HW_CONFIG_ID, fuse | mask, HAL_BSEC_NORMAL_PROG) != HAL_OK) {
+ fuse = 0;
+ } else if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) {
+ fuse = 0;
+ }
+ }
+
+ // Enable Vdd ADC, needed for the ADC to work.
+ LL_PWR_EnableVddADC();
+
+ // Configure VDDIO2. Only enable 1.8V mode if the fuse is set.
+ LL_PWR_EnableVddIO2();
+ if (fuse & BSEC_HWS_HSLV_VDDIO2) {
+ LL_PWR_SetVddIO2VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8);
+ }
+ SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO3. Only enable 1.8V mode if the fuse is set.
+ LL_PWR_EnableVddIO3();
+ if (fuse & BSEC_HWS_HSLV_VDDIO3) {
+ LL_PWR_SetVddIO3VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8);
+ }
+ SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO4.
+ LL_PWR_EnableVddIO4();
+ LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation
+
+ // Enable VDD for ADC and USB.
+ LL_PWR_EnableVddADC();
+ LL_PWR_EnableVddUSB();
+
+ // Enable XSPI in memory-mapped mode.
+ xspi_init();
+}
+
+void board_enter_bootloader(unsigned int n_args, const void *args) {
+ // Support both OpenMV bootloader and mboot.
+ *((uint32_t *)OMV_BOOT_MAGIC_ADDR) = OMV_BOOT_MAGIC_VALUE;
+ SCB_CleanDCache();
+ boardctrl_maybe_enter_mboot(n_args, args);
+}
+
+void board_early_init(void) {
+ // TODO: if (HAL_PWREx_ConfigSupply(PWR_EXTERNAL_SOURCE_SUPPLY ) != HAL_OK)
+
+ LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN3 | LL_PWR_WAKEUP_PIN2);
+ LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN3 | LL_PWR_WAKEUP_PIN2);
+}
+
+void board_leave_standby(void) {
+ // TODO: move some of the below code to a common location for all N6 boards?
+
+ // Enable PWR, BSEC and SYSCFG clocks.
+ LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC);
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG);
+
+ // Configure VDDIO2 (1.8V mode selection is retained).
+ LL_PWR_EnableVddIO2();
+ SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO3 (1.8V mode selection is retained).
+ LL_PWR_EnableVddIO3();
+ SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation
+
+ // Configure VDDIO4.
+ LL_PWR_EnableVddIO4();
+ LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3);
+ SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation
+
+ // Enable VDD for ADC and USB.
+ LL_PWR_EnableVddADC();
+ LL_PWR_EnableVddUSB();
+}
diff --git a/ports/stm32/boards/OPENMV_N6/board.ld b/ports/stm32/boards/OPENMV_N6/board.ld
new file mode 100644
index 0000000000..e9ded785f2
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/board.ld
@@ -0,0 +1,39 @@
+/*
+ Linker script for OPENMV_N6.
+
+ Note: upper 512k of SRAM2 is copied from external flash upon reset.
+*/
+
+/* Specify the memory areas */
+MEMORY
+{
+ FLEXRAM_S (xrw) : ORIGIN = 0x34000000, LENGTH = 80K
+ SRAM2_S_RAM (xrw) : ORIGIN = 0x34100000, LENGTH = 1024K
+ SRAM2_S_FSBL (xrw) : ORIGIN = 0x34180400, LENGTH = 511K /* mboot firmware, not needed after mboot exits */
+ EXT_FLASH (rx) : ORIGIN = 0x70080000, LENGTH = 3584K
+ EXT_FLASH_FS (rx) : ORIGIN = 0x70400000, LENGTH = 4M
+ EXT_FLASH_ROMFS (rx) : ORIGIN = 0x70800000, LENGTH = 24M
+}
+
+REGION_ALIAS("IRAM", FLEXRAM_S);
+REGION_ALIAS("RAM", SRAM2_S_RAM);
+REGION_ALIAS("FLASH_APP", EXT_FLASH);
+
+/* produce a link error if there is not this amount of RAM for these sections */
+_minimum_stack_size = 2K;
+_minimum_heap_size = 16K;
+
+/* Define the stack. The stack is full descending so begins just above last byte
+ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
+_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
+_sstack = _estack - 16K; /* tunable */
+
+/* RAM extents for the garbage collector */
+_ram_start = ORIGIN(RAM);
+_ram_end = ORIGIN(RAM) + LENGTH(RAM);
+_heap_start = _ebss; /* heap starts just after statically allocated memory */
+_heap_end = _sstack;
+
+/* ROMFS location */
+_micropy_hw_romfs_part0_start = ORIGIN(EXT_FLASH_ROMFS);
+_micropy_hw_romfs_part0_size = LENGTH(EXT_FLASH_ROMFS);
diff --git a/ports/stm32/boards/OPENMV_N6/manifest.py b/ports/stm32/boards/OPENMV_N6/manifest.py
new file mode 100644
index 0000000000..62990220f3
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/manifest.py
@@ -0,0 +1,3 @@
+include("$(PORT_DIR)/boards/manifest.py")
+require("bundle-networking")
+require("aioble")
diff --git a/ports/stm32/boards/OPENMV_N6/mpconfigboard.h b/ports/stm32/boards/OPENMV_N6/mpconfigboard.h
new file mode 100644
index 0000000000..ed7bb548a1
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/mpconfigboard.h
@@ -0,0 +1,167 @@
+#define MICROPY_HW_BOARD_NAME "OpenMV N6"
+#define MICROPY_HW_MCU_NAME "STM32N657X0"
+
+#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t
+#define MICROPY_ALLOC_GC_STACK_SIZE (128)
+#define MICROPY_FATFS_EXFAT (1)
+
+#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0)
+#define MICROPY_HW_HAS_SWITCH (0)
+#define MICROPY_HW_HAS_FLASH (1)
+#define MICROPY_HW_SDCARD_MOUNT_AT_BOOT (0)
+#define MICROPY_HW_ENABLE_RNG (1)
+#define MICROPY_HW_ENABLE_RTC (1)
+#define MICROPY_HW_ENABLE_DAC (0)
+#define MICROPY_HW_ENABLE_USB (1)
+#define MICROPY_HW_ENABLE_SDCARD (1)
+#define MICROPY_PY_PYB_LEGACY (0)
+
+#define MICROPY_BOARD_ENTER_BOOTLOADER board_enter_bootloader
+#define MICROPY_BOARD_EARLY_INIT board_early_init
+#define MICROPY_BOARD_LEAVE_STANDBY board_leave_standby()
+
+// HSE is 48MHz, this gives a CPU frequency of 800MHz.
+#define MICROPY_HW_CLK_PLLM (6)
+#define MICROPY_HW_CLK_PLLN (100)
+#define MICROPY_HW_CLK_PLLP1 (1)
+#define MICROPY_HW_CLK_PLLP2 (1)
+#define MICROPY_HW_CLK_PLLFRAC (0)
+
+// The LSE is a 32kHz crystal.
+#define MICROPY_HW_RTC_USE_LSE (1)
+#define MICROPY_HW_RTC_USE_US (1)
+
+// External SPI flash.
+#define MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2 (28) // 256Mbit
+
+// ROMFS config
+#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI (1)
+#define MICROPY_HW_ROMFS_XSPI_SPIBDEV_OBJ (&spi_bdev)
+#define MICROPY_HW_ROMFS_ENABLE_PART0 (1)
+
+// SPI flash, block device config.
+#define MICROPY_HW_BDEV_SPIFLASH (&spi_bdev)
+#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev)
+#define MICROPY_HW_BDEV_SPIFLASH_CONFIG (&spiflash_config)
+#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES (4 * 1024 * 1024)
+#define MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES (4 * 1024 * 1024)
+
+// UART buses
+#define MICROPY_HW_UART2_TX (pyb_pin_BT_TXD)
+#define MICROPY_HW_UART2_RX (pyb_pin_BT_RXD)
+#define MICROPY_HW_UART2_RTS (pyb_pin_BT_RTS)
+#define MICROPY_HW_UART2_CTS (pyb_pin_BT_CTS)
+#define MICROPY_HW_UART3_TX (pyb_pin_UART3_TX)
+#define MICROPY_HW_UART3_RX (pyb_pin_UART3_RX)
+#define MICROPY_HW_UART4_TX (pyb_pin_UART4_TX)
+#define MICROPY_HW_UART4_RX (pyb_pin_UART4_RX)
+#define MICROPY_HW_UART7_TX (pyb_pin_UART7_TX)
+#define MICROPY_HW_UART7_RX (pyb_pin_UART7_RX)
+
+// I2C buses
+#define MICROPY_HW_I2C2_SCL (pyb_pin_I2C2_SCL)
+#define MICROPY_HW_I2C2_SDA (pyb_pin_I2C2_SDA)
+#define MICROPY_HW_I2C4_SCL (pyb_pin_I2C4_SCL)
+#define MICROPY_HW_I2C4_SDA (pyb_pin_I2C4_SDA)
+
+// SPI buses
+#define MICROPY_HW_SPI2_NSS (pyb_pin_SPI2_CS)
+#define MICROPY_HW_SPI2_SCK (pyb_pin_SPI2_SCK)
+#define MICROPY_HW_SPI2_MISO (pyb_pin_SPI2_MISO)
+#define MICROPY_HW_SPI2_MOSI (pyb_pin_SPI2_MOSI)
+#define MICROPY_HW_SPI4_NSS (pyb_pin_SPI4_CS)
+#define MICROPY_HW_SPI4_SCK (pyb_pin_SPI4_SCK)
+#define MICROPY_HW_SPI4_MISO (pyb_pin_SPI4_MISO)
+#define MICROPY_HW_SPI4_MOSI (pyb_pin_SPI4_MOSI)
+
+// USER is pulled high, and pressing the button makes the input go low.
+#define MICROPY_HW_USRSW_PIN (pyb_pin_BUTTON)
+#define MICROPY_HW_USRSW_PULL (GPIO_NOPULL)
+#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_FALLING)
+#define MICROPY_HW_USRSW_PRESSED (0)
+
+// LEDs
+#define MICROPY_HW_LED1 (pyb_pin_LED_RED)
+#define MICROPY_HW_LED2 (pyb_pin_LED_GREEN)
+#define MICROPY_HW_LED3 (pyb_pin_LED_BLUE)
+#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin))
+#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin))
+
+// SD Card SDMMC
+// SD_VSELECT: low(default)=3.3V IO, high=1.8V IO
+// SD_RESET: drive low to turn off SD VCC (pulled high by default)
+// SD_DETECT: pulled high in hardware, goes low when SD inserted
+#define MICROPY_HW_SDCARD_SDMMC (1)
+#define MICROPY_HW_SDCARD_CK (pyb_pin_SD_SDIO_CK)
+#define MICROPY_HW_SDCARD_CMD (pyb_pin_SD_SDIO_CMD)
+#define MICROPY_HW_SDCARD_D0 (pyb_pin_SD_SDIO_D0)
+#define MICROPY_HW_SDCARD_D1 (pyb_pin_SD_SDIO_D1)
+#define MICROPY_HW_SDCARD_D2 (pyb_pin_SD_SDIO_D2)
+#define MICROPY_HW_SDCARD_D3 (pyb_pin_SD_SDIO_D3)
+#define MICROPY_HW_SDCARD_DETECT_PIN (pyb_pin_SD_DETECT)
+#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_NOPULL)
+#define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_RESET)
+
+// WiFi SDMMC
+#define MICROPY_HW_SDIO_SDMMC (2)
+#define MICROPY_HW_SDIO_CK (pyb_pin_WL_SDIO_CK)
+#define MICROPY_HW_SDIO_CMD (pyb_pin_WL_SDIO_CMD)
+#define MICROPY_HW_SDIO_D0 (pyb_pin_WL_SDIO_D0)
+#define MICROPY_HW_SDIO_D1 (pyb_pin_WL_SDIO_D1)
+#define MICROPY_HW_SDIO_D2 (pyb_pin_WL_SDIO_D2)
+#define MICROPY_HW_SDIO_D3 (pyb_pin_WL_SDIO_D3)
+
+// USB config
+#define MICROPY_HW_USB_HS (1)
+#define MICROPY_HW_USB_HS_IN_FS (1)
+#define MICROPY_HW_USB_MAIN_DEV (USB_PHY_HS_ID)
+#define MICROPY_HW_USB_VID 0x37C5
+#define MICROPY_HW_USB_PID 0x1206
+#define MICROPY_HW_USB_PID_CDC (MICROPY_HW_USB_PID)
+#define MICROPY_HW_USB_PID_MSC (MICROPY_HW_USB_PID)
+#define MICROPY_HW_USB_PID_CDC_MSC (MICROPY_HW_USB_PID)
+#define MICROPY_HW_USB_PID_CDC_HID (MICROPY_HW_USB_PID)
+#define MICROPY_HW_USB_PID_CDC_MSC_HID (MICROPY_HW_USB_PID)
+
+// Murata 1YN configuration
+#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/w43439_sdio_1yn_7_95_59_combined.h"
+#define CYW43_WIFI_NVRAM_INCLUDE_FILE "lib/cyw43-driver/firmware/wifi_nvram_1yn.h"
+#define CYW43_BT_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/cyw43_btfw_1yn.h"
+
+// Bluetooth config
+#define MICROPY_HW_BLE_UART_ID (PYB_UART_2)
+#define MICROPY_HW_BLE_UART_BAUDRATE (115200)
+#define MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY (3000000)
+#define MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE (3000000)
+
+/******************************************************************************/
+// Bootloader configuration
+
+#define MBOOT_BOARD_EARLY_INIT(initial_r0) mboot_board_early_init()
+
+#define MBOOT_FSLOAD (1)
+#define MBOOT_VFS_FAT (1)
+
+#define MBOOT_SPIFLASH_CS (pyb_pin_XSPIM_P2_CS)
+#define MBOOT_SPIFLASH_SCK (pyb_pin_XSPIM_P2_SCK)
+#define MBOOT_SPIFLASH_MOSI (pyb_pin_XSPIM_P2_IO0)
+#define MBOOT_SPIFLASH_MISO (pyb_pin_XSPIM_P2_IO1)
+#define MBOOT_SPIFLASH_ADDR (0x70000000)
+#define MBOOT_SPIFLASH_BYTE_SIZE (32 * 1024 * 1024)
+#define MBOOT_SPIFLASH_LAYOUT "/0x70000000/8192*4Kg"
+#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (1)
+#define MBOOT_SPIFLASH_SPIFLASH (&spi_bdev.spiflash)
+#define MBOOT_SPIFLASH_CONFIG (&spiflash_config)
+
+/******************************************************************************/
+// Function and variable declarations
+
+extern const struct _mp_spiflash_config_t spiflash_config;
+extern struct _spi_bdev_t spi_bdev;
+
+void mboot_board_early_init(void);
+void mboot_board_entry_init(void);
+
+void board_enter_bootloader(unsigned int n_args, const void *args);
+void board_early_init(void);
+void board_leave_standby(void);
diff --git a/ports/stm32/boards/OPENMV_N6/mpconfigboard.mk b/ports/stm32/boards/OPENMV_N6/mpconfigboard.mk
new file mode 100644
index 0000000000..0283a486c1
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/mpconfigboard.mk
@@ -0,0 +1,30 @@
+# This board requires a bootloader, either mboot or OpenMV's bootloader.
+USE_MBOOT = 1
+
+MCU_SERIES = n6
+CMSIS_MCU = STM32N657xx
+AF_FILE = boards/stm32n657_af.csv
+ifeq ($(BUILDING_MBOOT),1)
+SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_fsbl.o
+else
+SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_s.o
+endif
+STM32_N6_HEADER_VERSION = 2.3
+DKEL = $(STM32_CUBE_PROGRAMMER)/bin/ExternalLoader/MX25UM51245G_STM32N6570-NUCLEO.stldr
+
+LD_FILES = boards/OPENMV_N6/board.ld boards/common_n6_flash.ld
+TEXT0_ADDR = 0x70080000
+
+# MicroPython settings
+MICROPY_FLOAT_IMPL = double
+MICROPY_PY_BLUETOOTH ?= 1
+MICROPY_BLUETOOTH_NIMBLE ?= 1
+MICROPY_BLUETOOTH_BTSTACK ?= 0
+MICROPY_PY_LWIP ?= 1
+MICROPY_PY_NETWORK_CYW43 ?= 1
+MICROPY_PY_SSL ?= 1
+MICROPY_SSL_MBEDTLS ?= 1
+MICROPY_VFS_LFS2 ?= 1
+
+# Board specific frozen modules
+FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py
diff --git a/ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h b/ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h
new file mode 100644
index 0000000000..ac38dac748
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h
@@ -0,0 +1,5 @@
+// This board does not use any security settings, so can just stay in secure
+// mode without configuring the SAU.
+
+static inline void TZ_SAU_Setup(void) {
+}
diff --git a/ports/stm32/boards/OPENMV_N6/pins.csv b/ports/stm32/boards/OPENMV_N6/pins.csv
new file mode 100644
index 0000000000..b05b8b57f9
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/pins.csv
@@ -0,0 +1,142 @@
+,PA0
+,PA1
+,PA2
+,PA3
+,PA4
+,PA5
+,PA6
+,PA7
+,PA8
+,PA9
+,PA10
+SPI2_CS,PA11
+SPI2_SCK,PA12
+UART4_RX,PA11
+UART4_TX,PA12
+P3,PA11
+P2,PA12
+,PA13
+,PA14
+,PA15
+,PB0
+,PB1
+,PB2
+,PB3
+,PB4
+,PB5
+SPI4_MISO,PB6
+SPI4_MOSI,PB7
+,PB8
+,PB9
+I2C2_SCL,PB10
+I2C2_SDA,PB11
+UART3_TX,PB10
+UART3_RX,PB11
+P4,PB10
+P5,PB11
+,PB12
+,PB13
+,PB14
+,PB15
+,PC0
+,PC1
+,PC2
+,PC3
+,PC4
+,PC5
+,PC6
+,PC7
+,PC8
+,PC9
+,PC10
+,PC11
+,PC12
+P11,PC13
+,PC14
+,PC15
+,PD0
+,PD1
+,PD2
+,PD3
+,PD4
+,PD5
+P10,PD6
+SPI2_MOSI,PD7
+P0,PD7
+,PD8
+,PD9
+,PD10
+SPI2_MISO,PD11
+P1,PD11
+,PD12
+P8,PD13
+,PD14
+,PD15
+,PE0
+,PE1
+,PE2
+,PE3
+,PE4
+,PE5
+,PE6
+UART7_RX,PE7
+UART7_TX,PE8
+,PE9
+,PE10
+SPI4_CS,PE11
+SPI4_SCK,PE12
+I2C4_SCL,PE13
+I2C4_SDA,PE14
+,PE15
+P6,PG0
+P9,PG12
+P7,PG13
+,PG15
+
+BUTTON,PF4
+LED_RED,PG10
+LED_GREEN,PA7
+LED_BLUE,PB1
+
+-XSPIM_P2_DQS,PN0
+-XSPIM_P2_CS,PN1
+-XSPIM_P2_IO0,PN2
+-XSPIM_P2_IO1,PN3
+-XSPIM_P2_IO2,PN4
+-XSPIM_P2_IO3,PN5
+-XSPIM_P2_SCK,PN6
+-XSPIM_P2_NCLK,PN7
+-XSPIM_P2_IO4,PN8
+-XSPIM_P2_IO5,PN9
+-XSPIM_P2_IO6,PN10
+-XSPIM_P2_IO7,PN11
+-FLASH_RESET,PN12
+
+-WL_REG_ON,PB12
+-WL_HOST_WAKE,PB14
+-WL_SDIO_D0,PB8
+-WL_SDIO_D1,PG8
+-WL_SDIO_D2,PB9
+-WL_SDIO_D3,PB4
+-WL_SDIO_CMD,PA0
+-WL_SDIO_CK,PD2
+-WL_I2S_SDO,PG14
+-WL_I2S_WS,PB15
+-WL_I2S_SCLK,PB13
+-BT_RXD,PF6
+-BT_TXD,PD5
+-BT_CTS,PG5
+-BT_RTS,PF3
+-BT_REG_ON,PD10
+-BT_HOST_WAKE,PD14
+-BT_DEV_WAKE,PD15
+
+-SD_SDIO_D0,PC8
+-SD_SDIO_D1,PC9
+-SD_SDIO_D2,PC10
+-SD_SDIO_D3,PC11
+-SD_SDIO_CK,PC12
+-SD_SDIO_CMD,PH2
+-SD_RESET,PC7
+-SD_DETECT,PC6
+-SD_VSELECT,PG6
diff --git a/ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h b/ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h
new file mode 100644
index 0000000000..4012d56e5a
--- /dev/null
+++ b/ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h
@@ -0,0 +1,18 @@
+/* This file is part of the MicroPython project, http://micropython.org/
+ * The MIT License (MIT)
+ * Copyright (c) 2019 Damien P. George
+ */
+#ifndef MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
+#define MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
+
+// Oscillator values in Hz
+#define HSE_VALUE (48000000)
+#define LSE_VALUE (32768)
+
+// Oscillator timeouts in ms
+#define HSE_STARTUP_TIMEOUT (100)
+#define LSE_STARTUP_TIMEOUT (5000)
+
+#include "boards/stm32n6xx_hal_conf_base.h"
+
+#endif // MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H
diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json
index 0bd3573d64..5efd188618 100644
--- a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json
+++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json
@@ -8,8 +8,8 @@
"sparkfun_micromod_stm32.jpg"
],
"mcu": "stm32f4",
- "product": "Micromod STM32",
+ "product": "MicroMod STM32",
"thumbnail": "",
- "url": "",
- "vendor": "Sparkfun"
+ "url": "https://www.sparkfun.com/products/17713",
+ "vendor": "SparkFun"
}
diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h
index d2f44cf6cc..1e17905bb0 100644
--- a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h
+++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h
@@ -1,4 +1,4 @@
-// The Sparkfun MicroMod spec uses a zero-based peripheral numbering scheme.
+// The SparkFun MicroMod spec uses a zero-based peripheral numbering scheme.
// In cases where the 0th peripheral is the default, the "0" is omitted from
// the name (e.g. "I2C" instead of "I2C0").
//
diff --git a/ports/stm32/boards/common_n6_flash.ld b/ports/stm32/boards/common_n6_flash.ld
new file mode 100644
index 0000000000..a1f1fa531f
--- /dev/null
+++ b/ports/stm32/boards/common_n6_flash.ld
@@ -0,0 +1,57 @@
+/* Memory layout for N6 when the application runs from external flash in XIP mode.
+
+ FLASH_APP .isr_vector
+ FLASH_APP .text
+ FLASH_APP .data
+
+ RAM .data
+ RAM .bss
+ RAM .heap
+ RAM .stack
+*/
+
+ENTRY(Reset_Handler)
+
+REGION_ALIAS("FLASH_COMMON", FLASH_APP);
+
+/* define output sections */
+SECTIONS
+{
+ .isr_vector :
+ {
+ _siram = .;
+
+ /* This ISR is used for normal application mode. */
+ . = ALIGN(1024);
+ KEEP(*(.isr_vector));
+
+ /* This ISR is used when waking from STANDBY. */
+ . = ALIGN(1024);
+ KEEP(*(.rodata.iram_bootloader_isr_vector));
+
+ /* Need to place in RAM all the code necessary to write to
+ * flash, and to resume from STANDBY. */
+ *(.*.iram_bootloader_reset);
+ *(.*.memcpy);
+ *(.*.mp_hal_gpio_clock_enable);
+ *(.*.mp_hal_pin_config);
+ *(.*.mp_hal_pin_config_speed);
+ *drivers/memory/spiflash.o(.text.* .rodata.*)
+ *xspi.o(.text.* .rodata.*);
+ *boards*(.rodata.spiflash_config*)
+ *boards*(.*.board_leave_standby);
+ *(*.rodata.pin_N*_obj);
+ *(.text.LL_AHB4_GRP1_EnableClock);
+ *(.text.LL_APB4_GRP2_EnableClock);
+
+ . = ALIGN(4);
+ _eiram = .;
+ } >IRAM AT> FLASH_COMMON
+
+ INCLUDE common_text.ld
+ INCLUDE common_extratext_data_in_flash.ld
+ INCLUDE common_bss_heap_stack.ld
+}
+
+/* Used by the start-up code to initialise data */
+_siiram = LOADADDR(.isr_vector);
diff --git a/ports/stm32/boards/common_text.ld b/ports/stm32/boards/common_text.ld
index 16eea43bae..d95467babc 100644
--- a/ports/stm32/boards/common_text.ld
+++ b/ports/stm32/boards/common_text.ld
@@ -12,3 +12,11 @@
. = ALIGN(4);
_etext = .; /* define a global symbol at end of code */
} >FLASH_COMMON
+
+/* Secure Gateway stubs */
+.gnu.sgstubs :
+{
+ . = ALIGN(4);
+ *(.gnu.sgstubs*)
+ . = ALIGN(4);
+} >FLASH_COMMON
diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py
index 1b89fd6415..6f8a0a659d 100755
--- a/ports/stm32/boards/make-pins.py
+++ b/ports/stm32/boards/make-pins.py
@@ -215,7 +215,7 @@ class Stm32Pin(boardgen.Pin):
def validate_cpu_pin_name(cpu_pin_name):
boardgen.Pin.validate_cpu_pin_name(cpu_pin_name)
- if not re.match("P[A-K][0-9]+(_C)?$", cpu_pin_name):
+ if not re.match("P[A-O][0-9]+(_C)?$", cpu_pin_name):
raise boardgen.PinGeneratorError("Invalid cpu pin name '{}'".format(cpu_pin_name))
diff --git a/ports/stm32/boards/pllvalues.py b/ports/stm32/boards/pllvalues.py
index d8856bfecd..ae042d999c 100644
--- a/ports/stm32/boards/pllvalues.py
+++ b/ports/stm32/boards/pllvalues.py
@@ -293,7 +293,7 @@ def main():
break
# Relax constraint on PLLQ being 48MHz on MCUs which have separate PLLs for 48MHz
- relax_pll48 = mcu_series.startswith(("stm32f413", "stm32f7", "stm32h5", "stm32h7"))
+ relax_pll48 = mcu_series.startswith(("stm32f413", "stm32f7", "stm32h5", "stm32h7", "stm32n6"))
hse_valid_plls = compute_pll_table(hse, relax_pll48)
if hsi is not None:
diff --git a/ports/stm32/boards/stm32g474_af.csv b/ports/stm32/boards/stm32g474_af.csv
index 5853f5f900..59a0e91a14 100644
--- a/ports/stm32/boards/stm32g474_af.csv
+++ b/ports/stm32/boards/stm32g474_af.csv
@@ -2,12 +2,12 @@ Port ,Pin ,AF0 ,AF1 ,AF2
, ,I2C4/SYS_AF ,LPTIM1/TIM2/5/15/16/17,I2C1/3/TIM1/2/3/4/5/8/20/15/COMP1,QUADSPI1/I2C3/4/SAI1/USB/HRTIM1/TIM8/20/15/COMP3,I2C1/2/3/4/TIM1/8/16/17,QUADSPI1/SPI1/2/3/4/I2S2/3/I2C4/UART4/5/TIM8/Infrared,QUADSPI1/SPI2/3/I2S2/3/TIM1/5/8/20/Infrared,USART1/2/3/CAN/COMP7/5/6,I2C3/4/UART4/5/LPUART1/COMP1/2/7/4/5/6/3,CAN/TIM1/8/15/CAN1/2,QUADSPI1/TIM2/3/4/8/17,LPTIM1/TIM1/8/CAN1/3,FMC/LPUART1/SAI1/HRTIM1/TIM1,SAI1SAI1/HRTIM1/OPAMP2,UART4/5/SAI1/TIM2/15/UCPD1,SYS ,ADC
PortA,PA0 , ,TIM2_CH1 ,TIM5_CH1 , , , , ,USART2_CTS ,COMP1_OUT ,TIM8_BKIN ,TIM8_ETR , , , ,TIM2_ETR ,EVENTOUT,ADC12_IN1
PortA,PA1 ,RTC_REFIN ,TIM2_CH2 ,TIM5_CH2 , , , , ,USART2_RTS_DE , ,TIM15_CH1N , , , , , ,EVENTOUT,ADC12_IN2
-PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3 , , , , ,USART2_TX ,COMP2_OUT ,TIM15_CH1 ,QUADSPI1_BK1_NCS , ,LPUART1_TX , ,UCPD1_FRSTX ,EVENTOUT,ADC1_IN3
-PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4 ,SAI1_CK1 , , , ,USART2_RX , ,TIM15_CH2 ,QUADSPI1_CLK , ,LPUART1_RX ,SAI1_MCLK_A , ,EVENTOUT,ADC1_IN4
+PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3 , , , , ,USART2_TX ,COMP2_OUT ,TIM15_CH1 ,QUADSPI_BK1_NCS , ,LPUART1_TX , ,UCPD1_FRSTX ,EVENTOUT,ADC1_IN3
+PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4 ,SAI1_CK1 , , , ,USART2_RX , ,TIM15_CH2 ,QUADSPI_CLK , ,LPUART1_RX ,SAI1_MCLK_A , ,EVENTOUT,ADC1_IN4
PortA,PA4 , , ,TIM3_CH2 , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_CK , , , , , ,SAI1_FS_B , ,EVENTOUT,ADC2_IN17
PortA,PA5 , ,TIM2_CH1 ,TIM2_ETR , , ,SPI1_SCK , , , , , , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN13
-PortA,PA6 , ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_BKIN ,SPI1_MISO ,TIM1_BKIN , ,COMP1_OUT , ,QUADSPI1_BK1_IO3 , ,LPUART1_CTS , , ,EVENTOUT,ADC2_IN3
-PortA,PA7 , ,TIM17_CH1 ,TIM3_CH2 , ,TIM8_CH1N ,SPI1_MOSI ,TIM1_CH1N , ,COMP2_OUT , ,QUADSPI1_BK1_IO2 , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN4
+PortA,PA6 , ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_BKIN ,SPI1_MISO ,TIM1_BKIN , ,COMP1_OUT , ,QUADSPI_BK1_IO3 , ,LPUART1_CTS , , ,EVENTOUT,ADC2_IN3
+PortA,PA7 , ,TIM17_CH1 ,TIM3_CH2 , ,TIM8_CH1N ,SPI1_MOSI ,TIM1_CH1N , ,COMP2_OUT , ,QUADSPI_BK1_IO2 , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN4
PortA,PA8 ,MCO , ,I2C3_SCL , ,I2C2_SDA ,I2S2_MCK ,TIM1_CH1 ,USART1_CK ,COMP7_OUT , ,TIM4_ETR ,CAN3_RX ,SAI1_CK2 ,HRTIM1_CHA1 ,SAI1_SCK_A ,EVENTOUT,ADC5_IN1
PortA,PA9 , , ,I2C3_SMBA , ,I2C2_SCL ,I2S3_MCK ,TIM1_CH2 ,USART1_TX ,COMP5_OUT ,TIM15_BKIN ,TIM2_CH3 , , ,HRTIM1_CHA2 ,SAI1_FS_A ,EVENTOUT,ADC5_IN2
PortA,PA10, ,TIM17_BKIN , ,USB_CRS_SYNC ,I2C2_SMBA ,SPI2_MISO ,TIM1_CH3 ,USART1_RX ,COMP6_OUT , ,TIM2_CH4 ,TIM8_BKIN ,SAI1_D1 ,HRTIM1_CHB1 ,SAI1_SD_A ,EVENTOUT,
@@ -16,9 +16,9 @@ PortA,PA12, ,TIM16_CH1 ,
PortA,PA13,SWDIOJTMS ,TIM16_CH1N , ,I2C4_SCL ,I2C1_SCL ,IR_OUT , ,USART3_CTS , , ,TIM4_CH3 , , ,SAI1_SD_B , ,EVENTOUT,
PortA,PA14,SWCLKJTCK ,LPTIM1_OUT , ,I2C4_SMBA ,I2C1_SDA ,TIM8_CH2 ,TIM1_BKIN ,USART2_TX , , , , , ,SAI1_FS_B , ,EVENTOUT,
PortA,PA15,JTDI ,TIM2_CH1 ,TIM8_CH1 , ,I2C1_SCL ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_RX ,UART4_RTS_DE ,TIM1_BKIN , ,CAN3_TX , ,HRTIM1_FLT2 ,TIM2_ETR ,EVENTOUT,
-PortB,PB0 , , ,TIM3_CH3 , ,TIM8_CH2N , ,TIM1_CH2N , , , ,QUADSPI1_BK1_IO1 , , ,HRTIM1_FLT5 ,UCPD1_FRSTX ,EVENTOUT,ADC3_IN12/ADC1_IN15
-PortB,PB1 , , ,TIM3_CH4 , ,TIM8_CH3N , ,TIM1_CH3N , ,COMP4_OUT , ,QUADSPI1_BK1_IO0 , ,LPUART1_RTS_DE ,HRTIM1_SCOUT , ,EVENTOUT,ADC3_IN1/ADC1_IN12
-PortB,PB2 ,RTC_OUT2 ,LPTIM1_OUT ,TIM5_CH1 ,TIM20_CH1 ,I2C3_SMBA , , , , , ,QUADSPI1_BK2_IO1 , , ,HRTIM1_SCIN , ,EVENTOUT,ADC2_IN12
+PortB,PB0 , , ,TIM3_CH3 , ,TIM8_CH2N , ,TIM1_CH2N , , , ,QUADSPI_BK1_IO1 , , ,HRTIM1_FLT5 ,UCPD1_FRSTX ,EVENTOUT,ADC3_IN12/ADC1_IN15
+PortB,PB1 , , ,TIM3_CH4 , ,TIM8_CH3N , ,TIM1_CH3N , ,COMP4_OUT , ,QUADSPI_BK1_IO0 , ,LPUART1_RTS_DE ,HRTIM1_SCOUT , ,EVENTOUT,ADC3_IN1/ADC1_IN12
+PortB,PB2 ,RTC_OUT2 ,LPTIM1_OUT ,TIM5_CH1 ,TIM20_CH1 ,I2C3_SMBA , , , , , ,QUADSPI_BK2_IO1 , , ,HRTIM1_SCIN , ,EVENTOUT,ADC2_IN12
PortB,PB3 ,JTDOTRACESWO,TIM2_CH2 ,TIM4_ETR ,USB_CRS_SYNC ,TIM8_CH1N ,SPI1_SCK ,SPI3_SCK/I2S3_CK ,USART2_TX , , ,TIM3_ETR ,CAN3_RX ,HRTIM1_SCOUT ,HRTIM1_EEV9 ,SAI1_SCK_B ,EVENTOUT,
PortB,PB4 ,JTRST ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_CH2N ,SPI1_MISO ,SPI3_MISO ,USART2_RX ,UART5_RTS_DE , ,TIM17_BKIN ,CAN3_TX , ,HRTIM1_EEV7 ,SAI1_MCLK_B ,EVENTOUT,
PortB,PB5 , ,TIM16_BKIN ,TIM3_CH2 ,TIM8_CH3N ,I2C1_SMBA ,SPI1_MOSI ,SPI3_MOSI/I2S3_SD ,USART2_CK ,I2C3_SDA ,CAN2_RX ,TIM17_CH1 ,LPTIM1_IN1 ,SAI1_SD_B ,HRTIM1_EEV6 ,UART5_CTS ,EVENTOUT,
@@ -26,17 +26,17 @@ PortB,PB6 , ,TIM16_CH1N ,TIM4_CH1
PortB,PB7 , ,TIM17_CH1N ,TIM4_CH2 ,I2C4_SDA ,I2C1_SDA ,TIM8_BKIN , ,USART1_RX ,COMP3_OUT , ,TIM3_CH4 ,LPTIM1_IN2 ,FMC_NL ,HRTIM1_EEV3 ,UART4_CTS ,EVENTOUT,
PortB,PB8 , ,TIM16_CH1 ,TIM4_CH3 ,SAI1_CK1 ,I2C1_SCL , , ,USART3_RX ,COMP1_OUT ,CAN1_RX ,TIM8_CH2 , ,TIM1_BKIN ,HRTIM1_EEV8 ,SAI1_MCLK_A ,EVENTOUT,
PortB,PB9 , ,TIM17_CH1 ,TIM4_CH4 ,SAI1_D2 ,I2C1_SDA , ,IR_OUT ,USART3_TX ,COMP2_OUT ,CAN1_TX ,TIM8_CH3 , ,TIM1_CH3N ,HRTIM1_EEV5 ,SAI1_FS_A ,EVENTOUT,
-PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX ,LPUART1_RX , ,QUADSPI1_CLK , ,TIM1_BKIN ,HRTIM1_FLT3 ,SAI1_SCK_A ,EVENTOUT,
-PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX ,LPUART1_TX , ,QUADSPI1_BK1_NCS , , ,HRTIM1_FLT4 , ,EVENTOUT,ADC12_IN14
+PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX ,LPUART1_RX , ,QUADSPI_CLK , ,TIM1_BKIN ,HRTIM1_FLT3 ,SAI1_SCK_A ,EVENTOUT,
+PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX ,LPUART1_TX , ,QUADSPI_BK1_NCS , , ,HRTIM1_FLT4 , ,EVENTOUT,ADC12_IN14
PortB,PB12, , ,TIM5_ETR , ,I2C2_SMBA ,SPI2_NSS/I2S2_WS ,TIM1_BKIN ,USART3_CK ,LPUART1_RTS_DE ,CAN2_RX , , , ,HRTIM1_CHC1 , ,EVENTOUT,ADC4_IN3/ADC1_IN11
PortB,PB13, , , , , ,SPI2_SCK/I2S2_CK ,TIM1_CH1N ,USART3_CTS ,LPUART1_CTS ,CAN2_TX , , , ,HRTIM1_CHC2 , ,EVENTOUT,ADC3_IN5
PortB,PB14, ,TIM15_CH1 , , , ,SPI2_MISO ,TIM1_CH2N ,USART3_RTS_DE ,COMP4_OUT , , , , ,HRTIM1_CHD1 , ,EVENTOUT,ADC4_IN4/ADC1_IN5
PortB,PB15,RTC_REFIN ,TIM15_CH2 ,TIM15_CH1N ,COMP3_OUT ,TIM1_CH3N ,SPI2_MOSI/I2S2_SD , , , , , , , ,HRTIM1_CHD2 , ,EVENTOUT,ADC4_IN5/ADC2_IN15
PortC,PC0 , ,LPTIM1_IN1 ,TIM1_CH1 , , , , , ,LPUART1_RX , , , , , , ,EVENTOUT,ADC12_IN6
-PortC,PC1 , ,LPTIM1_OUT ,TIM1_CH2 , , , , , ,LPUART1_TX , ,QUADSPI1_BK2_IO0 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN7
-PortC,PC2 , ,LPTIM1_IN2 ,TIM1_CH3 ,COMP3_OUT , , ,TIM20_CH2 , , , ,QUADSPI1_BK2_IO1 , , , , ,EVENTOUT,ADC12_IN8
-PortC,PC3 , ,LPTIM1_ETR ,TIM1_CH4 ,SAI1_D1 , , ,TIM1_BKIN2 , , , ,QUADSPI1_BK2_IO2 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN9
-PortC,PC4 , , ,TIM1_ETR , ,I2C2_SCL , , ,USART1_TX , , ,QUADSPI1_BK2_IO3 , , , , ,EVENTOUT,ADC2_IN5
+PortC,PC1 , ,LPTIM1_OUT ,TIM1_CH2 , , , , , ,LPUART1_TX , ,QUADSPI_BK2_IO0 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN7
+PortC,PC2 , ,LPTIM1_IN2 ,TIM1_CH3 ,COMP3_OUT , , ,TIM20_CH2 , , , ,QUADSPI_BK2_IO1 , , , , ,EVENTOUT,ADC12_IN8
+PortC,PC3 , ,LPTIM1_ETR ,TIM1_CH4 ,SAI1_D1 , , ,TIM1_BKIN2 , , , ,QUADSPI_BK2_IO2 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN9
+PortC,PC4 , , ,TIM1_ETR , ,I2C2_SCL , , ,USART1_TX , , ,QUADSPI_BK2_IO3 , , , , ,EVENTOUT,ADC2_IN5
PortC,PC5 , , ,TIM15_BKIN ,SAI1_D3 , , ,TIM1_CH4N ,USART1_RX , , , , , ,HRTIM1_EEV10 , ,EVENTOUT,ADC2_IN11
PortC,PC6 , , ,TIM3_CH1 ,HRTIM1_EEV10 ,TIM8_CH1 , ,I2S2_MCK ,COMP6_OUT ,I2C4_SCL , , , , ,HRTIM1_CHF1 , ,EVENTOUT,
PortC,PC7 , , ,TIM3_CH2 ,HRTIM1_FLT5 ,TIM8_CH2 , ,I2S3_MCK ,COMP5_OUT ,I2C4_SDA , , , , ,HRTIM1_CHF2 , ,EVENTOUT,
@@ -51,11 +51,11 @@ PortC,PC15, , ,
PortD,PD0 , , , , , , ,TIM8_CH4N , , ,CAN1_RX , , ,FMC_D2 , , ,EVENTOUT,
PortD,PD1 , , , , ,TIM8_CH4 , ,TIM8_BKIN2 , , ,CAN1_TX , , ,FMC_D3 , , ,EVENTOUT,
PortD,PD2 , , ,TIM3_ETR , ,TIM8_BKIN ,UART5_RX , , , , , , , , , ,EVENTOUT,
-PortD,PD3 , , ,TIM2_CH1/TIM2_ETR , , , , ,USART2_CTS , , ,QUADSPI1_BK2_NCS , ,FMC_CLK , , ,EVENTOUT,
-PortD,PD4 , , ,TIM2_CH2 , , , , ,USART2_RTS_DE , , ,QUADSPI1_BK2_IO0 , ,FMC_NOE , , ,EVENTOUT,
-PortD,PD5 , , , , , , , ,USART2_TX , , ,QUADSPI1_BK2_IO1 , ,FMC_NWE , , ,EVENTOUT,
-PortD,PD6 , , ,TIM2_CH4 ,SAI1_D1 , , , ,USART2_RX , , ,QUADSPI1_BK2_IO2 , ,FMC_NWAIT ,SAI1_SD_A , ,EVENTOUT,
-PortD,PD7 , , ,TIM2_CH3 , , , , ,USART2_CK , , ,QUADSPI1_BK2_IO3 , ,FMC_NCE/FMC_NE1 , , ,EVENTOUT,
+PortD,PD3 , , ,TIM2_CH1/TIM2_ETR , , , , ,USART2_CTS , , ,QUADSPI_BK2_NCS , ,FMC_CLK , , ,EVENTOUT,
+PortD,PD4 , , ,TIM2_CH2 , , , , ,USART2_RTS_DE , , ,QUADSPI_BK2_IO0 , ,FMC_NOE , , ,EVENTOUT,
+PortD,PD5 , , , , , , , ,USART2_TX , , ,QUADSPI_BK2_IO1 , ,FMC_NWE , , ,EVENTOUT,
+PortD,PD6 , , ,TIM2_CH4 ,SAI1_D1 , , , ,USART2_RX , , ,QUADSPI_BK2_IO2 , ,FMC_NWAIT ,SAI1_SD_A , ,EVENTOUT,
+PortD,PD7 , , ,TIM2_CH3 , , , , ,USART2_CK , , ,QUADSPI_BK2_IO3 , ,FMC_NCE/FMC_NE1 , , ,EVENTOUT,
PortD,PD8 , , , , , , , ,USART3_TX , , , , ,FMC_D13 , , ,EVENTOUT,ADC4_IN12/ADC5_IN12
PortD,PD9 , , , , , , , ,USART3_RX , , , , ,FMC_D14 , , ,EVENTOUT,ADC4_IN13/ADC5_IN13
PortD,PD10, , , , , , , ,USART3_CK , , , , ,FMC_D15 , , ,EVENTOUT,ADC345_IN7
@@ -74,23 +74,23 @@ PortE,PE6 ,TRACED3 , ,
PortE,PE7 , , ,TIM1_ETR , , , , , , , , , ,FMC_D4 ,SAI1_SD_B , ,EVENTOUT,ADC3_IN4
PortE,PE8 , ,TIM5_CH3 ,TIM1_CH1N , , , , , , , , , ,FMC_D5 ,SAI1_SCK_B , ,EVENTOUT,ADC345_IN6
PortE,PE9 , ,TIM5_CH4 ,TIM1_CH1 , , , , , , , , , ,FMC_D6 ,SAI1_FS_B , ,EVENTOUT,ADC3_IN2
-PortE,PE10, , ,TIM1_CH2N , , , , , , , ,QUADSPI1_CLK , ,FMC_D7 ,SAI1_MCLK_B , ,EVENTOUT,ADC345_IN14
-PortE,PE11, , ,TIM1_CH2 , , ,SPI4_NSS , , , , ,QUADSPI1_BK1_NCS , ,FMC_D8 , , ,EVENTOUT,ADC345_IN15
-PortE,PE12, , ,TIM1_CH3N , , ,SPI4_SCK , , , , ,QUADSPI1_BK1_IO0 , ,FMC_D9 , , ,EVENTOUT,ADC345_IN16
-PortE,PE13, , ,TIM1_CH3 , , ,SPI4_MISO , , , , ,QUADSPI1_BK1_IO1 , ,FMC_D10 , , ,EVENTOUT,ADC3_IN3
-PortE,PE14, , ,TIM1_CH4 , , ,SPI4_MOSI ,TIM1_BKIN2 , , , ,QUADSPI1_BK1_IO2 , ,FMC_D11 , , ,EVENTOUT,ADC4_IN1
-PortE,PE15, , ,TIM1_BKIN , , , ,TIM1_CH4N ,USART3_RX , , ,QUADSPI1_BK1_IO3 , ,FMC_D12 , , ,EVENTOUT,ADC4_IN2
+PortE,PE10, , ,TIM1_CH2N , , , , , , , ,QUADSPI_CLK , ,FMC_D7 ,SAI1_MCLK_B , ,EVENTOUT,ADC345_IN14
+PortE,PE11, , ,TIM1_CH2 , , ,SPI4_NSS , , , , ,QUADSPI_BK1_NCS , ,FMC_D8 , , ,EVENTOUT,ADC345_IN15
+PortE,PE12, , ,TIM1_CH3N , , ,SPI4_SCK , , , , ,QUADSPI_BK1_IO0 , ,FMC_D9 , , ,EVENTOUT,ADC345_IN16
+PortE,PE13, , ,TIM1_CH3 , , ,SPI4_MISO , , , , ,QUADSPI_BK1_IO1 , ,FMC_D10 , , ,EVENTOUT,ADC3_IN3
+PortE,PE14, , ,TIM1_CH4 , , ,SPI4_MOSI ,TIM1_BKIN2 , , , ,QUADSPI_BK1_IO2 , ,FMC_D11 , , ,EVENTOUT,ADC4_IN1
+PortE,PE15, , ,TIM1_BKIN , , , ,TIM1_CH4N ,USART3_RX , , ,QUADSPI_BK1_IO3 , ,FMC_D12 , , ,EVENTOUT,ADC4_IN2
PortF,PF0 , , , , ,I2C2_SDA ,SPI2_NSS/I2S2_WS ,TIM1_CH3N , , , , , , , , ,EVENTOUT,ADC1_IN10
PortF,PF1 , , , , , ,SPI2_SCK/I2S2_CK , , , , , , , , , ,EVENTOUT,ADC2_IN10
PortF,PF2 , , ,TIM20_CH3 , ,I2C2_SMBA , , , , , , , ,FMC_A2 , , ,EVENTOUT,
PortF,PF3 , , ,TIM20_CH4 , ,I2C3_SCL , , , , , , , ,FMC_A3 , , ,EVENTOUT,
PortF,PF4 , , ,COMP1_OUT ,TIM20_CH1N ,I2C3_SDA , , , , , , , ,FMC_A4 , , ,EVENTOUT,
PortF,PF5 , , ,TIM20_CH2N , , , , , , , , , ,FMC_A5 , , ,EVENTOUT,
-PortF,PF6 , ,TIM5_ETR ,TIM4_CH4 ,SAI1_SD_B ,I2C2_SCL , ,TIM5_CH1 ,USART3_RTS , , ,QUADSPI1_BK1_IO3 , , , , ,EVENTOUT,
-PortF,PF7 , , ,TIM20_BKIN , , , ,TIM5_CH2 , , , ,QUADSPI1_BK1_IO2 , ,FMC_A1 ,SAI1_MCLK_B , ,EVENTOUT,
-PortF,PF8 , , ,TIM20_BKIN2 , , , ,TIM5_CH3 , , , ,QUADSPI1_BK1_IO0 , ,FMC_A24 ,SAI1_SCK_B , ,EVENTOUT,
-PortF,PF9 , , ,TIM20_BKIN ,TIM15_CH1 , ,SPI2_SCK ,TIM5_CH4 , , , ,QUADSPI1_BK1_IO1 , ,FMC_A25 ,SAI1_FS_B , ,EVENTOUT,
-PortF,PF10, , ,TIM20_BKIN2 ,TIM15_CH2 , ,SPI2_SCK , , , , ,QUADSPI1_CLK , ,FMC_A0 ,SAI1_D3 , ,EVENTOUT,
+PortF,PF6 , ,TIM5_ETR ,TIM4_CH4 ,SAI1_SD_B ,I2C2_SCL , ,TIM5_CH1 ,USART3_RTS , , ,QUADSPI_BK1_IO3 , , , , ,EVENTOUT,
+PortF,PF7 , , ,TIM20_BKIN , , , ,TIM5_CH2 , , , ,QUADSPI_BK1_IO2 , ,FMC_A1 ,SAI1_MCLK_B , ,EVENTOUT,
+PortF,PF8 , , ,TIM20_BKIN2 , , , ,TIM5_CH3 , , , ,QUADSPI_BK1_IO0 , ,FMC_A24 ,SAI1_SCK_B , ,EVENTOUT,
+PortF,PF9 , , ,TIM20_BKIN ,TIM15_CH1 , ,SPI2_SCK ,TIM5_CH4 , , , ,QUADSPI_BK1_IO1 , ,FMC_A25 ,SAI1_FS_B , ,EVENTOUT,
+PortF,PF10, , ,TIM20_BKIN2 ,TIM15_CH2 , ,SPI2_SCK , , , , ,QUADSPI_CLK , ,FMC_A0 ,SAI1_D3 , ,EVENTOUT,
PortF,PF11, , ,TIM20_ETR , , , , , , , , , ,FMC_NE4 , , ,EVENTOUT,
PortF,PF12, , ,TIM20_CH1 , , , , , , , , , ,FMC_A6 , , ,EVENTOUT,
PortF,PF13, , ,TIM20_CH2 , ,I2C4_SMBA , , , , , , , ,FMC_A7 , , ,EVENTOUT,
diff --git a/ports/stm32/boards/stm32h7xx_hal_conf_base.h b/ports/stm32/boards/stm32h7xx_hal_conf_base.h
index 670dee383f..1953ba020b 100644
--- a/ports/stm32/boards/stm32h7xx_hal_conf_base.h
+++ b/ports/stm32/boards/stm32h7xx_hal_conf_base.h
@@ -90,6 +90,7 @@
#include "stm32h7xx_hal_spi.h"
#include "stm32h7xx_hal_tim.h"
#include "stm32h7xx_hal_uart.h"
+#include "stm32h7xx_hal_uart_ex.h"
#include "stm32h7xx_hal_usart.h"
#include "stm32h7xx_hal_wwdg.h"
#include "stm32h7xx_ll_adc.h"
diff --git a/ports/stm32/boards/stm32n657_af.csv b/ports/stm32/boards/stm32n657_af.csv
new file mode 100644
index 0000000000..35e305a376
--- /dev/null
+++ b/ports/stm32/boards/stm32n657_af.csv
@@ -0,0 +1,42 @@
+Port ,Pin ,AF0 ,AF1 ,AF2 ,AF3 ,AF4 ,AF5 ,AF6 ,AF7 ,AF8 ,AF9 ,AF10 ,AF11 ,AF12 ,AF13 ,AF14 ,AF15 ,ADC
+ , ,SYS ,LPTIM1/TIM1/2/16/17,LPTIM3/PDM_SAI1/TIM3/4/5/12/15,I3C1/LPTIM2/3/LPUART1/OCTOSPI/TIM1/8,CEC/DCMI/I2C1/2/3/4/LPTIM1/2/SPI1/I2S1/TIM15/USART1,CEC/I3C1/LPTIM1/SPI1/I2S1/SPI2/I2S2/SPI3/I2S3/SPI4/5/6,I2C4/OCTOSPI/SAI1/SPI3/I2S3/SPI4/UART4/12/USART10/USB_PD,SDMMC1/SPI2/I2S2/SPI3/I2S3/SPI6/UART7/8/12/USART1/2/3/6/10/11,LPUART1/SAI2/SDMMC1/SPI6/UART4/5/8,FDCAN1/2/FMC[NAND16]/FMC[NORmux]/FMC[NOR_RAM]/OCTOSPI/SDMMC2/TIM13/14,CRS/FMC[NAND16]/OCTOSPI/SAI2/SDMMC2/TIM8/USB_,ETH[MII/RMII]/FMC[NAND16]/OCTOSPI/SDMMC2/UART7/9/USB_PD,FMC[NAND16]/FMC[NORmux]/FMC[NOR_RAM]/FMC[SDRAM_16bit]/SDMMC1,DCMI/FMC[NAND16]/FMC[NORmux]/FMC[NOR_RAM]/LPTIM5,LPTIM3/4/5/6/TIM2/UART5,SYS ,ADC
+PortA,PA0 , , , , , , , , , , , ,SDMMC2_CMD , , , , ,ADC12_INP0/ADC12_INN1
+PortA,PA3 , , , , , ,SPI5_NSS , , , , , , , , , , ,
+PortA,PA5 , , , , , , , , , , , , , , , , ,ADC2_INP18
+PortA,PA8 , , , , , , , , , , , , , , , , ,ADC12_INP5
+PortA,PA9 , , , , , , , , , , , , , , , , ,ADC12_INP10
+PortA,PA10, , , , , , , , , , , , , , , , ,ADC12_INP11/ADC12_INN10
+PortA,PA11, , , , , , , , ,UART4_RX , , , , , , , ,ADC12_INP12/ADC12_INN11
+PortA,PA12, , , , , , , , ,UART4_TX , , , , , , , ,ADC12_INP13/ADC12_INN12
+PortB,PB4 , , , , , , , , , , , ,SDMMC2_D3 , , , , ,
+PortB,PB8 , , , , , , , , , , , ,SDMMC2_D0 , , , , ,
+PortB,PB9 , , , , , , , , , , , ,SDMMC2_D2 , , , , ,
+PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX , , , , , , , , ,
+PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX , , , , , , , , ,
+PortC,PC8 , , , , , , , , , , ,SDMMC1_D0 , , , , , ,
+PortC,PC9 , , , , , , , , , , ,SDMMC1_D1 , , , , , ,
+PortC,PC10, , , , , , , , , , ,SDMMC1_D2 , , , , , ,
+PortC,PC11, , , , , , , , , , ,SDMMC1_D3 , , , , , ,
+PortC,PC12, , , , , , , , , , ,SDMMC1_CK , , , , , ,
+PortD,PD2 , , , , , , , , , , , ,SDMMC2_CK , , , , ,
+PortD,PD5 , , , , , , , ,USART2_TX , , , , , , , , ,
+PortD,PD6 , , , , ,TIM15_CH2 , , , , , , , , , , , ,
+PortD,PD8 , , , , , , , ,USART3_TX , , , , , , , , ,
+PortD,PD9 , , , , , , , ,USART3_RX , , , , , , , , ,
+PortD,PD13, , ,TIM4_CH2 , , , , , , , , , , , , , ,
+PortE,PE5 , , , , , , , ,USART1_TX , , , , , , , , ,
+PortE,PE6 , , , , , , , ,USART1_RX , , , , , , , , ,
+PortE,PE7 , , , , , , , , ,UART7_RX , , , , , , , ,
+PortE,PE8 , , , , , , , , ,UART7_TX , , , , , , , ,
+PortE,PE15, , , , , ,SPI5_SCK , , , , , , , , , , ,
+PortF,PF3 , , , , , , , ,USART2_RTS , , , , , , , , ,ADC1_INP16
+PortF,PF6 , , , , , , , ,USART2_RX , , , , , , , , ,
+PortG,PG0 , , ,TIM12_CH1 , , , , , , , , , , , , , ,
+PortG,PG1 , , , , , ,SPI5_MISO , , , , , , , , , , ,
+PortG,PG2 , , , , , ,SPI5_MOSI , , , , , , , , , , ,
+PortG,PG5 , , , , , , , ,USART2_CTS , , , , , , , , ,
+PortG,PG8 , , , , , , , , , , , ,SDMMC2_D1 , , , , ,
+PortG,PG12, ,TIM17_CH1 , , , , , , , , , , , , , , ,
+PortG,PG13, , ,TIM4_CH1 , , , , , , , , , , , , , ,
+PortG,PG15, , , , , , , , , , , , , , , , ,ADC12_INP7/ADC12_INN3
+PortC,PH2 , , , , , , , , , , ,SDMMC1_CMD , , , , , ,
diff --git a/ports/stm32/boards/stm32n657x0.ld b/ports/stm32/boards/stm32n657x0.ld
new file mode 100644
index 0000000000..242d113b30
--- /dev/null
+++ b/ports/stm32/boards/stm32n657x0.ld
@@ -0,0 +1,34 @@
+/*
+ GNU linker script for STM32N657x0
+
+ Note: upper 512k of SRAM2 is copied from external flash upon reset.
+*/
+
+/* Specify the memory areas */
+MEMORY
+{
+ FLEXRAM_S (xrw) : ORIGIN = 0x34000000, LENGTH = 80K
+ SRAM2_S_RAM (xrw) : ORIGIN = 0x34100000, LENGTH = 512K /* only use first half, second half may contain firmware */
+ SRAM2_S_FSBL (xrw) : ORIGIN = 0x34180400, LENGTH = 511K /* firmware loaded from SPI flash upon reset */
+ EXT_FLASH (rx) : ORIGIN = 0x70080000, LENGTH = 1536K
+}
+
+REGION_ALIAS("IRAM", FLEXRAM_S);
+REGION_ALIAS("RAM", SRAM2_S_RAM);
+REGION_ALIAS("FLASH", SRAM2_S_FSBL);
+REGION_ALIAS("FLASH_APP", EXT_FLASH);
+
+/* produce a link error if there is not this amount of RAM for these sections */
+_minimum_stack_size = 2K;
+_minimum_heap_size = 16K;
+
+/* Define the stack. The stack is full descending so begins just above last byte
+ of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */
+_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve;
+_sstack = _estack - 16K; /* tunable */
+
+/* RAM extents for the garbage collector */
+_ram_start = ORIGIN(RAM);
+_ram_end = ORIGIN(RAM) + LENGTH(RAM);
+_heap_start = _ebss; /* heap starts just after statically allocated memory */
+_heap_end = _sstack;
diff --git a/ports/stm32/boards/stm32n6xx_hal_conf_base.h b/ports/stm32/boards/stm32n6xx_hal_conf_base.h
new file mode 100644
index 0000000000..641a003d8b
--- /dev/null
+++ b/ports/stm32/boards/stm32n6xx_hal_conf_base.h
@@ -0,0 +1,215 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_STM32N6XX_HAL_CONF_BASE_H
+#define MICROPY_INCLUDED_STM32N6XX_HAL_CONF_BASE_H
+
+// Enable various HAL modules
+#define HAL_MODULE_ENABLED
+#define HAL_ADC_MODULE_ENABLED
+#define HAL_BSEC_MODULE_ENABLED
+#define HAL_CACHEAXI_MODULE_ENABLED
+#define HAL_CORTEX_MODULE_ENABLED
+#define HAL_CRC_MODULE_ENABLED
+#define HAL_CRYP_MODULE_ENABLED
+#define HAL_CSI_MODULE_ENABLED
+#define HAL_DCMI_MODULE_ENABLED
+#define HAL_DCMIPP_MODULE_ENABLED
+#define HAL_DMA_MODULE_ENABLED
+#define HAL_DMA2D_MODULE_ENABLED
+#define HAL_DTS_MODULE_ENABLED
+#define HAL_ETH_MODULE_ENABLED
+#define HAL_EXTI_MODULE_ENABLED
+#define HAL_FDCAN_MODULE_ENABLED
+#define HAL_GFXMMU_MODULE_ENABLED
+#define HAL_GFXTIM_MODULE_ENABLED
+#define HAL_GPIO_MODULE_ENABLED
+#define HAL_GPU2D_MODULE_ENABLED
+#define HAL_HASH_MODULE_ENABLED
+#define HAL_HCD_MODULE_ENABLED
+#define HAL_I2C_MODULE_ENABLED
+#define HAL_I3C_MODULE_ENABLED
+#define HAL_ICACHE_MODULE_ENABLED
+#define HAL_IRDA_MODULE_ENABLED
+#define HAL_IWDG_MODULE_ENABLED
+#define HAL_JPEG_MODULE_ENABLED
+#define HAL_LPTIM_MODULE_ENABLED
+#define HAL_LTDC_MODULE_ENABLED
+#define HAL_MCE_MODULE_ENABLED
+#define HAL_MDF_MODULE_ENABLED
+#define HAL_MDIOS_MODULE_ENABLED
+#define HAL_MMC_MODULE_ENABLED
+#define HAL_NAND_MODULE_ENABLED
+#define HAL_NOR_MODULE_ENABLED
+#define HAL_PCD_MODULE_ENABLED
+#define HAL_PKA_MODULE_ENABLED
+#define HAL_PSSI_MODULE_ENABLED
+#define HAL_PWR_MODULE_ENABLED
+#define HAL_RAMCFG_MODULE_ENABLED
+#define HAL_RCC_MODULE_ENABLED
+#define HAL_RIF_MODULE_ENABLED
+#define HAL_RNG_MODULE_ENABLED
+#define HAL_RTC_MODULE_ENABLED
+#define HAL_SAI_MODULE_ENABLED
+#define HAL_SD_MODULE_ENABLED
+#define HAL_SDRAM_MODULE_ENABLED
+#define HAL_SMARTCARD_MODULE_ENABLED
+#define HAL_SMBUS_MODULE_ENABLED
+#define HAL_SPDIFRX_MODULE_ENABLED
+#define HAL_SPI_MODULE_ENABLED
+#define HAL_SRAM_MODULE_ENABLED
+#define HAL_TIM_MODULE_ENABLED
+#define HAL_UART_MODULE_ENABLED
+#define HAL_USART_MODULE_ENABLED
+#define HAL_WWDG_MODULE_ENABLED
+#define HAL_XSPI_MODULE_ENABLED
+
+// Oscillator values in Hz
+#define HSI_VALUE (64000000UL)
+#define LSI_VALUE (32000UL)
+#define MSI_VALUE (4000000UL)
+
+// SysTick has the highest priority
+#define TICK_INT_PRIORITY (0x00)
+
+// Miscellaneous HAL settings
+#define VDD_VALUE 3300UL
+#define USE_RTOS 0
+#define USE_SD_TRANSCEIVER 0
+#define USE_SPI_CRC 1
+
+// Disable dynamic callback registration
+#define USE_HAL_ADC_REGISTER_CALLBACKS 0U /* ADC register callback disabled */
+#define USE_HAL_CACHEAXI_REGISTER_CALLBACKS 0U /* CACHEAXI register callback disabled */
+#define USE_HAL_CRYP_REGISTER_CALLBACKS 0U /* CRYP register callback disabled */
+#define USE_HAL_DCMI_REGISTER_CALLBACKS 0U /* DCMI register callback disabled */
+#define USE_HAL_DCMIPP_REGISTER_CALLBACKS 0U /* DCMIPP register callback disabled */
+#define USE_HAL_DMA2D_REGISTER_CALLBACKS 0U /* DMA2D register callback disabled */
+#define USE_HAL_DTS_REGISTER_CALLBACKS 0U /* DTS register callback disabled */
+#define USE_HAL_ETH_REGISTER_CALLBACKS 0U /* ETH register callback disabled */
+#define USE_HAL_FDCAN_REGISTER_CALLBACKS 0U /* FDCAN register callback disabled */
+#define USE_HAL_GFXMMU_REGISTER_CALLBACKS 0U /* GFXMMU register callback disabled */
+#define USE_HAL_GFXTIM_REGISTER_CALLBACKS 0U /* GFXTIM register callback disabled */
+#define USE_HAL_HASH_REGISTER_CALLBACKS 0U /* HASH register callback disabled */
+#define USE_HAL_HCD_REGISTER_CALLBACKS 0U /* HCD register callback disabled */
+#define USE_HAL_I2C_REGISTER_CALLBACKS 0U /* I2C register callback disabled */
+#define USE_HAL_I3C_REGISTER_CALLBACKS 0U /* I3C register callback disabled */
+#define USE_HAL_IWDG_REGISTER_CALLBACKS 0U /* IWDG register callback disabled */
+#define USE_HAL_IRDA_REGISTER_CALLBACKS 0U /* IRDA register callback disabled */
+#define USE_HAL_LPTIM_REGISTER_CALLBACKS 0U /* LPTIM register callback disabled */
+#define USE_HAL_LTDC_REGISTER_CALLBACKS 0U /* LTDC register callback disabled */
+#define USE_HAL_MCE_REGISTER_CALLBACKS 0U /* MCE register callback disabled */
+#define USE_HAL_MDF_REGISTER_CALLBACKS 0U /* MDF register callback disabled */
+#define USE_HAL_MMC_REGISTER_CALLBACKS 0U /* MMC register callback disabled */
+#define USE_HAL_NAND_REGISTER_CALLBACKS 0U /* NAND register callback disabled */
+#define USE_HAL_NOR_REGISTER_CALLBACKS 0U /* NOR register callback disabled */
+#define USE_HAL_PCD_REGISTER_CALLBACKS 0U /* PCD register callback disabled */
+#define USE_HAL_PKA_REGISTER_CALLBACKS 0U /* PKA register callback disabled */
+#define USE_HAL_PSSI_REGISTER_CALLBACKS 0U /* PSSI register callback disabled */
+#define USE_HAL_RAMCFG_REGISTER_CALLBACKS 0U /* RAMCFG register callback disabled */
+#define USE_HAL_RNG_REGISTER_CALLBACKS 0U /* RNG register callback disabled */
+#define USE_HAL_RTC_REGISTER_CALLBACKS 0U /* RTC register callback disabled */
+#define USE_HAL_SAI_REGISTER_CALLBACKS 0U /* SAI register callback disabled */
+#define USE_HAL_SD_REGISTER_CALLBACKS 0U /* SD register callback disabled */
+#define USE_HAL_SDRAM_REGISTER_CALLBACKS 0U /* SDRAM register callback disabled */
+#define USE_HAL_SMARTCARD_REGISTER_CALLBACKS 0U /* SMARTCARD register callback disabled */
+#define USE_HAL_SMBUS_REGISTER_CALLBACKS 0U /* SMBUS register callback disabled */
+#define USE_HAL_SPDIFRX_REGISTER_CALLBACKS 0U /* SPDIFRX register callback disabled */
+#define USE_HAL_SPI_REGISTER_CALLBACKS 0U /* SPI register callback disabled */
+#define USE_HAL_SRAM_REGISTER_CALLBACKS 0U /* SRAM register callback disabled */
+#define USE_HAL_TIM_REGISTER_CALLBACKS 0U /* TIM register callback disabled */
+#define USE_HAL_UART_REGISTER_CALLBACKS 0U /* UART register callback disabled */
+#define USE_HAL_USART_REGISTER_CALLBACKS 0U /* USART register callback disabled */
+#define USE_HAL_WWDG_REGISTER_CALLBACKS 0U /* WWDG register callback disabled */
+#define USE_HAL_XSPI_REGISTER_CALLBACKS 0U /* XSPI register callback disabled */
+
+// Include various HAL modules for convenience
+#include "stm32n6xx_hal_rcc.h"
+#include "stm32n6xx_hal_gpio.h"
+#include "stm32n6xx_hal_rif.h"
+#include "stm32n6xx_hal_dma.h"
+#include "stm32n6xx_hal_cacheaxi.h"
+#include "stm32n6xx_hal_cortex.h"
+#include "stm32n6xx_hal_adc.h"
+#include "stm32n6xx_hal_bsec.h"
+#include "stm32n6xx_hal_crc.h"
+#include "stm32n6xx_hal_cryp.h"
+#include "stm32n6xx_hal_dcmi.h"
+#include "stm32n6xx_hal_dcmipp.h"
+#include "stm32n6xx_hal_dma2d.h"
+#include "stm32n6xx_hal_dts.h"
+#include "stm32n6xx_hal_eth.h"
+#include "stm32n6xx_hal_exti.h"
+#include "stm32n6xx_hal_fdcan.h"
+#include "stm32n6xx_hal_gfxmmu.h"
+#include "stm32n6xx_hal_gfxtim.h"
+#include "stm32n6xx_hal_gpio.h"
+#include "stm32n6xx_hal_gpu2d.h"
+#include "stm32n6xx_hal_hash.h"
+#include "stm32n6xx_hal_hcd.h"
+#include "stm32n6xx_hal_i2c.h"
+#include "stm32n6xx_hal_i3c.h"
+#include "stm32n6xx_hal_icache.h"
+#include "stm32n6xx_hal_irda.h"
+#include "stm32n6xx_hal_iwdg.h"
+#include "stm32n6xx_hal_jpeg.h"
+#include "stm32n6xx_hal_lptim.h"
+#include "stm32n6xx_hal_ltdc.h"
+#include "stm32n6xx_hal_mce.h"
+#include "stm32n6xx_hal_mdf.h"
+#include "stm32n6xx_hal_mdios.h"
+#include "stm32n6xx_hal_mmc.h"
+#include "stm32n6xx_hal_nand.h"
+#include "stm32n6xx_hal_nor.h"
+#include "stm32n6xx_hal_nand.h"
+#include "stm32n6xx_hal_pcd.h"
+#include "stm32n6xx_hal_pka.h"
+#include "stm32n6xx_hal_pssi.h"
+#include "stm32n6xx_hal_pwr.h"
+#include "stm32n6xx_hal_ramcfg.h"
+#include "stm32n6xx_hal_rng.h"
+#include "stm32n6xx_hal_rtc.h"
+#include "stm32n6xx_hal_sai.h"
+#include "stm32n6xx_hal_sd.h"
+#include "stm32n6xx_hal_sdram.h"
+#include "stm32n6xx_hal_smartcard.h"
+#include "stm32n6xx_hal_smbus.h"
+#include "stm32n6xx_hal_spdifrx.h"
+#include "stm32n6xx_hal_spi.h"
+#include "stm32n6xx_hal_sram.h"
+#include "stm32n6xx_hal_tim.h"
+#include "stm32n6xx_hal_uart.h"
+#include "stm32n6xx_hal_usart.h"
+#include "stm32n6xx_hal_wwdg.h"
+#include "stm32n6xx_hal_xspi.h"
+#include "stm32n6xx_ll_lpuart.h"
+#include "stm32n6xx_ll_pwr.h"
+#include "stm32n6xx_ll_rtc.h"
+#include "stm32n6xx_ll_usart.h"
+
+// HAL parameter assertions are disabled
+#define assert_param(expr) ((void)0)
+
+#endif // MICROPY_INCLUDED_STM32N6XX_HAL_CONF_BASE_H
diff --git a/ports/stm32/cyw43_configport.h b/ports/stm32/cyw43_configport.h
index c26c554761..6528dbc625 100644
--- a/ports/stm32/cyw43_configport.h
+++ b/ports/stm32/cyw43_configport.h
@@ -27,14 +27,9 @@
#ifndef MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H
#define MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H
-// The board-level config will be included here, so it can set some CYW43 values.
-#include "py/mpconfig.h"
-#include "py/mperrno.h"
-#include "py/mphal.h"
-#include "extmod/modnetwork.h"
-#include "extmod/mpbthci.h"
#include "extint.h"
-#include "pendsv.h"
+#include "extmod/mpbthci.h"
+#include "extmod/cyw43_config_common.h"
#include "sdio.h"
#define CYW43_USE_SPI (0)
@@ -62,50 +57,12 @@
#define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE
#endif
-#define CYW43_IOCTL_TIMEOUT_US (1000000)
-#define CYW43_SLEEP_MAX (50)
-#define CYW43_NETUTILS (1)
#define CYW43_CLEAR_SDIO_INT (1)
-#define CYW43_EPERM MP_EPERM // Operation not permitted
-#define CYW43_EIO MP_EIO // I/O error
-#define CYW43_EINVAL MP_EINVAL // Invalid argument
-#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out
-
-#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER
-#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT
-#define CYW43_THREAD_LOCK_CHECK
-
-#define CYW43_HOST_NAME mod_network_hostname_data
-
#define CYW43_SDPCM_SEND_COMMON_WAIT __WFI();
#define CYW43_DO_IOCTL_WAIT __WFI();
#define CYW43_HAL_UART_READCHAR_BLOCKING_WAIT __WFI()
-#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_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate
#define cyw43_hal_uart_write mp_bluetooth_hci_uart_write
#define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar
@@ -132,24 +89,6 @@
#define CYW43_PIN_RFSW_SELECT pyb_pin_WL_GPIO_1
#endif
-#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func)
-
-void cyw43_post_poll_hook(void);
-
-static inline void cyw43_delay_us(uint32_t us) {
- uint32_t start = mp_hal_ticks_us();
- while (mp_hal_ticks_us() - start < us) {
- }
-}
-
-static inline void cyw43_delay_ms(uint32_t ms) {
- uint32_t us = ms * 1000;
- uint32_t start = mp_hal_ticks_us();
- while (mp_hal_ticks_us() - start < us) {
- MICROPY_EVENT_POLL_HOOK;
- }
-}
-
static inline void cyw43_hal_pin_config_irq_falling(cyw43_hal_pin_obj_t pin, int enable) {
if (enable) {
extint_set(pin, GPIO_MODE_IT_FALLING);
@@ -184,6 +123,4 @@ static inline int cyw43_sdio_transfer_cmd53(bool write, uint32_t block_size, uin
return sdio_transfer_cmd53(write, block_size, arg, len, buf);
}
-#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK
-
#endif // MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H
diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c
index df8a408cbf..c252770740 100644
--- a/ports/stm32/dma.c
+++ b/ports/stm32/dma.c
@@ -81,7 +81,7 @@ typedef union {
struct _dma_descr_t {
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
DMA_Stream_TypeDef *instance;
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
DMA_Channel_TypeDef *instance;
#else
#error "Unsupported Processor"
@@ -93,7 +93,7 @@ struct _dma_descr_t {
// Default parameters to dma_init() shared by spi and i2c; Channel and Direction
// vary depending on the peripheral instance so they get passed separately
-#if defined(STM32H5)
+#if defined(STM32H5) || defined(STM32N6)
static const DMA_InitTypeDef dma_init_struct_spi_i2c = {
.Request = 0, // set by dma_init_handle
.BlkHWRequest = DMA_BREQ_SINGLE_BURST,
@@ -157,7 +157,7 @@ static const DMA_InitTypeDef dma_init_struct_i2s = {
};
#endif
-#if ENABLE_SDIO && !defined(STM32H5) && !defined(STM32H7)
+#if ENABLE_SDIO && !defined(STM32H5) && !defined(STM32H7) && !defined(STM32N6)
// Parameters to dma_init() for SDIO tx and rx.
static const DMA_InitTypeDef dma_init_struct_sdio = {
#if defined(STM32F4) || defined(STM32F7)
@@ -735,9 +735,13 @@ const dma_descr_t dma_SPI_1_RX = { GPDMA1_Channel0, GPDMA1_REQUEST_SPI1_RX, dma_
const dma_descr_t dma_SPI_1_TX = { GPDMA1_Channel1, GPDMA1_REQUEST_SPI1_TX, dma_id_1, &dma_init_struct_spi_i2c };
const dma_descr_t dma_SPI_2_RX = { GPDMA1_Channel2, GPDMA1_REQUEST_SPI2_RX, dma_id_2, &dma_init_struct_spi_i2c };
const dma_descr_t dma_SPI_2_TX = { GPDMA1_Channel3, GPDMA1_REQUEST_SPI2_TX, dma_id_3, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_3_RX = { GPDMA1_Channel4, GPDMA1_REQUEST_SPI3_RX, dma_id_4, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_3_TX = { GPDMA1_Channel5, GPDMA1_REQUEST_SPI3_TX, dma_id_5, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_4_RX = { GPDMA1_Channel6, GPDMA1_REQUEST_SPI4_RX, dma_id_6, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_4_TX = { GPDMA1_Channel7, GPDMA1_REQUEST_SPI4_TX, dma_id_7, &dma_init_struct_spi_i2c };
#if MICROPY_HW_ENABLE_DAC
-const dma_descr_t dma_DAC_1_TX = { GPDMA1_Channel4, GPDMA1_REQUEST_DAC1_CH1, dma_id_4, &dma_init_struct_dac };
-const dma_descr_t dma_DAC_2_TX = { GPDMA1_Channel5, GPDMA1_REQUEST_DAC1_CH2, dma_id_5, &dma_init_struct_dac };
+const dma_descr_t dma_DAC_1_TX = { GPDMA2_Channel0, GPDMA1_REQUEST_DAC1_CH1, dma_id_8, &dma_init_struct_dac };
+const dma_descr_t dma_DAC_2_TX = { GPDMA2_Channel1, GPDMA1_REQUEST_DAC1_CH2, dma_id_9, &dma_init_struct_dac };
#endif
static const uint8_t dma_irqn[NSTREAM] = {
@@ -826,6 +830,46 @@ static const uint8_t dma_irqn[NSTREAM] = {
DMA2_Stream7_IRQn,
};
+#elif defined(STM32N6)
+
+#define NCONTROLLERS (1)
+#define NSTREAMS_PER_CONTROLLER (16)
+#define NSTREAM (NCONTROLLERS * NSTREAMS_PER_CONTROLLER)
+
+#define DMA_SUB_INSTANCE_AS_UINT8(dma_channel) (dma_channel)
+
+#define DMA1_ENABLE_MASK (0xffff) // Bits in dma_enable_mask corresponding to GPDMA1
+
+const dma_descr_t dma_SPI_1_RX = { GPDMA1_Channel0, GPDMA1_REQUEST_SPI1_RX, dma_id_0, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_1_TX = { GPDMA1_Channel1, GPDMA1_REQUEST_SPI1_TX, dma_id_1, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_2_RX = { GPDMA1_Channel2, GPDMA1_REQUEST_SPI2_RX, dma_id_2, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_2_TX = { GPDMA1_Channel3, GPDMA1_REQUEST_SPI2_TX, dma_id_3, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_3_RX = { GPDMA1_Channel4, GPDMA1_REQUEST_SPI3_RX, dma_id_4, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_3_TX = { GPDMA1_Channel5, GPDMA1_REQUEST_SPI3_TX, dma_id_5, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_4_RX = { GPDMA1_Channel6, GPDMA1_REQUEST_SPI4_RX, dma_id_6, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_4_TX = { GPDMA1_Channel7, GPDMA1_REQUEST_SPI4_TX, dma_id_7, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_5_RX = { GPDMA1_Channel8, GPDMA1_REQUEST_SPI5_RX, dma_id_8, &dma_init_struct_spi_i2c };
+const dma_descr_t dma_SPI_5_TX = { GPDMA1_Channel9, GPDMA1_REQUEST_SPI5_TX, dma_id_9, &dma_init_struct_spi_i2c };
+
+static const uint8_t dma_irqn[NSTREAM] = {
+ GPDMA1_Channel0_IRQn,
+ GPDMA1_Channel1_IRQn,
+ GPDMA1_Channel2_IRQn,
+ GPDMA1_Channel3_IRQn,
+ GPDMA1_Channel4_IRQn,
+ GPDMA1_Channel5_IRQn,
+ GPDMA1_Channel6_IRQn,
+ GPDMA1_Channel7_IRQn,
+ GPDMA1_Channel8_IRQn,
+ GPDMA1_Channel9_IRQn,
+ GPDMA1_Channel10_IRQn,
+ GPDMA1_Channel11_IRQn,
+ GPDMA1_Channel12_IRQn,
+ GPDMA1_Channel13_IRQn,
+ GPDMA1_Channel14_IRQn,
+ GPDMA1_Channel15_IRQn,
+};
+
#endif
static DMA_HandleTypeDef *dma_handle[NSTREAM] = {NULL};
@@ -849,6 +893,10 @@ volatile dma_idle_count_t dma_idle;
#define __HAL_RCC_DMA2_CLK_ENABLE __HAL_RCC_GPDMA2_CLK_ENABLE
#define __HAL_RCC_DMA1_CLK_DISABLE __HAL_RCC_GPDMA1_CLK_DISABLE
#define __HAL_RCC_DMA2_CLK_DISABLE __HAL_RCC_GPDMA2_CLK_DISABLE
+#elif defined(STM32N6)
+#define DMA1_IS_CLK_ENABLED() (__HAL_RCC_GPDMA1_IS_CLK_ENABLED())
+#define __HAL_RCC_DMA1_CLK_ENABLE __HAL_RCC_GPDMA1_CLK_ENABLE
+#define __HAL_RCC_DMA1_CLK_DISABLE __HAL_RCC_GPDMA1_CLK_DISABLE
#else
#define DMA1_IS_CLK_ENABLED() ((RCC->AHB1ENR & RCC_AHB1ENR_DMA1EN) != 0)
#define DMA2_IS_CLK_ENABLED() ((RCC->AHB1ENR & RCC_AHB1ENR_DMA2EN) != 0)
@@ -1181,10 +1229,11 @@ void DMA2_Channel8_IRQHandler(void) {
}
#endif
-#elif defined(STM32H5)
+#elif defined(STM32H5) || defined(STM32N6)
#define DEFINE_IRQ_HANDLER(periph, channel, id) \
void GPDMA##periph##_Channel##channel##_IRQHandler(void) { \
+ MP_STATIC_ASSERT(GPDMA##periph##_Channel##channel##_IRQn > 0); \
IRQ_ENTER(GPDMA##periph##_Channel##channel##_IRQn); \
if (dma_handle[id] != NULL) { \
HAL_DMA_IRQHandler(dma_handle[id]); \
@@ -1200,6 +1249,7 @@ DEFINE_IRQ_HANDLER(1, 4, dma_id_4)
DEFINE_IRQ_HANDLER(1, 5, dma_id_5)
DEFINE_IRQ_HANDLER(1, 6, dma_id_6)
DEFINE_IRQ_HANDLER(1, 7, dma_id_7)
+#if defined(STM32H5)
DEFINE_IRQ_HANDLER(2, 0, dma_id_8)
DEFINE_IRQ_HANDLER(2, 1, dma_id_9)
DEFINE_IRQ_HANDLER(2, 2, dma_id_10)
@@ -1208,6 +1258,16 @@ DEFINE_IRQ_HANDLER(2, 4, dma_id_12)
DEFINE_IRQ_HANDLER(2, 5, dma_id_13)
DEFINE_IRQ_HANDLER(2, 6, dma_id_14)
DEFINE_IRQ_HANDLER(2, 7, dma_id_15)
+#else
+DEFINE_IRQ_HANDLER(1, 8, dma_id_8)
+DEFINE_IRQ_HANDLER(1, 9, dma_id_9)
+DEFINE_IRQ_HANDLER(1, 10, dma_id_10)
+DEFINE_IRQ_HANDLER(1, 11, dma_id_11)
+DEFINE_IRQ_HANDLER(1, 12, dma_id_12)
+DEFINE_IRQ_HANDLER(1, 13, dma_id_13)
+DEFINE_IRQ_HANDLER(1, 14, dma_id_14)
+DEFINE_IRQ_HANDLER(1, 15, dma_id_15)
+#endif
#elif defined(STM32L0)
@@ -1420,7 +1480,7 @@ void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint3
dma->Instance = dma_descr->instance;
dma->Init = *dma_descr->init;
dma->Init.Direction = dir;
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
dma->Init.Request = dma_descr->sub_instance;
#else
#if !defined(STM32F0) && !defined(STM32L1)
@@ -1428,7 +1488,7 @@ void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint3
#endif
#endif
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
// Configure src/dest settings based on the DMA direction.
if (dir == DMA_MEMORY_TO_PERIPH) {
dma->Init.SrcInc = DMA_SINC_INCREMENTED;
@@ -1459,13 +1519,24 @@ void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir
dma_enable_clock(dma_id);
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
// Always reset and configure the H7 and G0/G4/H7/L0/L4/WB/WL DMA peripheral
// (dma->State is set to HAL_DMA_STATE_RESET by memset above)
// TODO: understand how L0/L4 DMA works so this is not needed
HAL_DMA_DeInit(dma);
dma->Parent = data; // HAL_DMA_DeInit may clear Parent, so set it again
HAL_DMA_Init(dma);
+
+ #if defined(STM32N6)
+ // Configure security attributes.
+ HAL_DMA_ConfigChannelAttributes(dma, DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC | DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC);
+ // Configure data handling.
+ DMA_DataHandlingConfTypeDef config;
+ config.DataExchange = DMA_EXCHANGE_NONE;
+ config.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
+ HAL_DMAEx_ConfigDataHandling(dma, &config);
+ #endif
+
NVIC_SetPriority(IRQn_NONNEG(dma_irqn[dma_id]), IRQ_PRI_DMA);
#else
// if this stream was previously configured for this channel/request and direction then we
@@ -1736,7 +1807,7 @@ void dma_nohal_start(const dma_descr_t *descr, uint32_t src_addr, uint32_t dst_a
dma->CCR |= DMA_CCR_EN;
}
-#elif defined(STM32G0) || defined(STM32WB) || defined(STM32WL)
+#elif defined(STM32G0) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
// These functions are currently not implemented or needed for this MCU.
diff --git a/ports/stm32/dma.h b/ports/stm32/dma.h
index 7ba04baf52..f05b22b5d0 100644
--- a/ports/stm32/dma.h
+++ b/ports/stm32/dma.h
@@ -147,6 +147,19 @@ extern const dma_descr_t dma_I2C_4_RX;
extern const dma_descr_t dma_SPI_SUBGHZ_TX;
extern const dma_descr_t dma_SPI_SUBGHZ_RX;
+#elif defined(STM32N6)
+
+extern const dma_descr_t dma_SPI_1_RX;
+extern const dma_descr_t dma_SPI_1_TX;
+extern const dma_descr_t dma_SPI_2_RX;
+extern const dma_descr_t dma_SPI_2_TX;
+extern const dma_descr_t dma_SPI_3_RX;
+extern const dma_descr_t dma_SPI_3_TX;
+extern const dma_descr_t dma_SPI_4_RX;
+extern const dma_descr_t dma_SPI_4_TX;
+extern const dma_descr_t dma_SPI_5_RX;
+extern const dma_descr_t dma_SPI_5_TX;
+
#endif
// API that configures the DMA via the HAL.
diff --git a/ports/stm32/extint.c b/ports/stm32/extint.c
index 5b5658809b..9c3c243253 100644
--- a/ports/stm32/extint.c
+++ b/ports/stm32/extint.c
@@ -92,7 +92,7 @@
#define EXTI_SWIER_BB(line) (*(__IO uint32_t *)(PERIPH_BB_BASE + ((EXTI_OFFSET + offsetof(EXTI_TypeDef, SWIER)) * 32) + ((line) * 4)))
#endif
-#if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+#if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
// The L4 MCU supports 40 Events/IRQs lines of the type configurable and direct.
// Here we only support configurable line types. Details, see page 330 of RM0351, Rev 1.
// The USB_FS_WAKUP event is a direct type and there is no support for it.
@@ -170,7 +170,7 @@ static const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = {
ADC1_COMP_IRQn,
#endif
- #elif defined(STM32H5)
+ #elif defined(STM32N6) || defined(STM32H5)
EXTI0_IRQn,
EXTI1_IRQn,
@@ -189,6 +189,13 @@ static const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = {
EXTI14_IRQn,
EXTI15_IRQn,
+ #if defined(STM32N6)
+ 0,
+ RTC_S_IRQn,
+ RTC_IRQn,
+ TAMP_IRQn,
+ #endif
+
#else
EXTI0_IRQn, EXTI1_IRQn, EXTI2_IRQn, EXTI3_IRQn, EXTI4_IRQn,
@@ -307,7 +314,7 @@ void EXTI15_10_IRQHandler(void) {
IRQ_EXIT(EXTI15_10_IRQn);
}
-#elif defined(STM32H5)
+#elif defined(STM32H5) || defined(STM32N6)
DEFINE_EXTI_IRQ_HANDLER(0)
DEFINE_EXTI_IRQ_HANDLER(1)
@@ -440,10 +447,10 @@ void extint_register_pin(const machine_pin_obj_t *pin, uint32_t mode, bool hard_
#if !defined(STM32H5) && !defined(STM32WB) && !defined(STM32WL)
__HAL_RCC_SYSCFG_CLK_ENABLE();
#endif
- #if defined(STM32G0) || defined(STM32H5)
+ #if defined(STM32G0) || defined(STM32H5) || defined(STM32N6)
EXTI->EXTICR[line >> 2] =
- (EXTI->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03))))
- | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (4 * (line & 0x03)));
+ (EXTI->EXTICR[line >> 2] & ~(0xff << (8 * (line & 0x03))))
+ | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (8 * (line & 0x03)));
#else
SYSCFG->EXTICR[line >> 2] =
(SYSCFG->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03))))
@@ -480,13 +487,13 @@ void extint_set(const machine_pin_obj_t *pin, uint32_t mode) {
pyb_extint_callback_arg[line] = MP_OBJ_FROM_PTR(pin);
// Route the GPIO to EXTI
- #if !defined(STM32H5) && !defined(STM32WB) && !defined(STM32WL)
+ #if !defined(STM32H5) && !defined(STM32N6) && !defined(STM32WB) && !defined(STM32WL)
__HAL_RCC_SYSCFG_CLK_ENABLE();
#endif
- #if defined(STM32G0) || defined(STM32H5)
+ #if defined(STM32G0) || defined(STM32H5) || defined(STM32N6)
EXTI->EXTICR[line >> 2] =
- (EXTI->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03))))
- | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (4 * (line & 0x03)));
+ (EXTI->EXTICR[line >> 2] & ~(0xff << (8 * (line & 0x03))))
+ | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (8 * (line & 0x03)));
#else
SYSCFG->EXTICR[line >> 2] =
(SYSCFG->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03))))
@@ -526,7 +533,7 @@ void extint_enable(uint line) {
if (pyb_extint_mode[line] == EXTI_Mode_Interrupt) {
#if defined(STM32H7)
EXTI_D1->IMR1 |= (1 << line);
- #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WB) || defined(STM32WL)
+ #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
EXTI->IMR1 |= (1 << line);
#else
EXTI->IMR |= (1 << line);
@@ -534,7 +541,7 @@ void extint_enable(uint line) {
} else {
#if defined(STM32H7)
EXTI_D1->EMR1 |= (1 << line);
- #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WB) || defined(STM32WL)
+ #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
EXTI->EMR1 |= (1 << line);
#else
EXTI->EMR |= (1 << line);
@@ -560,7 +567,7 @@ void extint_disable(uint line) {
#if defined(STM32H7)
EXTI_D1->IMR1 &= ~(1 << line);
EXTI_D1->EMR1 &= ~(1 << line);
- #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WB) || defined(STM32WL)
+ #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
EXTI->IMR1 &= ~(1 << line);
EXTI->EMR1 &= ~(1 << line);
#else
@@ -582,7 +589,7 @@ void extint_swint(uint line) {
return;
}
// we need 0 to 1 transition to trigger the interrupt
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
EXTI->SWIER1 &= ~(1 << line);
EXTI->SWIER1 |= (1 << line);
#else
@@ -662,7 +669,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_swint_obj, extint_obj_swint);
static mp_obj_t extint_regs(void) {
const mp_print_t *print = &mp_plat_print;
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
mp_printf(print, "EXTI_IMR1 %08x\n", (unsigned int)EXTI->IMR1);
mp_printf(print, "EXTI_IMR2 %08x\n", (unsigned int)EXTI->IMR2);
mp_printf(print, "EXTI_EMR1 %08x\n", (unsigned int)EXTI->EMR1);
@@ -673,7 +680,7 @@ static mp_obj_t extint_regs(void) {
mp_printf(print, "EXTI_FTSR2 %08x\n", (unsigned int)EXTI->FTSR2);
mp_printf(print, "EXTI_SWIER1 %08x\n", (unsigned int)EXTI->SWIER1);
mp_printf(print, "EXTI_SWIER2 %08x\n", (unsigned int)EXTI->SWIER2);
- #if defined(STM32G0) || defined(STM32H5)
+ #if defined(STM32G0) || defined(STM32N6) || defined(STM32H5)
mp_printf(print, "EXTI_RPR1 %08x\n", (unsigned int)EXTI->RPR1);
mp_printf(print, "EXTI_FPR1 %08x\n", (unsigned int)EXTI->FPR1);
mp_printf(print, "EXTI_RPR2 %08x\n", (unsigned int)EXTI->RPR2);
diff --git a/ports/stm32/extint.h b/ports/stm32/extint.h
index d5abb04d65..801dcf62b5 100644
--- a/ports/stm32/extint.h
+++ b/ports/stm32/extint.h
@@ -46,7 +46,7 @@
#if defined(STM32F0) || defined(STM32G4) || defined(STM32L1) || defined(STM32L4) || defined(STM32WL)
#define EXTI_RTC_TIMESTAMP (19)
#define EXTI_RTC_WAKEUP (20)
-#elif defined(STM32H5)
+#elif defined(STM32H5) || defined(STM32N6)
#define EXTI_RTC_WAKEUP (17)
#define EXTI_RTC_TAMP (19)
#elif defined(STM32H7) || defined(STM32WB)
@@ -55,8 +55,6 @@
#elif defined(STM32G0)
#define EXTI_RTC_WAKEUP (19)
#define EXTI_RTC_TIMESTAMP (21)
-#elif defined(STM32H5)
-#define EXTI_RTC_WAKEUP (17)
#else
#define EXTI_RTC_TIMESTAMP (21)
#define EXTI_RTC_WAKEUP (22)
diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c
index 7668b53a43..85bcee5a97 100644
--- a/ports/stm32/flash.c
+++ b/ports/stm32/flash.c
@@ -30,6 +30,8 @@
#include "py/mphal.h"
#include "flash.h"
+#if !defined(STM32N6)
+
#if defined(STM32F0)
#define FLASH_FLAG_ALL_ERRORS (FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR)
@@ -509,3 +511,5 @@ int flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) {
return mp_hal_status_to_neg_errno(status);
}
+
+#endif
diff --git a/ports/stm32/i2cslave.h b/ports/stm32/i2cslave.h
index a4b2957de3..cc4e7f9be9 100644
--- a/ports/stm32/i2cslave.h
+++ b/ports/stm32/i2cslave.h
@@ -51,6 +51,16 @@ static inline void i2c_slave_init(i2c_slave_t *i2c, int irqn, int irq_pri, int a
RCC->APB1LENR |= 1 << (RCC_APB1LENR_I2C1EN_Pos + i2c_idx);
volatile uint32_t tmp = RCC->APB1LENR; // Delay after enabling clock
(void)tmp;
+ #elif defined(STM32N6)
+ if (i2c_idx == 3) {
+ RCC->APB4ENR1 |= RCC_APB4ENR1_I2C4EN;
+ volatile uint32_t tmp = RCC->APB4ENR1; // Delay after enabling clock
+ (void)tmp;
+ } else {
+ RCC->APB1ENR1 |= 1 << (RCC_APB1ENR1_I2C1EN_Pos + i2c_idx);
+ volatile uint32_t tmp = RCC->APB1ENR1; // Delay after enabling clock
+ (void)tmp;
+ }
#elif defined(STM32WB)
RCC->APB1ENR1 |= 1 << (RCC_APB1ENR1_I2C1EN_Pos + i2c_idx);
volatile uint32_t tmp = RCC->APB1ENR1; // Delay after enabling clock
diff --git a/ports/stm32/irq.h b/ports/stm32/irq.h
index 58e6d0a804..dfe901ff74 100644
--- a/ports/stm32/irq.h
+++ b/ports/stm32/irq.h
@@ -155,6 +155,9 @@ static inline void restore_irq_pri(uint32_t state) {
// SDIO must be higher priority than DMA for SDIO DMA transfers to work.
#define IRQ_PRI_SDIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 4, 0)
+// SPI must be higher priority than DMA for SPI DMA transfers to work.
+#define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 4, 0)
+
// DMA should be higher priority than USB, since USB Mass Storage calls
// into the sdcard driver which waits for the DMA to complete.
#define IRQ_PRI_DMA NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 5, 0)
@@ -172,8 +175,6 @@ static inline void restore_irq_pri(uint32_t state) {
#define IRQ_PRI_SUBGHZ_RADIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0)
-#define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0)
-
#define IRQ_PRI_HSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 10, 0)
// Interrupt priority for non-special timers.
diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h
index 9e2402c8dc..ad1143845f 100644
--- a/ports/stm32/lwip_inc/lwipopts.h
+++ b/ports/stm32/lwip_inc/lwipopts.h
@@ -10,6 +10,15 @@
#define LWIP_RAND() rng_get()
+// Increase memory for lwIP to get better performance.
+#if defined(STM32N6)
+#define MEM_SIZE (16 * 1024)
+#define TCP_MSS (1460)
+#define TCP_WND (8 * TCP_MSS)
+#define TCP_SND_BUF (8 * TCP_MSS)
+#define MEMP_NUM_TCP_SEG (32)
+#endif
+
// Include common lwIP configuration.
#include "extmod/lwip-include/lwipopts_common.h"
diff --git a/ports/stm32/machine_adc.c b/ports/stm32/machine_adc.c
index 8f07075914..63cd4e089d 100644
--- a/ports/stm32/machine_adc.c
+++ b/ports/stm32/machine_adc.c
@@ -30,7 +30,7 @@
#include "py/mphal.h"
#include "adc.h"
-#if defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+#if defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
#define ADC_V2 (1)
#else
#define ADC_V2 (0)
@@ -80,11 +80,14 @@
#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5
#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_160CYCLES_5
#elif defined(STM32L1)
-#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_384CYCLES
+#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_16CYCLES
#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_384CYCLES
#elif defined(STM32L4) || defined(STM32WB)
#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5
#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_247CYCLES_5
+#elif defined(STM32N6)
+#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_11CYCLES_5
+#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_246CYCLES_5
#endif
// Timeout for waiting for end-of-conversion
@@ -127,6 +130,8 @@ static uint32_t adc_ll_channel(uint32_t channel_id) {
case MACHINE_ADC_INT_CH_TEMPSENSOR:
#if defined(STM32G4)
adc_ll_ch = ADC_CHANNEL_TEMPSENSOR_ADC1;
+ #elif defined(STM32N6)
+ adc_ll_ch = ADC_CHANNEL_0; // TODO
#else
adc_ll_ch = ADC_CHANNEL_TEMPSENSOR;
#endif
@@ -183,7 +188,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
if (adc == ADC1) {
#if defined(STM32H5)
__HAL_RCC_ADC_CLK_ENABLE();
- #elif defined(STM32G4) || defined(STM32H7)
+ #elif defined(STM32G4) || defined(STM32H7) || defined(STM32N6)
__HAL_RCC_ADC12_CLK_ENABLE();
#else
__HAL_RCC_ADC1_CLK_ENABLE();
@@ -193,7 +198,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
if (adc == ADC2) {
#if defined(STM32H5)
__HAL_RCC_ADC_CLK_ENABLE();
- #elif defined(STM32G4) || defined(STM32H7)
+ #elif defined(STM32G4) || defined(STM32H7) || defined(STM32N6)
__HAL_RCC_ADC12_CLK_ENABLE();
#else
__HAL_RCC_ADC2_CLK_ENABLE();
@@ -225,13 +230,15 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
ADC3_COMMON->CCR = 3 << ADC_CCR_CKMODE_Pos;
#elif defined(STM32L0)
ADC1_COMMON->CCR = 0; // ADCPR=PCLK/2
+ #elif defined(STM32L1)
+ ADC1_COMMON->CCR = 1 << ADC_CCR_ADCPRE_Pos; // ADCPRE=2
#elif defined(STM32WB)
ADC1_COMMON->CCR = 0 << ADC_CCR_PRESC_Pos | 0 << ADC_CCR_CKMODE_Pos; // PRESC=1, MODE=ASYNC
#elif defined(STM32WL)
ADC_COMMON->CCR = 0 << ADC_CCR_PRESC_Pos; // PRESC=1
#endif
- #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
+ #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
if (adc->CR & ADC_CR_DEEPPWD) {
adc->CR = 0; // disable deep powerdown
}
@@ -255,6 +262,11 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
LL_ADC_StartCalibration(adc);
#elif defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB)
LL_ADC_StartCalibration(adc, LL_ADC_SINGLE_ENDED);
+ #elif defined(STM32N6)
+ ADC_HandleTypeDef hadc;
+ hadc.Instance = adc;
+ hadc.State = 0;
+ HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED);
#else
LL_ADC_StartCalibration(adc, LL_ADC_CALIB_OFFSET_LINEARITY, LL_ADC_SINGLE_ENDED);
#endif
@@ -310,11 +322,17 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
uint32_t cfgr = res << ADC_CFGR_RES_Pos;
adc->CFGR = (adc->CFGR & ~cfgr_clr) | cfgr;
+ #elif defined(STM32N6)
+
+ uint32_t cfgr1_clr = ADC_CFGR1_CONT | ADC_CFGR1_EXTEN;
+ uint32_t cfgr1 = res << ADC_CFGR1_RES_Pos;
+ adc->CFGR1 = (adc->CFGR1 & ~cfgr1_clr) | cfgr1;
+
#endif
}
static int adc_get_bits(ADC_TypeDef *adc) {
- #if defined(STM32F0) || defined(STM32G0) || defined(STM32L0) || defined(STM32WL)
+ #if defined(STM32F0) || defined(STM32G0) || defined(STM32L0) || defined(STM32N6) || defined(STM32WL)
uint32_t res = (adc->CFGR1 & ADC_CFGR1_RES) >> ADC_CFGR1_RES_Pos;
#elif defined(STM32F4) || defined(STM32F7) || defined(STM32L1)
uint32_t res = (adc->CR1 & ADC_CR1_RES) >> ADC_CR1_RES_Pos;
@@ -385,8 +403,34 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp
}
*smpr = (*smpr & ~(7 << (channel * 3))) | sample_time << (channel * 3); // select sample time
- #elif defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)
- #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ)
+ #elif defined(STM32L1)
+
+ ADC_Common_TypeDef *adc_common = ADC1_COMMON;
+ if (channel == ADC_CHANNEL_VREFINT || channel == ADC_CHANNEL_TEMPSENSOR) {
+ adc_common->CCR |= ADC_CCR_TSVREFE;
+ if (channel == ADC_CHANNEL_TEMPSENSOR) {
+ adc_stabilisation_delay_us(ADC_TEMPSENSOR_DELAY_US);
+ }
+ }
+
+ adc->SQR1 = (1 - 1) << ADC_SQR1_L_Pos;
+ adc->SQR5 = (channel & 0x1f) << ADC_SQR5_SQ1_Pos;
+
+ __IO uint32_t *smpr;
+ if (channel >= 20) {
+ smpr = &adc->SMPR1;
+ channel -= 20;
+ } else if (channel >= 10) {
+ smpr = &adc->SMPR2;
+ channel -= 10;
+ } else {
+ smpr = &adc->SMPR3;
+ }
+ *smpr = (*smpr & ~(7 << (channel * 3))) | sample_time << (channel * 3); // select sample time
+
+ #elif defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB)
+
+ #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) || defined(STM32N6)
ADC_Common_TypeDef *adc_common = ADC12_COMMON;
#elif defined(STM32H7)
#if defined(ADC_VER_V5_V90)
@@ -404,6 +448,7 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp
#endif
if (channel == ADC_CHANNEL_VREFINT) {
adc_common->CCR |= ADC_CCR_VREFEN;
+ #if !defined(STM32N6)
#if defined(STM32G4)
} else if (channel == ADC_CHANNEL_TEMPSENSOR_ADC1) {
adc_common->CCR |= ADC_CCR_VSENSESEL;
@@ -412,19 +457,20 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp
adc_common->CCR |= ADC_CCR_TSEN;
#endif
adc_stabilisation_delay_us(ADC_TEMPSENSOR_DELAY_US);
+ #endif
} else if (channel == ADC_CHANNEL_VBAT) {
#if defined(STM32G4)
adc_common->CCR |= ADC_CCR_VBATSEL;
#else
adc_common->CCR |= ADC_CCR_VBATEN;
#endif
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
} else if (channel == ADC_CHANNEL_VDDCORE) {
adc->OR |= ADC_OR_OP0; // Enable Vddcore channel on ADC2
#endif
}
- #if defined(STM32G4) || defined(STM32H5)
- // G4 and H5 use encoded literals for internal channels -> extract ADC channel for following code
+ #if defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB)
+ // MCU uses encoded literals for internal channels -> extract ADC channel for following code
if (__LL_ADC_IS_CHANNEL_INTERNAL(channel)) {
channel = __LL_ADC_CHANNEL_TO_DECIMAL_NB(channel);
}
@@ -445,10 +491,13 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp
static uint32_t adc_read_channel(ADC_TypeDef *adc) {
uint32_t value;
- #if defined(STM32G4)
- // For STM32G4 there is errata 2.7.7, "Wrong ADC result if conversion done late after
- // calibration or previous conversion". According to the errata, this can be avoided
- // by performing two consecutive ADC conversions and keeping the second result.
+ #if defined(STM32G4) || defined(STM32WB)
+ // For STM32G4 errata 2.7.7 / STM32WB errata 2.7.1:
+ // "Wrong ADC result if conversion done late after calibration or previous conversion"
+ // states an incorrect reading is returned if more than 1ms has elapsed since the last
+ // reading or calibration. According to the errata, this can be avoided by performing
+ // two consecutive ADC conversions and keeping the second result.
+ // Note: On STM32WB55 @ 64Mhz each ADC read takes ~ 3us.
for (uint8_t i = 0; i < 2; i++)
#endif
{
diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c
index 8444b29989..8f1faea4b6 100644
--- a/ports/stm32/machine_uart.c
+++ b/ports/stm32/machine_uart.c
@@ -399,7 +399,7 @@ static bool mp_machine_uart_txdone(machine_uart_obj_t *self) {
// Send a break condition.
static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) {
- #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
self->uartx->RQR = USART_RQR_SBKRQ; // write-only register
#else
self->uartx->CR1 |= USART_CR1_SBK;
diff --git a/ports/stm32/main.c b/ports/stm32/main.c
index e8395013b9..137e132817 100644
--- a/ports/stm32/main.c
+++ b/ports/stm32/main.c
@@ -306,11 +306,30 @@ static bool init_sdcard_fs(void) {
}
#endif
+#if defined(STM32N6)
+static void risaf_init(void) {
+ RIMC_MasterConfig_t rimc_master = {0};
+
+ __HAL_RCC_RIFSC_CLK_ENABLE();
+ LL_AHB3_GRP1_EnableClockLowPower(LL_AHB3_GRP1_PERIPH_RIFSC | LL_AHB3_GRP1_PERIPH_RISAF);
+
+ rimc_master.MasterCID = RIF_CID_1;
+ rimc_master.SecPriv = RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV;
+
+ HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_SDMMC1, &rimc_master);
+ HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_SDMMC1, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV);
+ HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_SDMMC2, &rimc_master);
+ HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_SDMMC2, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV);
+}
+#endif
+
void stm32_main(uint32_t reset_mode) {
// Low-level MCU initialisation.
stm32_system_init();
- #if !defined(STM32F0)
+ // Set VTOR, the location of the interrupt vector table.
+ // On N6, SystemInit does this, setting VTOR to &g_pfnVectors.
+ #if !defined(STM32F0) && !defined(STM32N6)
#if MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM
// Copy IRQ vector table to RAM and point VTOR there
extern uint32_t __isr_vector_flash_addr, __isr_vector_ram_start, __isr_vector_ram_end;
@@ -325,8 +344,7 @@ void stm32_main(uint32_t reset_mode) {
#endif
#endif
-
- #if __CORTEX_M != 33
+ #if __CORTEX_M != 33 && __CORTEX_M != 55
// Enable 8-byte stack alignment for IRQ handlers, in accord with EABI
SCB->CCR |= SCB_CCR_STKALIGN_Msk;
#endif
@@ -349,14 +367,18 @@ void stm32_main(uint32_t reset_mode) {
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
- #elif defined(STM32F7) || defined(STM32H7)
+ #elif defined(STM32F7) || defined(STM32H7) || defined(STM32N6)
#if ART_ACCLERATOR_ENABLE
__HAL_FLASH_ART_ENABLE();
#endif
SCB_EnableICache();
+ #if defined(STM32N6) && !defined(NDEBUG)
+ // Don't enable D-cache on N6 when debugging; see ST Errata ES0620 - Rev 0.2 section 2.1.2.
+ #else
SCB_EnableDCache();
+ #endif
#elif defined(STM32H5)
@@ -376,6 +398,23 @@ void stm32_main(uint32_t reset_mode) {
#endif
+ #if defined(STM32N6)
+ // SRAM, XSPI needs to remain awake during sleep, eg so DMA from flash works.
+ LL_MEM_EnableClockLowPower(0xffffffff);
+ LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_XSPI2 | LL_AHB5_GRP1_PERIPH_XSPIM);
+ LL_APB4_GRP1_EnableClock(LL_APB4_GRP1_PERIPH_RTC | LL_APB4_GRP1_PERIPH_RTCAPB);
+ LL_APB4_GRP1_EnableClockLowPower(LL_APB4_GRP1_PERIPH_RTC | LL_APB4_GRP1_PERIPH_RTCAPB);
+
+ // Enable some AHB peripherals during sleep.
+ LL_AHB1_GRP1_EnableClockLowPower(0xffffffff); // GPDMA1, ADC12
+ LL_AHB4_GRP1_EnableClockLowPower(0xffffffff); // GPIOA-Q, PWR, CRC
+
+ // Enable some APB peripherals during sleep.
+ LL_APB1_GRP1_EnableClockLowPower(0xffffffff); // I2C, I3C, LPTIM, SPI, TIM, UART, WWDG
+ LL_APB2_GRP1_EnableClockLowPower(0xffffffff); // SAI, SPI, TIM, UART
+ LL_APB4_GRP1_EnableClockLowPower(0xffffffff); // I2C, LPTIM, LPUART, RTC, SPI
+ #endif
+
mpu_init();
#if __CORTEX_M >= 0x03
@@ -389,6 +428,10 @@ void stm32_main(uint32_t reset_mode) {
// set the system clock to be HSE
SystemClock_Config();
+ #if defined(STM32N6)
+ risaf_init();
+ #endif
+
#if defined(STM32F4) || defined(STM32F7)
#if defined(__HAL_RCC_DTCMRAMEN_CLK_ENABLE)
// The STM32F746 doesn't really have CCM memory, but it does have DTCM,
@@ -521,6 +564,23 @@ soft_reset:
// GC init
gc_init(MICROPY_HEAP_START, MICROPY_HEAP_END);
+ // Add additional GC blocks (if enabled).
+ #if MICROPY_GC_SPLIT_HEAP
+ typedef struct {
+ uint8_t *addr;
+ uint32_t size;
+ } gc_blocks_table_t;
+
+ extern const gc_blocks_table_t _gc_blocks_table_start;
+ extern const gc_blocks_table_t _gc_blocks_table_end;
+
+ for (gc_blocks_table_t const *block = &_gc_blocks_table_start; block < &_gc_blocks_table_end; block++) {
+ if (block->size) {
+ gc_add(block->addr, block->addr + block->size);
+ }
+ }
+ #endif
+
#if MICROPY_ENABLE_PYSTACK
static mp_obj_t pystack[384];
mp_pystack_init(pystack, &pystack[384]);
diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile
index 87bced1aee..7226dd353f 100755
--- a/ports/stm32/mboot/Makefile
+++ b/ports/stm32/mboot/Makefile
@@ -34,7 +34,13 @@ include ../../../py/mkenv.mk
include $(BOARD_DIR)/mpconfigboard.mk
# A board can set MBOOT_TEXT0_ADDR to a custom location where mboot should reside.
+ifeq ($(MCU_SERIES),n6)
+MBOOT_TEXT0_ADDR ?= 0x34180400
+MBOOT_LD_FILES ?= stm32_memory_n6.ld stm32_sections.ld
+else
MBOOT_TEXT0_ADDR ?= 0x08000000
+MBOOT_LD_FILES ?= stm32_memory.ld stm32_sections.ld
+endif
# The string in MBOOT_VERSION (default defined in version.c if not defined by a
# board) will be stored in the final MBOOT_VERSION_ALLOCATED_BYTES bytes of mboot flash.
@@ -42,6 +48,7 @@ MBOOT_TEXT0_ADDR ?= 0x08000000
MBOOT_VERSION_ALLOCATED_BYTES ?= 64
MBOOT_VERSION_INCLUDE_OPTIONS ?= 1 # if set to 1, this will append build options to version string (see version.c)
+CROSS_COMPILE ?= arm-none-eabi-
USBDEV_DIR=usbdev
DFU=$(TOP)/tools/dfu.py
PYDFU ?= $(TOP)/tools/pydfu.py
@@ -53,8 +60,6 @@ OPENOCD_CONFIG ?= boards/openocd_stm32f4.cfg
include ../stm32.mk
-CROSS_COMPILE ?= arm-none-eabi-
-
INC += -I.
INC += -I..
INC += -I$(TOP)
@@ -89,7 +94,6 @@ CFLAGS += -DMBOOT_VERSION=\"$(MBOOT_VERSION)\"
endif
CFLAGS += -DMBOOT_VERSION_ALLOCATED_BYTES=$(MBOOT_VERSION_ALLOCATED_BYTES) -DMBOOT_VERSION_INCLUDE_OPTIONS=$(MBOOT_VERSION_INCLUDE_OPTIONS)
-MBOOT_LD_FILES ?= stm32_memory.ld stm32_sections.ld
LDFLAGS += -nostdlib -L . $(addprefix -T,$(MBOOT_LD_FILES)) -Map=$(@:.elf=.map) --cref
LDFLAGS += --defsym mboot_version_len=$(MBOOT_VERSION_ALLOCATED_BYTES)
LIBS += $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
@@ -137,12 +141,11 @@ SRC_C += \
drivers/bus/softqspi.c \
drivers/memory/spiflash.c \
ports/stm32/flash.c \
- ports/stm32/flashbdev.c \
ports/stm32/i2cslave.c \
ports/stm32/powerctrlboot.c \
ports/stm32/qspi.c \
- ports/stm32/spibdev.c \
ports/stm32/usbd_conf.c \
+ ports/stm32/xspi.c \
$(wildcard $(BOARD_DIR)/*.c)
SRC_O += \
@@ -169,16 +172,22 @@ SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal.c \
hal_cortex.c \
hal_dma.c \
- hal_flash.c \
- hal_flash_ex.c \
hal_pcd.c \
hal_pcd_ex.c \
hal_pwr_ex.c \
hal_rcc.c \
hal_rcc_ex.c \
+ ll_rcc.c \
ll_usb.c \
)
+ifneq ($(MCU_SERIES),n6)
+SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
+ hal_flash.c \
+ hal_flash_ex.c \
+ )
+endif
+
ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h7))
SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
hal_mmc.c \
@@ -187,6 +196,12 @@ SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
)
endif
+ifeq ($(MCU_SERIES),n6)
+SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\
+ hal_bsec.c \
+ )
+endif
+
SRC_USBDEV += $(addprefix ports/stm32/$(USBDEV_DIR)/,\
core/src/usbd_core.c \
core/src/usbd_ctlreq.c \
@@ -206,7 +221,7 @@ $(TOP)/lib/stm32lib/README.md:
$(ECHO) "stm32lib submodule not found, fetching it now..."
(cd $(TOP) && git submodule update --init lib/stm32lib)
-.PHONY: deploy deploy-stlink
+.PHONY: deploy deploy-stlink deploy-trusted
deploy: $(BUILD)/firmware.dfu
$(ECHO) "Writing $< to the board"
@@ -216,9 +231,15 @@ deploy-stlink: $(BUILD)/firmware.dfu
$(ECHO) "Writing $< to the board via ST-LINK"
$(Q)$(STFLASH) write $(BUILD)/firmware.bin $(MBOOT_TEXT0_ADDR)
-$(BUILD)/firmware.dfu: $(BUILD)/firmware.elf
+deploy-trusted: $(BUILD)/firmware-trusted.bin
+ $(STM32_CUBE_PROGRAMMER)/bin/STM32_Programmer.sh -c port=SWD mode=HOTPLUG ap=1 -el $(DKEL) -w $^ 0x70000000 -hardRst
+
+$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
$(ECHO) "Create $@"
$(Q)$(OBJCOPY) -O binary -j .isr_vector -j .text -j .data -j .mboot_version_text $^ $(BUILD)/firmware.bin
+
+$(BUILD)/firmware.dfu: $(BUILD)/firmware.bin
+ $(ECHO) "Create $@"
$(Q)$(PYTHON) $(DFU) -b $(MBOOT_TEXT0_ADDR):$(BUILD)/firmware.bin $@
$(BUILD)/firmware.hex: $(BUILD)/firmware.elf
@@ -230,6 +251,10 @@ $(BUILD)/firmware.elf: $(OBJ)
$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
$(Q)$(SIZE) $@
+$(BUILD)/firmware-trusted.bin: $(BUILD)/firmware.bin
+ /bin/rm -f $@
+ $(STM32_CUBE_PROGRAMMER)/bin/STM32_SigningTool_CLI -bin $^ -nk -of 0x80000000 -t fsbl -o $@ -hv $(STM32_N6_HEADER_VERSION)
+
#########################################
# Rules to generate header files
diff --git a/ports/stm32/mboot/adc.c b/ports/stm32/mboot/adc.c
index c7b9749244..06db0b59b7 100644
--- a/ports/stm32/mboot/adc.c
+++ b/ports/stm32/mboot/adc.c
@@ -1,3 +1,5 @@
// Include the main ADC driver, so mboot can use adc_config() and adc_config_and_read_u16().
#include "py/obj.h"
+#if MICROPY_PY_MACHINE_ADC
#include "../machine_adc.c"
+#endif
diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c
index 01f8892a51..2be8793351 100644
--- a/ports/stm32/mboot/main.c
+++ b/ports/stm32/mboot/main.c
@@ -41,6 +41,7 @@
#include "sdcard.h"
#include "dfu.h"
#include "pack.h"
+#include "xspi.h"
// Whether the bootloader will leave via reset, or direct jump to the application.
#ifndef MBOOT_LEAVE_BOOTLOADER_VIA_RESET
@@ -176,7 +177,7 @@ void HAL_Delay(uint32_t ms) {
mp_hal_delay_ms(ms);
}
-NORETURN static void __fatal_error(const char *msg) {
+MP_NORETURN static void __fatal_error(const char *msg) {
NVIC_SystemReset();
for (;;) {
}
@@ -373,7 +374,7 @@ void SystemClock_Config(void) {
#elif defined(STM32G0)
#define AHBxENR IOPENR
#define AHBxENR_GPIOAEN_Pos RCC_IOPENR_GPIOAEN_Pos
-#elif defined(STM32H7)
+#elif defined(STM32H7) || defined(STM32N6)
#define AHBxENR AHB4ENR
#define AHBxENR_GPIOAEN_Pos RCC_AHB4ENR_GPIOAEN_Pos
#elif defined(STM32H5) || defined(STM32WB)
@@ -424,6 +425,10 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed) {
#define MBOOT_SPIFLASH2_LAYOUT ""
#endif
+#if defined(STM32N6)
+#define FLASH_LAYOUT_STR "@Internal Flash " MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT
+#else
+
#if defined(STM32F4) \
|| defined(STM32F722xx) \
|| defined(STM32F723xx) \
@@ -584,12 +589,18 @@ static int mboot_flash_write(uint32_t addr, const uint8_t *src8, size_t len) {
return 0;
}
+#endif
+
/******************************************************************************/
// Writable address space interface
static int do_mass_erase(void) {
+ #if defined(STM32N6)
+ return -1;
+ #else
// TODO spiflash erase ?
return mboot_flash_mass_erase();
+ #endif
}
#if defined(MBOOT_SPIFLASH_ADDR) || defined(MBOOT_SPIFLASH2_ADDR)
@@ -625,7 +636,12 @@ int hw_page_erase(uint32_t addr, uint32_t *next_addr) {
} else
#endif
{
+ #if defined(STM32N6)
+ dfu_context.status = DFU_STATUS_ERROR_ADDRESS;
+ dfu_context.error = MBOOT_ERROR_STR_INVALID_ADDRESS_IDX;
+ #else
ret = mboot_flash_page_erase(addr, next_addr);
+ #endif
}
mboot_state_change(MBOOT_STATE_ERASE_END, ret);
@@ -678,9 +694,12 @@ int hw_write(uint32_t addr, const uint8_t *src8, size_t len) {
ret = mp_spiflash_write(MBOOT_SPIFLASH2_SPIFLASH, addr - MBOOT_SPIFLASH2_ADDR, len, src8);
} else
#endif
+ #if !defined(STM32N6)
if (flash_is_valid_addr(addr)) {
ret = mboot_flash_write(addr, src8, len);
- } else {
+ } else
+ #endif
+ {
dfu_context.status = DFU_STATUS_ERROR_ADDRESS;
dfu_context.error = MBOOT_ERROR_STR_INVALID_ADDRESS_IDX;
}
@@ -1443,7 +1462,7 @@ static int pyb_usbdd_shutdown(void) {
/******************************************************************************/
// main
-NORETURN static __attribute__((naked)) void branch_to_application(uint32_t r0, uint32_t bl_addr) {
+MP_NORETURN static __attribute__((naked)) void branch_to_application(uint32_t r0, uint32_t bl_addr) {
__asm volatile (
"ldr r2, [r1, #0]\n" // get address of stack pointer
"msr msp, r2\n" // set stack pointer
@@ -1509,7 +1528,7 @@ void stm32_main(uint32_t initial_r0) {
// Make sure IRQ vector table points to flash where this bootloader lives.
SCB->VTOR = MBOOT_VTOR;
- #if __CORTEX_M != 33
+ #if __CORTEX_M != 33 && __CORTEX_M != 55
// Enable 8-byte stack alignment for IRQ handlers, in accord with EABI
SCB->CCR |= SCB_CCR_STKALIGN_Msk;
#endif
@@ -1539,6 +1558,12 @@ void stm32_main(uint32_t initial_r0) {
SCB_EnableDCache();
#endif
+ #if defined(STM32N6)
+ LL_PWR_EnableBkUpAccess();
+ initial_r0 = TAMP_S->BKP31R;
+ TAMP_S->BKP31R = 0;
+ #endif
+
MBOOT_BOARD_EARLY_INIT(&initial_r0);
#ifdef MBOOT_BOOTPIN_PIN
@@ -1748,6 +1773,12 @@ void USB_DRD_FS_IRQHandler(void) {
HAL_PCD_IRQHandler(&pcd_fs_handle);
}
+#elif defined(STM32N6)
+
+void USB1_OTG_HS_IRQHandler(void) {
+ HAL_PCD_IRQHandler(&pcd_hs_handle);
+}
+
#elif defined(STM32WB)
void USB_LP_IRQHandler(void) {
diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h
index 1fabff0080..bc1320c5cd 100644
--- a/ports/stm32/mboot/mboot.h
+++ b/ports/stm32/mboot/mboot.h
@@ -36,7 +36,7 @@
#define ELEM_DATA_START (&_estack[0])
#define ELEM_DATA_MAX (&_estack[ELEM_DATA_SIZE])
-#define NORETURN __attribute__((noreturn))
+#define MP_NORETURN __attribute__((noreturn))
#define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
// The default UI code in ui.c only works if there is at least one LED configured.
diff --git a/ports/stm32/mboot/mphalport.h b/ports/stm32/mboot/mphalport.h
index 45bf11d42b..9cac0f70c4 100644
--- a/ports/stm32/mboot/mphalport.h
+++ b/ports/stm32/mboot/mphalport.h
@@ -239,3 +239,20 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed);
#define pin_J13 (GPIOJ_BASE | 13)
#define pin_J14 (GPIOJ_BASE | 14)
#define pin_J15 (GPIOJ_BASE | 15)
+
+#define pin_N0 (GPION_BASE | 0)
+#define pin_N1 (GPION_BASE | 1)
+#define pin_N2 (GPION_BASE | 2)
+#define pin_N3 (GPION_BASE | 3)
+#define pin_N4 (GPION_BASE | 4)
+#define pin_N5 (GPION_BASE | 5)
+#define pin_N6 (GPION_BASE | 6)
+#define pin_N7 (GPION_BASE | 7)
+#define pin_N8 (GPION_BASE | 8)
+#define pin_N9 (GPION_BASE | 9)
+#define pin_N10 (GPION_BASE | 10)
+#define pin_N11 (GPION_BASE | 11)
+#define pin_N12 (GPION_BASE | 12)
+#define pin_N13 (GPION_BASE | 13)
+#define pin_N14 (GPION_BASE | 14)
+#define pin_N15 (GPION_BASE | 15)
diff --git a/ports/stm32/mboot/stm32_memory_n6.ld b/ports/stm32/mboot/stm32_memory_n6.ld
new file mode 100644
index 0000000000..bd2471dbfa
--- /dev/null
+++ b/ports/stm32/mboot/stm32_memory_n6.ld
@@ -0,0 +1,18 @@
+/*
+ Linker script fragment for mboot on an STM32N6xx MCU.
+ This defines the memory sections for the bootloader to use.
+
+ On N6, the hardware bootloader loads the first 512k of external flash into
+ the upper part of SRAM2 AXI S, starting at 0x34180000. The first 1024 bytes
+ is a header. Then comes the actual code, starting with the vector table.
+*/
+
+MEMORY
+{
+ FLASH_BL (rx) : ORIGIN = 0x34180400, LENGTH = 31744 /* AXISRAM2_S */
+ RAM (xrw) : ORIGIN = 0x341e0000, LENGTH = 128K /* AXISRAM2_S */
+}
+
+/* Location of protected flash area which must not be modified, because mboot lives there. */
+_mboot_protected_flash_start = ORIGIN(FLASH_BL);
+_mboot_protected_flash_end_exclusive = ORIGIN(FLASH_BL) + LENGTH(FLASH_BL);
diff --git a/ports/stm32/mboot/stm32_sections.ld b/ports/stm32/mboot/stm32_sections.ld
index 43511f0839..4a6fd44b2b 100644
--- a/ports/stm32/mboot/stm32_sections.ld
+++ b/ports/stm32/mboot/stm32_sections.ld
@@ -33,6 +33,14 @@ SECTIONS
_etext = .;
} >FLASH_BL
+ /* Secure Gateway stubs */
+ .gnu.sgstubs :
+ {
+ . = ALIGN(4);
+ *(.gnu.sgstubs*)
+ . = ALIGN(4);
+ } >FLASH_BL
+
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c
index 620ae468cb..8123cd8011 100644
--- a/ports/stm32/modmachine.c
+++ b/ports/stm32/modmachine.c
@@ -54,7 +54,7 @@
#define RCC_CSR_PORRSTF RCC_CSR_PWRRSTF
#endif
-#if defined(STM32H5)
+#if defined(STM32H5) || defined(STM32N6)
#define RCC_SR RSR
#define RCC_SR_IWDGRSTF RCC_RSR_IWDGRSTF
#define RCC_SR_WWDGRSTF RCC_RSR_WWDGRSTF
@@ -135,7 +135,7 @@ void machine_init(void) {
reset_cause = PYB_RESET_DEEPSLEEP;
PWR->PMCR |= PWR_PMCR_CSSF;
} else
- #elif defined(STM32H7)
+ #elif defined(STM32H7) || defined(STM32N6)
if (PWR->CPUCR & PWR_CPUCR_SBF || PWR->CPUCR & PWR_CPUCR_STOPF) {
// came out of standby or stop mode
reset_cause = PYB_RESET_DEEPSLEEP;
@@ -291,12 +291,12 @@ static mp_obj_t mp_machine_unique_id(void) {
}
// Resets the pyboard in a manner similar to pushing the external RESET button.
-NORETURN static void mp_machine_reset(void) {
+MP_NORETURN static void mp_machine_reset(void) {
powerctrl_mcu_reset();
}
// Activate the bootloader without BOOT* pins.
-NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
+MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
#if MICROPY_HW_ENABLE_USB
pyb_usb_dev_deinit();
#endif
@@ -323,6 +323,19 @@ NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) {
// get or set the MCU frequencies
static mp_obj_t mp_machine_get_freq(void) {
+ #if defined(STM32N6)
+ LL_RCC_ClocksTypeDef clocks;
+ LL_RCC_GetSystemClocksFreq(&clocks);
+ mp_obj_t tuple[] = {
+ mp_obj_new_int(clocks.CPUCLK_Frequency),
+ mp_obj_new_int(clocks.SYSCLK_Frequency),
+ mp_obj_new_int(clocks.HCLK_Frequency),
+ mp_obj_new_int(clocks.PCLK1_Frequency),
+ mp_obj_new_int(clocks.PCLK2_Frequency),
+ mp_obj_new_int(clocks.PCLK4_Frequency),
+ mp_obj_new_int(clocks.PCLK5_Frequency),
+ };
+ #else
mp_obj_t tuple[] = {
mp_obj_new_int(HAL_RCC_GetSysClockFreq()),
mp_obj_new_int(HAL_RCC_GetHCLKFreq()),
@@ -331,11 +344,12 @@ static mp_obj_t mp_machine_get_freq(void) {
mp_obj_new_int(HAL_RCC_GetPCLK2Freq()),
#endif
};
+ #endif
return mp_obj_new_tuple(MP_ARRAY_SIZE(tuple), tuple);
}
static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) {
- #if defined(STM32F0) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32G0)
+ #if defined(STM32F0) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32G0) || defined(STM32N6)
mp_raise_NotImplementedError(MP_ERROR_TEXT("machine.freq set not supported yet"));
#else
mp_int_t sysclk = mp_obj_get_int(args[0]);
diff --git a/ports/stm32/modtime.c b/ports/stm32/modtime.c
index ff1495a5d9..87a4536b04 100644
--- a/ports/stm32/modtime.c
+++ b/ports/stm32/modtime.c
@@ -59,5 +59,5 @@ static mp_obj_t mp_time_time_get(void) {
RTC_TimeTypeDef time;
HAL_RTC_GetTime(&RTCHandle, &time, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&RTCHandle, &date, RTC_FORMAT_BIN);
- return mp_obj_new_int(timeutils_seconds_since_epoch(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds));
+ return timeutils_obj_from_timestamp(timeutils_seconds_since_epoch(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds));
}
diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h
index e01a4d4b87..9fa9bf7714 100644
--- a/ports/stm32/mpconfigboard_common.h
+++ b/ports/stm32/mpconfigboard_common.h
@@ -64,8 +64,12 @@
// Whether machine.bootloader() will enter the bootloader via reset, or direct jump.
#ifndef MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET
+#if defined(STM32N6)
+#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0)
+#else
#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (1)
#endif
+#endif
// Whether to enable ROMFS on the internal flash.
#ifndef MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH
@@ -77,6 +81,11 @@
#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (0)
#endif
+// Whether to enable ROMFS on external XSPI flash.
+#ifndef MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI
+#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI (0)
+#endif
+
// Whether to enable ROMFS partition 0.
#ifndef MICROPY_HW_ROMFS_ENABLE_PART0
#define MICROPY_HW_ROMFS_ENABLE_PART0 (0)
@@ -465,6 +474,16 @@
#define MICROPY_HW_MAX_UART (5)
#define MICROPY_HW_MAX_LPUART (1)
+// Configuration for STM32N6 series
+#elif defined(STM32N6)
+
+#define MP_HAL_UNIQUE_ID_ADDRESS (UID_BASE)
+#define PYB_EXTI_NUM_VECTORS (20) // only EXTI[15:0], RTC and TAMP currently supported
+#define MICROPY_HW_MAX_I2C (4)
+#define MICROPY_HW_MAX_TIMER (18)
+#define MICROPY_HW_MAX_UART (10)
+#define MICROPY_HW_MAX_LPUART (1)
+
// Configuration for STM32WB series
#elif defined(STM32WB)
@@ -589,8 +608,16 @@
(op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (uint32_t)MICROPY_HW_BDEV_SPIFLASH_CONFIG) : \
spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (arg)) \
)
-#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(MICROPY_HW_BDEV_SPIFLASH, (dest), (bl), (n))
-#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(MICROPY_HW_BDEV_SPIFLASH, (src), (bl), (n))
+#ifndef MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES
+#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES (0)
+#endif
+#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BLOCKS (MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / FLASH_BLOCK_SIZE)
+#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(MICROPY_HW_BDEV_SPIFLASH, (dest), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BLOCKS + (bl), (n))
+#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(MICROPY_HW_BDEV_SPIFLASH, (src), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BLOCKS + (bl), (n))
+#endif
+
+#if defined(STM32N6)
+#define MICROPY_FATFS_MAX_SS (4096)
#endif
// Whether to enable caching for external SPI flash, to allow block writes that are
diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h
index d1d6fe249b..b910188c5a 100644
--- a/ports/stm32/mpconfigport.h
+++ b/ports/stm32/mpconfigport.h
@@ -76,12 +76,13 @@
#ifndef MICROPY_FLOAT_IMPL // can be configured by each board via mpconfigboard.mk
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#endif
+#define MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE (1)
#define MICROPY_USE_INTERNAL_ERRNO (1)
#define MICROPY_SCHEDULER_STATIC_NODES (1)
#define MICROPY_SCHEDULER_DEPTH (8)
#define MICROPY_VFS (1)
#ifndef MICROPY_VFS_ROM
-#define MICROPY_VFS_ROM (MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI)
+#define MICROPY_VFS_ROM (MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI)
#endif
// control over Python builtins
@@ -205,20 +206,6 @@ extern const struct _mp_obj_type_t network_lan_type;
#define MICROPY_HW_NIC_ETH
#endif
-#if MICROPY_PY_NETWORK_CYW43
-extern const struct _mp_obj_type_t mp_network_cyw43_type;
-#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) },
-#else
-#define MICROPY_HW_NIC_CYW43
-#endif
-
-#if MICROPY_PY_NETWORK_WIZNET5K
-extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k;
-#define MICROPY_HW_NIC_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) },
-#else
-#define MICROPY_HW_NIC_WIZNET5K
-#endif
-
// extra constants
#define MICROPY_PORT_CONSTANTS \
MACHINE_BUILTIN_MODULE_CONSTANTS \
@@ -231,8 +218,6 @@ extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k;
#define MICROPY_PORT_NETWORK_INTERFACES \
MICROPY_HW_NIC_ETH \
- MICROPY_HW_NIC_CYW43 \
- MICROPY_HW_NIC_WIZNET5K \
MICROPY_BOARD_NETWORK_INTERFACES \
#define MP_STATE_PORT MP_STATE_VM
diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c
index dfd50cebd3..fcd08cbd84 100644
--- a/ports/stm32/mphalport.c
+++ b/ports/stm32/mphalport.c
@@ -20,7 +20,7 @@ const byte mp_hal_status_to_errno_table[4] = {
uint8_t mp_hal_unique_id_address[12];
#endif
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) {
mp_raise_OSError(mp_hal_status_to_errno_table[status]);
}
@@ -105,7 +105,7 @@ void mp_hal_gpio_clock_enable(GPIO_TypeDef *gpio) {
#elif defined(STM32F4) || defined(STM32F7)
#define AHBxENR AHB1ENR
#define AHBxENR_GPIOAEN_Pos RCC_AHB1ENR_GPIOAEN_Pos
- #elif defined(STM32H7)
+ #elif defined(STM32H7) || defined(STM32N6)
#define AHBxENR AHB4ENR
#define AHBxENR_GPIOAEN_Pos RCC_AHB4ENR_GPIOAEN_Pos
#elif defined(STM32L0)
diff --git a/ports/stm32/mphalport.h b/ports/stm32/mphalport.h
index e520bc54ae..03b0f8e772 100644
--- a/ports/stm32/mphalport.h
+++ b/ports/stm32/mphalport.h
@@ -37,7 +37,7 @@ static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) {
return -mp_hal_status_to_errno_table[status];
}
-NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
+MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status);
void mp_hal_set_interrupt_char(int c); // -1 to disable
// Atomic section helpers.
diff --git a/ports/stm32/mpu.h b/ports/stm32/mpu.h
index a87c04a58d..8713fe8370 100644
--- a/ports/stm32/mpu.h
+++ b/ports/stm32/mpu.h
@@ -28,7 +28,7 @@
#include "irq.h"
-#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32H7) || defined(STM32WB)
+#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32WB)
#define MPU_REGION_ETH (MPU_REGION_NUMBER0)
#define MPU_REGION_QSPI1 (MPU_REGION_NUMBER1)
@@ -137,18 +137,26 @@ static inline void mpu_config_end(uint32_t irq_state) {
enable_irq(irq_state);
}
-#elif defined(STM32H5)
+#elif defined(STM32H5) || defined(STM32N6)
#define MPU_REGION_SIG (MPU_REGION_NUMBER0)
#define MPU_REGION_ETH (MPU_REGION_NUMBER1)
-#define MPU_REGION_LAST_USED (MPU_REGION_NUMBER1)
+#define MPU_REGION_DMA_UNCACHED_1 (MPU_REGION_NUMBER2)
+#define MPU_REGION_DMA_UNCACHED_2 (MPU_REGION_NUMBER3)
+#define MPU_REGION_LAST_USED (MPU_REGION_NUMBER3)
#define ST_DEVICE_SIGNATURE_BASE (0x08fff800)
#define ST_DEVICE_SIGNATURE_LIMIT (0x08ffffff)
// STM32H5 Cortex-M33 MPU works differently from older cores.
// Macro only takes region size in bytes, Attributes are coded in mpu_config_region().
+#define MPU_CONFIG_DISABLE (0)
#define MPU_CONFIG_ETH(size) (size)
+#define MPU_CONFIG_UNCACHED(size) (size)
+
+#if defined(STM32N6)
+#define MPU_REGION_SIZE_32B (32)
+#endif
static inline void mpu_init(void) {
// Configure attribute 0, inner-outer non-cacheable (=0x44).
@@ -180,8 +188,12 @@ static inline uint32_t mpu_config_start(void) {
}
static inline void mpu_config_region(uint32_t region, uint32_t base_addr, uint32_t size) {
- if (region == MPU_REGION_ETH) {
- // Configure region 1 to make DMA memory non-cacheable.
+ if (size == 0) {
+ // Disable MPU for this region.
+ MPU->RNR = region;
+ MPU->RLAR &= ~MPU_RLAR_EN_Msk;
+ } else if (region == MPU_REGION_ETH || region == MPU_REGION_DMA_UNCACHED_1 || region == MPU_REGION_DMA_UNCACHED_2) {
+ // Configure region to make DMA memory non-cacheable.
__DMB();
// Configure attribute 1, inner-outer non-cacheable (=0x44).
diff --git a/ports/stm32/pin_defs_stm32.h b/ports/stm32/pin_defs_stm32.h
index 6c67b64924..645ec5b2df 100644
--- a/ports/stm32/pin_defs_stm32.h
+++ b/ports/stm32/pin_defs_stm32.h
@@ -39,6 +39,10 @@ enum {
PORT_I,
PORT_J,
PORT_K,
+ PORT_L,
+ PORT_M,
+ PORT_N,
+ PORT_O,
};
// Must have matching entries in SUPPORTED_FN in boards/make-pins.py
diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c
index eea009e2d7..a750e8f5be 100644
--- a/ports/stm32/powerctrl.c
+++ b/ports/stm32/powerctrl.c
@@ -26,10 +26,10 @@
#include "py/mperrno.h"
#include "py/mphal.h"
+#include "boardctrl.h"
#include "powerctrl.h"
#include "rtc.h"
#include "extmod/modbluetooth.h"
-#include "py/mpconfig.h"
#ifndef NO_QSTR
#include "genhdr/pllfreqtable.h"
#endif
@@ -46,7 +46,7 @@ static uint32_t __attribute__((unused)) micropy_hw_hse_value = HSE_VALUE;
static uint32_t __attribute__((unused)) micropy_hw_clk_pllm = MICROPY_HW_CLK_PLLM;
#endif
-#if defined(STM32H5) || defined(STM32H7)
+#if defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
#define RCC_SR RSR
#if defined(STM32H747xx)
#define RCC_SR_SFTRSTF RCC_RSR_SFT2RSTF
@@ -63,7 +63,7 @@ static uint32_t __attribute__((unused)) micropy_hw_clk_pllm = MICROPY_HW_CLK_PLL
#define POWERCTRL_GET_VOLTAGE_SCALING() PWR_REGULATOR_VOLTAGE_SCALE0
#elif defined(STM32H723xx)
#define POWERCTRL_GET_VOLTAGE_SCALING() LL_PWR_GetRegulVoltageScaling()
-#elif defined(STM32H5)
+#elif defined(STM32H5) || defined(STM32N6)
#define POWERCTRL_GET_VOLTAGE_SCALING() LL_PWR_GetRegulVoltageScaling()
#else
#define POWERCTRL_GET_VOLTAGE_SCALING() \
@@ -115,7 +115,7 @@ static inline void powerctrl_disable_hsi_if_unused(void) {
#endif
}
-NORETURN void powerctrl_mcu_reset(void) {
+MP_NORETURN void powerctrl_mcu_reset(void) {
#if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET
*BL_STATE_PTR = BL_STATE_INVALID;
#if __DCACHE_PRESENT == 1
@@ -125,7 +125,7 @@ NORETURN void powerctrl_mcu_reset(void) {
NVIC_SystemReset();
}
-NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t bl_addr) {
+MP_NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t bl_addr) {
__asm volatile (
"ldr r2, [r1, #0]\n" // get address of stack pointer
"msr msp, r2\n" // get stack pointer
@@ -135,7 +135,13 @@ NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, ui
MP_UNREACHABLE;
}
-NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {
+MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {
+ #if defined(STM32N6)
+ LL_PWR_EnableBkUpAccess();
+ TAMP_S->BKP31R = r0;
+ NVIC_SystemReset();
+ #endif
+
#if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET
// Enter the bootloader via a reset, so everything is reset (including WDT).
@@ -169,6 +175,8 @@ void powerctrl_check_enter_bootloader(void) {
#endif
}
+#if !defined(STM32N6)
+
#if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32WB) && !defined(STM32WL)
typedef struct _sysclk_scaling_table_entry_t {
@@ -781,6 +789,8 @@ static void powerctrl_low_power_exit_wb55() {
#endif // !defined(STM32F0) && !defined(STM32G0) && !defined(STM32L0) && !defined(STM32L1) && !defined(STM32L4)
+#endif
+
void powerctrl_enter_stop_mode(void) {
// Disable IRQs so that the IRQ that wakes the device from stop mode is not
// executed until after the clocks are reconfigured
@@ -809,7 +819,7 @@ void powerctrl_enter_stop_mode(void) {
__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);
#endif
- #if !defined(STM32F0) && !defined(STM32G0) && !defined(STM32G4) && !defined(STM32L0) && !defined(STM32L1) && !defined(STM32L4) && !defined(STM32WB) && !defined(STM32WL)
+ #if !defined(STM32F0) && !defined(STM32G0) && !defined(STM32G4) && !defined(STM32L0) && !defined(STM32L1) && !defined(STM32L4) && !defined(STM32N6) && !defined(STM32WB) && !defined(STM32WL)
// takes longer to wake but reduces stop current
HAL_PWREx_EnableFlashPowerDown();
#endif
@@ -848,6 +858,8 @@ void powerctrl_enter_stop_mode(void) {
#if defined(STM32F7)
HAL_PWR_EnterSTOPMode((PWR_CR1_LPDS | PWR_CR1_LPUDS | PWR_CR1_FPDS | PWR_CR1_UDEN), PWR_STOPENTRY_WFI);
+ #elif defined(STM32N6)
+ HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
#else
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
#endif
@@ -912,6 +924,19 @@ void powerctrl_enter_stop_mode(void) {
while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL1) {
}
+ #elif defined(STM32N6)
+
+ // Enable PLL1, and switch the CPU and system clock source to use PLL1.
+ LL_RCC_PLL1_Enable();
+ while (!LL_RCC_PLL1_IsReady()) {
+ }
+ LL_RCC_SetCpuClkSource(LL_RCC_CPU_CLKSOURCE_IC1);
+ while (LL_RCC_GetCpuClkSource() != LL_RCC_CPU_CLKSOURCE_STATUS_IC1) {
+ }
+ LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_IC2_IC6_IC11);
+ while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_IC2_IC6_IC11) {
+ }
+
#else // defined(STM32H5)
// enable PLL
@@ -1016,9 +1041,47 @@ void powerctrl_enter_stop_mode(void) {
enable_irq(irq_state);
}
-NORETURN void powerctrl_enter_standby_mode(void) {
+#if defined(STM32N6)
+
+// Upon wake from standby, STM32N6 can resume execution from retained SRAM1.
+// Place a small bootloader there which initialises XSPI in memory-mapped mode
+// and jumps to the main application entry point.
+
+#include "xspi.h"
+
+extern uint32_t _estack;
+
+void Reset_Handler(void);
+
+void iram_bootloader_reset(void) {
+ #if defined(MICROPY_BOARD_LEAVE_STANDBY)
+ MICROPY_BOARD_LEAVE_STANDBY;
+ #endif
+ xspi_init();
+ Reset_Handler();
+}
+
+// Very simple ARM vector table.
+const uint32_t iram_bootloader_isr_vector[] = {
+ (uint32_t)&_estack,
+ (uint32_t)&iram_bootloader_reset,
+};
+
+#endif
+
+MP_NORETURN void powerctrl_enter_standby_mode(void) {
rtc_init_finalise();
+ #if defined(STM32N6)
+ // Upon wake from standby, jump to the code at SRAM1.
+ // A board can reconfigure this in MICROPY_BOARD_ENTER_STANDBY if needed.
+ LL_PWR_EnableTCMSBRetention();
+ LL_PWR_EnableTCMFLXSBRetention();
+ LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG);
+ SCB_CleanDCache();
+ SYSCFG->INITSVTORCR = (uint32_t)&iram_bootloader_isr_vector[0];
+ #endif
+
#if defined(MICROPY_BOARD_ENTER_STANDBY)
MICROPY_BOARD_ENTER_STANDBY
#endif
@@ -1039,6 +1102,13 @@ NORETURN void powerctrl_enter_standby_mode(void) {
mp_bluetooth_deinit();
#endif
+ #if defined(STM32N6)
+
+ // Clear all WKUPx flags.
+ LL_PWR_ClearFlag_WU();
+
+ #else
+
// We need to clear the PWR wake-up-flag before entering standby, since
// the flag may have been set by a previous wake-up event. Furthermore,
// we need to disable the wake-up sources while clearing this flag, so
@@ -1135,6 +1205,8 @@ NORETURN void powerctrl_enter_standby_mode(void) {
powerctrl_low_power_prep_wb55();
#endif
+ #endif
+
// enter standby mode
HAL_PWR_EnterSTANDBYMode();
diff --git a/ports/stm32/powerctrl.h b/ports/stm32/powerctrl.h
index 5b92405611..724ab58366 100644
--- a/ports/stm32/powerctrl.h
+++ b/ports/stm32/powerctrl.h
@@ -34,19 +34,25 @@ void stm32_system_init(void);
#else
static inline void stm32_system_init(void) {
SystemInit();
+
+ #if defined(STM32N6)
+ // The ROM bootloader uses PLL1 to set the CPU to 400MHz, so update
+ // the value of SystemCoreClock to reflect the hardware state.
+ SystemCoreClockUpdate();
+ #endif
}
#endif
void SystemClock_Config(void);
-NORETURN void powerctrl_mcu_reset(void);
-NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr);
+MP_NORETURN void powerctrl_mcu_reset(void);
+MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr);
void powerctrl_check_enter_bootloader(void);
void powerctrl_config_systick(void);
int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk_mhz, bool need_pllsai);
int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2);
void powerctrl_enter_stop_mode(void);
-NORETURN void powerctrl_enter_standby_mode(void);
+MP_NORETURN void powerctrl_enter_standby_mode(void);
#endif // MICROPY_INCLUDED_STM32_POWERCTRL_H
diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c
index 31dae527c1..059d2a45da 100644
--- a/ports/stm32/powerctrlboot.c
+++ b/ports/stm32/powerctrlboot.c
@@ -47,9 +47,15 @@ void stm32_system_init(void) {
#endif
void powerctrl_config_systick(void) {
+ #if defined(STM32N6)
+ uint32_t systick_source_freq = HAL_RCC_GetCpuClockFreq();
+ #else
+ uint32_t systick_source_freq = HAL_RCC_GetHCLKFreq();
+ #endif
+
// Configure SYSTICK to run at 1kHz (1ms interval)
SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK;
- SysTick_Config(HAL_RCC_GetHCLKFreq() / 1000);
+ SysTick_Config(systick_source_freq / 1000);
NVIC_SetPriority(SysTick_IRQn, IRQ_PRI_SYSTICK);
#if !BUILDING_MBOOT && (defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB))
@@ -410,6 +416,124 @@ void SystemClock_Config(void) {
DBGMCU->CR &= ~(DBGMCU_CR_DBG_SLEEP | DBGMCU_CR_DBG_STOP | DBGMCU_CR_DBG_STANDBY);
#endif
}
+
+#elif defined(STM32N6)
+
+void SystemClock_Config(void) {
+ // Enable HSI.
+ LL_RCC_HSI_Enable();
+ while (!LL_RCC_HSI_IsReady()) {
+ }
+
+ // Switch the CPU clock source to HSI.
+ LL_RCC_SetCpuClkSource(LL_RCC_CPU_CLKSOURCE_HSI);
+ while (LL_RCC_GetCpuClkSource() != LL_RCC_CPU_CLKSOURCE_STATUS_HSI) {
+ }
+
+ // Switch the system clock source to HSI.
+ LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
+ while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) {
+ }
+
+ // Disable all ICx clocks.
+ RCC->DIVENCR = 0x000fffff;
+
+ // This doesn't work, VOSRDY never becomes active.
+ #if 0
+ LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE0);
+ while (!LL_PWR_IsActiveFlag_VOSRDY()) {
+ }
+ #endif
+
+ // Enable HSE.
+ LL_RCC_HSE_Enable();
+ while (!LL_RCC_HSE_IsReady()) {
+ }
+
+ // Disable PLL1.
+ LL_RCC_PLL1_Disable();
+ while (LL_RCC_PLL1_IsReady()) {
+ }
+
+ // Configure PLL1 for use as system clock.
+ LL_RCC_PLL1_SetSource(LL_RCC_PLLSOURCE_HSE);
+ LL_RCC_PLL1_DisableBypass();
+ LL_RCC_PLL1_DisableFractionalModulationSpreadSpectrum();
+ LL_RCC_PLL1_SetM(MICROPY_HW_CLK_PLLM);
+ LL_RCC_PLL1_SetN(MICROPY_HW_CLK_PLLN);
+ LL_RCC_PLL1_SetP1(MICROPY_HW_CLK_PLLP1);
+ LL_RCC_PLL1_SetP2(MICROPY_HW_CLK_PLLP2);
+ LL_RCC_PLL1_SetFRACN(MICROPY_HW_CLK_PLLFRAC);
+ LL_RCC_PLL1P_Enable();
+
+ // Enable PLL1.
+ LL_RCC_PLL1_Enable();
+ while (!LL_RCC_PLL1_IsReady()) {
+ }
+
+ // Configure IC1, IC2, IC6, IC11.
+ LL_RCC_IC1_SetSource(LL_RCC_ICCLKSOURCE_PLL1);
+ LL_RCC_IC1_SetDivider(1);
+ LL_RCC_IC1_Enable();
+ LL_RCC_IC2_SetSource(LL_RCC_ICCLKSOURCE_PLL1);
+ LL_RCC_IC2_SetDivider(2);
+ LL_RCC_IC2_Enable();
+ LL_RCC_IC6_SetSource(LL_RCC_ICCLKSOURCE_PLL1);
+ LL_RCC_IC6_SetDivider(1);
+ LL_RCC_IC6_Enable();
+ LL_RCC_IC11_SetSource(LL_RCC_ICCLKSOURCE_PLL1);
+ LL_RCC_IC11_SetDivider(1);
+ LL_RCC_IC11_Enable();
+
+ // Configure IC14 at 100MHz for slower peripherals.
+ LL_RCC_IC14_SetSource(LL_RCC_ICCLKSOURCE_PLL1);
+ LL_RCC_IC14_SetDivider(8);
+ LL_RCC_IC14_Enable();
+
+ // Enable buses.
+ LL_BUS_EnableClock(LL_APB5 | LL_APB4 | LL_APB3 | LL_APB2 | LL_APB1 | LL_AHB5 | LL_AHB4 | LL_AHB3 | LL_AHB2 | LL_AHB1);
+ LL_MISC_EnableClock(LL_PER);
+
+ // Configure bus dividers.
+ LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_2);
+ LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
+ LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
+ LL_RCC_SetAPB4Prescaler(LL_RCC_APB4_DIV_1);
+ LL_RCC_SetAPB5Prescaler(LL_RCC_APB5_DIV_1);
+
+ // Switch the CPU clock source to IC1 (connected to PLL1).
+ LL_RCC_SetCpuClkSource(LL_RCC_CPU_CLKSOURCE_IC1);
+ while (LL_RCC_GetCpuClkSource() != LL_RCC_CPU_CLKSOURCE_STATUS_IC1) {
+ }
+
+ // Switch the system clock source to IC2/IC6/IC11 (connected to PLL1).
+ LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_IC2_IC6_IC11);
+ while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_IC2_IC6_IC11) {
+ }
+
+ // ADC clock configuration, HCLK/2.
+ LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_HCLK);
+ LL_RCC_SetADCPrescaler(2 - 1);
+
+ // USB clock configuration.
+ #if MICROPY_HW_ENABLE_USB
+
+ // Select HSE/2 as output of direct HSE signal.
+ LL_RCC_HSE_SelectHSEDiv2AsDiv2Clock();
+
+ // Select HSE/2 for OTG1 clock source.
+ LL_RCC_SetClockSource(LL_RCC_OTGPHY1_CLKSOURCE_HSE_DIV_2_OSC);
+ LL_RCC_SetClockSource(LL_RCC_OTGPHY1CKREF_CLKSOURCE_HSE_DIV_2_OSC);
+ LL_RCC_SetOTGPHYClockSource(LL_RCC_OTGPHY1_CLKSOURCE_HSE_DIV_2_OSC);
+ LL_RCC_SetOTGPHYCKREFClockSource(LL_RCC_OTGPHY1CKREF_CLKSOURCE_HSE_DIV_2_OSC);
+
+ #endif
+
+ // Reconfigure clock state and SysTick.
+ SystemCoreClockUpdate();
+ powerctrl_config_systick();
+}
+
#elif defined(STM32WB)
void SystemClock_Config(void) {
diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c
index 781aae803e..2ef9a4d018 100644
--- a/ports/stm32/qspi.c
+++ b/ports/stm32/qspi.c
@@ -213,11 +213,13 @@ static int qspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) {
qspi_memory_map();
break;
case MP_QSPI_IOCTL_MEMORY_MODIFIED: {
+ #if defined(__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
uintptr_t *addr_len = (uintptr_t *)arg;
volatile void *addr = (volatile void *)(QSPI_MAP_ADDR + addr_len[0]);
size_t len = addr_len[1];
SCB_InvalidateICache_by_Addr(addr, len);
SCB_InvalidateDCache_by_Addr(addr, len);
+ #endif
break;
}
}
diff --git a/ports/stm32/resethandler_iram.s b/ports/stm32/resethandler_iram.s
new file mode 100644
index 0000000000..49a8b40068
--- /dev/null
+++ b/ports/stm32/resethandler_iram.s
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018-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.
+ */
+
+ .syntax unified
+ .cpu cortex-m4
+ .thumb
+
+ .section .text.Reset_Handler
+ .global Reset_Handler
+ .type Reset_Handler, %function
+
+Reset_Handler:
+ /* Save the first argument to pass through to stm32_main */
+ mov r4, r0
+
+ /* Load the stack pointer */
+ ldr sp, =_estack
+
+ /* Initialise the iram section */
+ ldr r1, =_siiram
+ ldr r2, =_siram
+ ldr r3, =_eiram
+ b .iram_copy_entry
+ nop
+.iram_copy_loop:
+ ldr r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */
+ str r0, [r2], #4
+.iram_copy_entry:
+ cmp r2, r3
+ bcc .iram_copy_loop
+
+ /* Initialise the data section */
+ ldr r1, =_sidata
+ ldr r2, =_sdata
+ ldr r3, =_edata
+ b .data_copy_entry
+.data_copy_loop:
+ ldr r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */
+ str r0, [r2], #4
+.data_copy_entry:
+ cmp r2, r3
+ bcc .data_copy_loop
+
+ /* Zero out the BSS section */
+ movs r0, #0
+ ldr r1, =_sbss
+ ldr r2, =_ebss
+ b .bss_zero_entry
+.bss_zero_loop:
+ str r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */
+.bss_zero_entry:
+ cmp r1, r2
+ bcc .bss_zero_loop
+
+ /* Jump to the main code */
+ mov r0, r4
+ b stm32_main
+
+ .size Reset_Handler, .-Reset_Handler
diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c
index 8dadc4a88d..b90d17149b 100644
--- a/ports/stm32/rtc.c
+++ b/ports/stm32/rtc.c
@@ -100,6 +100,10 @@ static bool rtc_need_init_finalise = false;
#define RCC_BDCR_LSEBYP RCC_CSR_LSEBYP
#endif
+#if defined(STM32N6)
+#define RCC_DBP_TIMEOUT_VALUE (5)
+#endif
+
void rtc_init_start(bool force_init) {
// Enable the RTC APB bus clock, to communicate with the RTC.
#if defined(STM32H5)
@@ -129,6 +133,32 @@ void rtc_init_start(bool force_init) {
if (!force_init) {
bool rtc_running = false;
+ #if defined(STM32N6)
+ if (LL_RCC_IsEnabledRTC()
+ && LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSE
+ && LL_RCC_LSE_IsReady()) {
+ // LSE is enabled & ready --> no need to (re-)init RTC
+ rtc_running = true;
+ // remove Backup Domain write protection
+ HAL_PWR_EnableBkUpAccess();
+ // Clear source Reset Flag
+ __HAL_RCC_CLEAR_RESET_FLAGS();
+ // provide some status information
+ rtc_info |= 0x40000;
+ } else if (LL_RCC_IsEnabledRTC()
+ && LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSI) {
+ // LSI configured as the RTC clock source --> no need to (re-)init RTC
+ rtc_running = true;
+ // remove Backup Domain write protection
+ HAL_PWR_EnableBkUpAccess();
+ // Clear source Reset Flag
+ __HAL_RCC_CLEAR_RESET_FLAGS();
+ // Turn the LSI on (it may need this even if the RTC is running)
+ LL_RCC_LSI_Enable();
+ // provide some status information
+ rtc_info |= 0x80000;
+ }
+ #else
uint32_t bdcr = RCC->BDCR;
if ((bdcr & (RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL | RCC_BDCR_LSEON | RCC_BDCR_LSERDY))
== (RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_0 | RCC_BDCR_LSEON | RCC_BDCR_LSERDY)) {
@@ -157,6 +187,7 @@ void rtc_init_start(bool force_init) {
// provide some status information
rtc_info |= 0x80000;
}
+ #endif
if (rtc_running) {
// Provide information about the registers that indicated the RTC is running.
@@ -296,7 +327,7 @@ static HAL_StatusTypeDef PYB_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct
return HAL_TIMEOUT;
}
}
- #elif defined(STM32H5)
+ #elif defined(STM32H5) || defined(STM32N6)
// Wait for Backup domain Write protection disable
while (!LL_PWR_IsEnabledBkUpAccess()) {
if (HAL_GetTick() - tickstart > RCC_DBP_TIMEOUT_VALUE) {
@@ -381,7 +412,7 @@ static HAL_StatusTypeDef PYB_RTC_Init(RTC_HandleTypeDef *hrtc) {
#elif defined(STM32F7)
hrtc->Instance->OR &= (uint32_t) ~RTC_OR_ALARMTYPE;
hrtc->Instance->OR |= (uint32_t)(hrtc->Init.OutPutType);
- #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WL)
+ #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WL)
hrtc->Instance->CR &= (uint32_t) ~RTC_CR_TAMPALRM_TYPE_Msk;
hrtc->Instance->CR |= (uint32_t)(hrtc->Init.OutPutType);
#else
@@ -413,7 +444,14 @@ static void PYB_RTC_MspInit_Kick(RTC_HandleTypeDef *hrtc, bool rtc_use_lse, bool
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE;
+ #if defined(STM32N6)
+ RCC_OscInitStruct.PLL1.PLLState = RCC_PLL_NONE;
+ RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
+ RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_NONE;
+ RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_NONE;
+ #else
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
+ #endif
#if MICROPY_HW_RTC_USE_BYPASS
if (rtc_use_byp) {
RCC_OscInitStruct.LSEState = RCC_LSE_BYPASS;
@@ -651,6 +689,8 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_rtc_datetime_obj, 1, 2, pyb_rtc_datetime
#define RTC_WKUP_IRQn RTC_IRQn
#elif defined(STM32G0)
#define RTC_WKUP_IRQn RTC_TAMP_IRQn
+#elif defined(STM32N6)
+#define RTC_WKUP_IRQn RTC_S_IRQn
#endif
// wakeup(None)
@@ -759,8 +799,9 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) {
#if defined(STM32G0) || defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
EXTI->IMR1 |= 1 << EXTI_RTC_WAKEUP;
EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP;
- #elif defined(STM32H5)
+ #elif defined(STM32H5) || defined(STM32N6)
EXTI->IMR1 |= 1 << EXTI_RTC_WAKEUP;
+ EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP;
#elif defined(STM32H7)
EXTI_D1->IMR1 |= 1 << EXTI_RTC_WAKEUP;
EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP;
@@ -772,8 +813,8 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) {
// clear interrupt flags
#if defined(STM32G0) || defined(STM32G4) || defined(STM32WL)
RTC->ICSR &= ~RTC_ICSR_WUTWF;
- #elif defined(STM32H5)
- RTC->SCR = RTC_SCR_CWUTF;
+ #elif defined(STM32H5) || defined(STM32N6)
+ LL_RTC_ClearFlag_WUT(RTC);
#elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ)
RTC->SR &= ~RTC_SR_WUTF;
#else
@@ -783,7 +824,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) {
EXTI->PR1 = 1 << EXTI_RTC_WAKEUP;
#elif defined(STM32H7)
EXTI_D1->PR1 = 1 << EXTI_RTC_WAKEUP;
- #elif defined(STM32G0) || defined(STM32H5)
+ #elif defined(STM32G0) || defined(STM32H5) || defined(STM32N6)
// Do nothing
#else
EXTI->PR = 1 << EXTI_RTC_WAKEUP;
@@ -799,7 +840,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) {
RTC->WPR = 0xff;
// disable external interrupts on line EXTI_RTC_WAKEUP
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
EXTI->IMR1 &= ~(1 << EXTI_RTC_WAKEUP);
#elif defined(STM32H7)
EXTI_D1->IMR1 |= 1 << EXTI_RTC_WAKEUP;
diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c
index 706d6315c4..b91fa3a9c2 100644
--- a/ports/stm32/sdcard.c
+++ b/ports/stm32/sdcard.c
@@ -40,7 +40,7 @@
#if MICROPY_HW_ENABLE_SDCARD || MICROPY_HW_ENABLE_MMCARD
-#if defined(STM32F7) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4)
+#if defined(STM32F7) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6)
// The H7/F7/L4 have 2 SDMMC peripherals, but at the moment this driver only supports
// using one of them in a given build, selected by MICROPY_HW_SDCARD_SDMMC.
@@ -104,7 +104,7 @@
#define SDIO_HARDWARE_FLOW_CONTROL_DISABLE SDMMC_HARDWARE_FLOW_CONTROL_DISABLE
#define SDIO_HARDWARE_FLOW_CONTROL_ENABLE SDMMC_HARDWARE_FLOW_CONTROL_ENABLE
-#if defined(STM32H5) || defined(STM32H7)
+#if defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
#define SDIO_TRANSFER_CLK_DIV SDMMC_NSpeed_CLK_DIV
#define SDIO_USE_GPDMA 0
#else
@@ -214,7 +214,7 @@ static void sdmmc_msp_init(void) {
// enable SDIO clock
SDMMC_CLK_ENABLE();
- #if defined(STM32H7)
+ #if defined(STM32H7) || defined(STM32N6)
// Reset SDMMC
SDMMC_FORCE_RESET();
SDMMC_RELEASE_RESET();
@@ -270,7 +270,7 @@ static HAL_StatusTypeDef sdmmc_init_sd(void) {
// SD device interface configuration
sdmmc_handle.sd.Instance = SDIO;
sdmmc_handle.sd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
- #if !defined(STM32H5) && !defined(STM32H7)
+ #if !defined(STM32H5) && !defined(STM32H7) && !defined(STM32N6)
sdmmc_handle.sd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
#endif
sdmmc_handle.sd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_ENABLE;
diff --git a/ports/stm32/sdio.c b/ports/stm32/sdio.c
index 99d05a5155..de82ceadc5 100644
--- a/ports/stm32/sdio.c
+++ b/ports/stm32/sdio.c
@@ -77,7 +77,11 @@ static volatile uint8_t *sdmmc_buf_top;
#define SDMMC_IRQHandler SDMMC2_IRQHandler
#define SDMMC_CLK_ENABLE() __HAL_RCC_SDMMC2_CLK_ENABLE()
#define SDMMC_CLK_DISABLE() __HAL_RCC_SDMMC2_CLK_DISABLE()
+#if defined(STM32N6)
+#define SDMMC_IS_CLK_DISABLED() (!__HAL_RCC_SDMMC2_IS_CLK_ENABLED())
+#else
#define SDMMC_IS_CLK_DISABLED() __HAL_RCC_SDMMC2_IS_CLK_DISABLED()
+#endif
#define STATIC_AF_SDMMC_CK STATIC_AF_SDMMC2_CK
#define STATIC_AF_SDMMC_CMD STATIC_AF_SDMMC2_CMD
#define STATIC_AF_SDMMC_D0 STATIC_AF_SDMMC2_D0
@@ -96,9 +100,17 @@ static volatile uint8_t *sdmmc_buf_top;
#define MICROPY_HW_SDIO_CMD (pin_D2)
#endif
-#if defined(STM32H7)
+#if defined(STM32H7) || defined(STM32N6)
static uint32_t safe_divide(uint32_t denom) {
+ #if defined(STM32N6)
+ #if MICROPY_HW_SDIO_SDMMC == 1
+ uint32_t num = LL_RCC_GetSDMMCClockFreq(LL_RCC_SDMMC1_CLKSOURCE);
+ #else
+ uint32_t num = LL_RCC_GetSDMMCClockFreq(LL_RCC_SDMMC2_CLKSOURCE);
+ #endif
+ #else
uint32_t num = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC);
+ #endif
uint32_t divres;
divres = num / (2U * denom);
@@ -119,11 +131,15 @@ void sdio_init(uint32_t irq_pri) {
mp_hal_pin_config_alt_static(MICROPY_HW_SDIO_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_CMD);
SDMMC_CLK_ENABLE(); // enable SDIO peripheral
+ #if defined(STM32N6)
+ LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_SDMMC1);
+ LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_SDMMC2);
+ #endif
SDMMC_TypeDef *SDIO = SDMMC;
#if defined(STM32F7)
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | (120 - 2); // 1-bit, 400kHz
- #elif defined(STM32H7)
+ #elif defined(STM32H7) || defined(STM32N6)
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | safe_divide(400000U); // 1-bit, 400kHz
#else
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | (120 / 2); // 1-bit, 400kHz
@@ -172,7 +188,7 @@ void sdio_enable_high_speed_4bit(void) {
mp_hal_delay_us(10);
#if defined(STM32F7)
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0 | SDMMC_CLKCR_BYPASS /*| SDMMC_CLKCR_PWRSAV*/; // 4-bit, 48MHz
- #elif defined(STM32H7)
+ #elif defined(STM32H7) || defined(STM32N6)
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0 | safe_divide(48000000U); // 4-bit, 48MHz
#else
SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0; // 4-bit, 48MHz
@@ -199,7 +215,7 @@ void SDMMC_IRQHandler(void) {
sdmmc_irq_state = SDMMC_IRQ_STATE_DONE;
return;
}
- #if defined(STM32H7)
+ #if defined(STM32H7) || defined(STM32N6)
if (!sdmmc_dma) {
while (sdmmc_buf_cur < sdmmc_buf_top && (SDMMC->STA & SDMMC_STA_DPSMACT) && !(SDMMC->STA & SDMMC_STA_RXFIFOE)) {
*(uint32_t *)sdmmc_buf_cur = SDMMC->FIFO;
@@ -413,11 +429,15 @@ int sdio_transfer_cmd53(bool write, uint32_t block_size, uint32_t arg, size_t le
dma_nohal_init(&dma_SDIO_0, dma_config);
dma_nohal_start(&dma_SDIO_0, dma_src, dma_dest, dma_len);
#else
+ #if defined(STM32N6)
+ SDMMC->IDMABASER = (uint32_t)buf;
+ #else
SDMMC->IDMABASE0 = (uint32_t)buf;
+ #endif
SDMMC->IDMACTRL = SDMMC_IDMA_IDMAEN;
#endif
} else {
- #if defined(STM32H7)
+ #if defined(STM32H7) || defined(STM32N6)
SDMMC->IDMACTRL = 0;
#endif
}
diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c
index 96dd170652..248075579a 100644
--- a/ports/stm32/spi.c
+++ b/ports/stm32/spi.c
@@ -106,7 +106,7 @@ const spi_t spi_obj[6] = {
#error "spi_obj needs updating for new value of MICROPY_HW_SUBGHZSPI_ID"
#endif
-#if defined(STM32H5) || defined(STM32H7)
+#if defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
// STM32H5/H7 HAL requires SPI IRQs to be enabled and handled.
#if defined(MICROPY_HW_SPI1_SCK)
void SPI1_IRQHandler(void) {
@@ -176,6 +176,18 @@ void spi_init0(void) {
#if defined(MICROPY_HW_SUBGHZSPI_ID)
SPIHandleSubGhz.Instance = SUBGHZSPI;
#endif
+
+ #if defined(STM32N6)
+ // SPI1/2/3/6 clock configuration, PCLKx (max 200MHz).
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI1_CLKSOURCE_PCLK2);
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI2_CLKSOURCE_PCLK1);
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI3_CLKSOURCE_PCLK1);
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI6_CLKSOURCE_PCLK4);
+
+ // SPI4/5 clock configuration, IC14 (max 100MHz).
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI4_CLKSOURCE_IC14);
+ LL_RCC_SetSPIClockSource(LL_RCC_SPI5_CLKSOURCE_IC14);
+ #endif
}
int spi_find_index(mp_obj_t id) {
@@ -256,6 +268,20 @@ static uint32_t spi_get_source_freq(SPI_HandleTypeDef *spi) {
} else {
return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI6);
}
+ #elif defined(STM32N6)
+ if (spi->Instance == SPI1) {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI1_CLKSOURCE);
+ } else if (spi->Instance == SPI2) {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI2_CLKSOURCE);
+ } else if (spi->Instance == SPI3) {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI3_CLKSOURCE);
+ } else if (spi->Instance == SPI4) {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI4_CLKSOURCE);
+ } else if (spi->Instance == SPI5) {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI5_CLKSOURCE);
+ } else {
+ return LL_RCC_GetSPIClockFreq(LL_RCC_SPI6_CLKSOURCE);
+ }
#else // !STM32F0, !STM32G0, !STM32H
#if defined(SPI2)
if (spi->Instance == SPI2) {
@@ -455,7 +481,10 @@ int spi_init(const spi_t *self, bool enable_nss_pin) {
if (pins[i] == NULL) {
continue;
}
- mp_hal_pin_config_alt(pins[i], mode, pull, AF_FN_SPI, (self - &spi_obj[0]) + 1);
+ if (!mp_hal_pin_config_alt(pins[i], mode, pull, AF_FN_SPI, (self - &spi_obj[0]) + 1)) {
+ // Pin does not have SPI alternate function.
+ return -MP_EINVAL;
+ }
}
// init the SPI device
@@ -470,7 +499,7 @@ int spi_init(const spi_t *self, bool enable_nss_pin) {
dma_invalidate_channel(self->tx_dma_descr);
dma_invalidate_channel(self->rx_dma_descr);
- #if defined(STM32H5) || defined(STM32H7)
+ #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
NVIC_SetPriority(irqn, IRQ_PRI_SPI);
HAL_NVIC_EnableIRQ(irqn);
#else
@@ -724,7 +753,7 @@ void spi_print(const mp_print_t *print, const spi_t *spi_obj, bool legacy) {
if (spi->State != HAL_SPI_STATE_RESET) {
if (spi->Init.Mode == SPI_MODE_MASTER) {
// compute baudrate
- #if defined(STM32H5) || defined(STM32H7)
+ #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
uint log_prescaler = (spi->Init.BaudRatePrescaler >> 28) + 1;
#else
uint log_prescaler = (spi->Init.BaudRatePrescaler >> 3) + 1;
diff --git a/ports/stm32/spibdev.c b/ports/stm32/spibdev.c
index fecd4a9915..d7a75ed240 100644
--- a/ports/stm32/spibdev.c
+++ b/ports/stm32/spibdev.c
@@ -32,6 +32,16 @@
#if MICROPY_HW_ENABLE_STORAGE
+#if MICROPY_HW_RUNS_FROM_EXT_FLASH
+// Disable all interrupts.
+#define FLASH_WRITE_ENTER uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION()
+#define FLASH_WRITE_EXIT MICROPY_END_ATOMIC_SECTION(atomic_state)
+#else
+// Prevent cache flushing and USB access.
+#define FLASH_WRITE_ENTER uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH)
+#define FLASH_WRITE_EXIT restore_irq_pri(basepri)
+#endif
+
int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) {
switch (op) {
case BDEV_IOCTL_INIT:
@@ -68,6 +78,7 @@ int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) {
}
#if MICROPY_HW_SPIFLASH_ENABLE_CACHE
+
int spi_bdev_readblocks(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
int ret = mp_spiflash_cached_read(&bdev->spiflash, block_num * FLASH_BLOCK_SIZE, num_blocks * FLASH_BLOCK_SIZE, dest);
@@ -87,20 +98,36 @@ int spi_bdev_writeblocks(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_nu
return ret;
}
+
+#elif FLASH_BLOCK_SIZE == MP_SPIFLASH_ERASE_BLOCK_SIZE
+
+int spi_bdev_readblocks(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t num_blocks) {
+ int ret = spi_bdev_readblocks_raw(bdev, dest, block_num, 0, num_blocks * FLASH_BLOCK_SIZE);
+ return ret;
+}
+
+int spi_bdev_writeblocks(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_num, uint32_t num_blocks) {
+ int ret = spi_bdev_eraseblocks_raw(bdev, block_num, num_blocks * FLASH_BLOCK_SIZE);
+ if (ret == 0) {
+ ret = spi_bdev_writeblocks_raw(bdev, src, block_num, 0, num_blocks * FLASH_BLOCK_SIZE);
+ }
+ return ret;
+}
+
#endif // MICROPY_HW_SPIFLASH_ENABLE_CACHE
int spi_bdev_readblocks_raw(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t block_offset, uint32_t num_bytes) {
- uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
+ FLASH_WRITE_ENTER;
int ret = mp_spiflash_read(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE + block_offset, num_bytes, dest);
- restore_irq_pri(basepri);
+ FLASH_WRITE_EXIT;
return ret;
}
int spi_bdev_writeblocks_raw(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_num, uint32_t block_offset, uint32_t num_bytes) {
- uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
+ FLASH_WRITE_ENTER;
int ret = mp_spiflash_write(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE + block_offset, num_bytes, src);
- restore_irq_pri(basepri);
+ FLASH_WRITE_EXIT;
return ret;
}
@@ -108,9 +135,9 @@ int spi_bdev_writeblocks_raw(spi_bdev_t *bdev, const uint8_t *src, uint32_t bloc
int spi_bdev_eraseblocks_raw(spi_bdev_t *bdev, uint32_t block_num, uint32_t num_bytes) {
int ret = 0;
while (num_bytes >= MP_SPIFLASH_ERASE_BLOCK_SIZE) {
- uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access
+ FLASH_WRITE_ENTER;
ret = mp_spiflash_erase_block(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE);
- restore_irq_pri(basepri);
+ FLASH_WRITE_EXIT;
if (ret) {
break;
}
diff --git a/ports/stm32/stm32.mk b/ports/stm32/stm32.mk
index 718fa8cf06..e6526fc6bd 100644
--- a/ports/stm32/stm32.mk
+++ b/ports/stm32/stm32.mk
@@ -43,19 +43,17 @@ ifneq ($(BUILDING_MBOOT),1)
# Select hardware floating-point support.
SUPPORTS_HARDWARE_FP_SINGLE = 0
SUPPORTS_HARDWARE_FP_DOUBLE = 0
-ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H747xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ))
+ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H747xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ STM32N657xx))
CFLAGS_CORTEX_M += -mfpu=fpv5-d16 -mfloat-abi=hard -mfp16-format=ieee
SUPPORTS_HARDWARE_FP_SINGLE = 1
SUPPORTS_HARDWARE_FP_DOUBLE = 1
-else
-ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 g0 l0 l1 wl))
+else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 g0 l0 l1 wl))
CFLAGS_CORTEX_M += -msoft-float
else
CFLAGS_CORTEX_M += -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mfp16-format=ieee
SUPPORTS_HARDWARE_FP_SINGLE = 1
endif
endif
-endif
# Options for particular MCU series.
CFLAGS_MCU_f0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0 -mcpu=cortex-m0
@@ -68,6 +66,7 @@ CFLAGS_MCU_l1 = $(CFLAGS_CORTEX_M) -mtune=cortex-m3 -mcpu=cortex-m3
CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4
CFLAGS_MCU_h5 = $(CFLAGS_CORTEX_M) -mtune=cortex-m33 -mcpu=cortex-m33
CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7
+CFLAGS_MCU_n6 = $(CFLAGS_CORTEX_M) -mtune=cortex-m55 -mcpu=cortex-m55 -mcmse
CFLAGS_MCU_wb = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4
CFLAGS_MCU_wl = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4
@@ -81,5 +80,20 @@ MPY_CROSS_MCU_ARCH_l1 = armv7m
MPY_CROSS_MCU_ARCH_l4 = armv7m
MPY_CROSS_MCU_ARCH_h5 = armv7m
MPY_CROSS_MCU_ARCH_h7 = armv7m
+MPY_CROSS_MCU_ARCH_n6 = armv7m # really armv8m
MPY_CROSS_MCU_ARCH_wb = armv7m
MPY_CROSS_MCU_ARCH_wl = armv7m
+
+# gcc up to 14.2.0 have a known loop-optimisation bug:
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116799
+# This bug manifests for Cortex M55 targets, so require a newer compiler on such targets.
+ifeq ($(MCU_SERIES),n6)
+# Check if GCC version is less than 14.3
+GCC_VERSION := $(shell $(CROSS_COMPILE)gcc -dumpversion | cut -d. -f1-2)
+GCC_VERSION_MAJOR := $(shell echo $(GCC_VERSION) | cut -d. -f1)
+GCC_VERSION_MINOR := $(shell echo $(GCC_VERSION) | cut -d. -f2)
+GCC_VERSION_NUM := $(shell echo $$(($(GCC_VERSION_MAJOR) * 100 + $(GCC_VERSION_MINOR))))
+ifeq ($(shell test $(GCC_VERSION_NUM) -lt 1403 && echo yes),yes)
+$(error Error: GCC $(GCC_VERSION) has known issues with Cortex-M55; upgrade to GCC 14.3+ for proper CM55 support)
+endif
+endif
diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c
index 4bf509bb94..3639e2f049 100644
--- a/ports/stm32/stm32_it.c
+++ b/ports/stm32/stm32_it.c
@@ -343,14 +343,22 @@ void OTG_FS_IRQHandler(void) {
}
#endif
#if MICROPY_HW_USB_HS
+#if defined(STM32N6)
+void USB1_OTG_HS_IRQHandler(void) {
+ IRQ_ENTER(USB1_OTG_HS_IRQn);
+ HAL_PCD_IRQHandler(&pcd_hs_handle);
+ IRQ_EXIT(USB1_OTG_HS_IRQn);
+}
+#else
void OTG_HS_IRQHandler(void) {
IRQ_ENTER(OTG_HS_IRQn);
HAL_PCD_IRQHandler(&pcd_hs_handle);
IRQ_EXIT(OTG_HS_IRQn);
}
#endif
+#endif
-#if MICROPY_HW_USB_FS || MICROPY_HW_USB_HS
+#if (MICROPY_HW_USB_FS || MICROPY_HW_USB_HS) && !defined(STM32N6)
/**
* @brief This function handles USB OTG Common FS/HS Wakeup functions.
* @param *pcd_handle for FS or HS
@@ -421,7 +429,7 @@ void OTG_FS_WKUP_IRQHandler(void) {
}
#endif
-#if MICROPY_HW_USB_HS
+#if MICROPY_HW_USB_HS && !defined(STM32N6)
/**
* @brief This function handles USB OTG HS Wakeup IRQ Handler.
* @param None
@@ -480,7 +488,7 @@ void ETH_WKUP_IRQHandler(void) {
}
#endif
-#if defined(STM32H5)
+#if defined(STM32H5) || defined(STM32N6)
void TAMP_IRQHandler(void) {
IRQ_ENTER(TAMP_IRQn);
Handle_EXTI_Irq(EXTI_RTC_TAMP);
@@ -502,6 +510,9 @@ void TAMP_STAMP_IRQHandler(void) {
#if defined(STM32H5)
void RTC_IRQHandler(void)
+#elif defined(STM32N6)
+#define RTC_WKUP_IRQn RTC_S_IRQn
+void RTC_S_IRQHandler(void)
#else
void RTC_WKUP_IRQHandler(void)
#endif
@@ -509,8 +520,8 @@ void RTC_WKUP_IRQHandler(void)
IRQ_ENTER(RTC_WKUP_IRQn);
#if defined(STM32G0) || defined(STM32G4) || defined(STM32WL)
RTC->MISR &= ~RTC_MISR_WUTMF; // clear wakeup interrupt flag
- #elif defined(STM32H5)
- RTC->SCR = RTC_SCR_CWUTF; // clear wakeup interrupt flag
+ #elif defined(STM32H5) || defined(STM32N6)
+ LL_RTC_ClearFlag_WUT(RTC);
#elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ)
RTC->SR &= ~RTC_SR_WUTF; // clear wakeup interrupt flag
#else
@@ -520,6 +531,12 @@ void RTC_WKUP_IRQHandler(void)
IRQ_EXIT(RTC_WKUP_IRQn);
}
+#if defined(STM32N6)
+void RTC_IRQHandler(void) {
+ RTC_S_IRQHandler();
+}
+#endif
+
#if defined(STM32F0) || defined(STM32G0) || defined(STM32L0)
#if defined(STM32G0)
diff --git a/ports/stm32/storage.c b/ports/stm32/storage.c
index d810261fbc..d26ac821e5 100644
--- a/ports/stm32/storage.c
+++ b/ports/stm32/storage.c
@@ -32,6 +32,7 @@
#include "led.h"
#include "storage.h"
#include "irq.h"
+#include "xspi.h"
#if MICROPY_HW_ENABLE_STORAGE
@@ -44,13 +45,17 @@
static bool storage_is_initialised = false;
+#if !defined(STM32N6)
static void storage_systick_callback(uint32_t ticks_ms);
+#endif
void storage_init(void) {
if (!storage_is_initialised) {
storage_is_initialised = true;
+ #if !defined(STM32N6)
systick_enable_dispatch(SYSTICK_DISPATCH_STORAGE, storage_systick_callback);
+ #endif
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0);
@@ -58,10 +63,12 @@ void storage_init(void) {
MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_INIT, 0);
#endif
+ #if !defined(STM32N6)
// Enable the flash IRQ, which is used to also call our storage IRQ handler
// It must go at the same priority as USB (see comment in irq.h).
NVIC_SetPriority(FLASH_IRQn, IRQ_PRI_FLASH);
HAL_NVIC_EnableIRQ(FLASH_IRQn);
+ #endif
}
}
@@ -77,6 +84,7 @@ uint32_t storage_get_block_count(void) {
#endif
}
+#if !defined(STM32N6)
static void storage_systick_callback(uint32_t ticks_ms) {
if (STORAGE_IDLE_TICK(ticks_ms)) {
// Trigger a FLASH IRQ to execute at a lower priority
@@ -96,6 +104,7 @@ void FLASH_IRQHandler(void) {
#endif
IRQ_EXIT(FLASH_IRQn);
}
+#endif
void storage_flush(void) {
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_SYNC, 0);
@@ -235,11 +244,11 @@ int storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_bl
// Board defined an external SPI flash for use with extended block protocol
#define MICROPY_HW_BDEV_BLOCKSIZE_EXT (MP_SPIFLASH_ERASE_BLOCK_SIZE)
#define MICROPY_HW_BDEV_READBLOCKS_EXT(dest, bl, off, len) \
- (spi_bdev_readblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (dest), (bl), (off), (len)))
+ (spi_bdev_readblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (dest), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / MP_SPIFLASH_ERASE_BLOCK_SIZE + (bl), (off), (len)))
#define MICROPY_HW_BDEV_WRITEBLOCKS_EXT(src, bl, off, len) \
- (spi_bdev_writeblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (src), (bl), (off), (len)))
+ (spi_bdev_writeblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (src), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / MP_SPIFLASH_ERASE_BLOCK_SIZE + (bl), (off), (len)))
#define MICROPY_HW_BDEV_ERASEBLOCKS_EXT(bl, len) \
- (spi_bdev_eraseblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (bl), (len)))
+ (spi_bdev_eraseblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / MP_SPIFLASH_ERASE_BLOCK_SIZE + (bl), (len)))
#elif (MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2) && MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE
// Board uses littlefs and internal flash, so enable extended block protocol on internal flash
diff --git a/ports/stm32/storage.h b/ports/stm32/storage.h
index accf6c3904..75cb0e9c1e 100644
--- a/ports/stm32/storage.h
+++ b/ports/stm32/storage.h
@@ -28,7 +28,11 @@
#include "drivers/memory/spiflash.h"
+#if defined(STM32N6)
+#define FLASH_BLOCK_SIZE (4096)
+#else
#define FLASH_BLOCK_SIZE (512)
+#endif
#define FLASH_PART1_START_BLOCK (0x100)
// Try to match Python-level VFS block protocol where possible for these constants
diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c
index 9d65b484cd..4ec467d9db 100644
--- a/ports/stm32/timer.c
+++ b/ports/stm32/timer.c
@@ -261,6 +261,12 @@ uint32_t timer_get_source_freq(uint32_t tim_id) {
}
}
+ #elif defined(STM32N6)
+
+ // Timers are clocked either by ck_timg1 or ck_timg2.
+ // Both of those have the same frequency: sys_bus_ck / prescaler(TIMPRE)
+ return LL_RCC_GetSystemClockFreq() / (1 << LL_RCC_GetTIMPrescaler());
+
#else
uint32_t source, clk_div;
@@ -846,7 +852,9 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
TIM_ENTRY(1, TIM1_UP_TIM16_IRQn),
#endif
#endif
+
TIM_ENTRY(2, TIM2_IRQn),
+
#if defined(TIM3)
#if defined(STM32G0B1xx) || defined(STM32G0C1xx)
TIM_ENTRY(3, TIM3_TIM4_IRQn),
@@ -854,6 +862,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
TIM_ENTRY(3, TIM3_IRQn),
#endif
#endif
+
#if defined(TIM4)
#if defined(STM32G0B1xx) || defined(STM32G0C1xx)
TIM_ENTRY(3, TIM3_TIM4_IRQn),
@@ -861,20 +870,23 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
TIM_ENTRY(4, TIM4_IRQn),
#endif
#endif
+
#if defined(TIM5)
TIM_ENTRY(5, TIM5_IRQn),
#endif
+
#if defined(TIM6)
#if defined(STM32F412Zx) || defined(STM32L1)
TIM_ENTRY(6, TIM6_IRQn),
#elif defined(STM32G0)
TIM_ENTRY(6, TIM6_DAC_LPTIM1_IRQn),
- #elif defined(STM32H5)
+ #elif defined(STM32H5) || defined(STM32N6)
TIM_ENTRY(6, TIM6_IRQn),
#else
TIM_ENTRY(6, TIM6_DAC_IRQn),
#endif
#endif
+
#if defined(TIM7)
#if defined(STM32G0)
TIM_ENTRY(7, TIM7_LPTIM2_IRQn),
@@ -894,7 +906,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#endif
#if defined(TIM9)
- #if defined(STM32L1)
+ #if defined(STM32L1) || defined(STM32N6)
TIM_ENTRY(9, TIM9_IRQn),
#else
TIM_ENTRY(9, TIM1_BRK_TIM9_IRQn),
@@ -902,7 +914,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#endif
#if defined(TIM10)
- #if defined(STM32L1)
+ #if defined(STM32L1) || defined(STM32N6)
TIM_ENTRY(10, TIM10_IRQn),
#else
TIM_ENTRY(10, TIM1_UP_TIM10_IRQn),
@@ -910,7 +922,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#endif
#if defined(TIM11)
- #if defined(STM32L1)
+ #if defined(STM32L1) || defined(STM32N6)
TIM_ENTRY(11, TIM11_IRQn),
#else
TIM_ENTRY(11, TIM1_TRG_COM_TIM11_IRQn),
@@ -918,7 +930,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#endif
#if defined(TIM12)
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
TIM_ENTRY(12, TIM12_IRQn),
#else
TIM_ENTRY(12, TIM8_BRK_TIM12_IRQn),
@@ -926,21 +938,21 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#endif
#if defined(TIM13)
- #if defined(STM32H5)
+ #if defined(STM32H5) || defined(STM32N6)
TIM_ENTRY(13, TIM13_IRQn),
#else
TIM_ENTRY(13, TIM8_UP_TIM13_IRQn),
#endif
#endif
- #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5)
+ #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32N6)
TIM_ENTRY(14, TIM14_IRQn),
#elif defined(TIM14)
TIM_ENTRY(14, TIM8_TRG_COM_TIM14_IRQn),
#endif
#if defined(TIM15)
- #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7)
+ #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6)
TIM_ENTRY(15, TIM15_IRQn),
#else
TIM_ENTRY(15, TIM1_BRK_TIM15_IRQn),
@@ -950,7 +962,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#if defined(TIM16)
#if defined(STM32G0B1xx) || defined(STM32G0C1xx)
TIM_ENTRY(16, TIM16_FDCAN_IT0_IRQn),
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32WL)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WL)
TIM_ENTRY(16, TIM16_IRQn),
#else
TIM_ENTRY(16, TIM1_UP_TIM16_IRQn),
@@ -960,7 +972,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = {
#if defined(TIM17)
#if defined(STM32G0B1xx) || defined(STM32G0C1xx)
TIM_ENTRY(17, TIM17_FDCAN_IT1_IRQn),
- #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32WL)
+ #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WL)
TIM_ENTRY(17, TIM17_IRQn),
#else
TIM_ENTRY(17, TIM1_TRG_COM_TIM17_IRQn),
diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c
index 91db91395e..9354af4a29 100644
--- a/ports/stm32/uart.c
+++ b/ports/stm32/uart.c
@@ -91,7 +91,7 @@
#define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_WUFIE)
#endif
-#elif defined(STM32H7)
+#elif defined(STM32H7) || defined(STM32N6)
#define USART_CR1_IE_ALL (USART_CR1_IE_BASE | USART_CR1_RXFFIE | USART_CR1_TXFEIE | USART_CR1_EOBIE | USART_CR1_RTOIE | USART_CR1_CMIE)
#define USART_CR2_IE_ALL (USART_CR2_IE_BASE)
#define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_RXFTIE | USART_CR3_TCBGTIE | USART_CR3_TXFTIE | USART_CR3_WUFIE)
@@ -157,6 +157,18 @@ void uart_init0(void) {
if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit) != HAL_OK) {
MICROPY_BOARD_FATAL_ERROR("HAL_RCCEx_PeriphCLKConfig");
}
+ #elif defined(STM32N6)
+ // UART clock configuration, IC14 (max 100MHz).
+ LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_IC14);
+ LL_RCC_SetUSARTClockSource(LL_RCC_USART2_CLKSOURCE_IC14);
+ LL_RCC_SetUSARTClockSource(LL_RCC_USART3_CLKSOURCE_IC14);
+ LL_RCC_SetUARTClockSource(LL_RCC_UART4_CLKSOURCE_IC14);
+ LL_RCC_SetUARTClockSource(LL_RCC_UART5_CLKSOURCE_IC14);
+ LL_RCC_SetUSARTClockSource(LL_RCC_USART6_CLKSOURCE_IC14);
+ LL_RCC_SetUARTClockSource(LL_RCC_UART7_CLKSOURCE_IC14);
+ LL_RCC_SetUARTClockSource(LL_RCC_UART8_CLKSOURCE_IC14);
+ LL_RCC_SetUARTClockSource(LL_RCC_UART9_CLKSOURCE_IC14);
+ LL_RCC_SetUSARTClockSource(LL_RCC_USART10_CLKSOURCE_IC14);
#endif
}
@@ -653,7 +665,7 @@ bool uart_init(machine_uart_obj_t *uart_obj,
huart.Init.HwFlowCtl = flow;
huart.Init.OverSampling = UART_OVERSAMPLING_16;
- #if defined(STM32G4) // H7 and WB also have fifo..
+ #if defined(STM32G4) || defined(STM32H7) // WB also has a fifo..
huart.FifoMode = UART_FIFOMODE_ENABLE;
#endif
@@ -661,7 +673,7 @@ bool uart_init(machine_uart_obj_t *uart_obj,
huart.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
#endif
- #if defined(STM32H7) || defined(STM32WB)
+ #if defined(STM32H7) || defined(STM32N6) || defined(STM32WB)
// Compute the smallest prescaler that will allow the given baudrate.
uint32_t presc = UART_PRESCALER_DIV1;
if (uart_obj->uart_id == PYB_LPUART_1) {
@@ -689,6 +701,10 @@ bool uart_init(machine_uart_obj_t *uart_obj,
uart_obj->is_enabled = true;
uart_obj->attached_to_repl = false;
+ #if defined(STM32F4) || defined(STM32L1)
+ uart_obj->suppress_idle_irq = true;
+ #endif
+
if (bits == UART_WORDLENGTH_9B && parity == UART_PARITY_NONE) {
uart_obj->char_mask = 0x1ff;
uart_obj->char_width = CHAR_WIDTH_9BIT;
@@ -701,6 +717,12 @@ bool uart_init(machine_uart_obj_t *uart_obj,
uart_obj->char_width = CHAR_WIDTH_8BIT;
}
+ #if defined(STM32H7)
+ HAL_UARTEx_SetTxFifoThreshold(&huart, UART_TXFIFO_THRESHOLD_1_8);
+ HAL_UARTEx_SetRxFifoThreshold(&huart, UART_RXFIFO_THRESHOLD_1_8);
+ HAL_UARTEx_EnableFifoMode(&huart);
+ #endif
+
uart_obj->mp_irq_trigger = 0;
uart_obj->mp_irq_obj = NULL;
@@ -966,6 +988,29 @@ uint32_t uart_get_source_freq(machine_uart_obj_t *self) {
default:
break;
}
+
+ #elif defined(STM32N6)
+
+ static const uint16_t is_usart = 1 << 10 | 1 << 6 | 1 << 3 | 1 << 2 | 1 << 1;
+ static const uint32_t clksource[] = {
+ LL_RCC_USART1_CLKSOURCE,
+ LL_RCC_USART2_CLKSOURCE,
+ LL_RCC_USART3_CLKSOURCE,
+ LL_RCC_UART4_CLKSOURCE,
+ LL_RCC_UART5_CLKSOURCE,
+ LL_RCC_USART6_CLKSOURCE,
+ LL_RCC_UART7_CLKSOURCE,
+ LL_RCC_UART8_CLKSOURCE,
+ LL_RCC_UART9_CLKSOURCE,
+ LL_RCC_USART10_CLKSOURCE,
+ };
+
+ if (is_usart & (1 << self->uart_id)) {
+ uart_clk = LL_RCC_GetUSARTClockFreq(clksource[self->uart_id - 1]);
+ } else {
+ uart_clk = LL_RCC_GetUARTClockFreq(clksource[self->uart_id - 1]);
+ }
+
#else
if (self->uart_id == 1
#if defined(USART6)
@@ -991,14 +1036,14 @@ uint32_t uart_get_baudrate(machine_uart_obj_t *self) {
#if defined(LPUART1)
if (self->uart_id == PYB_LPUART_1) {
return LL_LPUART_GetBaudRate(self->uartx, uart_get_source_freq(self)
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
, self->uartx->PRESC
#endif
);
}
#endif
return LL_USART_GetBaudRate(self->uartx, uart_get_source_freq(self),
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
self->uartx->PRESC,
#endif
LL_USART_OVERSAMPLING_16);
@@ -1008,7 +1053,7 @@ void uart_set_baudrate(machine_uart_obj_t *self, uint32_t baudrate) {
#if defined(LPUART1)
if (self->uart_id == PYB_LPUART_1) {
LL_LPUART_SetBaudRate(self->uartx, uart_get_source_freq(self),
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
LL_LPUART_PRESCALER_DIV1,
#endif
baudrate);
@@ -1016,7 +1061,7 @@ void uart_set_baudrate(machine_uart_obj_t *self, uint32_t baudrate) {
}
#endif
LL_USART_SetBaudRate(self->uartx, uart_get_source_freq(self),
- #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
LL_USART_PRESCALER_DIV1,
#endif
LL_USART_OVERSAMPLING_16, baudrate);
@@ -1067,7 +1112,7 @@ int uart_rx_char(machine_uart_obj_t *self) {
return data;
} else {
// no buffering
- #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
int data = self->uartx->RDR & self->char_mask;
self->uartx->ICR = USART_ICR_ORECF; // clear ORE if it was set
return data;
@@ -1141,6 +1186,9 @@ size_t uart_tx_data(machine_uart_obj_t *self, const void *src_in, size_t num_cha
// timeout_char by FIFO size + 1.
// STM32G4 has 8 words FIFO.
timeout = (8 + 1) * self->timeout_char;
+ #elif defined(STM32H7)
+ // STM32H7 has 16 words FIFO.
+ timeout = (16 + 1) * self->timeout_char;
#else
// The timeout specified here is for waiting for the TX data register to
// become empty (ie between chars), as well as for the final char to be
@@ -1219,7 +1267,7 @@ void uart_irq_handler(mp_uint_t uart_id) {
uint16_t next_head = (self->read_buf_head + 1) % self->read_buf_len;
if (next_head != self->read_buf_tail) {
// only read data if room in buf
- #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL)
+ #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL)
int data = self->uartx->RDR; // clears UART_FLAG_RXNE
#else
self->mp_irq_flags = self->uartx->SR; // resample to get any new flags since next read of DR will clear SR
@@ -1265,6 +1313,9 @@ void uart_irq_handler(mp_uint_t uart_id) {
self->uartx->CR1 &= ~USART_CR1_RXNEIE;
}
}
+ if (self->suppress_idle_irq) {
+ self->mp_irq_flags &= ~USART_SR_IDLE;
+ }
#else
self->uartx->ICR = self->mp_irq_flags & (USART_ICR_IDLECF | USART_ICR_ORECF);
#endif
@@ -1273,6 +1324,14 @@ void uart_irq_handler(mp_uint_t uart_id) {
if (self->mp_irq_trigger & self->mp_irq_flags) {
mp_irq_handler(self->mp_irq_obj);
}
+
+ #if defined(STM32F4) || defined(STM32L1)
+ if (did_clear_sr) {
+ self->suppress_idle_irq = false;
+ } else {
+ self->suppress_idle_irq = true;
+ }
+ #endif
}
static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) {
diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h
index de4b70cdea..d92434c641 100644
--- a/ports/stm32/uart.h
+++ b/ports/stm32/uart.h
@@ -26,6 +26,7 @@
#ifndef MICROPY_INCLUDED_STM32_UART_H
#define MICROPY_INCLUDED_STM32_UART_H
+#include "py/mphal.h"
#include "shared/runtime/mpirq.h"
typedef enum {
@@ -63,6 +64,9 @@ typedef struct _machine_uart_obj_t {
pyb_uart_t uart_id : 8;
bool is_static : 1;
bool is_enabled : 1;
+ #if defined(STM32F4) || defined(STM32L1)
+ bool suppress_idle_irq : 1; // whether the RX idle IRQ is suppressed (F4/L1 only)
+ #endif
bool attached_to_repl; // whether the UART is attached to REPL
byte char_width; // 0 for 7,8 bit chars, 1 for 9 bit chars
uint16_t char_mask; // 0x7f for 7 bit, 0xff for 8 bit, 0x1ff for 9 bit
diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c
index af9dd1d70e..2d70dcb261 100644
--- a/ports/stm32/usb.c
+++ b/ports/stm32/usb.c
@@ -67,7 +67,7 @@
#define MAX_ENDPOINT(dev_id) ((dev_id) == USB_PHY_FS_ID ? 3 : 5)
#elif defined(STM32F7)
#define MAX_ENDPOINT(dev_id) ((dev_id) == USB_PHY_FS_ID ? 5 : 8)
-#elif defined(STM32H7)
+#elif defined(STM32H7) || defined(STM32N6)
#define MAX_ENDPOINT(dev_id) (8)
#endif
diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c
index 829037ba93..7a9e63c9f3 100644
--- a/ports/stm32/usbd_conf.c
+++ b/ports/stm32/usbd_conf.c
@@ -51,6 +51,11 @@ PCD_HandleTypeDef pcd_hs_handle;
#define USB_OTG_FS USB
#endif
+#if defined(STM32N6)
+#define USB_OTG_HS USB1_OTG_HS
+#define OTG_HS_IRQn USB1_OTG_HS_IRQn
+#endif
+
/*******************************************************************************
PCD BSP Routines
*******************************************************************************/
@@ -191,6 +196,10 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) {
mp_hal_pin_config(pin_A12, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0);
mp_hal_pin_config_speed(pin_A12, GPIO_SPEED_FREQ_VERY_HIGH);
+ #elif defined(STM32N6)
+
+ // These MCUs have dedicated USB pins.
+
#else
// Other MCUs have an alternate function for GPIO's to be in USB mode.
@@ -220,6 +229,23 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) {
mp_hal_pin_config(MICROPY_HW_USB_OTG_ID_PIN, MP_HAL_PIN_MODE_ALT_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, otg_alt);
#endif
+ #if defined(STM32N6)
+
+ __HAL_RCC_USB1_OTG_HS_FORCE_RESET();
+ __HAL_RCC_USB1_OTG_HS_PHY_FORCE_RESET();
+ __HAL_RCC_USB1_OTG_HS_PHY_RELEASE_RESET();
+ __HAL_RCC_USB1_OTG_HS_RELEASE_RESET();
+
+ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_OTG1);
+ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_OTGPHY1);
+ LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_OTG1);
+ LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_OTGPHY1);
+
+ // Select 24MHz clock.
+ MODIFY_REG(USB1_HS_PHYC->USBPHYC_CR, USB_USBPHYC_CR_FSEL, 2 << USB_USBPHYC_CR_FSEL_Pos);
+
+ #else
+
// Enable calling WFI and correct function of the embedded USB_FS_IN_HS phy
__HAL_RCC_USB_OTG_HS_ULPI_CLK_SLEEP_DISABLE();
__HAL_RCC_USB_OTG_HS_CLK_SLEEP_ENABLE();
@@ -235,6 +261,8 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) {
__HAL_RCC_USB_OTG_HS_CLK_ENABLE();
+ #endif
+
#else // !MICROPY_HW_USB_HS_IN_FS
// Configure USB HS GPIOs
@@ -283,7 +311,12 @@ void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd) {
#if MICROPY_HW_USB_HS
if (hpcd->Instance == USB_OTG_HS) {
/* Disable USB FS Clocks */
+ #if defined(STM32N6)
+ LL_AHB5_GRP1_DisableClock(LL_AHB5_GRP1_PERIPH_OTG1);
+ LL_AHB5_GRP1_DisableClock(LL_AHB5_GRP1_PERIPH_OTGPHY1);
+ #else
__USB_OTG_HS_CLK_DISABLE();
+ #endif
}
#endif
@@ -517,7 +550,7 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed, const
#if MICROPY_HW_USB_HS_IN_FS
- #if defined(STM32F723xx) || defined(STM32F733xx)
+ #if defined(STM32F723xx) || defined(STM32F733xx) || defined(STM32N6)
pcd_hs_handle.Init.phy_itface = USB_OTG_HS_EMBEDDED_PHY;
#else
pcd_hs_handle.Init.phy_itface = PCD_PHY_EMBEDDED;
diff --git a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h
index 2c90ce165e..34f0412534 100644
--- a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h
+++ b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h
@@ -11,7 +11,7 @@
// Work out if we should support USB high-speed device mode
#if MICROPY_HW_USB_HS \
- && (!MICROPY_HW_USB_HS_IN_FS || defined(STM32F723xx) || defined(STM32F733xx))
+ && (!MICROPY_HW_USB_HS_IN_FS || defined(STM32F723xx) || defined(STM32F733xx) || defined(STM32N6))
#define USBD_SUPPORT_HS_MODE (1)
#else
#define USBD_SUPPORT_HS_MODE (0)
@@ -31,7 +31,11 @@
#else
#define CDC_DATA_MAX_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE
#endif
+#if defined(STM32N6)
+#define MSC_MEDIA_PACKET (4096) // must be at least the SPI flash erase size
+#else
#define MSC_MEDIA_PACKET (2048) // was 8192; how low can it go whilst still working?
+#endif
#define HID_DATA_FS_MAX_PACKET_SIZE (64) // endpoint IN & OUT packet size
// Maximum number of LUN that can be exposed on the MSC interface
diff --git a/ports/stm32/vfs_rom_ioctl.c b/ports/stm32/vfs_rom_ioctl.c
index 7592aa22d6..5dbc855861 100644
--- a/ports/stm32/vfs_rom_ioctl.c
+++ b/ports/stm32/vfs_rom_ioctl.c
@@ -33,6 +33,7 @@
#include "flash.h"
#include "qspi.h"
#include "storage.h"
+#include "xspi.h"
#if MICROPY_VFS_ROM_IOCTL
@@ -142,6 +143,18 @@ mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) {
return MP_OBJ_NEW_SMALL_INT(4);
}
#endif
+
+ #if MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI
+ if (xspi_is_valid_addr(&xspi_flash2, dest)) {
+ dest -= xspi_get_xip_base(&xspi_flash2);
+ dest_max -= xspi_get_xip_base(&xspi_flash2);
+ int ret = spi_bdev_eraseblocks_raw(MICROPY_HW_ROMFS_XSPI_SPIBDEV_OBJ, dest / MP_SPIFLASH_ERASE_BLOCK_SIZE, dest_max - dest + MP_SPIFLASH_ERASE_BLOCK_SIZE - 1);
+ if (ret < 0) {
+ return MP_OBJ_NEW_SMALL_INT(ret);
+ }
+ return MP_OBJ_NEW_SMALL_INT(4);
+ }
+ #endif
}
if (cmd == MP_VFS_ROM_IOCTL_WRITE) {
@@ -170,6 +183,14 @@ mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) {
return MP_OBJ_NEW_SMALL_INT(ret);
}
#endif
+
+ #if MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI
+ if (xspi_is_valid_addr(&xspi_flash2, dest)) {
+ dest -= xspi_get_xip_base(&xspi_flash2);
+ int ret = spi_bdev_writeblocks_raw(MICROPY_HW_ROMFS_XSPI_SPIBDEV_OBJ, bufinfo.buf, 0, dest, bufinfo.len);
+ return MP_OBJ_NEW_SMALL_INT(ret);
+ }
+ #endif
}
return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL);
diff --git a/ports/stm32/xspi.c b/ports/stm32/xspi.c
new file mode 100644
index 0000000000..b113110c05
--- /dev/null
+++ b/ports/stm32/xspi.c
@@ -0,0 +1,599 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024-2025 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// This XSPI driver is currently configured to run in 1-line (SPI) mode.
+// It uses the mp_qspi_proto_t QSPI protocol and translates quad-commands
+// into 1-line commands.
+
+#include <string.h>
+#include "py/mperrno.h"
+#include "py/mphal.h"
+#include "xspi.h"
+
+#if defined(MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2)
+
+#ifndef MICROPY_HW_XSPI_PRESCALER
+#define MICROPY_HW_XSPI_PRESCALER (4) // F_CLK = F_AHB/4
+#endif
+
+#ifndef MICROPY_HW_XSPI_CS_HIGH_CYCLES
+#define MICROPY_HW_XSPI_CS_HIGH_CYCLES (2) // nCS stays high for 4 cycles
+#endif
+
+// Currently hard-coded to use XSPI2 instance.
+#define XSPIx (XSPI2)
+
+// For XSPI2, PN0 through PN12.
+#define XSPI2_AF (9)
+
+typedef struct _xspi_flash_t {
+ XSPI_TypeDef *xspi;
+ uintptr_t xip_base;
+} xspi_flash_t;
+
+const xspi_flash_t xspi_flash1 = {
+ .xspi = XSPI1,
+ .xip_base = 0x90000000,
+};
+
+const xspi_flash_t xspi_flash2 = {
+ .xspi = XSPI2,
+ .xip_base = 0x70000000,
+};
+
+static bool xspi_dtr_enabled = false;
+
+#ifdef pyb_pin_FLASH_RESET
+// Can't rely on SysTick being available, so use a busy loop for delays.
+// The timing here is approximate and assumes a CPU frequency of 800MHz.
+static void xspi_delay_us(unsigned int us) {
+ while (us--) {
+ for (unsigned int i = 0; i < 800; ++i) {
+ __NOP();
+ }
+ }
+}
+#endif
+
+static inline void mp_hal_pin_config_alt_speed(mp_hal_pin_obj_t pin, uint32_t pull, uint32_t alt, uint32_t speed) {
+ mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ALT, pull, alt);
+ mp_hal_pin_config_speed(pin, speed);
+}
+
+static int xspi_read_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, uint8_t *dest);
+static int xspi_write_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src);
+static int xspi_read_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, uint32_t num_dummy, size_t len, uint8_t *dest);
+static int xspi_write_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src);
+static void xspi_memory_map_111(void);
+static void xspi_memory_map_888(void);
+static void xspi_memory_map_exit(void);
+
+void xspi_init(void) {
+ // Configure XSPI pins.
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_CS, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_SCK, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_DQS, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO0, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO1, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO2, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO3, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO4, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO5, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO6, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+ mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO7, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH);
+
+ LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPIM);
+ LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPI1);
+ LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPI2);
+ LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPI3);
+
+ LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPIM);
+ LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPI1);
+ LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPI2);
+ LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPI3);
+
+ LL_RCC_SetXSPIClockSource(LL_RCC_XSPI1_CLKSOURCE_HCLK);
+ LL_RCC_SetXSPIClockSource(LL_RCC_XSPI2_CLKSOURCE_HCLK);
+
+ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_XSPIM);
+ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_XSPI1);
+ LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_XSPI2);
+
+ // Configure XSPIM in direct mode.
+ XSPI1->CR &= ~XSPI_CR_EN;
+ XSPI2->CR &= ~XSPI_CR_EN;
+ XSPIM->CR = 0;
+
+ // Configure the XSPIx peripheral.
+
+ XSPIx->CR =
+ 3 << XSPI_CR_FTHRES_Pos // 4 byte must be available to read/write
+ | 0 << XSPI_CR_MSEL_Pos // FLASH 0 selected
+ | 0 << XSPI_CR_CSSEL_Pos // use NCS1 as chip select
+ | 0 << XSPI_CR_DMM_Pos // dual-memory mode disabled
+ | 1 << XSPI_CR_TCEN_Pos // time-out counter enabled
+ | 0 << XSPI_CR_DMAEN_Pos // DMA disabled
+ | 0 << XSPI_CR_ABORT_Pos // no abort request
+ | 0 << XSPI_CR_EN_Pos // disabled
+ ;
+
+ XSPIx->DCR1 =
+ 1 << XSPI_DCR1_MTYP_Pos // Macronix mode
+ | (MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) << XSPI_DCR1_DEVSIZE_Pos
+ | (MICROPY_HW_XSPI_CS_HIGH_CYCLES - 1) << XSPI_DCR1_CSHT_Pos
+ | 0 << XSPI_DCR1_FRCK_Pos // CLK is not free running
+ | 0 << XSPI_DCR1_CKMODE_Pos // CLK idles at low state
+ ;
+
+ XSPIx->DCR2 =
+ 0 << XSPI_DCR2_WRAPSIZE_Pos // separate wrap reads are not supported by the memory
+ | (MICROPY_HW_XSPI_PRESCALER - 1) << XSPI_DCR2_PRESCALER_Pos
+ ;
+
+ XSPIx->DCR3 =
+ 0
+ // 10 << XSPI_DCR3_CSBOUND_Pos // transaction boundary at 1024
+ ;
+
+ XSPIx->DCR4 =
+ 0 << XSPI_DCR4_REFRESH_Pos // refresh disabled (it's non-volatile memory)
+ ;
+
+ XSPIx->TCR = 0;
+
+ // Enable the XSPI peripheral.
+ XSPIx->CR |= XSPI_CR_EN;
+
+ // XSPIM init
+ XSPI1->CR &= ~(1 << XSPI_CR_EN_Pos);
+ XSPI2->CR &= ~(1 << XSPI_CR_EN_Pos);
+ XSPIM->CR = 0; // can also be (1 << 4) to pass through CS signal
+ XSPIx->CR |= 1 << XSPI_CR_EN_Pos;
+
+ #ifdef pyb_pin_FLASH_RESET
+ // Reset SPI flash to make sure it's in a known state (SPI mode).
+ mp_hal_pin_output(pyb_pin_FLASH_RESET);
+ mp_hal_pin_low(pyb_pin_FLASH_RESET);
+ xspi_delay_us(1000);
+ mp_hal_pin_high(pyb_pin_FLASH_RESET);
+ xspi_delay_us(10000);
+ #endif
+
+ // Enable memory-mapped mode.
+ // Can select either SPI or DTR mode.
+ if (1) {
+ xspi_switch_to_dtr();
+ xspi_memory_map_888();
+ } else {
+ xspi_memory_map_111();
+ }
+}
+
+uint32_t xspi_get_xip_base(const xspi_flash_t *self) {
+ return self->xip_base;
+}
+
+bool xspi_is_valid_addr(const xspi_flash_t *self, uint32_t addr) {
+ return self->xip_base <= addr && addr < self->xip_base + 256 * 1024 * 1024;
+}
+
+static int xspi_read_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, uint8_t *dest) {
+ uint32_t admode = addr_enabled ? 1 : 0;
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 1 << XSPI_CR_FMODE_Pos; // indirect read mode
+ XSPIx->CCR =
+ 1 << XSPI_CCR_DMODE_Pos // data on 1 line
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size
+ | admode << XSPI_CCR_ADMODE_Pos // address on 1 line, or disabled
+ | 1 << XSPI_CCR_IMODE_Pos // instruction on 1 line
+ ;
+ XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // 0 dummy cycles
+ XSPIx->DLR = len - 1; // number of bytes to read
+ XSPIx->IR = cmd; // read opcode (triggers the start of the transaction if address disabled)
+ if (addr_enabled) {
+ XSPIx->AR = addr; // triggers the start of the transaction
+ }
+
+ #if 0 // untested code
+ // Read in the data 4 bytes at a time if dest is aligned.
+ if (((uintptr_t)dest & 3) == 0) {
+ while (len >= 4) {
+ while (!(XSPIx->SR & XSPI_SR_FTF)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+ *(uint32_t *)dest = XSPIx->DR;
+ dest += 4;
+ len -= 4;
+ }
+ }
+ #endif
+
+ // Read in data 1 byte at a time.
+ while (len--) {
+ while (!((XSPIx->SR >> XSPI_SR_FLEVEL_Pos) & 0x3f)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+ *dest++ = *(volatile uint8_t *)&XSPIx->DR;
+ }
+
+ XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag
+
+ return 0;
+}
+
+static int xspi_write_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src) {
+ uint32_t dmode = len == 0 ? 0 : 1;
+ uint32_t admode = addr_enabled ? 1 : 0;
+
+ // Configure and start the transfer.
+ // Transfer starts with IR write if no address or data, with AR write if no data,
+ // otherwise with DR write.
+
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 0 << XSPI_CR_FMODE_Pos; // indirect write mode
+ XSPIx->CCR =
+ dmode << XSPI_CCR_DMODE_Pos // data on 1 line, or disabled
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size
+ | admode << XSPI_CCR_ADMODE_Pos // address on 1 line, or disabled
+ | 1 << XSPI_CCR_IMODE_Pos // instruction on 1 line
+ ;
+ XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // 0 dummy cycles
+ if (len != 0) {
+ XSPIx->DLR = len - 1;
+ }
+ XSPIx->IR = cmd; // write opcode
+ if (addr_enabled) {
+ XSPIx->AR = addr; // address
+ }
+
+ // Write out the data one byte at a time
+ while (len--) {
+ while (!(XSPIx->SR & XSPI_SR_FTF)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+ *(volatile uint8_t *)&XSPIx->DR = *src++;
+ }
+
+ // Wait for write to finish
+ while (!(XSPIx->SR & XSPI_SR_TCF)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+
+ XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag
+
+ // Wait for peripheral to return to idle.
+ while (XSPIx->SR & XSPI_SR_BUSY) {
+ }
+
+ return 0;
+}
+
+static int xspi_read_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, uint32_t num_dummy, size_t len, uint8_t *dest) {
+ uint32_t admode = addr_enabled ? 4 : 0;
+
+ // Configure and start the transfer.
+ // Transfer starts with IR write if no address, otherwise with AR write.
+
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 1 << XSPI_CR_FMODE_Pos; // indirect read mode
+ XSPIx->CCR =
+ 1 << XSPI_CCR_DQSE_Pos // DQS enabled
+ | 1 << XSPI_CCR_DDTR_Pos // data DTR enabled
+ | 4 << XSPI_CCR_DMODE_Pos // data on 8 lines
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size
+ | 1 << XSPI_CCR_ADDTR_Pos // address DTR enabled
+ | admode << XSPI_CCR_ADMODE_Pos // address on 8 lines, or disabled
+ | 1 << XSPI_CCR_ISIZE_Pos // 16-bit instruction
+ | 1 << XSPI_CCR_IDTR_Pos // instruction DTR enabled
+ | 4 << XSPI_CCR_IMODE_Pos // instruction on 8 lines
+ ;
+ XSPIx->TCR = num_dummy << XSPI_TCR_DCYC_Pos; // N dummy cycles
+ XSPIx->DLR = len - 1;
+ XSPIx->IR = cmd; // read opcode
+ if (addr_enabled) {
+ XSPIx->AR = addr; // address
+ }
+
+ // Read in data 1 byte at a time.
+ while (len--) {
+ while (!((XSPIx->SR >> XSPI_SR_FLEVEL_Pos) & 0x3f)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+ *dest++ = *(volatile uint8_t *)&XSPIx->DR;
+ }
+
+ XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag
+
+ return 0;
+}
+
+static int xspi_write_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src) {
+ uint32_t dmode = len == 0 ? 0 : 4;
+ uint32_t admode = addr_enabled ? 4 : 0;
+
+ // Configure and start the transfer.
+ // Transfer starts with IR write if no address or data, with AR write if no data,
+ // otherwise with DR write.
+
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 0 << XSPI_CR_FMODE_Pos; // indirect write mode
+ XSPIx->CCR =
+ 1 << XSPI_CCR_DDTR_Pos // data DTR enabled
+ | dmode << XSPI_CCR_DMODE_Pos // data on 8 lines, or disabled
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size
+ | 1 << XSPI_CCR_ADDTR_Pos // address DTR enabled
+ | admode << XSPI_CCR_ADMODE_Pos // address on 8 lines, or disabled
+ | 1 << XSPI_CCR_ISIZE_Pos // 16-bit instruction
+ | 1 << XSPI_CCR_IDTR_Pos // instruction DTR enabled
+ | 4 << XSPI_CCR_IMODE_Pos // instruction on 8 lines
+ ;
+ XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // 0 dummy cycles
+ if (len != 0) {
+ XSPIx->DLR = len - 1;
+ }
+ XSPIx->IR = cmd; // write opcode
+ if (addr_enabled) {
+ XSPIx->AR = addr; // address
+ }
+
+ // Write out the data one byte at a time
+ while (len--) {
+ while (!(XSPIx->SR & XSPI_SR_FTF)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+ *(volatile uint8_t *)&XSPIx->DR = *src++;
+ }
+
+ // Wait for write to finish
+ while (!(XSPIx->SR & XSPI_SR_TCF)) {
+ if (XSPIx->SR & XSPI_SR_TEF) {
+ return -MP_EIO;
+ }
+ }
+
+ XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag
+
+ // Wait for peripheral to return to idle.
+ while (XSPIx->SR & XSPI_SR_BUSY) {
+ }
+
+ return 0;
+}
+
+static void xspi_memory_map_111(void) {
+ XSPIx->CCR =
+ 1 << XSPI_CCR_DMODE_Pos // data on 1 line
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address
+ | 1 << XSPI_CCR_ADMODE_Pos // address on 1 line
+ | 1 << XSPI_CCR_IMODE_Pos // instruction on 1 line
+ ;
+
+ XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // no dummy cycles
+ XSPIx->IR = 0x13; // READ4B
+ XSPIx->LPTR = 1024; // timeout period in number of CLK cycles
+
+ // Enable the XSPI peripheral in memory-mapped mode.
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 3 << XSPI_CR_FMODE_Pos;
+}
+
+static void xspi_memory_map_888(void) {
+ XSPIx->CCR =
+ 1 << XSPI_CCR_DQSE_Pos // DQS enabled
+ | 1 << XSPI_CCR_DDTR_Pos // data DTR enabled
+ | 4 << XSPI_CCR_DMODE_Pos // data on 8 lines
+ | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address
+ | 1 << XSPI_CCR_ADDTR_Pos // address DTR enabled
+ | 4 << XSPI_CCR_ADMODE_Pos // address on 8 lines
+ | 1 << XSPI_CCR_ISIZE_Pos // 16-bit instruction
+ | 1 << XSPI_CCR_IDTR_Pos // instruction DTR enabled
+ | 4 << XSPI_CCR_IMODE_Pos // instruction on 8 lines
+ ;
+
+ XSPIx->TCR = 20 << XSPI_TCR_DCYC_Pos; // 20 dummy cycles for reading (minimum, flash may insert more by holding DQS low)
+ XSPIx->IR = 0xee11; // octal DTR read mode (8DTRD)
+ XSPIx->LPTR = 1024; // timeout period in number of CLK cycles
+
+ // Enable the XSPI peripheral in memory-mapped mode.
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 3 << XSPI_CR_FMODE_Pos;
+}
+
+static void xspi_memory_map_exit(void) {
+ // Abort any ongoing transfer if peripheral is busy.
+ if (XSPIx->SR & XSPI_SR_BUSY) {
+ XSPIx->CR |= XSPI_CR_ABORT;
+ while (!(XSPIx->SR & XSPI_SR_TCF)) {
+ }
+ XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag
+ while (XSPIx->SR & XSPI_SR_BUSY) {
+ }
+ }
+ XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 0 << XSPI_CR_FMODE_Pos; // indirect write mode
+}
+
+void xspi_switch_to_dtr(void) {
+ uint8_t buf[4];
+
+ // WREN.
+ xspi_write_111_ext(0x06, false, 0, 0, NULL);
+
+ // Wait WEL=1, with small timeout.
+ for (unsigned int i = 0; i < 100; ++i) {
+ xspi_read_111_ext(0x05, false, 0, 1, buf);
+ if (buf[0] & 2) {
+ break;
+ }
+ }
+
+ // Switch to DOPI DTR mode.
+ buf[0] = 2;
+ xspi_write_111_ext(0x72, true, 0x00000000, 1, buf);
+
+ xspi_dtr_enabled = true;
+}
+
+void xspi_switch_to_spi(void) {
+ uint8_t buf[4];
+
+ // WREN.
+ xspi_write_888_dtr_ext(0x06f9, false, 0, 0, NULL);
+
+ // Wait WEL=1, with small timeout.
+ for (unsigned int i = 0; i < 100; ++i) {
+ xspi_read_111_ext(0x05, false, 0, 1, buf);
+ if (buf[0] & 2) {
+ break;
+ }
+ }
+
+ // Switch to SPI mode.
+ buf[0] = 0;
+ buf[1] = 0;
+ xspi_write_888_dtr_ext(0x728d, true, 0x00000000, 2, buf);
+
+ xspi_dtr_enabled = false;
+}
+
+static int xspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) {
+ xspi_flash_t *self = self_in;
+ switch (cmd) {
+ case MP_QSPI_IOCTL_INIT:
+ // XSPI must be manually initialise by calling `xspi_init()` at boot.
+ // Here, just determine if it's in SPI or DTR mode.
+ xspi_dtr_enabled = XSPIx->IR == 0xee11;
+ break;
+ case MP_QSPI_IOCTL_BUS_ACQUIRE:
+ xspi_memory_map_exit();
+ break;
+ case MP_QSPI_IOCTL_BUS_RELEASE:
+ if (xspi_dtr_enabled) {
+ xspi_memory_map_888();
+ } else {
+ xspi_memory_map_111();
+ }
+ break;
+ case MP_QSPI_IOCTL_MEMORY_MODIFIED: {
+ uintptr_t *addr_len = (uintptr_t *)arg;
+ volatile void *addr = (volatile void *)(self->xip_base + addr_len[0]);
+ size_t len = addr_len[1];
+ SCB_InvalidateICache_by_Addr(addr, len);
+ SCB_InvalidateDCache_by_Addr(addr, len);
+ break;
+ }
+ }
+ return 0; // success
+}
+
+// These commands may be passed to this function.
+#define CMD_WREN (0x06)
+#define CMD_RSTEN (0x66)
+#define CMD_RESET (0x99)
+#define CMD_SLEEP (0xb9)
+#define CMD_AWAKE (0xab)
+static int xspi_write_cmd_data(void *self_in, uint8_t cmd, size_t len, uint32_t data) {
+ if (xspi_dtr_enabled) {
+ uint16_t cmd16 = 0;
+ if (cmd == CMD_WREN) {
+ cmd16 = 0x06f9;
+ } else if (cmd == CMD_SLEEP) {
+ cmd16 = 0xb946;
+ } else if (cmd == CMD_AWAKE) {
+ cmd16 = 0xab54;
+ }
+ return xspi_write_888_dtr_ext(cmd16, false, 0, len, (const uint8_t *)&data);
+ }
+ return xspi_write_111_ext(cmd, false, 0, len, (const uint8_t *)&data);
+}
+
+// These commands may be passed to this function.
+#define CMD_WRITE (0x02)
+#define CMD_WRITE_32 (0x12)
+#define CMD_SEC_ERASE (0x20)
+#define CMD_SEC_ERASE_32 (0x21)
+static int xspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
+ // Convert 24-bit address commands to 32-bit address commands.
+ if (cmd == CMD_WRITE) {
+ cmd = CMD_WRITE_32;
+ } else if (cmd == CMD_SEC_ERASE) {
+ cmd = CMD_SEC_ERASE_32;
+ }
+ if (xspi_dtr_enabled) {
+ uint16_t cmd16 = 0;
+ if (cmd == CMD_WRITE_32) {
+ cmd16 = 0x12ed;
+ } else if (cmd == CMD_SEC_ERASE_32) {
+ cmd16 = 0x21de;
+ }
+ return xspi_write_888_dtr_ext(cmd16, true, addr, len, src);
+ }
+ return xspi_write_111_ext(cmd, true, addr, len, src);
+}
+
+// These commands may be passed to this function.
+#define CMD_RDSR (0x05)
+#define CMD_RD_DEVID (0x9f)
+static int xspi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_t *dest) {
+ (void)self_in;
+ if (xspi_dtr_enabled) {
+ uint16_t cmd16 = 0;
+ uint32_t num_dummy = 0;
+ if (cmd == CMD_RDSR) {
+ cmd16 = 0x05fa;
+ num_dummy = 4;
+ len = 2;
+ } else if (cmd == CMD_RD_DEVID) {
+ // TODO this doesn't really work, because result is in STR format.
+ cmd16 = 0x9f60;
+ num_dummy = 4;
+ }
+ return xspi_read_888_dtr_ext(cmd16, true, 0, num_dummy, len, (uint8_t *)dest);
+ }
+ return xspi_read_111_ext(cmd, false, 0, len, (uint8_t *)dest);
+}
+
+static int xspi_direct_read(void *self_in, uint32_t addr, size_t len, uint8_t *dest) {
+ xspi_flash_t *self = self_in;
+ memcpy(dest, (const void *)(self->xip_base + addr), len);
+ return 0;
+}
+
+const mp_qspi_proto_t xspi_proto = {
+ .ioctl = xspi_ioctl,
+ .write_cmd_data = xspi_write_cmd_data,
+ .write_cmd_addr_data = xspi_write_cmd_addr_data,
+ .read_cmd = xspi_read_cmd,
+ .read_cmd_qaddr_qdata = NULL, // unused because .direct_read is set below, and caching is disabled
+ .direct_read = xspi_direct_read,
+};
+
+#endif // defined(MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2)
diff --git a/ports/stm32/xspi.h b/ports/stm32/xspi.h
new file mode 100644
index 0000000000..cd21662966
--- /dev/null
+++ b/ports/stm32/xspi.h
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024-2025 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_STM32_XSPI_H
+#define MICROPY_INCLUDED_STM32_XSPI_H
+
+#include "drivers/bus/qspi.h"
+
+typedef struct _xspi_flash_t xspi_flash_t;
+
+extern const mp_qspi_proto_t xspi_proto;
+extern const xspi_flash_t xspi_flash1;
+extern const xspi_flash_t xspi_flash2;
+
+void xspi_init(void);
+uint32_t xspi_get_xip_base(const xspi_flash_t *self);
+bool xspi_is_valid_addr(const xspi_flash_t *self, uint32_t addr);
+void xspi_switch_to_spi(void);
+void xspi_switch_to_dtr(void);
+
+#endif // MICROPY_INCLUDED_STM32_XSPI_H
diff --git a/ports/unix/Makefile b/ports/unix/Makefile
index 88fa1af045..8bd58a2542 100644
--- a/ports/unix/Makefile
+++ b/ports/unix/Makefile
@@ -52,6 +52,10 @@ CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EX
# 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
@@ -256,21 +260,24 @@ endif
include $(TOP)/py/mkrules.mk
-.PHONY: test test_full
+.PHONY: test test_full_no_native test_full
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_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py
+test_full_no_native: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py -d thread
- cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --emit native
cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) -d basics float micropython
- cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) --emit native -d basics float micropython
cat $(TOP)/tests/basics/0prelim.py | ./$(BUILD)/$(PROG) | grep -q 'abc'
+test_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py test_full_no_native
+ $(eval DIRNAME=ports/$(notdir $(CURDIR)))
+ cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --emit native
+ cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) --emit native -d basics float micropython
+
test_gcov: test_full
gcov -o $(BUILD)/py $(TOP)/py/*.c
gcov -o $(BUILD)/extmod $(TOP)/extmod/*.c
diff --git a/ports/unix/README.md b/ports/unix/README.md
index b7aa6e3fef..656d4303d3 100644
--- a/ports/unix/README.md
+++ b/ports/unix/README.md
@@ -155,3 +155,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 2b65b47fc5..0df6bf279a 100644
--- a/ports/unix/coverage.c
+++ b/ports/unix/coverage.c
@@ -184,6 +184,18 @@ static void pairheap_test(size_t nops, int *ops) {
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)
@@ -208,6 +220,7 @@ static mp_obj_t extra_coverage(void) {
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, "%%\n"); // literal % character
+ mp_printf(&mp_plat_print, ".%-3s.\n", "a"); // left adjust
}
// GC
@@ -463,6 +476,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", 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", 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", 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", 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", 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
@@ -480,7 +525,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", 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)));
@@ -505,6 +550,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));
}
@@ -557,12 +618,24 @@ 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);
}
@@ -621,6 +694,19 @@ 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);
+
+ 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);
+ }
}
// ringbuf
@@ -794,7 +880,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/main.c b/ports/unix/main.c
index 9f51573fbf..530e20a386 100644
--- a/ports/unix/main.c
+++ b/ports/unix/main.c
@@ -775,7 +775,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
diff --git a/ports/unix/modsocket.c b/ports/unix/modsocket.c
index 6d6059ae44..2aaa21183a 100644
--- a/ports/unix/modsocket.c
+++ b/ports/unix/modsocket.c
@@ -701,6 +701,7 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = {
C(MSG_DONTROUTE),
C(MSG_DONTWAIT),
+ C(MSG_PEEK),
C(SOL_SOCKET),
C(SO_BROADCAST),
diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c
index fbd94b5ecd..41b7c89df4 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);
@@ -196,7 +192,7 @@ static mp_obj_t mod_time_mktime(mp_obj_t tuple) {
if (ret == -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 21ce75a351..973b5e74ce 100644
--- a/ports/unix/mpconfigport.h
+++ b/ports/unix/mpconfigport.h
@@ -124,6 +124,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)
diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h
index 04b5b8ae1a..2f5d9683b3 100644
--- a/ports/unix/variants/coverage/mpconfigvariant.h
+++ b/ports/unix/variants/coverage/mpconfigvariant.h
@@ -39,11 +39,13 @@
// Enable additional features.
#define MICROPY_DEBUG_PARSE_RULE_NAME (1)
+#define MICROPY_PY_SYS_SETTRACE (1)
#define MICROPY_TRACKED_ALLOC (1)
#define MICROPY_WARNINGS_CATEGORY (1)
#undef MICROPY_VFS_ROM_IOCTL
#define MICROPY_VFS_ROM_IOCTL (1)
#define MICROPY_PY_CRYPTOLIB_CTR (1)
+#define MICROPY_SCHEDULER_STATIC_NODES (1)
// Enable os.uname for attrtuple coverage test
#define MICROPY_PY_OS_UNAME (1)
diff --git a/ports/webassembly/main.c b/ports/webassembly/main.c
index c542f0cd72..770dfbe0ca 100644
--- a/ports/webassembly/main.c
+++ b/ports/webassembly/main.c
@@ -233,7 +233,7 @@ void nlr_jump_fail(void *val) {
}
}
-void NORETURN __fatal_error(const char *msg) {
+void MP_NORETURN __fatal_error(const char *msg) {
while (1) {
;
}
diff --git a/ports/webassembly/objpyproxy.js b/ports/webassembly/objpyproxy.js
index 3b94f8aadc..0eafd0dec5 100644
--- a/ports/webassembly/objpyproxy.js
+++ b/ports/webassembly/objpyproxy.js
@@ -148,6 +148,12 @@ const py_proxy_handler = {
};
},
has(target, prop) {
+ // avoid throwing on `Symbol() in proxy` checks
+ if (typeof prop !== "string") {
+ // returns true only on iterator because other
+ // symbols are not considered in the `get` trap
+ return prop === Symbol.iterator;
+ }
return Module.ccall(
"proxy_c_to_js_has_attr",
"number",
diff --git a/ports/windows/Makefile b/ports/windows/Makefile
index 115d1a61ef..9eee98cdd4 100644
--- a/ports/windows/Makefile
+++ b/ports/windows/Makefile
@@ -46,6 +46,10 @@ LDFLAGS += -lm -lbcrypt $(LDFLAGS_EXTRA)
# 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
CFLAGS += -g
diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h
index fabc9072d6..4e140d5edb 100644
--- a/ports/windows/mpconfigport.h
+++ b/ports/windows/mpconfigport.h
@@ -242,7 +242,7 @@ typedef long mp_off_t;
// CL specific overrides from mpconfig
-#define NORETURN __declspec(noreturn)
+#define MP_NORETURN __declspec(noreturn)
#define MP_WEAK
#define MP_NOINLINE __declspec(noinline)
#define MP_ALWAYSINLINE __forceinline
@@ -266,6 +266,11 @@ typedef long mp_off_t;
#endif
#endif
+// VC++ 2017 fixes
+#if (_MSC_VER < 1920)
+#define MICROPY_PY_MATH_COPYSIGN_FIX_NAN (1)
+#endif
+
// CL specific definitions
#ifndef __cplusplus
diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt
index debf2bd2c1..a5cc477204 100644
--- a/ports/zephyr/CMakeLists.txt
+++ b/ports/zephyr/CMakeLists.txt
@@ -66,10 +66,20 @@ set(MICROPY_SOURCE_SHARED
runtime/mpirq.c
runtime/pyexec.c
runtime/stdout_helpers.c
+ runtime/sys_stdio_mphal.c
timeutils/timeutils.c
)
list(TRANSFORM MICROPY_SOURCE_SHARED PREPEND ${MICROPY_DIR}/shared/)
+set(MICROPY_SOURCE_DRIVERS
+ bus/softspi.c
+)
+list(TRANSFORM MICROPY_SOURCE_DRIVERS PREPEND ${MICROPY_DIR}/drivers/)
+
+set(MICROPY_QSTRDEFS_PORT
+ ${MICROPY_PORT_DIR}/qstrdefsport.h
+)
+
set(MICROPY_SOURCE_LIB
oofatfs/ff.c
oofatfs/ffunicode.c
@@ -84,6 +94,7 @@ set(MICROPY_SOURCE_QSTR
${MICROPY_SOURCE_PY}
${MICROPY_SOURCE_EXTMOD}
${MICROPY_SOURCE_SHARED}
+ ${MICROPY_SOURCE_DRIVERS}
${MICROPY_SOURCE_LIB}
${MICROPY_SOURCE_PORT}
)
diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md
index fc18d25c0a..17c1f613de 100644
--- a/ports/zephyr/README.md
+++ b/ports/zephyr/README.md
@@ -95,7 +95,7 @@ qemu_cortex_m3):
Networking is enabled with the default configuration, so you need to follow
instructions in
-https://docs.zephyrproject.org/latest/guides/networking/qemu_setup.html#networking-with-qemu
+https://docs.zephyrproject.org/latest/connectivity/networking/qemu_setup.html#networking-with-qemu
to setup the host side of TAP/SLIP networking. If you get an error like:
could not connect serial device to character backend 'unix:/tmp/slip.sock'
diff --git a/ports/zephyr/boards/bbc_microbit_v2.conf b/ports/zephyr/boards/bbc_microbit_v2.conf
index 31872244ca..43742078f5 100644
--- a/ports/zephyr/boards/bbc_microbit_v2.conf
+++ b/ports/zephyr/boards/bbc_microbit_v2.conf
@@ -1,4 +1,3 @@
-CONFIG_CONSOLE_SUBSYS=n
CONFIG_NETWORKING=n
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
diff --git a/ports/zephyr/boards/beagleconnect_freedom.conf b/ports/zephyr/boards/beagleconnect_freedom.conf
index 8fe2583205..e31e09444f 100644
--- a/ports/zephyr/boards/beagleconnect_freedom.conf
+++ b/ports/zephyr/boards/beagleconnect_freedom.conf
@@ -7,3 +7,13 @@ CONFIG_SPI=y
# Flash drivers
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
+
+# Networking
+CONFIG_BT=n
+CONFIG_NET_IPV4=n
+CONFIG_NET_CONFIG_NEED_IPV6=y
+CONFIG_NET_CONFIG_NEED_IPV4=n
+CONFIG_NET_CONFIG_MY_IPV4_ADDR=""
+CONFIG_NET_L2_IEEE802154=y
+CONFIG_NET_DHCPV4=n
+CONFIG_NET_CONFIG_IEEE802154_CHANNEL=1
diff --git a/ports/zephyr/boards/beagleconnect_freedom.overlay b/ports/zephyr/boards/beagleconnect_freedom.overlay
deleted file mode 100644
index 7dd4469c99..0000000000
--- a/ports/zephyr/boards/beagleconnect_freedom.overlay
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2024 Ayush Singh <ayush@beagleboard.org>
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-
-&pinctrl {
- /* MB1 PWM */
- pwm0_default: pwm0_default {
- pinmux = <17 IOC_PORT_MCU_PORT_EVENT1>;
- bias-disable;
- drive-strength = <2>;
- };
-
- /* MB2 PWM */
- pwm1_default: pwm1_default {
- pinmux = <19 IOC_PORT_MCU_PORT_EVENT3>;
- bias-disable;
- drive-strength = <2>;
- };
-};
-
-&gpt0 {
- status = "okay";
-};
-
-&gpt1 {
- status = "okay";
-};
-
-&pwm0 {
- status = "okay";
- pinctrl-0 = <&pwm0_default>;
- pinctrl-names = "default";
-};
-
-&pwm1 {
- status = "okay";
- pinctrl-0 = <&pwm1_default>;
- pinctrl-names = "default";
-};
diff --git a/ports/zephyr/boards/beagleplay_cc1352p7.conf b/ports/zephyr/boards/beagleplay_cc1352p7.conf
new file mode 100644
index 0000000000..f63d184e38
--- /dev/null
+++ b/ports/zephyr/boards/beagleplay_cc1352p7.conf
@@ -0,0 +1,13 @@
+# Flash drivers
+CONFIG_FLASH=y
+CONFIG_FLASH_MAP=y
+
+# Networking
+CONFIG_BT=n
+CONFIG_NET_IPV4=n
+CONFIG_NET_CONFIG_NEED_IPV6=y
+CONFIG_NET_CONFIG_NEED_IPV4=n
+CONFIG_NET_CONFIG_MY_IPV4_ADDR=""
+CONFIG_NET_L2_IEEE802154=y
+CONFIG_NET_DHCPV4=n
+CONFIG_NET_CONFIG_IEEE802154_CHANNEL=1
diff --git a/ports/zephyr/boards/frdm_k64f.conf b/ports/zephyr/boards/frdm_k64f.conf
index c164c0c32c..71f8cc178b 100644
--- a/ports/zephyr/boards/frdm_k64f.conf
+++ b/ports/zephyr/boards/frdm_k64f.conf
@@ -3,6 +3,7 @@ CONFIG_NET_L2_ETHERNET=y
# Hardware features
CONFIG_I2C=y
+CONFIG_PWM=y
CONFIG_SPI=y
# Sensor drivers
@@ -14,3 +15,6 @@ CONFIG_FXOS8700_TEMP=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_MPU_ALLOW_FLASH_WRITE=y
+
+# MicroPython config
+CONFIG_MICROPY_HEAP_SIZE=114688
diff --git a/ports/zephyr/boards/nrf52840dk_nrf52840.conf b/ports/zephyr/boards/nrf52840dk_nrf52840.conf
index e9ec78b64f..dc2ed1c4b4 100644
--- a/ports/zephyr/boards/nrf52840dk_nrf52840.conf
+++ b/ports/zephyr/boards/nrf52840dk_nrf52840.conf
@@ -1,8 +1,13 @@
CONFIG_NETWORKING=n
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
+CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
+CONFIG_BT_GATT_CLIENT=y
+CONFIG_BT_L2CAP_TX_MTU=252
+CONFIG_BT_BUF_ACL_RX_SIZE=256
+CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n
CONFIG_MICROPY_HEAP_SIZE=98304
CONFIG_MAIN_STACK_SIZE=8192
diff --git a/ports/zephyr/boards/nucleo_h743zi.conf b/ports/zephyr/boards/nucleo_h743zi.conf
index 942415b796..ea6a60b352 100644
--- a/ports/zephyr/boards/nucleo_h743zi.conf
+++ b/ports/zephyr/boards/nucleo_h743zi.conf
@@ -1,6 +1,3 @@
-# disable console subsys to get REPL working
-CONFIG_CONSOLE_SUBSYS=n
-
# flash config
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
diff --git a/ports/zephyr/boards/nucleo_wb55rg.conf b/ports/zephyr/boards/nucleo_wb55rg.conf
index baf0f28075..adfab367c8 100644
--- a/ports/zephyr/boards/nucleo_wb55rg.conf
+++ b/ports/zephyr/boards/nucleo_wb55rg.conf
@@ -1,6 +1,21 @@
-CONFIG_CONSOLE_SUBSYS=n
CONFIG_NETWORKING=n
+
+# Hardware features
+CONFIG_FLASH=y
+CONFIG_FLASH_MAP=y
+CONFIG_I2C=y
+CONFIG_SPI=y
+
+# Bluetooth
CONFIG_BT=y
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
+CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y
+CONFIG_BT_GATT_CLIENT=y
+CONFIG_BT_L2CAP_TX_MTU=252
+CONFIG_BT_BUF_ACL_RX_SIZE=256
+CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n
+
+# MicroPython config
+CONFIG_MICROPY_HEAP_SIZE=131072
diff --git a/ports/zephyr/boards/nucleo_wb55rg.overlay b/ports/zephyr/boards/nucleo_wb55rg.overlay
new file mode 100644
index 0000000000..d9a4d3f24c
--- /dev/null
+++ b/ports/zephyr/boards/nucleo_wb55rg.overlay
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2025 Damien P. George
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* Delete the defined partitions and create bigger one for storage. */
+/delete-node/ &slot1_partition;
+/delete-node/ &scratch_partition;
+/delete-node/ &storage_partition;
+&flash0 {
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ /* Storage slot: 424 KiB placed after slot0_partition. */
+ storage_partition: partition@70000 {
+ label = "storage";
+ reg = <0x00070000 DT_SIZE_K(424)>;
+ };
+ };
+};
diff --git a/ports/zephyr/boards/qemu_cortex_m3.conf b/ports/zephyr/boards/qemu_cortex_m3.conf
index dac0c358dc..11eebd9606 100644
--- a/ports/zephyr/boards/qemu_cortex_m3.conf
+++ b/ports/zephyr/boards/qemu_cortex_m3.conf
@@ -1,7 +1,6 @@
-# Interrupt-driven UART console has emulation artifacts under QEMU,
-# disable it
-CONFIG_CONSOLE_SUBSYS=n
+# This QEMU target only has 256k ROM and 64k RAM, so disable networking
+CONFIG_NETWORKING=n
-# Networking drivers
-# SLIP driver for QEMU
-CONFIG_NET_SLIP_TAP=y
+# QEMU doesn't have a watchdog, so disable it
+CONFIG_WATCHDOG=n
+CONFIG_WDT_DISABLE_AT_BOOT=n
diff --git a/ports/zephyr/boards/qemu_x86.conf b/ports/zephyr/boards/qemu_x86.conf
index dac0c358dc..1d04675df4 100644
--- a/ports/zephyr/boards/qemu_x86.conf
+++ b/ports/zephyr/boards/qemu_x86.conf
@@ -1,7 +1,7 @@
-# Interrupt-driven UART console has emulation artifacts under QEMU,
-# disable it
-CONFIG_CONSOLE_SUBSYS=n
-
# Networking drivers
# SLIP driver for QEMU
CONFIG_NET_SLIP_TAP=y
+
+# QEMU doesn't have a watchdog, so disable it
+CONFIG_WATCHDOG=n
+CONFIG_WDT_DISABLE_AT_BOOT=n
diff --git a/ports/zephyr/boards/rpi_pico.conf b/ports/zephyr/boards/rpi_pico.conf
new file mode 100644
index 0000000000..6b31bc9f98
--- /dev/null
+++ b/ports/zephyr/boards/rpi_pico.conf
@@ -0,0 +1,19 @@
+# Disable floating point hardware.
+CONFIG_FPU=n
+
+# Configure serial console over USB CDC ACM.
+CONFIG_USB_DEVICE_STACK_NEXT=y
+CONFIG_USBD_CDC_ACM_CLASS=y
+CONFIG_UART_LINE_CTRL=y
+
+# Disable networking.
+CONFIG_NETWORKING=n
+
+# Hardware features.
+CONFIG_FLASH=y
+CONFIG_FLASH_MAP=y
+CONFIG_I2C=y
+CONFIG_SPI=y
+
+# MicroPython config.
+CONFIG_MICROPY_HEAP_SIZE=196608
diff --git a/ports/zephyr/boards/rpi_pico.overlay b/ports/zephyr/boards/rpi_pico.overlay
new file mode 100644
index 0000000000..d63ed73bdf
--- /dev/null
+++ b/ports/zephyr/boards/rpi_pico.overlay
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2025 Damien P. George
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+ chosen {
+ /* Use USB CDC ACM as the console. */
+ zephyr,console = &cdc_acm_uart0;
+ };
+};
+
+/* Delete defined partitions and make a layout matching the rp2 port RPI_PICO configuration. */
+/delete-node/ &code_partition;
+&flash0 {
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ /* Code slot: 640 KiB - 0x100 placed after second-stage bootloader. */
+ code_partition: partition@100 {
+ label = "code-partition";
+ reg = <0x00000100 (DT_SIZE_K(640) - 0x100)>;
+ read-only;
+ };
+
+ /* Storage slot: 1408 KiB placed after code_partition. */
+ storage_partition: partition@a0000 {
+ label = "storage";
+ reg = <0x000a0000 DT_SIZE_K(1408)>;
+ };
+ };
+};
+
+&zephyr_udc0 {
+ cdc_acm_uart0: cdc_acm_uart0 {
+ compatible = "zephyr,cdc-acm-uart";
+ };
+};
+
+&i2c1 {
+ clock-frequency = <I2C_BITRATE_STANDARD>;
+ status = "okay";
+ pinctrl-0 = <&i2c1_default>;
+ pinctrl-names = "default";
+};
diff --git a/ports/zephyr/machine_pin.c b/ports/zephyr/machine_pin.c
index 2240196690..e0718588d6 100644
--- a/ports/zephyr/machine_pin.c
+++ b/ports/zephyr/machine_pin.c
@@ -36,6 +36,7 @@
#include "py/gc.h"
#include "py/mphal.h"
#include "extmod/modmachine.h"
+#include "extmod/virtpin.h"
#include "shared/runtime/mpirq.h"
#include "modmachine.h"
#include "zephyr_device.h"
@@ -115,6 +116,10 @@ static mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_ar
}
int ret = gpio_pin_configure(self->port, self->pin, mode | pull | init);
+ if (ret == -ENOTSUP && mode == (GPIO_OUTPUT | GPIO_INPUT)) {
+ // Some targets (eg frdm_k64f) don't support GPIO_OUTPUT|GPIO_INPUT, so try again with just GPIO_OUTPUT.
+ ret = gpio_pin_configure(self->port, self->pin, GPIO_OUTPUT | pull | init);
+ }
if (ret) {
mp_raise_ValueError(MP_ERROR_TEXT("invalid pin"));
}
@@ -126,19 +131,26 @@ static mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_ar
mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
- // get the wanted port
- if (!mp_obj_is_type(args[0], &mp_type_tuple)) {
+ machine_pin_obj_t *pin;
+ if (mp_obj_is_type(args[0], &machine_pin_type)) {
+ // Already a Pin object, reuse it.
+ pin = MP_OBJ_TO_PTR(args[0]);
+ } else if (mp_obj_is_type(args[0], &mp_type_tuple)) {
+ // Get the wanted (port, pin) values.
+ mp_obj_t *items;
+ mp_obj_get_array_fixed_n(args[0], 2, &items);
+ const struct device *wanted_port = zephyr_device_find(items[0]);
+ int wanted_pin = mp_obj_get_int(items[1]);
+
+ pin = m_new_obj(machine_pin_obj_t);
+ pin->base = machine_pin_obj_template;
+ pin->port = wanted_port;
+ pin->pin = wanted_pin;
+ pin->irq = NULL;
+ } else {
+ // Unknown Pin.
mp_raise_ValueError(MP_ERROR_TEXT("Pin id must be tuple of (\"GPIO_x\", pin#)"));
}
- mp_obj_t *items;
- mp_obj_get_array_fixed_n(args[0], 2, &items);
- const struct device *wanted_port = zephyr_device_find(items[0]);
- int wanted_pin = mp_obj_get_int(items[1]);
-
- machine_pin_obj_t *pin = m_new_obj(machine_pin_obj_t);
- pin->base = machine_pin_obj_template;
- pin->port = wanted_port;
- pin->pin = wanted_pin;
if (n_args > 1 || n_kw > 0) {
// pin mode given, so configure this GPIO
@@ -270,7 +282,8 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = {
// class constants
{ MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_INPUT) },
- { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_OUTPUT) },
+ { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_OUTPUT | GPIO_INPUT) },
+ { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(GPIO_OUTPUT | GPIO_INPUT | GPIO_OPEN_DRAIN) },
{ MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULL_UP) },
{ MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULL_DOWN) },
{ MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_INT_EDGE_RISING) },
diff --git a/ports/zephyr/machine_timer.c b/ports/zephyr/machine_timer.c
index 8ab2f62913..1bb743c2eb 100644
--- a/ports/zephyr/machine_timer.c
+++ b/ports/zephyr/machine_timer.c
@@ -77,9 +77,14 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr
}
static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
- mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
-
- if (mp_obj_get_int(args[0]) != -1) {
+ // Get timer id (only soft timer (-1) supported at the moment)
+ mp_int_t id = -1;
+ if (n_args > 0) {
+ id = mp_obj_get_int(args[0]);
+ --n_args;
+ ++args;
+ }
+ if (id != -1) {
mp_raise_ValueError(MP_ERROR_TEXT("only virtual timers are supported"));
}
@@ -90,10 +95,10 @@ static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args,
self->next = MP_STATE_PORT(machine_timer_obj_head);
MP_STATE_PORT(machine_timer_obj_head) = self;
- if (n_args > 1 || n_kw > 0) {
+ if (n_args > 0 || n_kw > 0) {
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
- machine_timer_init_helper(self, n_args - 1, args + 1, &kw_args);
+ machine_timer_init_helper(self, n_args, args, &kw_args);
}
return self;
}
diff --git a/ports/zephyr/machine_uart.c b/ports/zephyr/machine_uart.c
index 1927335ddf..60613befb1 100644
--- a/ports/zephyr/machine_uart.c
+++ b/ports/zephyr/machine_uart.c
@@ -5,6 +5,7 @@
*
* Copyright (c) 2016 Damien P. George
* Copyright (c) 2020 Yonatan Schachter
+ * Copyright (c) 2025 Daniel Campora on behalf of REMOTE TECH LTD
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,16 +33,32 @@
#include <zephyr/drivers/uart.h>
#include "py/mperrno.h"
+#include "py/ringbuf.h"
#include "zephyr_device.h"
-// The UART class doesn't have any constants for this port.
-#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS
+
+#define MACHINE_UART_RTS 1
+#define MACHINE_UART_CTS 2
+
+// This class needs a finalizer, so we add it here
+#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \
+ { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(MACHINE_UART_RTS) }, \
+ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(MACHINE_UART_CTS) }, \
+ { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_uart_deinit_obj) },
+
+#define UART_RX_RING_BUF_DEF_SIZE 128
+#define UART_TX_RING_BUF_DEF_SIZE 128
+
+static void uart_interrupt_handler(const struct device *dev, void *user_data);
typedef struct _machine_uart_obj_t {
mp_obj_base_t base;
const struct device *dev;
uint16_t timeout; // timeout waiting for first char (in ms)
uint16_t timeout_char; // timeout waiting between chars (in ms)
+ ringbuf_t rx_ringbuffer;
+ ringbuf_t tx_ringbuffer;
+ bool tx_complete;
} machine_uart_obj_t;
static const char *_parity_name[] = {"None", "Odd", "Even", "Mark", "Space"};
@@ -60,23 +77,96 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_
}
static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- enum { ARG_timeout, ARG_timeout_char };
+ enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_txbuf, ARG_rxbuf, ARG_timeout, ARG_timeout_char, ARG_flow };
static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 115200} },
+ { MP_QSTR_bits, MP_ARG_INT, {.u_int = 8} },
+ { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_stop, MP_ARG_INT, {.u_int = 1} },
+ { MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_RX_RING_BUF_DEF_SIZE} },
+ { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_TX_RING_BUF_DEF_SIZE} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_flow, 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, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
self->timeout = args[ARG_timeout].u_int;
self->timeout_char = args[ARG_timeout_char].u_int;
+
+ uint8_t data_bits;
+ if (args[ARG_bits].u_int == 5) {
+ data_bits = UART_CFG_DATA_BITS_5;
+ } else if (args[ARG_bits].u_int == 6) {
+ data_bits = UART_CFG_DATA_BITS_6;
+ } else if (args[ARG_bits].u_int == 7) {
+ data_bits = UART_CFG_DATA_BITS_7;
+ } else if (args[ARG_bits].u_int == 8) {
+ data_bits = UART_CFG_DATA_BITS_8;
+ } else if (args[ARG_bits].u_int == 9) {
+ data_bits = UART_CFG_DATA_BITS_9;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid data bits"));
+ }
+
+ uint8_t parity;
+ if (args[ARG_parity].u_obj == mp_const_none) {
+ parity = UART_CFG_PARITY_NONE;
+ } else if (mp_obj_get_int(args[ARG_parity].u_obj) == 0) {
+ parity = UART_CFG_PARITY_EVEN;
+ } else if (mp_obj_get_int(args[ARG_parity].u_obj) == 1) {
+ parity = UART_CFG_PARITY_ODD;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid parity"));
+ }
+
+ uint8_t stop_bits;
+ if (args[ARG_stop].u_int == 1) {
+ stop_bits = UART_CFG_STOP_BITS_1;
+ } else if (args[ARG_stop].u_int == 2) {
+ data_bits = UART_CFG_STOP_BITS_2;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid stop bits"));
+ }
+
+ uint8_t flow_ctrl;
+ if (args[ARG_flow].u_int == 0) {
+ flow_ctrl = UART_CFG_FLOW_CTRL_NONE;
+ } else if (args[ARG_flow].u_int == (MACHINE_UART_RTS | MACHINE_UART_CTS)) {
+ flow_ctrl = UART_CFG_FLOW_CTRL_RTS_CTS;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid flow control"));
+ }
+
+ const struct uart_config cfg = {
+ .baudrate = args[ARG_baudrate].u_int,
+ .parity = parity,
+ .stop_bits = args[ARG_stop].u_int,
+ .data_bits = data_bits,
+ .flow_ctrl = flow_ctrl
+ };
+
+ int ret = uart_configure(self->dev, &cfg);
+ if (ret < 0) {
+ mp_raise_OSError(-ret);
+ }
+
+ ringbuf_alloc(&self->tx_ringbuffer, args[ARG_txbuf].u_int);
+ ringbuf_alloc(&self->rx_ringbuffer, args[ARG_rxbuf].u_int);
+
+ uart_irq_callback_user_data_set(self->dev, uart_interrupt_handler, (void *)self);
+ uart_irq_rx_enable(self->dev);
}
static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
- machine_uart_obj_t *self = mp_obj_malloc(machine_uart_obj_t, &machine_uart_type);
- self->dev = zephyr_device_find(args[0]);
+ const struct device *dev = zephyr_device_find(args[0]);
+ machine_uart_obj_t *self = mp_obj_malloc_with_finaliser(machine_uart_obj_t, &machine_uart_type);
+ self->dev = dev;
+ self->tx_complete = true;
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
@@ -86,37 +176,38 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
}
static void mp_machine_uart_deinit(machine_uart_obj_t *self) {
- (void)self;
+ uart_irq_rx_disable(self->dev);
+ uart_irq_tx_disable(self->dev);
}
static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) {
- (void)self;
- mp_raise_NotImplementedError(NULL); // TODO
+ return ringbuf_avail(&self->rx_ringbuffer);
}
static bool mp_machine_uart_txdone(machine_uart_obj_t *self) {
- (void)self;
- mp_raise_NotImplementedError(NULL); // TODO
+ return self->tx_complete && !ringbuf_avail(&self->tx_ringbuffer) ? true : false;
}
static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint8_t *buffer = (uint8_t *)buf_in;
- uint8_t data;
mp_uint_t bytes_read = 0;
size_t elapsed_ms = 0;
size_t time_to_wait = self->timeout;
- while ((elapsed_ms < time_to_wait) && (bytes_read < size)) {
- if (!uart_poll_in(self->dev, &data)) {
- buffer[bytes_read++] = data;
+ do {
+ int _rx_len = MIN(ringbuf_avail(&self->rx_ringbuffer), size - bytes_read);
+ if (_rx_len > 0) {
+ ringbuf_get_bytes(&self->rx_ringbuffer, &buffer[bytes_read], _rx_len);
+ bytes_read += _rx_len;
elapsed_ms = 0;
time_to_wait = self->timeout_char;
} else {
k_msleep(1);
elapsed_ms++;
}
- }
+ } while ((elapsed_ms < time_to_wait) && (bytes_read < size));
+
return bytes_read;
}
@@ -124,27 +215,86 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint8_t *buffer = (uint8_t *)buf_in;
- for (mp_uint_t i = 0; i < size; i++) {
+ // wait for any pending transmission to complete
+ while (!mp_machine_uart_txdone(self)) {
+ MICROPY_EVENT_POLL_HOOK;
+ }
+
+ int _ex_size = 0;
+ int _free_space = ringbuf_free(&self->tx_ringbuffer);
+ if (size > _free_space) {
+ _ex_size = size - _free_space;
+ }
+
+ // do a blocking tx of what doesn't fit into the outgoing ring buffer
+ for (mp_uint_t i = 0; i < _ex_size; i++) {
uart_poll_out(self->dev, buffer[i]);
}
+ ringbuf_put_bytes(&self->tx_ringbuffer, &buffer[_ex_size], size - _ex_size);
+ self->tx_complete = false;
+ uart_irq_tx_enable(self->dev);
+
return size;
}
static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
- mp_uint_t ret;
+ machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_uint_t ret = 0;
if (request == MP_STREAM_POLL) {
- ret = 0;
- // read is always blocking
-
- if (arg & MP_STREAM_POLL_WR) {
+ uintptr_t flags = arg;
+ if ((flags & MP_STREAM_POLL_RD) && (mp_machine_uart_any(self) > 0)) {
+ ret |= MP_STREAM_POLL_RD;
+ }
+ if ((flags & MP_STREAM_POLL_WR) && mp_machine_uart_txdone(self)) {
ret |= MP_STREAM_POLL_WR;
}
- return ret;
+ } else if (request == MP_STREAM_FLUSH) {
+ while (!mp_machine_uart_txdone(self)) {
+ MICROPY_EVENT_POLL_HOOK;
+ }
} else {
*errcode = MP_EINVAL;
ret = MP_STREAM_ERROR;
}
+
return ret;
}
+
+static void uart_interrupt_handler(const struct device *dev, void *user_data) {
+ machine_uart_obj_t *self = (machine_uart_obj_t *)user_data;
+
+ while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
+ if (uart_irq_rx_ready(dev)) {
+ uint8_t _rx_buffer[32];
+ size_t _free_space = MIN(ringbuf_free(&self->rx_ringbuffer), sizeof(_rx_buffer));
+
+ // empty the uart fifo even if we can't store bytes anymore
+ // otherwise we will never exit this interrupt handler
+ int rcv_len = uart_fifo_read(dev, _rx_buffer, (_free_space > 0) ? _free_space : 1);
+ if ((rcv_len <= 0) || (_free_space == 0)) {
+ continue;
+ }
+
+ ringbuf_put_bytes(&self->rx_ringbuffer, _rx_buffer, rcv_len);
+ }
+
+ int _max_uart_tx_len = uart_irq_tx_ready(dev);
+ if (_max_uart_tx_len > 0) {
+ uint8_t _tx_buffer[32];
+ size_t _buffer_tx_len;
+
+ _max_uart_tx_len = MIN(_max_uart_tx_len, sizeof(_tx_buffer));
+ _buffer_tx_len = ringbuf_avail(&self->tx_ringbuffer);
+ if (_buffer_tx_len > 0) {
+ _buffer_tx_len = MIN(_max_uart_tx_len, _buffer_tx_len);
+ ringbuf_get_bytes(&self->tx_ringbuffer, _tx_buffer, _buffer_tx_len);
+ uart_fifo_fill(dev, _tx_buffer, _buffer_tx_len);
+ } else if (uart_irq_tx_complete(dev)) {
+ uart_irq_tx_disable(dev);
+ self->tx_complete = true;
+ }
+ }
+ }
+}
diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c
index d4498c1079..eaef34a786 100644
--- a/ports/zephyr/main.c
+++ b/ports/zephyr/main.c
@@ -99,6 +99,7 @@ static void vfs_init(void) {
mp_obj_t bdev = NULL;
mp_obj_t mount_point;
const char *mount_point_str = NULL;
+ qstr path_lib_qstr = MP_QSTRnull;
int ret = 0;
#ifdef CONFIG_DISK_DRIVER_SDMMC
@@ -109,15 +110,18 @@ static void vfs_init(void) {
#endif
bdev = MP_OBJ_TYPE_GET_SLOT(&zephyr_disk_access_type, make_new)(&zephyr_disk_access_type, ARRAY_SIZE(args), 0, args);
mount_point_str = "/sd";
+ path_lib_qstr = MP_QSTR__slash_sd_slash_lib;
#elif defined(CONFIG_FLASH_MAP) && FIXED_PARTITION_EXISTS(storage_partition)
mp_obj_t args[] = { MP_OBJ_NEW_SMALL_INT(FIXED_PARTITION_ID(storage_partition)), MP_OBJ_NEW_SMALL_INT(4096) };
bdev = MP_OBJ_TYPE_GET_SLOT(&zephyr_flash_area_type, make_new)(&zephyr_flash_area_type, ARRAY_SIZE(args), 0, args);
mount_point_str = "/flash";
+ path_lib_qstr = MP_QSTR__slash_flash_slash_lib;
#endif
if ((bdev != NULL)) {
mount_point = mp_obj_new_str_from_cstr(mount_point_str);
ret = mp_vfs_mount_and_chdir_protected(bdev, mount_point);
+ mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(path_lib_qstr));
// TODO: if this failed, make a new file system and try to mount again
}
}
@@ -156,7 +160,17 @@ soft_reset:
#endif
#if MICROPY_MODULE_FROZEN || MICROPY_VFS
- pyexec_file_if_exists("main.py");
+ // Execute user scripts.
+ int ret = pyexec_file_if_exists("boot.py");
+ if (ret & PYEXEC_FORCED_EXIT) {
+ goto soft_reset_exit;
+ }
+ if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) {
+ ret = pyexec_file_if_exists("main.py");
+ if (ret & PYEXEC_FORCED_EXIT) {
+ goto soft_reset_exit;
+ }
+ }
#endif
for (;;) {
@@ -171,7 +185,11 @@ soft_reset:
}
}
- printf("soft reboot\n");
+ #if MICROPY_MODULE_FROZEN || MICROPY_VFS
+soft_reset_exit:
+ #endif
+
+ mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n");
#if MICROPY_PY_BLUETOOTH
mp_bluetooth_deinit();
@@ -219,7 +237,7 @@ mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs)
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);
#endif
-NORETURN void nlr_jump_fail(void *val) {
+MP_NORETURN void nlr_jump_fail(void *val) {
while (1) {
;
}
diff --git a/ports/zephyr/modbluetooth_zephyr.c b/ports/zephyr/modbluetooth_zephyr.c
index 8947bdf535..c1b8ddfd8b 100644
--- a/ports/zephyr/modbluetooth_zephyr.c
+++ b/ports/zephyr/modbluetooth_zephyr.c
@@ -31,8 +31,12 @@
#if MICROPY_PY_BLUETOOTH
+#include <zephyr/types.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
+#include <zephyr/bluetooth/conn.h>
+#include <zephyr/bluetooth/uuid.h>
+#include <zephyr/bluetooth/gatt.h>
#include "extmod/modbluetooth.h"
#define DEBUG_printf(...) // printk("BLE: " __VA_ARGS__)
@@ -44,6 +48,23 @@
#define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV
+#define MP_BLUETOOTH_ZEPHYR_MAX_SERVICES (8)
+
+/* This masks Permission bits from GATT API */
+#define GATT_PERM_MASK (BT_GATT_PERM_READ | \
+ BT_GATT_PERM_READ_AUTHEN | \
+ BT_GATT_PERM_READ_ENCRYPT | \
+ BT_GATT_PERM_WRITE | \
+ BT_GATT_PERM_WRITE_AUTHEN | \
+ BT_GATT_PERM_WRITE_ENCRYPT | \
+ BT_GATT_PERM_PREPARE_WRITE)
+
+#define GATT_PERM_ENC_READ_MASK (BT_GATT_PERM_READ_ENCRYPT | \
+ BT_GATT_PERM_READ_AUTHEN)
+
+#define GATT_PERM_ENC_WRITE_MASK (BT_GATT_PERM_WRITE_ENCRYPT | \
+ BT_GATT_PERM_WRITE_AUTHEN)
+
enum {
MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF,
MP_BLUETOOTH_ZEPHYR_BLE_STATE_ACTIVE,
@@ -56,9 +77,42 @@ enum {
MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_ACTIVE,
};
+union uuid_u {
+ struct bt_uuid uuid;
+ struct bt_uuid_16 u16;
+ struct bt_uuid_32 u32;
+ struct bt_uuid_128 u128;
+};
+
+struct add_characteristic {
+ uint8_t properties;
+ uint8_t permissions;
+ const struct bt_uuid *uuid;
+};
+
+struct add_descriptor {
+ uint8_t permissions;
+ const struct bt_uuid *uuid;
+};
+
+typedef struct _mp_bt_zephyr_conn_t {
+ struct bt_conn *conn;
+ struct _mp_bt_zephyr_conn_t *next;
+} mp_bt_zephyr_conn_t;
+
typedef struct _mp_bluetooth_zephyr_root_pointers_t {
+ // list of objects to be tracked by the gc
+ mp_obj_t objs_list;
+
// Characteristic (and descriptor) value storage.
mp_gatts_db_t gatts_db;
+
+ // Service definitions.
+ size_t n_services;
+ struct bt_gatt_service *services[MP_BLUETOOTH_ZEPHYR_MAX_SERVICES];
+
+ // active connections
+ mp_bt_zephyr_conn_t *connections;
} mp_bluetooth_zephyr_root_pointers_t;
static int mp_bluetooth_zephyr_ble_state;
@@ -69,6 +123,98 @@ static struct k_timer mp_bluetooth_zephyr_gap_scan_timer;
static struct bt_le_scan_cb mp_bluetooth_zephyr_gap_scan_cb_struct;
#endif
+static struct bt_data bt_ad_data[8];
+static size_t bt_ad_len = 0;
+static struct bt_data bt_sd_data[8];
+static size_t bt_sd_len = 0;
+
+static mp_bt_zephyr_conn_t *mp_bt_zephyr_next_conn;
+
+static mp_bt_zephyr_conn_t *mp_bt_zephyr_find_connection(uint8_t conn_handle);
+static void mp_bt_zephyr_insert_connection(mp_bt_zephyr_conn_t *connection);
+static void mp_bt_zephyr_remove_connection(uint8_t conn_handle);
+static void mp_bt_zephyr_connected(struct bt_conn *connected, uint8_t err);
+static void mp_bt_zephyr_disconnected(struct bt_conn *disconn, uint8_t reason);
+static struct bt_uuid *create_zephyr_uuid(const mp_obj_bluetooth_uuid_t *uuid);
+static void gatt_db_add(const struct bt_gatt_attr *pattern, struct bt_gatt_attr *attr, size_t user_data_len);
+static void add_service(const struct bt_uuid *u, struct bt_gatt_attr *attr);
+static void add_characteristic(struct add_characteristic *ch, struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_value);
+static void add_ccc(struct bt_gatt_attr *attr, struct bt_gatt_attr *attr_desc);
+static void add_cep(const struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_desc);
+static void add_descriptor(struct bt_gatt_attr *chrc, struct add_descriptor *d, struct bt_gatt_attr *attr_desc);
+static void mp_bt_zephyr_gatt_indicate_done(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err);
+static struct bt_gatt_attr *mp_bt_zephyr_find_attr_by_handle(uint16_t value_handle);
+
+static struct bt_conn_cb mp_bt_zephyr_conn_callbacks = {
+ .connected = mp_bt_zephyr_connected,
+ .disconnected = mp_bt_zephyr_disconnected,
+};
+
+static mp_bt_zephyr_conn_t *mp_bt_zephyr_find_connection(uint8_t conn_handle) {
+ struct bt_conn_info info;
+ for (mp_bt_zephyr_conn_t *connection = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; connection != NULL; connection = connection->next) {
+ if (connection->conn) {
+ bt_conn_get_info(connection->conn, &info);
+ if (info.id == conn_handle) {
+ return connection;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void mp_bt_zephyr_insert_connection(mp_bt_zephyr_conn_t *connection) {
+ connection->next = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections;
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = connection;
+}
+
+static void mp_bt_zephyr_remove_connection(uint8_t conn_handle) {
+ struct bt_conn_info info;
+ mp_bt_zephyr_conn_t *prev = NULL;
+ for (mp_bt_zephyr_conn_t *connection = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; connection != NULL; connection = connection->next) {
+ if (connection->conn) {
+ bt_conn_get_info(connection->conn, &info);
+ if (info.id == conn_handle) {
+ // unlink this item and the gc will eventually collect it
+ if (prev != NULL) {
+ prev->next = connection->next;
+ } else {
+ // move the start pointer
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = connection->next;
+ }
+ break;
+ } else {
+ prev = connection;
+ }
+ }
+ }
+}
+
+static void mp_bt_zephyr_connected(struct bt_conn *conn, uint8_t err) {
+ struct bt_conn_info info;
+ bt_conn_get_info(conn, &info);
+
+ if (err) {
+ uint8_t addr[6] = {0};
+ DEBUG_printf("Connection from central failed (err %u)\n", err);
+ mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, info.id, 0xff, addr);
+ } else {
+ DEBUG_printf("Central connected with id %d\n", info.id);
+ mp_bt_zephyr_next_conn->conn = bt_conn_ref(conn);
+ mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_CONNECT, info.id, info.le.dst->type, info.le.dst->a.val);
+ mp_bt_zephyr_insert_connection(mp_bt_zephyr_next_conn);
+ }
+}
+
+static void mp_bt_zephyr_disconnected(struct bt_conn *conn, uint8_t reason) {
+ struct bt_conn_info info;
+ bt_conn_get_info(conn, &info);
+ DEBUG_printf("Central disconnected (id %d reason %u)\n", info.id, reason);
+ bt_conn_unref(conn);
+ mp_bt_zephyr_remove_connection(info.id);
+ mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, info.id, info.le.dst->type, info.le.dst->a.val);
+}
+
static int bt_err_to_errno(int err) {
// Zephyr uses errno codes directly, but they are negative.
return -err;
@@ -128,6 +274,11 @@ int mp_bluetooth_init(void) {
MP_STATE_PORT(bluetooth_zephyr_root_pointers) = m_new0(mp_bluetooth_zephyr_root_pointers_t, 1);
mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db);
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = NULL;
+ mp_bt_zephyr_next_conn = NULL;
+
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list = mp_obj_new_list(0, NULL);
+
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
mp_bluetooth_zephyr_gap_scan_state = MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_INACTIVE;
k_timer_init(&mp_bluetooth_zephyr_gap_scan_timer, gap_scan_cb_timeout, NULL);
@@ -137,6 +288,9 @@ int mp_bluetooth_init(void) {
#endif
if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF) {
+
+ bt_conn_cb_register(&mp_bt_zephyr_conn_callbacks);
+
// bt_enable can only be called once.
int ret = bt_enable(NULL);
if (ret) {
@@ -151,15 +305,22 @@ int mp_bluetooth_init(void) {
return 0;
}
-void mp_bluetooth_deinit(void) {
+int mp_bluetooth_deinit(void) {
DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_zephyr_ble_state);
if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF
|| mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED) {
- return;
+ return 0;
}
mp_bluetooth_gap_advertise_stop();
+ #if CONFIG_BT_GATT_DYNAMIC_DB
+ for (size_t i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; ++i) {
+ bt_gatt_service_unregister(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]);
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i] = NULL;
+ }
+ #endif
+
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
mp_bluetooth_gap_scan_stop();
bt_le_scan_cb_unregister(&mp_bluetooth_zephyr_gap_scan_cb_struct);
@@ -170,6 +331,8 @@ void mp_bluetooth_deinit(void) {
mp_bluetooth_zephyr_ble_state = MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED;
MP_STATE_PORT(bluetooth_zephyr_root_pointers) = NULL;
+ mp_bt_zephyr_next_conn = NULL;
+ return 0;
}
bool mp_bluetooth_is_active(void) {
@@ -191,7 +354,7 @@ void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) {
}
void mp_bluetooth_set_address_mode(uint8_t addr_mode) {
- // TODO: implement
+ mp_raise_OSError(MP_EOPNOTSUPP);
}
size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) {
@@ -232,15 +395,11 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons
mp_bluetooth_gap_advertise_stop();
- struct bt_data bt_ad_data[8];
- size_t bt_ad_len = 0;
if (adv_data) {
bt_ad_len = MP_ARRAY_SIZE(bt_ad_data);
mp_bluetooth_prepare_bt_data(adv_data, adv_data_len, bt_ad_data, &bt_ad_len);
}
- struct bt_data bt_sd_data[8];
- size_t bt_sd_len = 0;
if (sr_data) {
bt_sd_len = MP_ARRAY_SIZE(bt_sd_data);
mp_bluetooth_prepare_bt_data(sr_data, sr_data_len, bt_sd_data, &bt_sd_len);
@@ -259,6 +418,10 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons
.peer = NULL,
};
+ // pre-allocate a new connection structure as we cannot allocate this inside the connection callback
+ mp_bt_zephyr_next_conn = m_new0(mp_bt_zephyr_conn_t, 1);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(mp_bt_zephyr_next_conn));
+
return bt_err_to_errno(bt_le_adv_start(&param, bt_ad_data, bt_ad_len, bt_sd_data, bt_sd_len));
}
@@ -271,6 +434,8 @@ void mp_bluetooth_gap_advertise_stop(void) {
}
int mp_bluetooth_gatts_register_service_begin(bool append) {
+ #if CONFIG_BT_GATT_DYNAMIC_DB
+
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
@@ -280,25 +445,190 @@ int mp_bluetooth_gatts_register_service_begin(bool append) {
return MP_EOPNOTSUPP;
}
+ // Unregister and unref any previous service definitions.
+ for (size_t i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; ++i) {
+ bt_gatt_service_unregister(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]);
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i] = NULL;
+ }
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services = 0;
+
// Reset the gatt characteristic value db.
mp_bluetooth_gatts_db_reset(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db);
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = NULL;
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list = mp_obj_new_list(0, NULL);
+ mp_bt_zephyr_next_conn = NULL;
+ return 0;
+
+ #else
return MP_EOPNOTSUPP;
+ #endif
}
int mp_bluetooth_gatts_register_service_end(void) {
- return MP_EOPNOTSUPP;
+ return 0;
}
int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, mp_obj_bluetooth_uuid_t **characteristic_uuids, uint16_t *characteristic_flags, mp_obj_bluetooth_uuid_t **descriptor_uuids, uint16_t *descriptor_flags, uint8_t *num_descriptors, uint16_t *handles, size_t num_characteristics) {
+ #if CONFIG_BT_GATT_DYNAMIC_DB
+ if (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services >= MP_BLUETOOTH_ZEPHYR_MAX_SERVICES) {
+ return MP_E2BIG;
+ }
+
+ // first of all allocate the entire memory for all the attributes that this service is composed of
+ // 1 for the service itself, 2 for each characteristic (the declaration and the value), and one for each descriptor
+ size_t total_descriptors = 0;
+ for (size_t i = 0; i < num_characteristics; ++i) {
+ total_descriptors += num_descriptors[i];
+ // we have to add the CCC manually
+ if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) {
+ total_descriptors += 1;
+ }
+ }
+ size_t total_attributes = 1 + (num_characteristics * 2) + total_descriptors;
+
+ // allocate one extra so that we can know later where the final attribute is
+ struct bt_gatt_attr *svc_attributes = m_new(struct bt_gatt_attr, total_attributes + 1);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(svc_attributes));
+
+ size_t handle_index = 0;
+ size_t descriptor_index = 0;
+ size_t attr_index = 0;
+ // bitfield of the handles we should ignore, should be more than enough for most applications
+ uint64_t attrs_to_ignore = 0;
+ uint64_t attrs_are_chrs = 0;
+ uint64_t chr_has_ccc = 0;
+
+ add_service(create_zephyr_uuid(service_uuid), &svc_attributes[attr_index]);
+ attr_index += 1;
+
+ for (size_t i = 0; i < num_characteristics; ++i) {
+
+ struct add_characteristic add_char;
+ add_char.uuid = create_zephyr_uuid(characteristic_uuids[i]);
+ add_char.permissions = 0;
+ add_char.properties = 0;
+ if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) {
+ add_char.permissions |= BT_GATT_PERM_READ;
+ add_char.properties |= BT_GATT_CHRC_READ;
+ }
+ if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) {
+ add_char.properties |= BT_GATT_CHRC_NOTIFY;
+ }
+ if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE) {
+ add_char.properties |= BT_GATT_CHRC_INDICATE;
+ }
+ if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE | MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE)) {
+ add_char.permissions |= BT_GATT_PERM_WRITE;
+ add_char.properties |= (BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP);
+ }
+
+ add_characteristic(&add_char, &svc_attributes[attr_index], &svc_attributes[attr_index + 1]);
+
+ struct bt_gatt_attr *curr_char = &svc_attributes[attr_index];
+ attrs_are_chrs |= (1 << attr_index);
+ if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) {
+ chr_has_ccc |= (1 << attr_index);
+ }
+ attr_index += 1;
+ attrs_to_ignore |= (1 << attr_index); // ignore the value handle
+ attr_index += 1;
+
+ if (num_descriptors[i] > 0) {
+ for (size_t j = 0; j < num_descriptors[i]; ++j) {
+
+ struct add_descriptor add_desc;
+ add_desc.uuid = create_zephyr_uuid(descriptor_uuids[descriptor_index]);
+ add_desc.permissions = 0;
+ if (descriptor_flags[descriptor_index] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) {
+ add_desc.permissions |= BT_GATT_PERM_READ;
+ }
+ if (descriptor_flags[descriptor_index] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE | MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE)) {
+ add_desc.permissions |= BT_GATT_PERM_WRITE;
+ }
+
+ add_descriptor(curr_char, &add_desc, &svc_attributes[attr_index]);
+ attr_index += 1;
+
+ descriptor_index++;
+ }
+ }
+
+ // to support indications and notifications we must add the CCC descriptor manually
+ if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) {
+ struct add_descriptor add_desc;
+ mp_obj_bluetooth_uuid_t ccc_uuid;
+ ccc_uuid.base.type = &mp_type_bluetooth_uuid;
+ ccc_uuid.data[0] = BT_UUID_GATT_CCC_VAL & 0xff;
+ ccc_uuid.data[1] = (BT_UUID_GATT_CCC_VAL >> 8) & 0xff;
+ ccc_uuid.type = MP_BLUETOOTH_UUID_TYPE_16;
+ add_desc.uuid = create_zephyr_uuid(&ccc_uuid);
+ add_desc.permissions = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE;
+
+ attrs_to_ignore |= (1 << attr_index);
+
+ add_descriptor(curr_char, &add_desc, &svc_attributes[attr_index]);
+ attr_index += 1;
+ }
+ }
+
+ struct bt_gatt_service *service = m_new(struct bt_gatt_service, 1);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(service));
+ service->attrs = svc_attributes;
+ service->attr_count = attr_index;
+ // invalidate the last attribute uuid pointer so that we new this is the end of attributes for this service
+ svc_attributes[attr_index].uuid = NULL;
+
+ // Note: advertising must be stopped for gatts registration to work
+
+ int err = bt_gatt_service_register(service);
+ if (err) {
+ return bt_err_to_errno(err);
+ }
+
+ // now that the service has been registered, we can assign the handles for the characteristics and the descriptors
+ // we are not interested in the handle of the service itself, so we start the loop from index 1
+ for (int i = 1; i < total_attributes; i++) {
+ // store all the relevant handles (characteristics and descriptors defined in Python)
+ if (!((uint64_t)(attrs_to_ignore >> i) & (uint64_t)0x01)) {
+ if (svc_attributes[i].user_data == NULL) {
+ mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN);
+ mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle);
+ svc_attributes[i].user_data = entry->data;
+ } else if (((uint64_t)(attrs_are_chrs >> i) & (uint64_t)0x01)) {
+ if (svc_attributes[i + 1].user_data == NULL) {
+ mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN);
+ mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle);
+ svc_attributes[i + 1].user_data = entry->data;
+
+ if (((uint64_t)(chr_has_ccc >> i) & (uint64_t)0x01)) {
+ // create another database entry for the ccc of this characteristic
+ mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle + 2, 1);
+ }
+ }
+ }
+ handles[handle_index++] = svc_attributes[i].handle;
+ }
+ }
+
+ MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services++] = service;
+
+ return 0;
+
+ #else
return MP_EOPNOTSUPP;
+ #endif
}
int mp_bluetooth_gap_disconnect(uint16_t conn_handle) {
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
- return MP_EOPNOTSUPP;
+ mp_bt_zephyr_conn_t *connection = mp_bt_zephyr_find_connection(conn_handle);
+ if (connection) {
+ return bt_conn_disconnect(connection->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
+ }
+ return MP_ENOENT;
}
int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) {
@@ -312,24 +642,155 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
- if (send_update) {
- return MP_EOPNOTSUPP;
+
+ int err = mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len);
+
+ if ((err == 0) && send_update) {
+ struct bt_gatt_attr *attr_val = mp_bt_zephyr_find_attr_by_handle(value_handle + 1);
+ mp_bluetooth_gatts_db_entry_t *ccc_entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle + 2);
+
+ if (ccc_entry && (ccc_entry->data[0] == BT_GATT_CCC_NOTIFY)) {
+ err = bt_gatt_notify(NULL, attr_val, value, value_len);
+ } else if (ccc_entry && (ccc_entry->data[0] == BT_GATT_CCC_INDICATE)) {
+ struct bt_gatt_indicate_params params = {
+ .uuid = NULL,
+ .attr = attr_val,
+ .func = mp_bt_zephyr_gatt_indicate_done,
+ .destroy = NULL,
+ .data = value,
+ .len = value_len
+ };
+ err = bt_gatt_indicate(NULL, &params);
+ }
+ }
+ return err;
+}
+
+static void mp_bt_zephyr_gatt_indicate_done(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err) {
+ struct bt_conn_info info;
+ bt_conn_get_info(conn, &info);
+ uint16_t chr_handle = params->attr->handle - 1;
+ mp_bluetooth_gatts_on_indicate_complete(info.id, chr_handle, err);
+}
+
+static ssize_t mp_bt_zephyr_gatts_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) {
+ // we receive the value handle, but to look up in the gatts db we need the characteristic handle, and that is is the value handle minus 1
+ uint16_t _handle = attr->handle - 1;
+
+ DEBUG_printf("BLE attr read for handle %d\n", attr->handle);
+
+ mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle);
+ if (!entry) {
+ // it could be a descriptor instead
+ _handle = attr->handle;
+ entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle);
+ if (!entry) {
+ return BT_GATT_ERR(BT_ATT_ERR_INVALID_HANDLE);
+ }
+ }
+
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, entry->data, entry->data_len);
+}
+
+static ssize_t mp_bt_zephyr_gatts_attr_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) {
+ struct bt_conn_info info;
+ bt_conn_get_info(conn, &info);
+
+ DEBUG_printf("BLE attr write for handle %d\n", attr->handle);
+
+ // the characteristic handle is the value handle minus 1
+ uint16_t _handle = attr->handle - 1;
+
+ mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle);
+ if (!entry) {
+ // it could be a descriptor instead
+ _handle = attr->handle;
+ entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle);
+ if (!entry) {
+ return BT_GATT_ERR(BT_ATT_ERR_INVALID_HANDLE);
+ }
+ }
+
+ // Don't write anything if prepare flag is set
+ if (flags & BT_GATT_WRITE_FLAG_PREPARE) {
+ return 0;
+ }
+
+ if (offset > entry->data_alloc) {
+ return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
+ }
+
+ if ((offset + len) > entry->data_alloc) {
+ return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
+ }
+
+ if (entry->append) {
+ offset = entry->data_len;
+ }
+
+ // copy the data into the buffer in the gatts database
+ memcpy(&entry->data[offset], buf, len);
+ entry->data_len = offset + len;
+
+ mp_bluetooth_gatts_on_write(info.id, _handle);
+
+ return len;
+}
+
+static struct bt_gatt_attr *mp_bt_zephyr_find_attr_by_handle(uint16_t value_handle) {
+ for (int i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; i++) {
+ int j = 0;
+ while (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j].uuid != NULL) {
+ if (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j].handle == value_handle) {
+ return &MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j];
+ }
+ j++;
+ }
}
- return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len);
+ return NULL;
}
int mp_bluetooth_gatts_notify_indicate(uint16_t conn_handle, uint16_t value_handle, int gatts_op, const uint8_t *value, size_t value_len) {
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
- return MP_EOPNOTSUPP;
+
+ int err = MP_ENOENT;
+ mp_bt_zephyr_conn_t *connection = mp_bt_zephyr_find_connection(conn_handle);
+
+ if (connection) {
+ struct bt_gatt_attr *attr_val = mp_bt_zephyr_find_attr_by_handle(value_handle + 1);
+
+ if (attr_val) {
+ switch (gatts_op) {
+ case MP_BLUETOOTH_GATTS_OP_NOTIFY: {
+ err = bt_gatt_notify(connection->conn, attr_val, value, value_len);
+ break;
+ }
+ case MP_BLUETOOTH_GATTS_OP_INDICATE: {
+ struct bt_gatt_indicate_params params = {
+ .uuid = NULL,
+ .attr = attr_val,
+ .func = mp_bt_zephyr_gatt_indicate_done,
+ .destroy = NULL,
+ .data = value,
+ .len = value_len
+ };
+ err = bt_gatt_indicate(connection->conn, &params);
+ break;
+ }
+ }
+ }
+ }
+
+ return err;
}
int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) {
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
- return MP_EOPNOTSUPP;
+ return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, len, append);
}
int mp_bluetooth_get_preferred_mtu(void) {
@@ -404,6 +865,133 @@ int mp_bluetooth_gap_peripheral_connect_cancel(void) {
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
+// Note: modbluetooth UUIDs store their data in LE.
+static struct bt_uuid *create_zephyr_uuid(const mp_obj_bluetooth_uuid_t *uuid) {
+ struct bt_uuid *result = (struct bt_uuid *)m_new(union uuid_u, 1);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(result));
+ if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) {
+ bt_uuid_create(result, uuid->data, 2);
+ } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_32) {
+ bt_uuid_create(result, uuid->data, 4);
+ } else { // MP_BLUETOOTH_UUID_TYPE_128
+ bt_uuid_create(result, uuid->data, 16);
+ }
+ return result;
+}
+
+static void gatt_db_add(const struct bt_gatt_attr *pattern, struct bt_gatt_attr *attr, size_t user_data_len) {
+ const union uuid_u *u = CONTAINER_OF(pattern->uuid, union uuid_u, uuid);
+ size_t uuid_size = sizeof(u->u16);
+
+ if (u->uuid.type == BT_UUID_TYPE_32) {
+ uuid_size = sizeof(u->u32);
+ } else if (u->uuid.type == BT_UUID_TYPE_128) {
+ uuid_size = sizeof(u->u128);
+ }
+
+ memcpy(attr, pattern, sizeof(*attr));
+
+ // Store the UUID.
+ attr->uuid = (const struct bt_uuid *)m_new(union uuid_u, 1);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(attr->uuid));
+ memcpy((void *)attr->uuid, &u->uuid, uuid_size);
+
+ // Copy user_data to the buffer.
+ if (user_data_len) {
+ attr->user_data = m_new(uint8_t, user_data_len);
+ mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(attr->user_data));
+ memcpy(attr->user_data, pattern->user_data, user_data_len);
+ }
+}
+
+static void add_service(const struct bt_uuid *u, struct bt_gatt_attr *attr) {
+ union uuid_u *uuid = (union uuid_u *)u;
+
+ size_t uuid_size = sizeof(uuid->u16);
+
+ if (uuid->uuid.type == BT_UUID_TYPE_32) {
+ uuid_size = sizeof(uuid->u32);
+ } else if (uuid->uuid.type == BT_UUID_TYPE_128) {
+ uuid_size = sizeof(uuid->u128);
+ }
+
+ gatt_db_add(&(struct bt_gatt_attr)BT_GATT_PRIMARY_SERVICE(&uuid->uuid), attr, uuid_size);
+}
+
+static void add_characteristic(struct add_characteristic *ch, struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_value) {
+ struct bt_gatt_chrc *chrc_data;
+
+ // Add Characteristic Declaration
+ gatt_db_add(&(struct bt_gatt_attr)
+ BT_GATT_ATTRIBUTE(BT_UUID_GATT_CHRC,
+ BT_GATT_PERM_READ,
+ bt_gatt_attr_read_chrc, NULL,
+ (&(struct bt_gatt_chrc) {})), attr_chrc, sizeof(*chrc_data));
+
+ // Allow prepare writes
+ ch->permissions |= BT_GATT_PERM_PREPARE_WRITE;
+
+ // Add Characteristic Value
+ gatt_db_add(&(struct bt_gatt_attr)
+ BT_GATT_ATTRIBUTE(ch->uuid,
+ ch->permissions & GATT_PERM_MASK,
+ mp_bt_zephyr_gatts_attr_read, mp_bt_zephyr_gatts_attr_write, NULL), attr_value, 0);
+
+ chrc_data = attr_chrc->user_data;
+ chrc_data->properties = ch->properties;
+ chrc_data->uuid = attr_value->uuid;
+}
+
+static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) {
+ mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, attr->handle);
+ entry->data[0] = value;
+}
+
+static struct bt_gatt_attr ccc_definition = BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE);
+
+static void add_ccc(struct bt_gatt_attr *attr, struct bt_gatt_attr *attr_desc) {
+ struct bt_gatt_chrc *chrc = attr->user_data;
+
+ // Check characteristic properties
+ if (!(chrc->properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE))) {
+ mp_raise_OSError(MP_EINVAL);
+ }
+
+ // Add CCC descriptor to GATT database
+ gatt_db_add(&ccc_definition, attr_desc, 0);
+}
+
+static void add_cep(const struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_desc) {
+ struct bt_gatt_chrc *chrc = attr_chrc->user_data;
+ struct bt_gatt_cep cep_value;
+
+ // Extended Properties bit shall be set
+ if (!(chrc->properties & BT_GATT_CHRC_EXT_PROP)) {
+ mp_raise_OSError(MP_EINVAL);
+ }
+
+ cep_value.properties = 0x0000;
+
+ // Add CEP descriptor to GATT database
+ gatt_db_add(&(struct bt_gatt_attr)BT_GATT_CEP(&cep_value), attr_desc, sizeof(cep_value));
+}
+
+static void add_descriptor(struct bt_gatt_attr *chrc, struct add_descriptor *d, struct bt_gatt_attr *attr_desc) {
+ if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CEP)) {
+ add_cep(chrc, attr_desc);
+ } else if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CCC)) {
+ add_ccc(chrc, attr_desc);
+ } else {
+ // Allow prepare writes
+ d->permissions |= BT_GATT_PERM_PREPARE_WRITE;
+
+ gatt_db_add(&(struct bt_gatt_attr)
+ BT_GATT_DESCRIPTOR(d->uuid,
+ d->permissions & GATT_PERM_MASK,
+ mp_bt_zephyr_gatts_attr_read, mp_bt_zephyr_gatts_attr_write, NULL), attr_desc, 0);
+ }
+}
+
MP_REGISTER_ROOT_POINTER(struct _mp_bluetooth_zephyr_root_pointers_t *bluetooth_zephyr_root_pointers);
#endif // MICROPY_PY_BLUETOOTH
diff --git a/ports/zephyr/modsocket.c b/ports/zephyr/modsocket.c
index 1ca84edcac..d8955bffbe 100644
--- a/ports/zephyr/modsocket.c
+++ b/ports/zephyr/modsocket.c
@@ -32,8 +32,6 @@
#include <stdio.h>
#include <zephyr/kernel.h>
-// Zephyr's generated version header
-#include <version.h>
#include <zephyr/net/net_context.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/dns_resolve.h>
diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h
index e015776a4e..62226a2ded 100644
--- a/ports/zephyr/mpconfigport.h
+++ b/ports/zephyr/mpconfigport.h
@@ -26,7 +26,7 @@
#include <alloca.h>
// Include Zephyr's autoconf.h, which should be made first by Zephyr makefiles
-#include "autoconf.h"
+#include <zephyr/autoconf.h>
// Included here to get basic Zephyr environment (macros, etc.)
#include <zephyr/kernel.h>
#include <zephyr/drivers/spi.h>
@@ -36,17 +36,21 @@
#define MICROPY_HEAP_SIZE (16 * 1024)
#endif
+// We can't guarantee object layout of nlr code so use long jump by default.
+#define MICROPY_NLR_THUMB_USE_LONG_JUMP (1)
+
+#define MICROPY_PERSISTENT_CODE_LOAD (1)
#define MICROPY_ENABLE_SOURCE_LINE (1)
#define MICROPY_STACK_CHECK (1)
#define MICROPY_ENABLE_GC (1)
#define MICROPY_ENABLE_FINALISER (MICROPY_VFS)
#define MICROPY_HELPER_REPL (1)
#define MICROPY_REPL_AUTO_INDENT (1)
+#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
#define MICROPY_KBD_EXCEPTION (1)
#define MICROPY_PY_ASYNC_AWAIT (0)
#define MICROPY_PY_BUILTINS_BYTES_HEX (1)
#define MICROPY_PY_BUILTINS_FILTER (0)
-#define MICROPY_PY_BUILTINS_MIN_MAX (0)
#define MICROPY_PY_BUILTINS_PROPERTY (0)
#define MICROPY_PY_BUILTINS_RANGE_ATTRS (0)
#define MICROPY_PY_BUILTINS_REVERSED (0)
@@ -55,16 +59,18 @@
#define MICROPY_PY_BUILTINS_HELP (1)
#define MICROPY_PY_BUILTINS_HELP_TEXT zephyr_help_text
#define MICROPY_PY_ARRAY (0)
+#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1)
#define MICROPY_PY_COLLECTIONS (0)
#define MICROPY_PY_CMATH (0)
-#define MICROPY_PY_IO (0)
#define MICROPY_PY_MICROPYTHON_MEM_INFO (1)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/zephyr/modmachine.c"
#define MICROPY_PY_MACHINE_I2C (1)
+#define MICROPY_PY_MACHINE_SOFTI2C (1)
#define MICROPY_PY_MACHINE_SPI (1)
#define MICROPY_PY_MACHINE_SPI_MSB (SPI_TRANSFER_MSB)
#define MICROPY_PY_MACHINE_SPI_LSB (SPI_TRANSFER_LSB)
+#define MICROPY_PY_MACHINE_SOFTSPI (1)
#define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new
#define MICROPY_PY_MACHINE_UART (1)
#define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/zephyr/machine_uart.c"
@@ -95,7 +101,8 @@
#define MICROPY_PY_TIME_INCLUDEFILE "ports/zephyr/modtime.c"
#define MICROPY_PY_ZEPHYR (1)
#define MICROPY_PY_ZSENSOR (1)
-#define MICROPY_PY_SYS_MODULES (0)
+#define MICROPY_PY_SYS_MAXSIZE (1)
+#define MICROPY_PY_SYS_STDFILES (1)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_PY_BUILTINS_COMPLEX (0)
@@ -139,12 +146,14 @@ void mp_hal_signal_event(void);
#define MICROPY_HW_MCU_NAME "unknown-cpu"
#endif
-typedef int mp_int_t; // must be pointer size
-typedef unsigned mp_uint_t; // must be pointer size
+typedef intptr_t mp_int_t; // must be pointer size
+typedef uintptr_t mp_uint_t; // must be pointer size
typedef long mp_off_t;
#define MP_STATE_PORT MP_STATE_VM
+#define MP_SSIZE_MAX (0x7fffffff)
+
// extra built in names to add to the global namespace
#define MICROPY_PORT_BUILTINS \
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
diff --git a/ports/zephyr/mpconfigport_minimal.h b/ports/zephyr/mpconfigport_minimal.h
index 9aae20c72c..a0a7f97394 100644
--- a/ports/zephyr/mpconfigport_minimal.h
+++ b/ports/zephyr/mpconfigport_minimal.h
@@ -26,7 +26,7 @@
#include <alloca.h>
// Include Zephyr's autoconf.h, which should be made first by Zephyr makefiles
-#include "autoconf.h"
+#include <zephyr/autoconf.h>
// Included here to get basic Zephyr environment (macros, etc.)
#include <zephyr/kernel.h>
diff --git a/ports/zephyr/mphalport.c b/ports/zephyr/mphalport.c
index 2c95032843..db536ec085 100644
--- a/ports/zephyr/mphalport.c
+++ b/ports/zephyr/mphalport.c
@@ -26,6 +26,7 @@
#include "py/runtime.h"
#include "py/mphal.h"
+#include "extmod/modmachine.h"
static struct k_poll_signal wait_signal;
static struct k_poll_event wait_events[2] = {
@@ -75,3 +76,10 @@ void mp_hal_wait_sem(struct k_sem *sem, uint32_t timeout_ms) {
}
}
}
+
+mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in) {
+ if (mp_obj_is_type(pin_in, &machine_pin_type)) {
+ return MP_OBJ_TO_PTR(pin_in);
+ }
+ mp_raise_ValueError(MP_ERROR_TEXT("invalid pin"));
+}
diff --git a/ports/zephyr/mphalport.h b/ports/zephyr/mphalport.h
index e5414c2705..7410204621 100644
--- a/ports/zephyr/mphalport.h
+++ b/ports/zephyr/mphalport.h
@@ -1,4 +1,5 @@
#include <zephyr/kernel.h>
+#include <zephyr/drivers/gpio.h>
#include "shared/runtime/interrupt_char.h"
#define MICROPY_BEGIN_ATOMIC_SECTION irq_lock
@@ -35,3 +36,48 @@ static inline uint64_t mp_hal_time_ns(void) {
}
#define mp_hal_delay_us_fast(us) (mp_hal_delay_us(us))
+
+// C-level pin HAL
+
+#include "modmachine.h"
+
+#define MP_HAL_PIN_FMT "%u"
+#define mp_hal_pin_obj_t const machine_pin_obj_t *
+
+mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in);
+
+static inline unsigned int mp_hal_pin_name(mp_hal_pin_obj_t pin) {
+ // TODO make it include the port
+ return pin->pin;
+}
+
+static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) {
+ (void)gpio_pin_configure(pin->port, pin->pin, GPIO_INPUT);
+}
+
+static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) {
+ if (gpio_pin_configure(pin->port, pin->pin, GPIO_OUTPUT | GPIO_INPUT) == -ENOTSUP) {
+ // If GPIO_OUTPUT|GPIO_INPUT is not supported (eg frdm_k64f) then try just GPIO_OUTPUT.
+ (void)gpio_pin_configure(pin->port, pin->pin, GPIO_OUTPUT);
+ }
+}
+
+static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) {
+ (void)gpio_pin_configure(pin->port, pin->pin, GPIO_OUTPUT | GPIO_INPUT | GPIO_OPEN_DRAIN);
+}
+
+static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) {
+ return gpio_pin_get_raw(pin->port, pin->pin);
+}
+
+static inline void mp_hal_pin_write(mp_hal_pin_obj_t pin, int v) {
+ (void)gpio_pin_set_raw(pin->port, pin->pin, v);
+}
+
+static inline void mp_hal_pin_od_low(mp_hal_pin_obj_t pin) {
+ (void)gpio_pin_set_raw(pin->port, pin->pin, 0);
+}
+
+static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) {
+ (void)gpio_pin_set_raw(pin->port, pin->pin, 1);
+}
diff --git a/ports/zephyr/prj.conf b/ports/zephyr/prj.conf
index 0325cddd20..0939e226cf 100644
--- a/ports/zephyr/prj.conf
+++ b/ports/zephyr/prj.conf
@@ -6,11 +6,6 @@ CONFIG_STDOUT_CONSOLE=y
CONFIG_CONSOLE_HANDLER=y
CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS=y
-CONFIG_CONSOLE_SUBSYS=y
-CONFIG_CONSOLE_GETCHAR=y
-CONFIG_CONSOLE_GETCHAR_BUFSIZE=258
-CONFIG_CONSOLE_PUTCHAR_BUFSIZE=128
-
CONFIG_NEWLIB_LIBC=y
CONFIG_FPU=y
CONFIG_MAIN_STACK_SIZE=4736
@@ -81,3 +76,10 @@ CONFIG_MICROPY_VFS_LFS2=y
CONFIG_WATCHDOG=y
CONFIG_WDT_DISABLE_AT_BOOT=y
+
+CONFIG_SERIAL=y
+CONFIG_UART_INTERRUPT_DRIVEN=y
+CONFIG_UART_LINE_CTRL=y
+CONFIG_UART_USE_RUNTIME_CONFIGURE=y
+
+CONFIG_LEGACY_GENERATED_INCLUDE_PATH=n
diff --git a/ports/zephyr/prj_minimal.conf b/ports/zephyr/prj_minimal.conf
index 6c850751d7..f58c932cea 100644
--- a/ports/zephyr/prj_minimal.conf
+++ b/ports/zephyr/prj_minimal.conf
@@ -8,8 +8,7 @@ CONFIG_CONSOLE_SUBSYS=y
CONFIG_CONSOLE_GETCHAR=y
CONFIG_CONSOLE_GETCHAR_BUFSIZE=258
CONFIG_CONSOLE_PUTCHAR_BUFSIZE=32
-# TODO: Disable once https://github.com/zephyrproject-rtos/zephyr/pull/13731 is merged
-#CONFIG_CONSOLE=n
-#CONFIG_UART_CONSOLE=n
CONFIG_MICROPY_HEAP_SIZE=16384
+
+CONFIG_LEGACY_GENERATED_INCLUDE_PATH=n
diff --git a/ports/zephyr/qstrdefsport.h b/ports/zephyr/qstrdefsport.h
new file mode 100644
index 0000000000..66819e432c
--- /dev/null
+++ b/ports/zephyr/qstrdefsport.h
@@ -0,0 +1,4 @@
+// qstrs specific to this port
+// *FORMAT-OFF*
+Q(/flash/lib)
+Q(/sd/lib)
diff --git a/ports/zephyr/src/usbd.c b/ports/zephyr/src/usbd.c
index 2444706cbe..36b07a8638 100644
--- a/ports/zephyr/src/usbd.c
+++ b/ports/zephyr/src/usbd.c
@@ -34,12 +34,22 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(mp_usbd);
+#if KERNEL_VERSION_NUMBER >= ZEPHYR_VERSION(4, 1, 0)
+
+#define BLOCKLIST , blocklist
+
/* By default, do not register the USB DFU class DFU mode instance. */
static const char *const blocklist[] = {
"dfu_dfu",
NULL,
};
+#else
+
+#define BLOCKLIST
+
+#endif
+
USBD_DEVICE_DEFINE(mp_usbd,
DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
CONFIG_MICROPY_USB_DEVICE_VID, CONFIG_MICROPY_USB_DEVICE_PID);
@@ -121,7 +131,7 @@ struct usbd_context *mp_usbd_init_device(usbd_msg_cb_t msg_cb) {
return NULL;
}
- err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_HS, 1, blocklist);
+ err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_HS, 1 BLOCKLIST);
if (err) {
LOG_ERR("Failed to add register classes");
return NULL;
@@ -137,7 +147,7 @@ struct usbd_context *mp_usbd_init_device(usbd_msg_cb_t msg_cb) {
return NULL;
}
- err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_FS, 1, blocklist);
+ err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_FS, 1 BLOCKLIST);
if (err) {
LOG_ERR("Failed to add register classes");
return NULL;
diff --git a/ports/zephyr/src/zephyr_getchar.c b/ports/zephyr/src/zephyr_getchar.c
index 94e35e2e84..bf504a97c9 100644
--- a/ports/zephyr/src/zephyr_getchar.c
+++ b/ports/zephyr/src/zephyr_getchar.c
@@ -23,12 +23,10 @@
extern int mp_interrupt_char;
void mp_sched_keyboard_interrupt(void);
void mp_hal_signal_event(void);
-void mp_hal_wait_sem(struct k_sem *sem, uint32_t timeout_ms);
-static struct k_sem uart_sem;
-#define UART_BUFSIZE 256
+#define UART_BUFSIZE (512)
static uint8_t uart_ringbuf[UART_BUFSIZE];
-static uint8_t i_get, i_put;
+static uint16_t i_get, i_put;
static int console_irq_input_hook(uint8_t ch) {
int i_next = (i_put + 1) & (UART_BUFSIZE - 1);
@@ -44,14 +42,16 @@ static int console_irq_input_hook(uint8_t ch) {
uart_ringbuf[i_put] = ch;
i_put = i_next;
}
- // printk("%x\n", ch);
- k_sem_give(&uart_sem);
return 1;
}
+// Returns true if a char is available for reading.
+int zephyr_getchar_check(void) {
+ return i_get != i_put;
+}
+
int zephyr_getchar(void) {
- mp_hal_wait_sem(&uart_sem, 0);
- if (k_sem_take(&uart_sem, K_MSEC(0)) == 0) {
+ if (i_get != i_put) {
unsigned int key = irq_lock();
int c = (int)uart_ringbuf[i_get++];
i_get &= UART_BUFSIZE - 1;
@@ -62,7 +62,6 @@ int zephyr_getchar(void) {
}
void zephyr_getchar_init(void) {
- k_sem_init(&uart_sem, 0, UINT_MAX);
uart_console_in_debug_hook_install(console_irq_input_hook);
// All NULLs because we're interested only in the callback above
uart_register_input(NULL, NULL, NULL);
diff --git a/ports/zephyr/src/zephyr_getchar.h b/ports/zephyr/src/zephyr_getchar.h
index fee899e1b4..3f31c4317f 100644
--- a/ports/zephyr/src/zephyr_getchar.h
+++ b/ports/zephyr/src/zephyr_getchar.h
@@ -17,4 +17,5 @@
#include <stdint.h>
void zephyr_getchar_init(void);
+int zephyr_getchar_check(void);
int zephyr_getchar(void);
diff --git a/ports/zephyr/uart_core.c b/ports/zephyr/uart_core.c
index ee525c33f7..fe8a2a51db 100644
--- a/ports/zephyr/uart_core.c
+++ b/ports/zephyr/uart_core.c
@@ -26,6 +26,7 @@
#include <unistd.h>
#include "py/mpconfig.h"
#include "py/runtime.h"
+#include "py/stream.h"
#include "src/zephyr_getchar.h"
// Zephyr headers
#include <zephyr/kernel.h>
@@ -52,6 +53,24 @@ static uint8_t mp_console_txbuf[CONFIG_CONSOLE_PUTCHAR_BUFSIZE];
* Core UART functions to implement for a port
*/
+uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
+ uintptr_t ret = 0;
+ if (poll_flags & MP_STREAM_POLL_RD) {
+ #ifdef CONFIG_CONSOLE_SUBSYS
+ // It's not easy to test if tty is readable, so just unconditionally set it for now.
+ ret |= MP_STREAM_POLL_RD;
+ #else
+ if (zephyr_getchar_check()) {
+ ret |= MP_STREAM_POLL_RD;
+ }
+ #endif
+ }
+ if (poll_flags & MP_STREAM_POLL_WR) {
+ ret |= MP_STREAM_POLL_WR;
+ }
+ return ret;
+}
+
// Receive single character
int mp_hal_stdin_rx_chr(void) {
for (;;) {
diff --git a/py/argcheck.c b/py/argcheck.c
index 35b116ec0d..298c19bcfd 100644
--- a/py/argcheck.c
+++ b/py/argcheck.c
@@ -137,12 +137,12 @@ void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args,
mp_arg_parse_all(n_pos, args, &kw_args, n_allowed, allowed, out_vals);
}
-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"));
}
#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
diff --git a/py/asmarm.c b/py/asmarm.c
index 6fa751b32e..15bc73b61e 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 4a4253aef6..405457d440 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);
@@ -159,12 +164,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); \
@@ -202,18 +207,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 3fce543a7f..f1b823fa36 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;
diff --git a/py/asmrv32.c b/py/asmrv32.c
index c24d05a138..158b552191 100644
--- a/py/asmrv32.c
+++ b/py/asmrv32.c
@@ -450,18 +450,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 +475,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 +503,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.");
+
+ int32_t scaled_offset = offset << operation_size;
- if (FIT_SIGNED(scaled_offset, 12)) {
- // sw rd, offset(rs)
- asm_rv32_opcode_sw(state, rd, rs, scaled_offset);
+ 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 +524,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 +544,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
diff --git a/py/asmrv32.h b/py/asmrv32.h
index b09f48eb12..dac9c028b0 100644
--- a/py/asmrv32.h
+++ b/py/asmrv32.h
@@ -709,17 +709,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 +731,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 +750,50 @@ 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) \
+ do { \
+ asm_rv32_opcode_cadd(state, rs1, rs2); \
+ asm_rv32_opcode_lbu(state, rd, rs1, 0); \
+ } while (0)
+#define ASM_LOAD16_REG_REG_REG(state, rd, rs1, rs2) \
+ do { \
+ asm_rv32_opcode_slli(state, rs2, rs2, 1); \
+ asm_rv32_opcode_cadd(state, rs1, rs2); \
+ asm_rv32_opcode_lhu(state, rd, rs1, 0); \
+ } while (0)
+#define ASM_LOAD32_REG_REG_REG(state, rd, rs1, rs2) \
+ do { \
+ asm_rv32_opcode_slli(state, rs2, rs2, 2); \
+ asm_rv32_opcode_cadd(state, rs1, rs2); \
+ asm_rv32_opcode_lw(state, rd, rs1, 0); \
+ } while (0)
+#define ASM_STORE8_REG_REG_REG(state, rd, rs1, rs2) \
+ do { \
+ asm_rv32_opcode_cadd(state, rs1, rs2); \
+ asm_rv32_opcode_sb(state, rd, rs1, 0); \
+ } while (0)
+#define ASM_STORE16_REG_REG_REG(state, rd, rs1, rs2) \
+ do { \
+ asm_rv32_opcode_slli(state, rs2, rs2, 1); \
+ asm_rv32_opcode_cadd(state, rs1, rs2); \
+ asm_rv32_opcode_sh(state, rd, rs1, 0); \
+ } while (0)
+#define ASM_STORE32_REG_REG_REG(state, rd, rs1, rs2) \
+ do { \
+ asm_rv32_opcode_slli(state, rs2, rs2, 2); \
+ asm_rv32_opcode_cadd(state, rs1, rs2); \
+ asm_rv32_opcode_sw(state, rd, rs1, 0); \
+ } while (0)
#endif
diff --git a/py/asmthumb.c b/py/asmthumb.c
index 420815e802..18c3db9e4e 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);
}
@@ -432,11 +425,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 +438,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 +452,48 @@ 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_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);
}
}
@@ -495,6 +501,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 +521,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 +575,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 +604,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 a9e68d7adb..5edf6573e1 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
@@ -375,12 +403,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); \
@@ -418,18 +446,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 c63e31797e..d80c5dcc13 100644
--- a/py/asmx64.h
+++ b/py/asmx64.h
@@ -154,12 +154,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) { \
@@ -205,18 +205,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 027d44151e..d2e078ad51 100644
--- a/py/asmx86.h
+++ b/py/asmx86.h
@@ -149,12 +149,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) { \
@@ -200,18 +200,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 0fbe351dcf..bc3e717d9f 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 d2f37bf828..559b3cacd5 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,15 +293,24 @@ 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
+// 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
@@ -329,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
@@ -359,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
@@ -407,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 899dbd6a07..cea31c93bd 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 718ba4a684..f24510ea7e 100644
--- a/py/bc.h
+++ b/py/bc.h
@@ -308,25 +308,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 4fc8f751ad..48d3421bca 100644
--- a/py/binary.c
+++ b/py/binary.c
@@ -196,7 +196,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;
}
diff --git a/py/dynruntime.h b/py/dynruntime.h
index c93111bbd4..0e438da4b7 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");
@@ -295,7 +295,7 @@ 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 (;;) {
}
diff --git a/py/dynruntime.mk b/py/dynruntime.mk
index 1ef521bd9a..030728cfc9 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)
@@ -172,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/emitinlinethumb.c b/py/emitinlinethumb.c
index 7818bb4f46..d6596337ae 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
diff --git a/py/emitinlinextensa.c b/py/emitinlinextensa.c
index fed259cfc6..d0eb3d566f 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 1aab0a9eb7..3aacf69a81 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -180,12 +180,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,
@@ -425,6 +419,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
@@ -471,7 +489,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);
@@ -542,7 +560,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;
@@ -574,7 +592,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
@@ -640,6 +658,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) {
@@ -1537,87 +1559,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:
@@ -1638,40 +1623,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:
@@ -1814,92 +1795,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:
@@ -1930,50 +1866,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:
diff --git a/py/emitndebug.c b/py/emitndebug.c
index bd896a75c8..e49c5cdbff 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/makeversionhdr.py b/py/makeversionhdr.py
index cb7325e416..406a061a09 100644
--- a/py/makeversionhdr.py
+++ b/py/makeversionhdr.py
@@ -117,8 +117,8 @@ def make_version_header(repo_path, filename):
build_date = datetime.date.today()
if "SOURCE_DATE_EPOCH" in os.environ:
- build_date = datetime.datetime.utcfromtimestamp(
- int(os.environ["SOURCE_DATE_EPOCH"])
+ build_date = datetime.datetime.fromtimestamp(
+ int(os.environ["SOURCE_DATE_EPOCH"]), datetime.timezone.utc
).date()
# Generate the file with the git and version info
diff --git a/py/misc.h b/py/misc.h
index e05fbe61a9..5d0893bbdd 100644
--- a/py/misc.h
+++ b/py/misc.h
@@ -105,7 +105,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
@@ -390,11 +390,16 @@ 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
+#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) {
#ifdef __XC16__
diff --git a/py/modio.c b/py/modio.c
index d3e563dbcf..9aeb42d30a 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 4d51a28d0b..919a8ccd9d 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"));
}
@@ -161,6 +161,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/mpconfig.h b/py/mpconfig.h
index d06932a77b..4c12762759 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -406,6 +406,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)
@@ -421,6 +426,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)
@@ -883,6 +893,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)
@@ -1318,6 +1386,11 @@ typedef double mp_float_t;
#define MICROPY_PY___FILE__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
#endif
+// Whether to process __all__ when importing all public symbols from module
+#ifndef MICROPY_MODULE___ALL__
+#define MICROPY_MODULE___ALL__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES)
+#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)
@@ -1469,7 +1542,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
@@ -2115,8 +2188,12 @@ typedef double mp_float_t;
#endif // INT_FMT
// Modifier for function which doesn't return
-#ifndef NORETURN
-#define NORETURN __attribute__((noreturn))
+#ifndef MP_NORETURN
+#define MP_NORETURN __attribute__((noreturn))
+#endif
+
+#if !MICROPY_PREVIEW_VERSION_2
+#define NORETURN MP_NORETURN
#endif
// Modifier for weak functions
diff --git a/py/mpprint.c b/py/mpprint.c
index 291e4145ff..86dbfad05e 100644
--- a/py/mpprint.c
+++ b/py/mpprint.c
@@ -58,7 +58,7 @@ 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;
@@ -201,7 +201,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 +248,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,7 +337,7 @@ 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) {
+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[32];
char sign = '\0';
int chrs = 0;
@@ -416,8 +413,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';
diff --git a/py/mpprint.h b/py/mpprint.h
index 8383ea8579..583f00bda8 100644
--- a/py/mpprint.h
+++ b/py/mpprint.h
@@ -31,13 +31,12 @@
#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_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 +68,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/mpz.c b/py/mpz.c
index 084aebda9e..5a4d7d27d9 100644
--- a/py/mpz.c
+++ b/py/mpz.c
@@ -1537,7 +1537,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;
}
@@ -1552,11 +1553,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;
}
@@ -1577,11 +1579,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;
}
@@ -1642,7 +1645,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;
}
@@ -1672,6 +1676,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) {
@@ -1717,7 +1723,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 e96fd7b66a..2c7923c56d 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
void (*raise_msg)(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg);
const mp_obj_type_t *(*obj_get_type)(mp_const_obj_t o_in);
diff --git a/py/nlr.c b/py/nlr.c
index 7ab0c0955a..de2a38ceff 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 ce30bc91d6..47447c5d17 100644
--- a/py/nlr.h
+++ b/py/nlr.h
@@ -177,18 +177,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 d6d87ebc50..3318004b5e 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 cba52b16a2..5c55db7e26 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 8a69fe1eec..cf140400e6 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 9a12ede400..565a8629db 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 e7ba79797b..b7d1467b8f 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 e7b24f242b..8546308a3d 100644
--- a/py/nlrthumb.c
+++ b/py/nlrthumb.c
@@ -100,7 +100,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 d1ad91ff7d..51224729fc 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 085e30d203..26bf0dc6cc 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 ff7af6edee..2d1bf35e38 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 1606ad5209..5867594607 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -314,6 +314,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 0736252247..a1df661ff0 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 <math.h>
+// 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) {
@@ -207,6 +209,10 @@ 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
+ return mp_const_float_nan;
+ }
union {
mp_float_t f;
mp_uint_t u;
@@ -257,12 +263,13 @@ 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 <math.h>
#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))}
-#define mp_const_float_nan {((mp_obj_t)((uint64_t)0xfff8000000000000 + 0x8004000000000000))}
#endif
static inline bool mp_obj_is_float(mp_const_obj_t o) {
@@ -276,6 +283,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;
@@ -1037,6 +1051,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 9d68aa7061..1fd0269390 100644
--- a/py/objarray.c
+++ b/py/objarray.c
@@ -113,6 +113,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
@@ -142,18 +155,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
@@ -413,7 +415,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;
- mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ);
+ if (!mp_get_buffer(arg_in, &arg_bufinfo, MP_BUFFER_READ)) {
+ array_extend_impl(self, arg_in, 0, 0);
+ return mp_const_none;
+ }
size_t sz = mp_binary_get_size('@', self->typecode, NULL);
diff --git a/py/objcode.c b/py/objcode.c
index 9b98a69679..52df84d012 100644
--- a/py/objcode.c
+++ b/py/objcode.c
@@ -107,6 +107,67 @@ static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) {
return o;
}
+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) {
// not load attribute
@@ -143,6 +204,10 @@ static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
}
dest[0] = o->lnotab;
break;
+ case MP_QSTR_co_lines:
+ dest[0] = MP_OBJ_FROM_PTR(&code_colines_obj);
+ dest[1] = self_in;
+ break;
}
}
diff --git a/py/objcode.h b/py/objcode.h
index 8db9a34b6e..8f26bd9dbd 100644
--- a/py/objcode.h
+++ b/py/objcode.h
@@ -72,7 +72,7 @@ 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
diff --git a/py/objfloat.c b/py/objfloat.c
index 0728fce315..81b0daa620 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 <math.h>
#include "py/formatfloat.h"
@@ -47,13 +52,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;
diff --git a/py/objint.c b/py/objint.c
index 4be6009a44..87d8a27852 100644
--- a/py/objint.c
+++ b/py/objint.c
@@ -209,7 +209,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
}
@@ -251,6 +251,7 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co
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);
@@ -275,7 +276,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 1940b81538..5b60eb65ad 100644
--- a/py/objint_longlong.c
+++ b/py/objint_longlong.c
@@ -295,6 +295,22 @@ mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) {
return mp_obj_int_get_truncated(self_in);
}
+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
mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in) {
assert(mp_obj_is_exact_type(self_in, &mp_type_int));
diff --git a/py/objlist.c b/py/objlist.c
index 9a88de3892..71414a849f 100644
--- a/py/objlist.c
+++ b/py/objlist.c
@@ -159,76 +159,63 @@ 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 (value == MP_OBJ_NULL) {
- // delete
- #if MICROPY_PY_BUILTINS_SLICE
- if (mp_obj_is_type(index, &mp_type_slice)) {
- mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in);
- mp_bound_slice_t slice;
- if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) {
- mp_raise_NotImplementedError(NULL);
+ #if MICROPY_PY_BUILTINS_SLICE
+ if (mp_obj_is_type(index, &mp_type_slice)) {
+ mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in);
+ 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
+ self->len += len_adj;
+ return mp_const_none;
+ }
+ #endif
+ if (value == MP_OBJ_NULL) {
+ // delete
mp_obj_t args[2] = {self_in, index};
list_pop(2, args);
return mp_const_none;
} else if (value == MP_OBJ_SENTINEL) {
// load
mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in);
- #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
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)) {
- mp_obj_list_t *self = MP_OBJ_TO_PTR(self_in);
- 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;
}
diff --git a/py/objstr.c b/py/objstr.c
index a160ab415c..c81fc682fd 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -40,7 +40,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);
@@ -1001,7 +1001,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
@@ -1184,7 +1184,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)
@@ -1229,8 +1229,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 == '.') {
@@ -2357,7 +2358,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/parsenum.c b/py/parsenum.c
index 3281eb4b85..7e6695fbfc 100644
--- a/py/parsenum.c
+++ b/py/parsenum.c
@@ -36,7 +36,7 @@
#include <math.h>
#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) {
@@ -227,13 +227,13 @@ mp_obj_t mp_parse_num_float(const char *str, size_t len, bool allow_imag, mp_lex
const char *top = str + len;
mp_float_t dec_val = 0;
- bool dec_neg = false;
#if MICROPY_PY_BUILTINS_COMPLEX
unsigned int real_imag_state = REAL_IMAG_STATE_START;
mp_float_t dec_real = 0;
-parse_start:
+parse_start:;
#endif
+ bool dec_neg = false;
// skip leading space
for (; str < top && unichar_isspace(*str); str++) {
@@ -252,24 +252,18 @@ 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;
diff --git a/py/persistentcode.c b/py/persistentcode.c
index 2a42b904bc..6ec0717f94 100644
--- a/py/persistentcode.c
+++ b/py/persistentcode.c
@@ -72,6 +72,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 +88,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;
@@ -415,15 +415,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);
}
@@ -757,7 +759,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) {
@@ -768,7 +770,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/profile.c b/py/profile.c
index 397d0291f9..b5a0c54728 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 at 0x%p, file '%q', line %d, code %q>",
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/repl.c b/py/repl.c
index 87c171cc87..b0ccfa383a 100644
--- a/py/repl.c
+++ b/py/repl.c
@@ -218,6 +218,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 58819819ad..90587a010a 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -123,7 +123,7 @@ void mp_init(void) {
MP_STATE_VM(mp_module_builtins_override_dict) = NULL;
#endif
- #if MICROPY_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
@@ -1247,6 +1247,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'"),
@@ -1593,8 +1606,28 @@ mp_obj_t mp_import_from(mp_obj_t module, qstr 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
@@ -1649,7 +1682,7 @@ mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_i
#endif // MICROPY_ENABLE_COMPILER
-NORETURN void m_malloc_fail(size_t num_bytes) {
+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()) {
@@ -1662,25 +1695,25 @@ NORETURN void m_malloc_fail(size_t num_bytes) {
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE
-NORETURN void mp_raise_type(const mp_obj_type_t *exc_type) {
+MP_NORETURN void mp_raise_type(const mp_obj_type_t *exc_type) {
nlr_raise(mp_obj_new_exception(exc_type));
}
-NORETURN void mp_raise_ValueError_no_msg(void) {
+MP_NORETURN void mp_raise_ValueError_no_msg(void) {
mp_raise_type(&mp_type_ValueError);
}
-NORETURN void mp_raise_TypeError_no_msg(void) {
+MP_NORETURN void mp_raise_TypeError_no_msg(void) {
mp_raise_type(&mp_type_TypeError);
}
-NORETURN void mp_raise_NotImplementedError_no_msg(void) {
+MP_NORETURN void mp_raise_NotImplementedError_no_msg(void) {
mp_raise_type(&mp_type_NotImplementedError);
}
#else
-NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, 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) {
if (msg == NULL) {
nlr_raise(mp_obj_new_exception(exc_type));
} else {
@@ -1688,7 +1721,7 @@ NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t ms
}
}
-NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) {
+MP_NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) {
va_list args;
va_start(args, fmt);
mp_obj_t exc = mp_obj_new_exception_msg_vlist(exc_type, fmt, args);
@@ -1696,25 +1729,25 @@ NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text
nlr_raise(exc);
}
-NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg) {
+MP_NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg) {
mp_raise_msg(&mp_type_ValueError, msg);
}
-NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg) {
+MP_NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg) {
mp_raise_msg(&mp_type_TypeError, msg);
}
-NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg) {
+MP_NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg) {
mp_raise_msg(&mp_type_NotImplementedError, msg);
}
#endif
-NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg) {
+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 {
@@ -1722,7 +1755,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"));
@@ -1732,11 +1765,11 @@ NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg) {
#endif
}
-NORETURN void mp_raise_OSError(int errno_) {
+MP_NORETURN void mp_raise_OSError(int errno_) {
mp_raise_type_arg(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_));
}
-NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename) {
+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);
@@ -1746,7 +1779,7 @@ NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename) {
}
#if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK
-NORETURN void mp_raise_recursion_depth(void) {
+MP_NORETURN void mp_raise_recursion_depth(void) {
mp_raise_type_arg(&mp_type_RuntimeError, MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded));
}
#endif
diff --git a/py/runtime.h b/py/runtime.h
index 77cdd0e203..f42039cab9 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -128,7 +128,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) {
@@ -136,8 +136,8 @@ 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);
static inline mp_obj_dict_t *mp_locals_get(void) {
return MP_STATE_THREAD(dict_locals);
@@ -165,9 +165,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;
@@ -245,10 +252,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()
@@ -256,19 +263,19 @@ 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
-NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg);
-NORETURN void mp_raise_StopIteration(mp_obj_t arg);
-NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg);
-NORETURN void mp_raise_OSError(int errno_);
-NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename);
-NORETURN void mp_raise_recursion_depth(void);
+MP_NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg);
+MP_NORETURN void mp_raise_StopIteration(mp_obj_t arg);
+MP_NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg);
+MP_NORETURN void mp_raise_OSError(int errno_);
+MP_NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename);
+MP_NORETURN void mp_raise_recursion_depth(void);
#if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG
#undef mp_check_self
diff --git a/py/scheduler.c b/py/scheduler.c
index 2170b9577e..d4cdb59efb 100644
--- a/py/scheduler.c
+++ b/py/scheduler.c
@@ -88,17 +88,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
diff --git a/py/showbc.c b/py/showbc.c
index 6913d18c1c..792fccd013 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/pyproject.toml b/pyproject.toml
index 263b120d3f..0dd15d06c7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -29,10 +29,12 @@ target-version = "py37"
[tool.ruff.lint]
exclude = [ # Ruff finds Python SyntaxError in these files
"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",
diff --git a/shared/libc/abort_.c b/shared/libc/abort_.c
index 3051eae81e..54eab67d3f 100644
--- a/shared/libc/abort_.c
+++ b/shared/libc/abort_.c
@@ -1,7 +1,7 @@
#include <py/runtime.h>
-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/timeutils/timeutils.c b/shared/timeutils/timeutils.c
index 4282a0178d..0c6916e06d 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 874d16e976..35356b462a 100644
--- a/shared/timeutils/timeutils.h
+++ b/shared/timeutils/timeutils.h
@@ -27,9 +27,23 @@
#ifndef MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H
#define MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H
+#include "py/obj.h"
+#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
+#include <math.h> // 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
@@ -45,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);
+}
+
+// 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);
+}
+
+// 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 uint64_t timeutils_seconds_since_epoch_to_nanoseconds_since_1970(mp_uint_t s) {
- return ((uint64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000ULL;
+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 mp_uint_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(uint64_t ns) {
- return ns / 1000000000ULL - 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/shared/tinyusb/mp_usbd.c b/shared/tinyusb/mp_usbd.c
index 7ccfa7018d..f03f7f7db4 100644
--- a/shared/tinyusb/mp_usbd.c
+++ b/shared/tinyusb/mp_usbd.c
@@ -30,10 +30,6 @@
#include "mp_usbd.h"
-#ifndef NO_QSTR
-#include "device/dcd.h"
-#endif
-
#if !MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
void mp_usbd_task(void) {
@@ -47,13 +43,8 @@ void mp_usbd_task_callback(mp_sched_node_t *node) {
#endif // !MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
-extern void __real_dcd_event_handler(dcd_event_t const *event, bool in_isr);
-
-// If -Wl,--wrap=dcd_event_handler is passed to the linker, then this wrapper
-// will be called and allows MicroPython to schedule the TinyUSB task when
-// dcd_event_handler() is called from an ISR.
-TU_ATTR_FAST_FUNC void __wrap_dcd_event_handler(dcd_event_t const *event, bool in_isr) {
- __real_dcd_event_handler(event, in_isr);
+// Schedule the TinyUSB task on demand, when there is a new USB device event
+TU_ATTR_FAST_FUNC void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
mp_usbd_schedule_task();
mp_hal_wake_main_task_from_isr();
}
diff --git a/tests/basics/array_add.py b/tests/basics/array_add.py
index 76ce59f761..e78615541c 100644
--- a/tests/basics/array_add.py
+++ b/tests/basics/array_add.py
@@ -14,3 +14,9 @@ print(a1)
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/fun_code_colines.py b/tests/basics/fun_code_colines.py
new file mode 100644
index 0000000000..a8867770ed
--- /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 0000000000..19bd4ef6e2
--- /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 0000000000..5eb23150df
--- /dev/null
+++ b/tests/basics/fun_code_full.py
@@ -0,0 +1,47 @@
+# 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
+
+try:
+ import warnings
+ warnings.simplefilter("ignore") # ignore deprecation warning about co_lnotab
+except ImportError:
+ pass
+
+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)
+print(type(code.co_lnotab)) # both bytes
+
+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/io_buffered_writer.py b/tests/basics/io_buffered_writer.py
index 5c065f158a..60cf2c837d 100644
--- a/tests/basics/io_buffered_writer.py
+++ b/tests/basics/io_buffered_writer.py
@@ -28,3 +28,27 @@ print(bts.getvalue())
# 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 2209348f5a..d61eb148b4 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'
<class 'int'>
+OSError
+OSError
+writing bytearray(b'foobar')
+flushed
+flushed
diff --git a/tests/basics/string_format.py b/tests/basics/string_format.py
index e8600f5836..11e7836a73 100644
--- a/tests/basics/string_format.py
+++ b/tests/basics/string_format.py
@@ -22,7 +22,17 @@ test("{:4o}", 123)
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/cmdline/repl_autocomplete_underscore.py b/tests/cmdline/repl_autocomplete_underscore.py
new file mode 100644
index 0000000000..98bbb69920
--- /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 0000000000..35617554f5
--- /dev/null
+++ b/tests/cmdline/repl_autocomplete_underscore.py.exp
@@ -0,0 +1,41 @@
+MicroPython \.\+ version
+Use Ctrl-D to exit, Ctrl-E for paste mode
+>>> # 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_paste.py b/tests/cmdline/repl_paste.py
new file mode 100644
index 0000000000..7cec450fce
--- /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 0000000000..22d9bd5740
--- /dev/null
+++ b/tests/cmdline/repl_paste.py.exp
@@ -0,0 +1,133 @@
+MicroPython \.\+ version
+Use Ctrl-D to exit, Ctrl-E for paste mode
+>>> # 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 "<stdin>", 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 "<stdin>", line 5, in <module>
+ File "<stdin>", 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_fstring_concat.py b/tests/cpydiff/core_fstring_concat.py
index 3daa13d753..2fbe1b961a 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 22bbc5866e..570b92434a 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 d37fb48db7..2589a34b7e 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 0fbe9d4d4e..0000000000
--- 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 27a2bf2ad9..0000000000
--- 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 dd9b9d4ddd..0000000000
--- a/tests/cpydiff/modules3/foo.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def hello():
- print("hello")
diff --git a/tests/cpydiff/syntax_arg_unpacking.py b/tests/cpydiff/syntax_arg_unpacking.py
index e54832ddb9..7133a8a282 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 0000000000..4b1406e9f3
--- /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 03d25d5619..670cefdeac 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 @@ try:
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 0000000000..4a012987d9
--- /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_str_formatsep.py b/tests/cpydiff/types_str_formatsep.py
new file mode 100644
index 0000000000..05d0b8d3d2
--- /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/extmod/asyncio_event_queue.py b/tests/extmod/asyncio_event_queue.py
new file mode 100644
index 0000000000..e0125b1aef
--- /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 0000000000..ee42c96d83
--- /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_iterator_event.py b/tests/extmod/asyncio_iterator_event.py
new file mode 100644
index 0000000000..6efa6b8645
--- /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(f"bar {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 0000000000..a1893197d0
--- /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_wait_for_linked_task.py b/tests/extmod/asyncio_wait_for_linked_task.py
new file mode 100644
index 0000000000..4dda62d547
--- /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/framebuf_blit.py b/tests/extmod/framebuf_blit.py
new file mode 100644
index 0000000000..b1d98b330a
--- /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 0000000000..e340f1990c
--- /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/json_loads.py b/tests/extmod/json_loads.py
index f9073c121e..095e67d740 100644
--- a/tests/extmod/json_loads.py
+++ b/tests/extmod/json_loads.py
@@ -71,3 +71,27 @@ try:
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/machine_uart_tx.py b/tests/extmod/machine_uart_tx.py
index f0cc912da6..85bf7e9fb8 100644
--- a/tests/extmod/machine_uart_tx.py
+++ b/tests/extmod/machine_uart_tx.py
@@ -28,7 +28,10 @@ elif "mimxrt" in sys.platform:
initial_delay_ms = 20 # UART sends idle frame after init, so wait for that
bit_margin = 1
elif "pyboard" in sys.platform:
- uart_id = 4
+ if "STM32WB" in sys.implementation._machine:
+ uart_id = "LP1"
+ else:
+ uart_id = 4
pins = {}
initial_delay_ms = 50 # UART sends idle frame after init, so wait for that
bit_margin = 1 # first start-bit must wait to sync with the UART clock
diff --git a/tests/extmod/platform_basic.py b/tests/extmod/platform_basic.py
new file mode 100644
index 0000000000..eb6f2be13c
--- /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 3b37ed8dce..03973c5834 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/select_poll_eintr.py b/tests/extmod/select_poll_eintr.py
index e1cbc2aaf5..d9e9b31909 100644
--- a/tests/extmod/select_poll_eintr.py
+++ b/tests/extmod/select_poll_eintr.py
@@ -10,6 +10,18 @@ except (ImportError, AttributeError):
print("SKIP")
raise SystemExit
+# Use a new UDP socket for tests, which should be writable but not readable.
+s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+localhost_addr_info = socket.getaddrinfo("127.0.0.1", 8000)
+try:
+ s.bind(localhost_addr_info[0][-1])
+except OSError:
+ # Target can't bind to localhost.
+ # Most likely it doesn't have a NIC and the test cannot be run.
+ s.close()
+ print("SKIP")
+ raise SystemExit
+
def thread_main():
lock.acquire()
@@ -26,10 +38,6 @@ lock = _thread.allocate_lock()
lock.acquire()
_thread.start_new_thread(thread_main, ())
-# Use a new UDP socket for tests, which should be writable but not readable.
-s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-s.bind(socket.getaddrinfo("127.0.0.1", 8000)[0][-1])
-
# Create the poller object.
poller = select.poll()
poller.register(s, select.POLLIN)
diff --git a/tests/extmod/time_mktime.py b/tests/extmod/time_mktime.py
new file mode 100644
index 0000000000..7fc643dc3c
--- /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/ssl_noleak.py b/tests/extmod/tls_noleak.py
index 870032d58e..870032d58e 100644
--- a/tests/extmod/ssl_noleak.py
+++ b/tests/extmod/tls_noleak.py
diff --git a/tests/extmod/ssl_threads.py b/tests/extmod/tls_threads.py
index 4564abd3d8..4564abd3d8 100644
--- a/tests/extmod/ssl_threads.py
+++ b/tests/extmod/tls_threads.py
diff --git a/tests/extmod/vfs_lfs.py b/tests/extmod/vfs_lfs.py
index 3ad57fd9c3..40d58e9c9f 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 2ac7629bfa..73cdf34373 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 f4327f6962..440607ed84 100644
--- a/tests/extmod/vfs_lfs_error.py.exp
+++ b/tests/extmod/vfs_lfs_error.py.exp
@@ -1,28 +1,28 @@
test <class 'VfsLfs1'>
-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 <class 'VfsLfs2'>
-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_rom.py b/tests/extmod/vfs_rom.py
index 770b6863b9..cd14542ea6 100644
--- a/tests/extmod/vfs_rom.py
+++ b/tests/extmod/vfs_rom.py
@@ -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_hardware/machine_uart_irq_rx.py b/tests/extmod_hardware/machine_uart_irq_rx.py
index ecc95e62ae..3602c260e3 100644
--- a/tests/extmod_hardware/machine_uart_irq_rx.py
+++ b/tests/extmod_hardware/machine_uart_irq_rx.py
@@ -24,9 +24,14 @@ elif "esp32" in sys.platform:
tx_pin = 4
rx_pin = 5
elif "pyboard" in sys.platform:
- uart_id = 4
- tx_pin = None # PA0
- rx_pin = None # PA1
+ if "STM32WB" in sys.implementation._machine:
+ # LPUART(1) is on PA2/PA3
+ uart_id = "LP1"
+ else:
+ # UART(4) is on PA0/PA1
+ uart_id = 4
+ tx_pin = None
+ rx_pin = None
elif "samd" in sys.platform and "ItsyBitsy M0" in sys.implementation._machine:
uart_id = 0
tx_pin = "D1"
diff --git a/tests/extmod_hardware/machine_uart_irq_rxidle.py b/tests/extmod_hardware/machine_uart_irq_rxidle.py
index af2412c75e..3c743c9e0c 100644
--- a/tests/extmod_hardware/machine_uart_irq_rxidle.py
+++ b/tests/extmod_hardware/machine_uart_irq_rxidle.py
@@ -13,6 +13,9 @@ except (ImportError, AttributeError):
import time, sys
+# Target tuning options.
+tune_wait_initial_rxidle = False
+
# Configure pins based on the target.
if "alif" in sys.platform:
uart_id = 1
@@ -26,9 +29,15 @@ elif "mimxrt" in sys.platform:
uart_id = 1
tx_pin = None
elif "pyboard" in sys.platform:
- uart_id = 4
- tx_pin = None # PA0
- rx_pin = None # PA1
+ tune_wait_initial_rxidle = True
+ if "STM32WB" in sys.implementation._machine:
+ # LPUART(1) is on PA2/PA3
+ uart_id = "LP1"
+ else:
+ # UART(4) is on PA0/PA1
+ uart_id = 4
+ tx_pin = None
+ rx_pin = None
elif "renesas-ra" in sys.platform:
uart_id = 9
tx_pin = None # P602 @ RA6M2
@@ -55,20 +64,31 @@ def irq(u):
print("IRQ_RXIDLE:", bool(u.irq().flags() & u.IRQ_RXIDLE), "data:", u.read())
-text = "12345678"
+text = ("12345678", "abcdefgh")
# Test that the IRQ is called for each set of byte received.
for bits_per_s in (2400, 9600, 115200):
+ print("========")
+ print("bits_per_s:", bits_per_s)
+
if tx_pin is None:
uart = UART(uart_id, bits_per_s)
else:
uart = UART(uart_id, bits_per_s, tx=tx_pin, rx=rx_pin)
+ # Ignore a possible initial RXIDLE condition after creating UART.
+ if tune_wait_initial_rxidle:
+ uart.irq(lambda _: None, uart.IRQ_RXIDLE)
+ time.sleep_ms(10)
+
+ # Configure desired IRQ.
uart.irq(irq, uart.IRQ_RXIDLE)
- print("write", bits_per_s)
- uart.write(text)
- uart.flush()
- print("ready")
- time.sleep_ms(100)
- print("done")
+ for i in range(2):
+ # Write data and wait for IRQ.
+ print("write")
+ uart.write(text[i])
+ uart.flush()
+ print("ready")
+ time.sleep_ms(100)
+ print("done")
diff --git a/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp b/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp
index ce1890a06a..f3c7497e4c 100644
--- a/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp
+++ b/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp
@@ -1,12 +1,30 @@
-write 2400
+========
+bits_per_s: 2400
+write
ready
IRQ_RXIDLE: True data: b'12345678'
done
-write 9600
+write
+ready
+IRQ_RXIDLE: True data: b'abcdefgh'
+done
+========
+bits_per_s: 9600
+write
ready
IRQ_RXIDLE: True data: b'12345678'
done
-write 115200
+write
+ready
+IRQ_RXIDLE: True data: b'abcdefgh'
+done
+========
+bits_per_s: 115200
+write
ready
IRQ_RXIDLE: True data: b'12345678'
done
+write
+ready
+IRQ_RXIDLE: True data: b'abcdefgh'
+done
diff --git a/tests/float/complex1.py b/tests/float/complex1.py
index f4107a1390..0a1d98b9af 100644
--- a/tests/float/complex1.py
+++ b/tests/float/complex1.py
@@ -12,9 +12,11 @@ print(complex("1.2j"))
print(complex("1+j"))
print(complex("1+2j"))
print(complex("-1-2j"))
+print(complex("-1+2j"))
print(complex("+1-2j"))
print(complex(" -1-2j "))
print(complex(" +1-2j "))
+print(complex(" -1+2j "))
print(complex("nanj"))
print(complex("nan-infj"))
print(complex(1, 2))
diff --git a/tests/float/float_array.py b/tests/float/float_array.py
index 3d128da838..cfff3b220c 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/math_constants.py b/tests/float/math_constants.py
index 2e4c321052..21d822a01e 100644
--- a/tests/float/math_constants.py
+++ b/tests/float/math_constants.py
@@ -1,11 +1,30 @@
# Tests various constants of the math module.
+
+import sys
+
try:
- import math
- from math import exp, cos
+ from array import array
+ from math import e, pi
except ImportError:
print("SKIP")
raise SystemExit
-print(math.e == exp(1.0))
+# Hexadecimal representations of e and pi constants.
+e_truth_single = 0x402DF854
+pi_truth_single = 0x40490FDB
+e_truth_double = 0x4005BF0A8B145769
+pi_truth_double = 0x400921FB54442D18
+
+# Detect the floating-point precision of the system, to determine the exact values of
+# the constants (parsing the float from a decimal string can lead to inaccuracies).
+if float("1e300") == float("inf"):
+ # Single precision floats.
+ e_truth = array("f", e_truth_single.to_bytes(4, sys.byteorder))[0]
+ pi_truth = array("f", pi_truth_single.to_bytes(4, sys.byteorder))[0]
+else:
+ # Double precision floats.
+ e_truth = array("d", e_truth_double.to_bytes(8, sys.byteorder))[0]
+ pi_truth = array("d", pi_truth_double.to_bytes(8, sys.byteorder))[0]
-print(cos(math.pi))
+print("e:", e == e_truth or (e, e_truth, e - e_truth))
+print("pi:", pi == pi_truth or (pi, pi_truth, pi - pi_truth))
diff --git a/tests/float/math_constants_extra.py b/tests/float/math_constants_extra.py
index dea49aef5a..756cb45803 100644
--- a/tests/float/math_constants_extra.py
+++ b/tests/float/math_constants_extra.py
@@ -9,9 +9,12 @@ except (ImportError, AttributeError):
raise SystemExit
print(math.tau == 2.0 * math.pi)
+print(math.copysign(1.0, math.tau))
print(math.inf == float("inf"))
print(-math.inf == -float("inf"))
+print(math.copysign(1.0, math.inf))
print(isnan(math.nan))
print(isnan(-math.nan))
+print(math.copysign(1.0, math.nan))
diff --git a/tests/import/import_star.py b/tests/import/import_star.py
new file mode 100644
index 0000000000..0947f6a835
--- /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/pkgstar_all_array/__init__.py b/tests/import/pkgstar_all_array/__init__.py
new file mode 100644
index 0000000000..4499a94d59
--- /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 0000000000..7540d70f66
--- /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 0000000000..7022476c19
--- /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 0000000000..d960c7d0e2
--- /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 0000000000..a97715ed39
--- /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 0000000000..4947e4ce7f
--- /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/xtensa/asmargs.py b/tests/inlineasm/xtensa/asmargs.py
new file mode 100644
index 0000000000..2bfccfcc69
--- /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 0000000000..e33a6964f4
--- /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 0000000000..1c0934eb7a
--- /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 0000000000..7aba46a27d
--- /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 0000000000..22bcd5a7c7
--- /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 0000000000..319a4b435e
--- /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 0000000000..f41c948231
--- /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 0000000000..51993f072d
--- /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 0000000000..b185e30520
--- /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 0000000000..e6672df6f8
--- /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 0000000000..271ab83662
--- /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 0000000000..eefaa35daf
--- /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 0000000000..271ca1ccd4
--- /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 0000000000..3e2bb3b4ae
--- /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/micropython/viper_ptr16_load_boundary.py b/tests/micropython/viper_ptr16_load_boundary.py
new file mode 100644
index 0000000000..0d4c3105b6
--- /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 0000000000..56f1d32290
--- /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 0000000000..1694c61ac0
--- /dev/null
+++ b/tests/micropython/viper_ptr16_store_boundary.py
@@ -0,0 +1,53 @@
+# Test boundary conditions for various architectures
+
+SET_TEMPLATE = """
+@micropython.viper
+def set{off}(dest: ptr16):
+ dest[{off}] = {val}
+set{off}(buffer)
+print(hex(get_index(buffer, {off})))
+"""
+
+BIT_THRESHOLDS = (5, 8, 11, 12)
+SIZE = 2
+MASK = (1 << (8 * SIZE)) - 1
+
+
+@micropython.viper
+def set_index(dest: ptr16, i: int, val: uint):
+ dest[i] = val
+
+
+def get_index(src, i):
+ return src[i * SIZE] + (src[(i * SIZE) + 1] << 8)
+
+
+buffer = bytearray(((1 << max(BIT_THRESHOLDS) + 1) // 1024) * 1024)
+next = 1
+val = 0
+for bit in BIT_THRESHOLDS:
+ print("---", bit)
+ pre, idx, post = (
+ (((1 << bit) - (2 * SIZE)) // SIZE),
+ (((1 << bit) - (1 * SIZE)) // SIZE),
+ ((1 << bit) // SIZE),
+ )
+ val = (val << 8) + next
+ next += 1
+ set_index(buffer, pre, val & MASK)
+ val = (val << 8) + next
+ next += 1
+ set_index(buffer, idx, val & MASK)
+ val = (val << 8) + next
+ next += 1
+ set_index(buffer, post, val & MASK)
+ val = (val << 8) + next
+ next += 1
+ print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post)))
+ exec(SET_TEMPLATE.format(off=pre, val=val & MASK))
+ val = (val << 8) + next
+ next += 1
+ exec(SET_TEMPLATE.format(off=idx, val=val & MASK))
+ val = (val << 8) + next
+ next += 1
+ exec(SET_TEMPLATE.format(off=post, val=val & MASK))
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 0000000000..1c084da2d9
--- /dev/null
+++ b/tests/micropython/viper_ptr16_store_boundary.py.exp
@@ -0,0 +1,20 @@
+--- 5
+0x1 0x102 0x203
+0x304
+0x405
+0x506
+--- 8
+0x607 0x708 0x809
+0x90a
+0xa0b
+0xb0c
+--- 11
+0xc0d 0xd0e 0xe0f
+0xf10
+0x1011
+0x1112
+--- 12
+0x1213 0x1314 0x1415
+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 0000000000..971d1113c4
--- /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 0000000000..1e22a8b361
--- /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 0000000000..5109abb9dc
--- /dev/null
+++ b/tests/micropython/viper_ptr32_store_boundary.py
@@ -0,0 +1,58 @@
+# Test boundary conditions for various architectures
+
+SET_TEMPLATE = """
+@micropython.viper
+def set{off}(dest: ptr32):
+ dest[{off}] = {val}
+set{off}(buffer)
+print(hex(get_index(buffer, {off})))
+"""
+
+BIT_THRESHOLDS = (5, 8, 11, 12)
+SIZE = 4
+MASK = (1 << (8 * SIZE)) - 1
+
+
+@micropython.viper
+def set_index(dest: ptr32, i: int, val: uint):
+ dest[i] = val
+
+
+def get_index(src, i):
+ return (
+ src[i * SIZE]
+ + (src[(i * SIZE) + 1] << 8)
+ + (src[(i * SIZE) + 2] << 16)
+ + (src[(i * SIZE) + 3] << 24)
+ )
+
+
+buffer = bytearray(((1 << max(BIT_THRESHOLDS) + 1) // 1024) * 1024)
+next = 1
+val = 0
+for bit in BIT_THRESHOLDS:
+ print("---", bit)
+ pre, idx, post = (
+ (((1 << bit) - (2 * SIZE)) // SIZE),
+ (((1 << bit) - (1 * SIZE)) // SIZE),
+ ((1 << bit) // SIZE),
+ )
+ val = (val << 8) + next
+ next += 1
+ set_index(buffer, pre, val & MASK)
+ val = (val << 8) + next
+ next += 1
+ set_index(buffer, idx, val & MASK)
+ val = (val << 8) + next
+ next += 1
+ set_index(buffer, post, val & MASK)
+ val = (val << 8) + next
+ next += 1
+ print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post)))
+ exec(SET_TEMPLATE.format(off=pre, val=val & MASK))
+ val = (val << 8) + next
+ next += 1
+ exec(SET_TEMPLATE.format(off=idx, val=val & MASK))
+ val = (val << 8) + next
+ next += 1
+ exec(SET_TEMPLATE.format(off=post, val=val & MASK))
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 0000000000..67b114d335
--- /dev/null
+++ b/tests/micropython/viper_ptr32_store_boundary.py.exp
@@ -0,0 +1,20 @@
+--- 5
+0x1 0x102 0x10203
+0x1020304
+0x2030405
+0x3040506
+--- 8
+0x4050607 0x5060708 0x6070809
+0x708090a
+0x8090a0b
+0x90a0b0c
+--- 11
+0xa0b0c0d 0xb0c0d0e 0xc0d0e0f
+0xd0e0f10
+0xe0f1011
+0xf101112
+--- 12
+0x10111213 0x11121314 0x12131415
+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 0000000000..57e06da570
--- /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 0000000000..a0e423686b
--- /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 0000000000..e1fe6dcae3
--- /dev/null
+++ b/tests/micropython/viper_ptr8_store_boundary.py
@@ -0,0 +1,49 @@
+# Test boundary conditions for various architectures
+
+SET_TEMPLATE = """
+@micropython.viper
+def set{off}(dest: ptr8):
+ dest[{off}] = {val}
+set{off}(buffer)
+print(hex(get_index(buffer, {off})))
+"""
+
+BIT_THRESHOLDS = (5, 8, 11, 12)
+SIZE = 1
+MASK = (1 << (8 * SIZE)) - 1
+
+
+@micropython.viper
+def set_index(dest: ptr8, i: int, val: uint):
+ dest[i] = val
+
+
+def get_index(src: ptr8, i: int):
+ return src[i]
+
+
+buffer = bytearray(((1 << max(BIT_THRESHOLDS) + 1) // 1024) * 1024)
+next = 1
+val = 0
+for bit in BIT_THRESHOLDS:
+ print("---", bit)
+ pre, idx, post = (((1 << bit) - (2 * SIZE)), ((1 << bit) - (1 * SIZE)), (1 << bit))
+ val = (val << 8) + next
+ next += 1
+ set_index(buffer, pre, val & MASK)
+ val = (val << 8) + next
+ next += 1
+ set_index(buffer, idx, val & MASK)
+ val = (val << 8) + next
+ next += 1
+ set_index(buffer, post, val & MASK)
+ val = (val << 8) + next
+ next += 1
+ print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post)))
+ exec(SET_TEMPLATE.format(off=pre, val=val & MASK))
+ val = (val << 8) + next
+ next += 1
+ exec(SET_TEMPLATE.format(off=idx, val=val & MASK))
+ val = (val << 8) + next
+ next += 1
+ exec(SET_TEMPLATE.format(off=post, val=val & MASK))
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 0000000000..6b0f7ce13e
--- /dev/null
+++ b/tests/micropython/viper_ptr8_store_boundary.py.exp
@@ -0,0 +1,20 @@
+--- 5
+0x1 0x2 0x3
+0x4
+0x5
+0x6
+--- 8
+0x7 0x8 0x9
+0xa
+0xb
+0xc
+--- 11
+0xd 0xe 0xf
+0x10
+0x11
+0x12
+--- 12
+0x13 0x14 0x15
+0x16
+0x17
+0x18
diff --git a/tests/misc/print_exception.py b/tests/misc/print_exception.py
index 1d196d6ab1..92754733b5 100644
--- a/tests/misc/print_exception.py
+++ b/tests/misc/print_exception.py
@@ -1,3 +1,5 @@
+# Test sys.print_exception (MicroPython) / traceback.print_exception (CPython).
+
try:
import io
import sys
diff --git a/tests/misc/sys_settrace_cov.py b/tests/misc/sys_settrace_cov.py
new file mode 100644
index 0000000000..579c8a4a25
--- /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 0000000000..423d78ec42
--- /dev/null
+++ b/tests/misc/sys_settrace_cov.py.exp
@@ -0,0 +1,2 @@
+FRAME <frame at 0x\[0-9a-f\]\+, file '\.\*/sys_settrace_cov.py', line \\d\+, code f>
+LASTI \\d\+
diff --git a/tests/multi_net/tcp_accept_recv.py b/tests/multi_net/tcp_accept_recv.py
index dee14e3b97..4108a6f8a3 100644
--- a/tests/multi_net/tcp_accept_recv.py
+++ b/tests/multi_net/tcp_accept_recv.py
@@ -1,30 +1,73 @@
-# Test recv on socket that just accepted a connection
+# Test recv on listening socket after accept(), with various listen() arguments
import socket
PORT = 8000
+# Test cases for listen() function
+LISTEN_ARGS = [None, 0, 1, 2] # None means no argument
+
# Server
def instance0():
multitest.globals(IP=multitest.get_network_ip())
- s = socket.socket()
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
- s.listen(1)
multitest.next()
- s.accept()
- try:
- print("recv", s.recv(10)) # should raise Errno 107 ENOTCONN
- except OSError as er:
- print(er.errno in (107, 128))
- s.close()
+
+ test_num = 0
+ for blocking_mode in [True, False]:
+ for listen_arg in LISTEN_ARGS:
+ test_num += 1
+ s = socket.socket()
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
+
+ # Call listen with or without argument based on test case
+ if listen_arg is None:
+ print(f"Test case {test_num}/8: listen() blocking={blocking_mode}")
+ s.listen()
+ else:
+ print(f"Test case {test_num}/8: listen({listen_arg}) blocking={blocking_mode}")
+ s.listen(listen_arg)
+
+ # Signal client that server is ready
+ multitest.broadcast(f"server_ready_{test_num}")
+
+ # Wait for client connection
+ c, _ = s.accept()
+
+ # Set blocking mode after accept
+ s.setblocking(blocking_mode)
+
+ try:
+ print("recv", s.recv(10)) # should raise Errno 107 ENOTCONN
+ except OSError as er:
+ # Verify the error code is either 107 (ENOTCONN) or 128 (ENOTCONN on Windows)
+ print(er.errno in (107, 128))
+
+ # Cleanup
+ c.close()
+ s.close()
+
+ # Signal client we're done with this test case
+ multitest.broadcast(f"server_done_{test_num}")
# Client
def instance1():
multitest.next()
- s = socket.socket()
- s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
- s.send(b"GET / HTTP/1.0\r\n\r\n")
- s.close()
+
+ test_num = 0
+ for blocking_mode in [True, False]:
+ for _ in LISTEN_ARGS:
+ test_num += 1
+ # Wait for server to be ready
+ multitest.wait(f"server_ready_{test_num}")
+
+ # Connect to server
+ s = socket.socket()
+ s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
+ s.send(b"GET / HTTP/1.0\r\n\r\n")
+ s.close()
+
+ # Wait for server to finish this test case
+ multitest.wait(f"server_done_{test_num}")
diff --git a/tests/multi_net/tcp_recv_peek.py b/tests/multi_net/tcp_recv_peek.py
new file mode 100644
index 0000000000..ff540dd3c3
--- /dev/null
+++ b/tests/multi_net/tcp_recv_peek.py
@@ -0,0 +1,46 @@
+# Test TCP recv with MSG_PEEK
+#
+# Note that bare metal LWIP only returns at most one packet's worth of TCP data
+# in any recv() call - including when peeking - so can't be too clever with
+# different recv() combinations
+import socket
+import random
+
+
+# Server
+def instance0():
+ PORT = random.randrange(10000, 50000)
+ multitest.globals(IP=multitest.get_network_ip(), PORT=PORT)
+ s = socket.socket()
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
+ s.listen()
+ multitest.next()
+ s2, _ = s.accept()
+ print(s2.recv(8, socket.MSG_PEEK))
+ print(s2.recv(8))
+ s2.send(b"1234567890")
+ multitest.broadcast("0-sent")
+ multitest.wait("1-sent")
+ print(s2.recv(5, socket.MSG_PEEK))
+ print(s2.recv(5, socket.MSG_PEEK))
+ multitest.broadcast("0-recved")
+ multitest.wait("1-recved") # sync here necessary as MP sends RST if closing TCP early
+ s2.close()
+ s.close()
+
+
+# Client
+def instance1():
+ multitest.next()
+ s = socket.socket()
+ s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
+ s.send(b"abcdefgh")
+ multitest.broadcast("1-sent")
+ multitest.wait("0-sent")
+ s.send(b"klmnopqr")
+ print(s.recv(5, socket.MSG_PEEK))
+ print(s.recv(10))
+ multitest.broadcast("1-recved")
+ multitest.wait("0-recved")
+ s.close()
diff --git a/tests/multi_net/udp_data_multi.py b/tests/multi_net/udp_data_multi.py
new file mode 100644
index 0000000000..5d7b13e518
--- /dev/null
+++ b/tests/multi_net/udp_data_multi.py
@@ -0,0 +1,69 @@
+# Test UDP reception when there are multiple incoming UDP packets that need to be
+# queued internally in the TCP/IP stack.
+
+import socket
+
+NUM_NEW_SOCKETS = 4
+NUM_PACKET_BURSTS = 6
+NUM_PACKET_GROUPS = 5
+TOTAL_PACKET_BURSTS = NUM_NEW_SOCKETS * NUM_PACKET_BURSTS
+# The tast passes if more than 75% of packets are received in each group.
+PACKET_RECV_THRESH = 0.75 * TOTAL_PACKET_BURSTS
+PORT = 8000
+
+
+# Server
+def instance0():
+ recv_count = {i: 0 for i in range(NUM_PACKET_GROUPS)}
+ multitest.globals(IP=multitest.get_network_ip())
+ multitest.next()
+ for i in range(NUM_NEW_SOCKETS):
+ print("test socket", i)
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(socket.getaddrinfo("0.0.0.0", PORT + i)[0][-1])
+ s.settimeout(0.250)
+ multitest.broadcast("server ready")
+ for burst in range(NUM_PACKET_BURSTS):
+ # Wait for all packets to be sent, without receiving any yet.
+ multitest.wait("data sent burst={}".format(burst))
+ # Try to receive all packets (they should be waiting in the queue).
+ for group in range(NUM_PACKET_GROUPS):
+ try:
+ data, addr = s.recvfrom(1000)
+ except:
+ continue
+ recv_burst, recv_group = data.split(b":")
+ recv_burst = int(recv_burst)
+ recv_group = int(recv_group)
+ if recv_burst == burst:
+ recv_count[recv_group] += 1
+ # Inform the client that all data was received.
+ multitest.broadcast("data received burst={}".format(burst))
+ s.close()
+
+ # Check how many packets were received.
+ for group, count in recv_count.items():
+ if count >= PACKET_RECV_THRESH:
+ print("pass group={}".format(group))
+ else:
+ print("fail group={} received={}%".format(group, 100 * count // TOTAL_PACKET_BURSTS))
+
+
+# Client
+def instance1():
+ multitest.next()
+ for i in range(NUM_NEW_SOCKETS):
+ print("test socket", i)
+ ai = socket.getaddrinfo(IP, PORT + i)[0][-1]
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ multitest.wait("server ready")
+ for burst in range(NUM_PACKET_BURSTS):
+ # Send a bunch of packets all in a row.
+ for group in range(NUM_PACKET_GROUPS):
+ s.sendto(b"%d:%d" % (burst, group), ai)
+ # Inform the server that the data has been sent.
+ multitest.broadcast("data sent burst={}".format(burst))
+ # Wait for the server to finish receiving.
+ multitest.wait("data received burst={}".format(burst))
+ s.close()
diff --git a/tests/multi_net/udp_data_multi.py.exp b/tests/multi_net/udp_data_multi.py.exp
new file mode 100644
index 0000000000..bc67c6ab0c
--- /dev/null
+++ b/tests/multi_net/udp_data_multi.py.exp
@@ -0,0 +1,15 @@
+--- instance0 ---
+test socket 0
+test socket 1
+test socket 2
+test socket 3
+pass group=0
+pass group=1
+pass group=2
+pass group=3
+pass group=4
+--- instance1 ---
+test socket 0
+test socket 1
+test socket 2
+test socket 3
diff --git a/tests/multi_net/udp_recv_dontwait.py b/tests/multi_net/udp_recv_dontwait.py
new file mode 100644
index 0000000000..640f3f060e
--- /dev/null
+++ b/tests/multi_net/udp_recv_dontwait.py
@@ -0,0 +1,59 @@
+# Test UDP recv and recvfrom with MSG_DONTWAIT
+import random
+import socket
+
+try:
+ import errno, time
+except ImportError:
+ print("SKIP")
+ raise SystemExit
+
+
+# Server
+def instance0():
+ PORT = random.randrange(10000, 50000)
+ multitest.globals(IP=multitest.get_network_ip(), PORT=PORT)
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
+ multitest.next()
+ begin = time.ticks_ms()
+
+ # do some recvs before instance1 starts, when we know no packet is waiting
+ try:
+ print(s.recvfrom(8, socket.MSG_DONTWAIT))
+ except OSError as e:
+ print(e.errno == errno.EAGAIN)
+ try:
+ print(s.recv(8, socket.MSG_DONTWAIT))
+ except OSError as e:
+ print(e.errno == errno.EAGAIN)
+
+ # the above steps should not have taken any substantial time
+ elapsed = time.ticks_diff(time.ticks_ms(), begin)
+ print(True if elapsed < 50 else elapsed)
+
+ # Now instance1 will send us a UDP packet
+ multitest.broadcast("0-ready")
+ multitest.wait("1-sent")
+
+ for _ in range(10): # retry if necessary, to allow for network delay
+ time.sleep_ms(100)
+ try:
+ print(s.recv(8, socket.MSG_DONTWAIT))
+ break
+ except OSError as er:
+ if er.errno != errno.EAGAIN:
+ raise er
+ s.close()
+
+
+# Client
+def instance1():
+ multitest.next()
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
+ multitest.wait("0-ready")
+ print(s.send(b"abcdefgh"))
+ multitest.broadcast("1-sent")
+ s.close()
diff --git a/tests/multi_net/udp_recv_dontwait.py.exp b/tests/multi_net/udp_recv_dontwait.py.exp
new file mode 100644
index 0000000000..f61fd4bbe2
--- /dev/null
+++ b/tests/multi_net/udp_recv_dontwait.py.exp
@@ -0,0 +1,7 @@
+--- instance0 ---
+True
+True
+True
+b'abcdefgh'
+--- instance1 ---
+8
diff --git a/tests/multi_net/udp_recv_peek.py b/tests/multi_net/udp_recv_peek.py
new file mode 100644
index 0000000000..47897ce553
--- /dev/null
+++ b/tests/multi_net/udp_recv_peek.py
@@ -0,0 +1,36 @@
+# Test UDP recv and recvfrom with MSG_PEEK
+import random
+import socket
+import time
+
+
+# Server
+def instance0():
+ PORT = random.randrange(10000, 50000)
+ multitest.globals(IP=multitest.get_network_ip(), PORT=PORT)
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ s.bind(socket.getaddrinfo("0.0.0.0", PORT)[0][-1])
+ multitest.next()
+ peek_bytes, peek_addr = s.recvfrom(8, socket.MSG_PEEK)
+ print(peek_bytes)
+ real_bytes, real_addr = s.recvfrom(8)
+ print(real_bytes)
+ print(peek_addr == real_addr) # source addr should be the same for each
+ res = s.sendto(b"1234567890", peek_addr)
+ print(res)
+ print(s.recv(5, socket.MSG_PEEK))
+ print(s.recv(5, socket.MSG_PEEK))
+ s.close()
+
+
+# Client
+def instance1():
+ multitest.next()
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.connect(socket.getaddrinfo(IP, PORT)[0][-1])
+ s.send(b"abcdefgh")
+ s.send(b"klmnopqr")
+ print(s.recv(5, socket.MSG_PEEK))
+ print(s.recv(10))
+ s.close()
diff --git a/tests/net_inet/mpycert.der b/tests/net_inet/mpycert.der
index 0b0eabc9bc..ac22dcf9e8 100644
--- a/tests/net_inet/mpycert.der
+++ b/tests/net_inet/mpycert.der
Binary files differ
diff --git a/tests/net_inet/ssl_cert.py b/tests/net_inet/ssl_cert.py
index 3597b7855d..83d83e3f8e 100644
--- a/tests/net_inet/ssl_cert.py
+++ b/tests/net_inet/ssl_cert.py
@@ -1,4 +1,3 @@
-import binascii
import socket
import ssl
@@ -6,48 +5,48 @@ import ssl
# This certificate was obtained from micropython.org using openssl:
# $ openssl s_client -showcerts -connect micropython.org:443 </dev/null 2>/dev/null
# The certificate is from Let's Encrypt:
-# 1 s:C=US, O=Let's Encrypt, CN=R11
+# 1 s:C=US, O=Let's Encrypt, CN=R10
# i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
-# a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
+# a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption
# v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT
-# Copy PEM content to a file (certmpy.pem) and convert to DER e.g.
-# $ openssl x509 -in certmpy.pem -out certmpy.der -outform DER
-# Then convert to hex format, eg using binascii.hexlify(data).
+# Copy PEM content to a file (mpycert.pem) and convert to DER e.g.
+# $ openssl x509 -in mpycert.pem -out mpycert.der -outform DER
+# Then convert to hex format using: for i in range(0,len(data),40):print(data[i:i+40].hex())
-ca_cert_chain = binascii.unhexlify(
- b"30820506308202eea0030201020211008a7d3e13d62f30ef2386bd29076b34f8300d06092a864886"
- b"f70d01010b0500304f310b300906035504061302555331293027060355040a1320496e7465726e65"
- b"742053656375726974792052657365617263682047726f7570311530130603550403130c49535247"
- b"20526f6f74205831301e170d3234303331333030303030305a170d3237303331323233353935395a"
- b"3033310b300906035504061302555331163014060355040a130d4c6574277320456e637279707431"
- b"0c300a0603550403130352313130820122300d06092a864886f70d01010105000382010f00308201"
- b"0a0282010100ba87bc5c1b0039cbca0acdd46710f9013ca54ea561cb26ca52fb1501b7b928f5281e"
- b"ed27b324183967090c08ece03ab03b770ebdf3e53954410c4eae41d69974de51dbef7bff58bda8b7"
- b"13f6de31d5f272c9726a0b8374959c4600641499f3b1d922d9cda892aa1c267a3ffeef58057b0895"
- b"81db710f8efbe33109bb09be504d5f8f91763d5a9d9e83f2e9c466b3e106664348188065a037189a"
- b"9b843297b1b2bdc4f815009d2788fbe26317966c9b27674bc4db285e69c279f0495ce02450e1c4bc"
- b"a105ac7b406d00b4c2413fa758b82fc55c9ba5bb099ef1feebb08539fda80aef45c478eb652ac2cf"
- b"5f3cdee35c4d1bf70b272baa0b4277534f796a1d87d90203010001a381f83081f5300e0603551d0f"
- b"0101ff040403020186301d0603551d250416301406082b0601050507030206082b06010505070301"
- b"30120603551d130101ff040830060101ff020100301d0603551d0e04160414c5cf46a4eaf4c3c07a"
- b"6c95c42db05e922f26e3b9301f0603551d2304183016801479b459e67bb6e5e40173800888c81a58"
- b"f6e99b6e303206082b0601050507010104263024302206082b060105050730028616687474703a2f"
- b"2f78312e692e6c656e63722e6f72672f30130603551d20040c300a3008060667810c010201302706"
- b"03551d1f0420301e301ca01aa0188616687474703a2f2f78312e632e6c656e63722e6f72672f300d"
- b"06092a864886f70d01010b050003820201004ee2895d0a031c9038d0f51ff9715cf8c38fb237887a"
- b"6fb0251fedbeb7d886068ee90984cd72bf81f3fccacf5348edbdf66942d4a5113e35c813b2921d05"
- b"5fea2ed4d8f849c3adf599969cef26d8e1b4240b48204dfcd354b4a9c621c8e1361bff77642917b9"
- b"f04bef5deacd79d0bf90bfbe23b290da4aa9483174a9440be1e2f62d8371a4757bd294c10519461c"
- b"b98ff3c47448252a0de5f5db43e2db939bb919b41f2fdf6a0e8f31d3630fbb29dcdd662c3fb01b67"
- b"51f8413ce44db9acb8a49c6663f5ab85231dcc53b6ab71aedcc50171da36ee0a182a32fd09317c8f"
- b"f673e79c9cb54a156a77825acfda8d45fe1f2a6405303e73c2c60cb9d63b634aab4603fe99c04640"
- b"276063df503a0747d8154a9fea471f995a08620cb66c33084dd738ed482d2e0568ae805def4cdcd8"
- b"20415f68f1bb5acde30eb00c31879b43de4943e1c8043fd13c1b87453069a8a9720e79121c31d83e"
- b"2357dda74fa0f01c81d1771f6fd6d2b9a8b3031681394b9f55aed26ae4b3bfeaa5d59f4ba3c9d63b"
- b"72f34af654ab0cfc38f76080df6e35ca75a154e42fbc6e17c91aa537b5a29abaecf4c075464f77a8"
- b"e8595691662d6ede2981d6a697055e6445be2cceea644244b0c34fadf0b4dc03ca999b098295820d"
- b"638a66f91972f8d5b98910e289980935f9a21cbe92732374e99d1fd73b4a9a845810c2f3a7e235ec"
- b"7e3b45ce3046526bc0c0"
+ca_cert_chain = bytes.fromhex(
+ "30820505308202eda00302010202104ba85293f79a2fa273064ba8048d75d0300d06092a864886f7"
+ "0d01010b0500304f310b300906035504061302555331293027060355040a1320496e7465726e6574"
+ "2053656375726974792052657365617263682047726f7570311530130603550403130c4953524720"
+ "526f6f74205831301e170d3234303331333030303030305a170d3237303331323233353935395a30"
+ "33310b300906035504061302555331163014060355040a130d4c6574277320456e6372797074310c"
+ "300a0603550403130352313030820122300d06092a864886f70d01010105000382010f003082010a"
+ "0282010100cf57e5e6c45412edb447fec92758764650288c1d3e88df059dd5b51829bdddb55abffa"
+ "f6cea3beaf00214b625a5a3c012fc55803f689ff8e1143ebc1b5e01407968f6f1fd7e7ba81390975"
+ "65b7c2af185b372628e7a3f4072b6d1affab58bc95ae40ffe9cb57c4b55b7f780d1861bc17e754c6"
+ "bb4991cd6e18d18085eea66536bc74eabc504ceafc21f338169394bab0d36b3806cd16127aca5275"
+ "c8ad76b2c29c5d98455c6f617bc62dee3c13528601d957e6381cdf8db51f92919ae74a1ccc45a872"
+ "55f0b0e6a307ecfda71b669e3f488b71847158c93afaef5ef25b442b3c74e78fb247c1076acd9ab7"
+ "0d96f712812651540aec61f6f7f5e2f28ac8950d8d0203010001a381f83081f5300e0603551d0f01"
+ "01ff040403020186301d0603551d250416301406082b0601050507030206082b0601050507030130"
+ "120603551d130101ff040830060101ff020100301d0603551d0e04160414bbbcc347a5e4bca9c6c3"
+ "a4720c108da235e1c8e8301f0603551d2304183016801479b459e67bb6e5e40173800888c81a58f6"
+ "e99b6e303206082b0601050507010104263024302206082b060105050730028616687474703a2f2f"
+ "78312e692e6c656e63722e6f72672f30130603551d20040c300a3008060667810c01020130270603"
+ "551d1f0420301e301ca01aa0188616687474703a2f2f78312e632e6c656e63722e6f72672f300d06"
+ "092a864886f70d01010b0500038202010092b1e74137eb799d81e6cde225e13a20e9904495a3815c"
+ "cfc35dfdbda070d5b19628220bd2f228cf0ce7d4e6438c24221dc14292d109af9f4bf4c8704f2016"
+ "b15add01f61ff81f616b1427b0728d63aeeee2ce4bcf37ddbba3d4cde7ad50adbdbfe3ec3e623670"
+ "9931a7e88dddea62e212aef59cd43d2c0caad09c79beea3d5c446e9631635a7dd67e4f24a04b057f"
+ "5e6fd2d4ea5f334b13d657b6cade51b85da3098274fdc7789eb3b9ac16da4a2b96c3b68b628ff974"
+ "19a29e03dee96f9bb00fd2a05af6855cc204b7c8d54e32c4bf045dbc29f6f7818f0c5d3c53c94090"
+ "8bfbb60865b9a421d509e51384843782ce1028fc76c206257a46524dda5372a4273f6270acbe6948"
+ "00fb670fdb5ba1e8d703212dd7c9f69942398343df770a1208f125d6ba9419541888a5c58ee11a99"
+ "93796bec1cf93140b0cc3200df9f5ee7b492ab9082918d0de01e95ba593b2e4b5fc2b74635523906"
+ "c0bdaaac52c122a0449799f70ca021a7a16c714716170168c0caa62665047cb3aec9e79455c26f9b"
+ "3c1ca9f92ec5201af076e0beec18d64fd825fb7611e8bfe6210fe8e8ccb5b6a7d5b8f79f41cf6122"
+ "466a83b668972e7cea4e95db23eb2ec82b2884a460e949f4442e3bf9ca625701e25d9016f9c9fc7a"
+ "23488ea6d58172f128fa5dcefbed4e738f942ed241949899dba7af705ff5befb0220bf66276cb4ad"
+ "fa75120b2b3ece039e"
)
diff --git a/tests/net_inet/test_sslcontext_client.py b/tests/net_inet/test_sslcontext_client.py
index 30ec0ac7c8..119a42721f 100644
--- a/tests/net_inet/test_sslcontext_client.py
+++ b/tests/net_inet/test_sslcontext_client.py
@@ -5,14 +5,12 @@ import ssl
# This certificate was obtained from micropython.org using openssl:
# $ openssl s_client -showcerts -connect micropython.org:443 </dev/null 2>/dev/null
# The certificate is from Let's Encrypt:
-# 1 s:C=US, O=Let's Encrypt, CN=R11
+# 1 s:C=US, O=Let's Encrypt, CN=R10
# i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
-# a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
+# a:PKEY: RSA, 2048 (bit); sigalg: sha256WithRSAEncryption
# v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT
-# Copy PEM content to a file (certmpy.pem) and convert to DER e.g.
-# $ openssl x509 -in certmpy.pem -out certmpy.der -outform DER
-# Then convert to hex format, eg using binascii.hexlify(data).
-
+# Copy PEM content to a file (mpycert.pem) and convert to DER e.g.
+# $ openssl x509 -in mpycert.pem -out mpycert.der -outform DER
ca_cert_chain = "mpycert.der"
try:
diff --git a/tests/ports/rp2/rp2_lightsleep_thread.py b/tests/ports/rp2/rp2_lightsleep_thread.py
new file mode 100644
index 0000000000..494ead4223
--- /dev/null
+++ b/tests/ports/rp2/rp2_lightsleep_thread.py
@@ -0,0 +1,67 @@
+# Verify that a thread running on CPU1 can go to lightsleep
+# and wake up in the expected timeframe
+import _thread
+import time
+import unittest
+from machine import lightsleep
+
+N_SLEEPS = 5
+SLEEP_MS = 250
+
+IDEAL_RUNTIME = N_SLEEPS * SLEEP_MS
+MAX_RUNTIME = (N_SLEEPS + 1) * SLEEP_MS
+MAX_DELTA = 20
+
+
+class LightSleepInThread(unittest.TestCase):
+ def thread_entry(self, is_thread=True):
+ for _ in range(N_SLEEPS):
+ lightsleep(SLEEP_MS)
+ if is_thread:
+ self.thread_done = True
+
+ def elapsed_ms(self):
+ return time.ticks_diff(time.ticks_ms(), self.t0)
+
+ def setUp(self):
+ self.thread_done = False
+ self.t0 = time.ticks_ms()
+
+ def test_cpu0_busy(self):
+ _thread.start_new_thread(self.thread_entry, ())
+ # CPU0 is busy-waiting not asleep itself
+ while not self.thread_done:
+ self.assertLessEqual(self.elapsed_ms(), MAX_RUNTIME)
+ self.assertAlmostEqual(self.elapsed_ms(), IDEAL_RUNTIME, delta=MAX_DELTA)
+
+ def test_cpu0_sleeping(self):
+ _thread.start_new_thread(self.thread_entry, ())
+ time.sleep_ms(MAX_RUNTIME)
+ self.assertTrue(self.thread_done)
+ self.assertAlmostEqual(self.elapsed_ms(), MAX_RUNTIME, delta=MAX_DELTA)
+
+ def test_cpu0_also_lightsleep(self):
+ _thread.start_new_thread(self.thread_entry, ())
+ time.sleep_ms(50) # account for any delay in starting the thread
+ self.thread_entry(False) # does the same lightsleep loop, doesn't set the done flag
+ while not self.thread_done:
+ time.sleep_ms(10)
+ #
+ # Only one thread can actually be in lightsleep at a time to avoid
+ # races, but otherwise the behaviour when both threads call lightsleep()
+ # is unspecified.
+ #
+ # Currently, the other thread will return immediately if one is already
+ # in lightsleep. Therefore, runtime can be between IDEAL_RUNTIME and
+ # IDEAL_RUNTIME * 2 depending on how many times the calls to lightsleep() race
+ # each other.
+ #
+ # Note this test case is really only here to ensure that the rp2 hasn't
+ # hung or failed to sleep at all - not to verify any correct behaviour
+ # when there's a race to call lightsleep().
+ self.assertGreaterEqual(self.elapsed_ms(), IDEAL_RUNTIME - MAX_DELTA)
+ self.assertLessEqual(self.elapsed_ms(), IDEAL_RUNTIME * 2 + MAX_DELTA)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/ports/rp2/rp2_machine_idle.py b/tests/ports/rp2/rp2_machine_idle.py
index 3135110b82..f9c2828478 100644
--- a/tests/ports/rp2/rp2_machine_idle.py
+++ b/tests/ports/rp2/rp2_machine_idle.py
@@ -1,4 +1,3 @@
-import sys
import machine
import time
@@ -18,11 +17,6 @@ import time
# Verification uses the average idle time, as individual iterations will always
# have outliers due to interrupts, scheduler, etc.
-# RP2350 currently fails this test because machine.idle() resumes immediately.
-if "RP2350" in sys.implementation._machine:
- print("SKIP")
- raise SystemExit
-
ITERATIONS = 500
total = 0
diff --git a/tests/ports/rp2/rp2_machine_timer.py b/tests/ports/rp2/rp2_machine_timer.py
new file mode 100644
index 0000000000..ac4efcf7f3
--- /dev/null
+++ b/tests/ports/rp2/rp2_machine_timer.py
@@ -0,0 +1,20 @@
+from machine import Timer
+from time import sleep_ms
+
+# Test the rp2-specific adjustable tick_hz and hard/soft IRQ handlers
+# for both one-shot and periodic timers.
+
+modes = {Timer.ONE_SHOT: "one-shot", Timer.PERIODIC: "periodic"}
+kinds = {False: "soft", True: "hard"}
+
+for mode in modes:
+ for hard in kinds:
+ for period in 2, 4:
+ timer = Timer(
+ mode=mode,
+ period=period,
+ hard=hard,
+ callback=lambda t: print("callback", modes[mode], kinds[hard], period),
+ )
+ sleep_ms(9)
+ timer.deinit()
diff --git a/tests/ports/rp2/rp2_machine_timer.py.exp b/tests/ports/rp2/rp2_machine_timer.py.exp
new file mode 100644
index 0000000000..b3dd93dfab
--- /dev/null
+++ b/tests/ports/rp2/rp2_machine_timer.py.exp
@@ -0,0 +1,16 @@
+callback one-shot soft 2
+callback one-shot soft 4
+callback one-shot hard 2
+callback one-shot hard 4
+callback periodic soft 2
+callback periodic soft 2
+callback periodic soft 2
+callback periodic soft 2
+callback periodic soft 4
+callback periodic soft 4
+callback periodic hard 2
+callback periodic hard 2
+callback periodic hard 2
+callback periodic hard 2
+callback periodic hard 4
+callback periodic hard 4
diff --git a/tests/ports/stm32/adc.py b/tests/ports/stm32/adc.py
index 875d31d732..299a5af9c6 100644
--- a/tests/ports/stm32/adc.py
+++ b/tests/ports/stm32/adc.py
@@ -1,5 +1,10 @@
+import sys
from pyb import ADC, Timer
+if "STM32WB" in sys.implementation._machine:
+ print("SKIP")
+ raise SystemExit
+
adct = ADC(16) # Temperature 930 -> 20C
print(str(adct)[:19])
adcv = ADC(17) # Voltage 1500 -> 3.3V
diff --git a/tests/ports/stm32/adcall.py b/tests/ports/stm32/adcall.py
index cfe179a97b..18896c40cb 100644
--- a/tests/ports/stm32/adcall.py
+++ b/tests/ports/stm32/adcall.py
@@ -1,5 +1,13 @@
+import sys
from pyb import Pin, ADCAll
+if "STM32WB" in sys.implementation._machine:
+ pa0_adc_channel = 5
+ skip_temp_test = True # temperature fails on WB55
+else:
+ pa0_adc_channel = 0
+ skip_temp_test = False
+
pins = [Pin.cpu.A0, Pin.cpu.A1, Pin.cpu.A2, Pin.cpu.A3]
# set pins to IN mode, init ADCAll, then check pins are ANALOG
@@ -12,7 +20,7 @@ for p in pins:
# set pins to IN mode, init ADCAll with mask, then check some pins are ANALOG
for p in pins:
p.init(p.IN)
-adc = ADCAll(12, 0x70003)
+adc = ADCAll(12, 0x70000 | 3 << pa0_adc_channel)
for p in pins:
print(p)
@@ -25,7 +33,11 @@ for c in range(19):
print(type(adc.read_channel(c)))
# call special reading functions
-print(0 < adc.read_core_temp() < 100)
+print(skip_temp_test or 0 < adc.read_core_temp() < 100)
print(0 < adc.read_core_vbat() < 4)
print(0 < adc.read_core_vref() < 2)
print(0 < adc.read_vref() < 4)
+
+if sys.implementation._build == "NUCLEO_WB55":
+ # Restore button pin settings.
+ Pin("SW", Pin.IN, Pin.PULL_UP)
diff --git a/tests/ports/stm32/extint.py b/tests/ports/stm32/extint.py
index 5510600020..d3161f7cc7 100644
--- a/tests/ports/stm32/extint.py
+++ b/tests/ports/stm32/extint.py
@@ -1,7 +1,8 @@
import pyb
# test basic functionality
-ext = pyb.ExtInt("X5", pyb.ExtInt.IRQ_RISING, pyb.Pin.PULL_DOWN, lambda l: print("line:", l))
+pin = pyb.Pin.cpu.A4
+ext = pyb.ExtInt(pin, pyb.ExtInt.IRQ_RISING, pyb.Pin.PULL_DOWN, lambda l: print("line:", l))
ext.disable()
ext.enable()
print(ext.line())
diff --git a/tests/ports/stm32/i2c.py b/tests/ports/stm32/i2c.py
index c968843273..7e7fd25040 100644
--- a/tests/ports/stm32/i2c.py
+++ b/tests/ports/stm32/i2c.py
@@ -1,5 +1,8 @@
-import pyb
-from pyb import I2C
+try:
+ from pyb import I2C
+except ImportError:
+ print("SKIP")
+ raise SystemExit
# test we can correctly create by id
for bus in (-1, 0, 1):
diff --git a/tests/ports/stm32/i2c_accel.py b/tests/ports/stm32/i2c_accel.py
index 8b87d406d0..11ff1392ba 100644
--- a/tests/ports/stm32/i2c_accel.py
+++ b/tests/ports/stm32/i2c_accel.py
@@ -1,15 +1,14 @@
# use accelerometer to test i2c bus
-import pyb
-from pyb import I2C
-
-if not hasattr(pyb, "Accel"):
+try:
+ from pyb import Accel, I2C
+except ImportError:
print("SKIP")
raise SystemExit
accel_addr = 76
-pyb.Accel() # this will init the MMA for us
+Accel() # this will init the MMA for us
i2c = I2C(1, I2C.CONTROLLER, baudrate=400000)
diff --git a/tests/ports/stm32/i2c_error.py b/tests/ports/stm32/i2c_error.py
index 1228962f5f..de6e1ca6fe 100644
--- a/tests/ports/stm32/i2c_error.py
+++ b/tests/ports/stm32/i2c_error.py
@@ -1,12 +1,13 @@
# test I2C errors, with polling (disabled irqs) and DMA
import pyb
-from pyb import I2C
if not hasattr(pyb, "Accel"):
print("SKIP")
raise SystemExit
+from pyb import I2C
+
# init accelerometer
pyb.Accel()
diff --git a/tests/ports/stm32/irq.py b/tests/ports/stm32/irq.py
index 04e70a7b79..fd8742d3ea 100644
--- a/tests/ports/stm32/irq.py
+++ b/tests/ports/stm32/irq.py
@@ -1,3 +1,4 @@
+import time
import pyb
@@ -8,7 +9,7 @@ def test_irq():
pyb.enable_irq() # by default should enable IRQ
# check that interrupts are enabled by waiting for ticks
- pyb.delay(10)
+ time.sleep_ms(10)
# check nested disable/enable
i1 = pyb.disable_irq()
@@ -18,7 +19,7 @@ def test_irq():
pyb.enable_irq(i1)
# check that interrupts are enabled by waiting for ticks
- pyb.delay(10)
+ time.sleep_ms(10)
test_irq()
diff --git a/tests/ports/stm32/modstm.py b/tests/ports/stm32/modstm.py
index f1e147c052..1459ee2a9e 100644
--- a/tests/ports/stm32/modstm.py
+++ b/tests/ports/stm32/modstm.py
@@ -1,13 +1,13 @@
# test stm module
import stm
-import pyb
+import time
# test storing a full 32-bit number
# turn on then off the A15(=yellow) LED
BSRR = 0x18
stm.mem32[stm.GPIOA + BSRR] = 0x00008000
-pyb.delay(100)
+time.sleep_ms(100)
print(hex(stm.mem32[stm.GPIOA + stm.GPIO_ODR] & 0x00008000))
stm.mem32[stm.GPIOA + BSRR] = 0x80000000
print(hex(stm.mem32[stm.GPIOA + stm.GPIO_ODR] & 0x00008000))
diff --git a/tests/ports/stm32/pin.py b/tests/ports/stm32/pin.py
index 3d2bef97e3..cbc78e68ab 100644
--- a/tests/ports/stm32/pin.py
+++ b/tests/ports/stm32/pin.py
@@ -1,14 +1,20 @@
+import sys
from pyb import Pin
-p = Pin("X8", Pin.IN)
+if "PYB" in sys.implementation._machine:
+ test_pin = "X8"
+else:
+ test_pin = Pin.cpu.A7
+
+p = Pin(test_pin, Pin.IN)
print(p)
print(p.name())
print(p.pin())
print(p.port())
-p = Pin("X8", Pin.IN, Pin.PULL_UP)
-p = Pin("X8", Pin.IN, pull=Pin.PULL_UP)
-p = Pin("X8", mode=Pin.IN, pull=Pin.PULL_UP)
+p = Pin(test_pin, Pin.IN, Pin.PULL_UP)
+p = Pin(test_pin, Pin.IN, pull=Pin.PULL_UP)
+p = Pin(test_pin, mode=Pin.IN, pull=Pin.PULL_UP)
print(p)
print(p.value())
diff --git a/tests/ports/stm32/pyb1.py b/tests/ports/stm32/pyb1.py
index e9626ecf4e..5627946dbc 100644
--- a/tests/ports/stm32/pyb1.py
+++ b/tests/ports/stm32/pyb1.py
@@ -2,6 +2,10 @@
import pyb
+if not hasattr(pyb, "delay"):
+ print("SKIP")
+ raise SystemExit
+
# test delay
pyb.delay(-1)
diff --git a/tests/ports/stm32/rtc.py b/tests/ports/stm32/rtc.py
index 013b2f3314..03ed93adc2 100644
--- a/tests/ports/stm32/rtc.py
+++ b/tests/ports/stm32/rtc.py
@@ -1,13 +1,15 @@
-import pyb, stm
+import time, stm
from pyb import RTC
+prediv_a = stm.mem32[stm.RTC + stm.RTC_PRER] >> 16
+
rtc = RTC()
rtc.init()
print(rtc)
# make sure that 1 second passes correctly
rtc.datetime((2014, 1, 1, 1, 0, 0, 0, 0))
-pyb.delay(1002)
+time.sleep_ms(1002)
print(rtc.datetime()[:7])
@@ -38,8 +40,12 @@ cal_tmp = rtc.calibration()
def set_and_print_calib(cal):
- rtc.calibration(cal)
- print(rtc.calibration())
+ if cal > 0 and prediv_a < 3:
+ # can't set positive calibration if prediv_a<3, so just make test pass
+ print(cal)
+ else:
+ rtc.calibration(cal)
+ print(rtc.calibration())
set_and_print_calib(512)
diff --git a/tests/ports/stm32/servo.py b/tests/ports/stm32/servo.py
index d15cafe483..0784f64d33 100644
--- a/tests/ports/stm32/servo.py
+++ b/tests/ports/stm32/servo.py
@@ -1,4 +1,8 @@
-from pyb import Servo
+try:
+ from pyb import Servo
+except ImportError:
+ print("SKIP")
+ raise SystemExit
servo = Servo(1)
print(servo)
diff --git a/tests/ports/stm32/timer.py b/tests/ports/stm32/timer.py
index 251a06c081..add8c29937 100644
--- a/tests/ports/stm32/timer.py
+++ b/tests/ports/stm32/timer.py
@@ -1,10 +1,15 @@
# check basic functionality of the timer class
-import pyb
+import sys
from pyb import Timer
-tim = Timer(4)
-tim = Timer(4, prescaler=100, period=200)
+if "STM32WB" in sys.implementation._machine:
+ tim_id = 16
+else:
+ tim_id = 4
+
+tim = Timer(tim_id)
+tim = Timer(tim_id, prescaler=100, period=200)
print(tim.prescaler())
print(tim.period())
tim.prescaler(300)
diff --git a/tests/ports/stm32/timer_callback.py b/tests/ports/stm32/timer_callback.py
index 5f170ccde1..4add88ec6a 100644
--- a/tests/ports/stm32/timer_callback.py
+++ b/tests/ports/stm32/timer_callback.py
@@ -1,8 +1,14 @@
# check callback feature of the timer class
-import pyb
+import sys
+import time
from pyb import Timer
+if "STM32WB" in sys.implementation._machine:
+ tim_extra_id = 16
+else:
+ tim_extra_id = 4
+
# callback function that disables the callback when called
def cb1(t):
@@ -29,27 +35,27 @@ def cb3(x):
# create a timer with a callback, using callback(None) to stop
tim = Timer(1, freq=100, callback=cb1)
-pyb.delay(5)
+time.sleep_ms(5)
print("before cb1")
-pyb.delay(15)
+time.sleep_ms(15)
# create a timer with a callback, using deinit to stop
tim = Timer(2, freq=100, callback=cb2)
-pyb.delay(5)
+time.sleep_ms(5)
print("before cb2")
-pyb.delay(15)
+time.sleep_ms(15)
# create a timer, then set the freq, then set the callback
-tim = Timer(4)
+tim = Timer(tim_extra_id)
tim.init(freq=100)
tim.callback(cb1)
-pyb.delay(5)
+time.sleep_ms(5)
print("before cb1")
-pyb.delay(15)
+time.sleep_ms(15)
# test callback with a closure
tim.init(freq=100)
tim.callback(cb3(3))
-pyb.delay(5)
+time.sleep_ms(5)
print("before cb4")
-pyb.delay(15)
+time.sleep_ms(15)
diff --git a/tests/ports/stm32/uart.py b/tests/ports/stm32/uart.py
index 53b0ea6ade..28eb2261b7 100644
--- a/tests/ports/stm32/uart.py
+++ b/tests/ports/stm32/uart.py
@@ -1,5 +1,11 @@
+import sys
from pyb import UART
+if "STM32WB" in sys.implementation._machine:
+ # UART(1) is usually connected to the REPL on these MCUs.
+ print("SKIP")
+ raise SystemExit
+
# test we can correctly create by id
for bus in (-1, 0, 1, 2, 5, 6):
try:
diff --git a/tests/ports/unix/extra_coverage.py b/tests/ports/unix/extra_coverage.py
index ec68a55508..72f5fe56b3 100644
--- a/tests/ports/unix/extra_coverage.py
+++ b/tests/ports/unix/extra_coverage.py
@@ -6,6 +6,16 @@ except NameError:
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()
diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp
index 5ff947e883..00658ab3ad 100644
--- a/tests/ports/unix/extra_coverage.py.exp
+++ b/tests/ports/unix/extra_coverage.py.exp
@@ -14,6 +14,7 @@ false true
80000000
abc
%
+.a .
# GC
0
0
@@ -50,16 +51,16 @@ RuntimeError:
ame__
port
-builtins micropython _asyncio _thread
-array binascii btree cexample
-cmath collections cppexample cryptolib
-deflate errno example_package
-ffi framebuf gc hashlib
-heapq io json machine
-marshal math os platform
-random re select socket
-struct sys termios time
-tls uctypes vfs websocket
+builtins micropython array binascii
+btree cexample cmath collections
+cppexample cryptolib deflate errno
+example_package ffi framebuf
+gc hashlib heapq io
+json machine marshal math
+os platform random re
+select socket struct sys
+termios time tls uctypes
+vfs websocket
me
micropython machine marshal math
@@ -68,8 +69,8 @@ argv atexit byteorder exc_info
executable exit getsizeof implementation
intern maxsize modules path
platform print_exception ps1
-ps2 stderr stdin stdout
-tracebacklimit version version_info
+ps2 settrace stderr stdin
+stdout tracebacklimit version version_info
ementation
# attrtuple
(start=1, stop=2, step=3)
@@ -89,6 +90,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'
@@ -97,6 +108,8 @@ 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
@@ -122,6 +135,13 @@ unlocked
KeyboardInterrupt:
KeyboardInterrupt:
10
+loop
+scheduled function
+loop
+scheduled function
+loop
+scheduled function
+scheduled function
# ringbuf
99 0
98 1
diff --git a/tests/ports/webassembly/py_proxy_has.mjs b/tests/ports/webassembly/py_proxy_has.mjs
index 8881776fdb..37df0ae179 100644
--- a/tests/ports/webassembly/py_proxy_has.mjs
+++ b/tests/ports/webassembly/py_proxy_has.mjs
@@ -9,3 +9,5 @@ x = []
const x = mp.globals.get("x");
console.log("no_exist" in x);
console.log("sort" in x);
+console.log(Symbol.toStringTag in x);
+console.log(Symbol.iterator in x);
diff --git a/tests/ports/webassembly/py_proxy_has.mjs.exp b/tests/ports/webassembly/py_proxy_has.mjs.exp
index 1d474d5255..7565230c01 100644
--- a/tests/ports/webassembly/py_proxy_has.mjs.exp
+++ b/tests/ports/webassembly/py_proxy_has.mjs.exp
@@ -1,2 +1,4 @@
false
true
+false
+true
diff --git a/tests/run-multitests.py b/tests/run-multitests.py
index 387eec7018..92bd64193d 100755
--- a/tests/run-multitests.py
+++ b/tests/run-multitests.py
@@ -15,6 +15,8 @@ import itertools
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:
@@ -488,9 +490,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 +526,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 +549,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():
@@ -583,6 +575,12 @@ def main():
default=1,
help="repeat the test with this many permutations of the instance order",
)
+ cmd_parser.add_argument(
+ "-r",
+ "--result-dir",
+ default=run_tests_module.base_path("results"),
+ help="directory for test results",
+ )
cmd_parser.epilog = (
"Supported instance types:\r\n"
" -i pyb:<port> physical device (eg. pyboard) on provided repl port.\n"
@@ -623,13 +621,15 @@ def main():
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 b858989daa..f9d2074f6f 100755
--- a/tests/run-natmodtests.py
+++ b/tests/run-natmodtests.py
@@ -9,6 +9,8 @@ import subprocess
import sys
import argparse
+run_tests_module = __import__("run-tests")
+
sys.path.append("../tools")
import pyboard
@@ -73,6 +75,7 @@ class __FS:
return __File()
vfs.mount(__FS(), '/__remote')
sys.path.insert(0, '/__remote')
+{import_prelude}
sys.modules['{}'] = __import__('__injected')
"""
@@ -132,7 +135,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)
@@ -155,7 +166,8 @@ 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 += test_file_data
@@ -187,17 +199,18 @@ 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", ""))
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(
@@ -212,6 +225,19 @@ def main():
cmd_parser.add_argument(
"-a", "--arch", choices=AVAILABLE_ARCHS, help="override native architecture of the target"
)
+ cmd_parser.add_argument(
+ "-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()
@@ -236,20 +262,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 81d873c459..cac2fee58f 100755
--- a/tests/run-perfbench.py
+++ b/tests/run-perfbench.py
@@ -10,10 +10,12 @@ import sys
import argparse
from glob import glob
+run_tests_module = __import__("run-tests")
+
sys.path.append("../tools")
import pyboard
-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":
@@ -90,9 +92,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 +107,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
@@ -125,6 +128,7 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list):
if isinstance(target, pyboard.Pyboard) 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 +166,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 +186,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):
@@ -265,6 +272,12 @@ def main():
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)"
)
cmd_parser.add_argument("M", nargs=1, help="M parameter (approximate target heap in kbytes)")
@@ -307,13 +320,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):
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 9e7cab4689..faf1d2e3b4 100755
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -16,7 +16,7 @@ import threading
import tempfile
# Maximum time to run a PC-based test, in seconds.
-TEST_TIMEOUT = 30
+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.
@@ -95,6 +95,7 @@ class __FS:
return __File()
vfs.mount(__FS(), '/__vfstest')
os.chdir('/__vfstest')
+{import_prologue}
__import__('__injected_test')
"""
@@ -105,14 +106,11 @@ PC_PLATFORMS = ("darwin", "linux", "win32")
# 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
+ "misc/rge_sm.py", # incorrect values due to object representation C
),
"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": (
@@ -162,6 +160,9 @@ platform_tests_to_skip = {
"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",
@@ -269,22 +270,17 @@ def detect_test_platform(pyb, args):
print()
-def prepare_script_for_target(args, *, script_filename=None, script_text=None, force_plain=False):
+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 +296,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,9 +307,21 @@ 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
@@ -325,9 +332,19 @@ def run_script_on_remote_target(pyb, args, test_file, is_special):
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
@@ -337,6 +354,7 @@ special_tests = [
"micropython/meminfo.py",
"basics/bytes_compare3.py",
"basics/builtin_help.py",
+ "misc/sys_settrace_cov.py",
"thread/thread_exc2.py",
"ports/esp32/partition_ota.py",
)
@@ -389,6 +407,10 @@ def run_micropython(pyb, args, test_file, test_file_abspath, is_special=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,7 +493,7 @@ def run_micropython(pyb, args, test_file, test_file_abspath, is_special=False):
output_mupy = output_mupy.replace(b"\r\n", b"\n")
# 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
@@ -600,11 +622,8 @@ class PyboardNodeRunner:
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
@@ -843,6 +862,8 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
) # native doesn't have proper traceback info
skip_tests.add("micropython/schedule.py") # native code doesn't check pending events
skip_tests.add("stress/bytecode_limit.py") # bytecode specific test
+ skip_tests.add("extmod/asyncio_event_queue.py") # native can't run schedule
+ skip_tests.add("extmod/asyncio_iterator_event.py") # native can't run schedule
def run_one_test(test_file):
test_file = test_file.replace("\\", "/")
@@ -859,11 +880,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
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_bytearray = test_name.startswith("bytearray") or test_name.endswith("_bytearray")
@@ -891,7 +908,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
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.
@@ -906,7 +923,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
# 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.
@@ -989,7 +1010,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
# 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:
@@ -1001,9 +1022,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
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:
@@ -1030,17 +1049,49 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
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):
@@ -1048,23 +1099,22 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
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):
@@ -1082,6 +1132,8 @@ class append_filter(argparse.Action):
def main():
+ global injected_import_hook_code
+
cmd_parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Run and manage tests for MicroPython.
@@ -1191,8 +1243,20 @@ the last matching regex is used:
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",
+ )
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]
@@ -1233,7 +1297,7 @@ the last matching regex is used:
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:
@@ -1311,7 +1375,8 @@ the last matching regex is used:
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/tools/autobuild/autobuild.sh b/tools/autobuild/autobuild.sh
index 7854945bbe..7073152df7 100755
--- a/tools/autobuild/autobuild.sh
+++ b/tools/autobuild/autobuild.sh
@@ -69,7 +69,9 @@ fi
FW_TAG="-$FW_DATE-$FW_SEMVER"
# build new firmware
-cd ports/cc3200
+cd ports/alif
+build_alif_boards ${FW_TAG} ${LOCAL_FIRMWARE}
+cd ../cc3200
build_cc3200_boards ${FW_TAG} ${LOCAL_FIRMWARE}
cd ../esp8266
build_esp8266_boards ${FW_TAG} ${LOCAL_FIRMWARE}
diff --git a/tools/autobuild/build-boards.sh b/tools/autobuild/build-boards.sh
index bd6828cb40..165743cab5 100755
--- a/tools/autobuild/build-boards.sh
+++ b/tools/autobuild/build-boards.sh
@@ -86,6 +86,10 @@ function build_boards {
done
}
+function build_alif_boards {
+ build_boards modalif.c $1 $2 zip
+}
+
function build_cc3200_boards {
build_boards hal/cc3200_hal.c $1 $2 zip
}
diff --git a/tools/boardgen.py b/tools/boardgen.py
index 39bedf71cd..3723e7ce31 100644
--- a/tools/boardgen.py
+++ b/tools/boardgen.py
@@ -108,6 +108,10 @@ class Pin:
)
)
+ # 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 @@ class PinGenerator:
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 @@ class PinGenerator:
# #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/ci.sh b/tools/ci.sh
index a4bd43567c..4007dfebfc 100755
--- a/tools/ci.sh
+++ b/tools/ci.sh
@@ -93,23 +93,30 @@ function ci_code_size_build {
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
+ tools/metrics.py build $PORTS_TO_CHECK | tee $OUTFILE
+ return $?
}
+ # Allow errors from tools/metrics.py to propagate out of the pipe above.
+ set -o pipefail
+
# build reference, save to size0
# ignore any errors with this build, in case master is failing
- code_size_build_step $REFERENCE ~/size0 true
+ code_size_build_step $REFERENCE ~/size0
# build PR/branch, save to size1
- code_size_build_step $COMPARISON ~/size1 false
+ code_size_build_step $COMPARISON ~/size1
+ STATUS=$?
+ set +o pipefail
unset -f code_size_build_step
+
+ return $STATUS
}
########################################################################################
@@ -159,7 +166,7 @@ function ci_cc3200_build {
# ports/esp32
# GitHub tag of ESP-IDF to use for CI (note: must be a tag or a branch)
-IDF_VER=v5.2.2
+IDF_VER=v5.4.1
PYTHON=$(command -v python3 2> /dev/null)
PYTHON_VER=$(${PYTHON:-python} --version | cut -d' ' -f2)
@@ -317,13 +324,24 @@ function ci_qemu_setup_rv32 {
qemu-system-riscv32 --version
}
-function ci_qemu_build_arm {
+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
+}
+
+function ci_qemu_build_arm_thumb {
+ ci_qemu_build_arm_prepare
+ make ${MAKEOPTS} -C ports/qemu test_full
# Test building and running native .mpy with armv7m architecture.
ci_native_mpy_modules_build armv7m
@@ -462,13 +480,6 @@ function ci_stm32_misc_build {
########################################################################################
# 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 +505,18 @@ CI_UNIX_OPTS_QEMU_RISCV64=(
MICROPY_STANDALONE=1
)
+CI_UNIX_OPTS_SANITIZE_ADDRESS=(
+ VARIANT=coverage
+ CFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0"
+ LDFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0"
+)
+
+CI_UNIX_OPTS_SANITIZE_UNDEFINED=(
+ VARIANT=coverage
+ CFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute"
+ LDFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute"
+)
+
function ci_unix_build_helper {
make ${MAKEOPTS} -C mpy-cross
make ${MAKEOPTS} -C ports/unix "$@" submodules
@@ -509,13 +532,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 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,38 +560,23 @@ function ci_native_mpy_modules_build {
else
arch=$1
fi
- for natmod in features1 features3 features4 heapq re
+ for natmod in 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
+ # btree requires thread local storage support on rv32imc.
+ if [ $arch != "rv32imc" ]; then
+ make -C examples/natmod/btree ARCH=$arch clean
make -C examples/natmod/btree ARCH=$arch
fi
}
@@ -591,9 +612,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 +625,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 {
@@ -668,7 +689,7 @@ function ci_unix_nanbox_build {
}
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 PYTHON=python2.7
}
function ci_unix_float_build {
@@ -706,24 +727,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 "${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 "${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 "${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 "${CI_UNIX_OPTS_SANITIZE_ADDRESS[@]}"
}
function ci_unix_macos_build {
diff --git a/tools/gen-cpydiff.py b/tools/gen-cpydiff.py
index 2f9394deea..3bb928090b 100644
--- a/tools/gen-cpydiff.py
+++ b/tools/gen-cpydiff.py
@@ -45,6 +45,12 @@ else:
CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3")
MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/build-standard/micropython")
+# Set PYTHONIOENCODING so that CPython will use utf-8 on systems which set another encoding in the locale
+os.environ["PYTHONIOENCODING"] = "utf-8"
+
+# Set PYTHONUNBUFFERED so that CPython will interleave stdout & stderr without buffering
+os.environ["PYTHONUNBUFFERED"] = "a non-empty string"
+
TESTPATH = "../tests/cpydiff"
DOCPATH = "../docs/genrst"
SRCDIR = "../docs/differences"
@@ -111,7 +117,7 @@ def run_tests(tests):
results = []
for test in tests:
test_fullpath = os.path.join(TESTPATH, test.name)
- with open(test_fullpath, "rb") as f:
+ with open(test_fullpath, "r") as f:
input_py = f.read()
process = subprocess.Popen(
@@ -119,20 +125,22 @@ def run_tests(tests):
shell=True,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
- stderr=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ encoding="utf-8",
)
- output_cpy = [com.decode("utf8") for com in process.communicate(input_py)]
+ output_cpy = process.communicate(input_py)[0]
process = subprocess.Popen(
MICROPYTHON,
shell=True,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
- stderr=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ encoding="utf-8",
)
- output_upy = [com.decode("utf8") for com in process.communicate(input_py)]
+ output_upy = process.communicate(input_py)[0]
- if output_cpy[0] == output_upy[0] and output_cpy[1] == output_upy[1]:
+ if output_cpy == output_upy:
print("Error: Test has same output in CPython vs MicroPython: " + test_fullpath)
same_results = True
else:
@@ -211,6 +219,8 @@ def gen_rst(results):
class_ = []
for output in results:
section = output.class_.split(",")
+ if len(section) < 2:
+ raise SystemExit("Each item must have at least 2 categories")
for i in range(len(section)):
section[i] = section[i].rstrip()
if section[i] in CLASSMAP:
@@ -220,8 +230,8 @@ def gen_rst(results):
filename = section[i].replace(" ", "_").lower()
rst = open(os.path.join(DOCPATH, filename + ".rst"), "w")
rst.write(HEADER)
- rst.write(section[i] + "\n")
- rst.write(RSTCHARS[0] * len(section[i]))
+ rst.write(section[0] + "\n")
+ rst.write(RSTCHARS[0] * len(section[0]) + "\n\n")
rst.write(time.strftime("\nGenerated %a %d %b %Y %X UTC\n\n", time.gmtime()))
# If a file docs/differences/<filename>_preamble.txt exists
# then its output is inserted after the top-level heading,
@@ -239,16 +249,16 @@ def gen_rst(results):
class_ = section
rst.write(".. _cpydiff_%s:\n\n" % os.path.splitext(output.name)[0])
rst.write(output.desc + "\n")
- rst.write("~" * len(output.desc) + "\n\n")
+ rst.write(RSTCHARS[min(i + 1, len(RSTCHARS) - 1)] * len(output.desc) + "\n\n")
if output.cause != "Unknown":
rst.write("**Cause:** " + output.cause + "\n\n")
if output.workaround != "Unknown":
rst.write("**Workaround:** " + output.workaround + "\n\n")
rst.write("Sample code::\n\n" + indent(output.code, TAB) + "\n")
- output_cpy = indent("".join(output.output_cpy[0:2]), TAB).rstrip()
+ output_cpy = indent(output.output_cpy, TAB).rstrip()
output_cpy = ("::\n\n" if output_cpy != "" else "") + output_cpy
- output_upy = indent("".join(output.output_upy[0:2]), TAB).rstrip()
+ output_upy = indent(output.output_upy, TAB).rstrip()
output_upy = ("::\n\n" if output_upy != "" else "") + output_upy
table = gen_table([["CPy output:", output_cpy], ["uPy output:", output_upy]])
rst.write(table)
diff --git a/tools/mpremote/mpremote/commands.py b/tools/mpremote/mpremote/commands.py
index 452384728a..428600baf4 100644
--- a/tools/mpremote/mpremote/commands.py
+++ b/tools/mpremote/mpremote/commands.py
@@ -334,6 +334,49 @@ def do_filesystem_recursive_rm(state, path, args):
print(f"removed: '{path}'")
+def human_size(size, decimals=1):
+ for unit in ['B', 'K', 'M', 'G', 'T']:
+ if size < 1024.0 or unit == 'T':
+ break
+ size /= 1024.0
+ return f"{size:.{decimals}f}{unit}" if unit != 'B' else f"{int(size)}"
+
+
+def do_filesystem_tree(state, path, args):
+ """Print a tree of the device's filesystem starting at path."""
+ connectors = ("├── ", "└── ")
+
+ def _tree_recursive(path, prefix=""):
+ entries = state.transport.fs_listdir(path)
+ entries.sort(key=lambda e: e.name)
+ for i, entry in enumerate(entries):
+ connector = connectors[1] if i == len(entries) - 1 else connectors[0]
+ is_dir = entry.st_mode & 0x4000 # Directory
+ size_str = ""
+ # most MicroPython filesystems don't support st_size on directories, reduce clutter
+ if entry.st_size > 0 or not is_dir:
+ if args.size:
+ size_str = f"[{entry.st_size:>9}] "
+ elif args.human:
+ size_str = f"[{human_size(entry.st_size):>6}] "
+ print(f"{prefix}{connector}{size_str}{entry.name}")
+ if is_dir:
+ _tree_recursive(
+ _remote_path_join(path, entry.name),
+ prefix + (" " if i == len(entries) - 1 else "│ "),
+ )
+
+ if not path or path == ".":
+ path = state.transport.exec("import os;print(os.getcwd())").strip().decode("utf-8")
+ if not (path == "." or state.transport.fs_isdir(path)):
+ raise CommandError(f"tree: '{path}' is not a directory")
+ if args.verbose:
+ print(f":{path} on {state.transport.device_name}")
+ else:
+ print(f":{path}")
+ _tree_recursive(path)
+
+
def do_filesystem(state, args):
state.ensure_raw_repl()
state.did_action()
@@ -361,8 +404,8 @@ def do_filesystem(state, args):
# leading ':' if the user included them.
paths = [path[1:] if path.startswith(":") else path for path in paths]
- # ls implicitly lists the cwd.
- if command == "ls" and not paths:
+ # ls and tree implicitly lists the cwd.
+ if command in ("ls", "tree") and not paths:
paths = [""]
try:
@@ -404,6 +447,8 @@ def do_filesystem(state, args):
)
else:
do_filesystem_cp(state, path, cp_dest, len(paths) > 1, not args.force)
+ elif command == "tree":
+ do_filesystem_tree(state, path, args)
except OSError as er:
raise CommandError("{}: {}: {}.".format(command, er.strerror, os.strerror(er.errno)))
except TransportError as er:
diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py
index b30a1a2135..0441857fab 100644
--- a/tools/mpremote/mpremote/main.py
+++ b/tools/mpremote/mpremote/main.py
@@ -181,7 +181,11 @@ def argparse_rtc():
def argparse_filesystem():
- cmd_parser = argparse.ArgumentParser(description="execute filesystem commands on the device")
+ cmd_parser = argparse.ArgumentParser(
+ description="execute filesystem commands on the device",
+ add_help=False,
+ )
+ cmd_parser.add_argument("--help", action="help", help="show this help message and exit")
_bool_flag(cmd_parser, "recursive", "r", False, "recursive (for cp and rm commands)")
_bool_flag(
cmd_parser,
@@ -197,10 +201,26 @@ def argparse_filesystem():
None,
"enable verbose output (defaults to True for all commands except cat)",
)
+ size_group = cmd_parser.add_mutually_exclusive_group()
+ size_group.add_argument(
+ "--size",
+ "-s",
+ default=False,
+ action="store_true",
+ help="show file size in bytes(tree command only)",
+ )
+ size_group.add_argument(
+ "--human",
+ "-h",
+ default=False,
+ action="store_true",
+ help="show file size in a more human readable way (tree command only)",
+ )
+
cmd_parser.add_argument(
"command",
nargs=1,
- help="filesystem command (e.g. cat, cp, sha256sum, ls, rm, rmdir, touch)",
+ help="filesystem command (e.g. cat, cp, sha256sum, ls, rm, rmdir, touch, tree)",
)
cmd_parser.add_argument("path", nargs="+", help="local and remote paths")
return cmd_parser
@@ -355,10 +375,31 @@ _BUILTIN_COMMAND_EXPANSIONS = {
"rmdir": "fs rmdir",
"sha256sum": "fs sha256sum",
"touch": "fs touch",
+ "tree": "fs tree",
# Disk used/free.
"df": [
"exec",
- "import os\nprint('mount \\tsize \\tused \\tavail \\tuse%')\nfor _m in [''] + os.listdir('/'):\n _s = os.stat('/' + _m)\n if not _s[0] & 1 << 14: continue\n _s = os.statvfs(_m)\n if _s[0]:\n _size = _s[0] * _s[2]; _free = _s[0] * _s[3]; print(_m, _size, _size - _free, _free, int(100 * (_size - _free) / _size), sep='\\t')",
+ """
+import os,vfs
+_f = "{:<10}{:>9}{:>9}{:>9}{:>5} {}"
+print(_f.format("filesystem", "size", "used", "avail", "use%", "mounted on"))
+try:
+ _ms = vfs.mount()
+except:
+ _ms = []
+ for _m in [""] + os.listdir("/"):
+ _m = "/" + _m
+ _s = os.stat(_m)
+ if _s[0] & 1 << 14:
+ _ms.append(("<unknown>",_m))
+for _v,_p in _ms:
+ _s = os.statvfs(_p)
+ _sz = _s[0]*_s[2]
+ if _sz:
+ _av = _s[0]*_s[3]
+ _us = 100*(_sz-_av)//_sz
+ print(_f.format(str(_v), _sz, _sz-_av, _av, _us, _p))
+""",
],
# Other shortcuts.
"reset": {
@@ -552,8 +593,13 @@ def main():
command_args = remaining_args
extra_args = []
- # Special case: "fs ls" allowed have no path specified.
- if cmd == "fs" and len(command_args) == 1 and command_args[0] == "ls":
+ # Special case: "fs ls" and "fs tree" can have only options and no path specified.
+ if (
+ cmd == "fs"
+ and len(command_args) >= 1
+ and command_args[0] in ("ls", "tree")
+ and sum(1 for a in command_args if not a.startswith('-')) == 1
+ ):
command_args.append("")
# Use the command-specific argument parser.
@@ -574,7 +620,11 @@ def main():
# If no commands were "actions" then implicitly finish with the REPL
# using default args.
if state.run_repl_on_completion():
- do_repl(state, argparse_repl().parse_args([]))
+ disconnected = do_repl(state, argparse_repl().parse_args([]))
+
+ # Handle disconnection message
+ if disconnected:
+ print("\ndevice disconnected")
return 0
except CommandError as e:
diff --git a/tools/mpremote/mpremote/mp_errno.py b/tools/mpremote/mpremote/mp_errno.py
new file mode 100644
index 0000000000..37cb1e0cb9
--- /dev/null
+++ b/tools/mpremote/mpremote/mp_errno.py
@@ -0,0 +1,53 @@
+import errno
+
+# This table maps numeric values defined by `py/mperrno.h` to host errno code.
+MP_ERRNO_TABLE = {
+ 1: errno.EPERM,
+ 2: errno.ENOENT,
+ 3: errno.ESRCH,
+ 4: errno.EINTR,
+ 5: errno.EIO,
+ 6: errno.ENXIO,
+ 7: errno.E2BIG,
+ 8: errno.ENOEXEC,
+ 9: errno.EBADF,
+ 10: errno.ECHILD,
+ 11: errno.EAGAIN,
+ 12: errno.ENOMEM,
+ 13: errno.EACCES,
+ 14: errno.EFAULT,
+ 15: errno.ENOTBLK,
+ 16: errno.EBUSY,
+ 17: errno.EEXIST,
+ 18: errno.EXDEV,
+ 19: errno.ENODEV,
+ 20: errno.ENOTDIR,
+ 21: errno.EISDIR,
+ 22: errno.EINVAL,
+ 23: errno.ENFILE,
+ 24: errno.EMFILE,
+ 25: errno.ENOTTY,
+ 26: errno.ETXTBSY,
+ 27: errno.EFBIG,
+ 28: errno.ENOSPC,
+ 29: errno.ESPIPE,
+ 30: errno.EROFS,
+ 31: errno.EMLINK,
+ 32: errno.EPIPE,
+ 33: errno.EDOM,
+ 34: errno.ERANGE,
+ 95: errno.EOPNOTSUPP,
+ 97: errno.EAFNOSUPPORT,
+ 98: errno.EADDRINUSE,
+ 103: errno.ECONNABORTED,
+ 104: errno.ECONNRESET,
+ 105: errno.ENOBUFS,
+ 106: errno.EISCONN,
+ 107: errno.ENOTCONN,
+ 110: errno.ETIMEDOUT,
+ 111: errno.ECONNREFUSED,
+ 113: errno.EHOSTUNREACH,
+ 114: errno.EALREADY,
+ 115: errno.EINPROGRESS,
+ 125: errno.ECANCELED,
+}
diff --git a/tools/mpremote/mpremote/repl.py b/tools/mpremote/mpremote/repl.py
index d24a7774ac..4fda04a2e2 100644
--- a/tools/mpremote/mpremote/repl.py
+++ b/tools/mpremote/mpremote/repl.py
@@ -7,51 +7,53 @@ def do_repl_main_loop(
state, console_in, console_out_write, *, escape_non_printable, code_to_inject, file_to_inject
):
while True:
- console_in.waitchar(state.transport.serial)
- c = console_in.readchar()
- if c:
- if c in (b"\x1d", b"\x18"): # ctrl-] or ctrl-x, quit
- break
- elif c == b"\x04": # ctrl-D
- # special handling needed for ctrl-D if filesystem is mounted
- state.transport.write_ctrl_d(console_out_write)
- elif c == b"\x0a" and code_to_inject is not None: # ctrl-j, inject code
- state.transport.serial.write(code_to_inject)
- elif c == b"\x0b" and file_to_inject is not None: # ctrl-k, inject script
- console_out_write(bytes("Injecting %s\r\n" % file_to_inject, "utf8"))
- state.transport.enter_raw_repl(soft_reset=False)
- with open(file_to_inject, "rb") as f:
- pyfile = f.read()
- try:
- state.transport.exec_raw_no_follow(pyfile)
- except TransportError as er:
- console_out_write(b"Error:\r\n")
- console_out_write(er)
- state.transport.exit_raw_repl()
- else:
- state.transport.serial.write(c)
-
try:
+ console_in.waitchar(state.transport.serial)
+ c = console_in.readchar()
+ if c:
+ if c in (b"\x1d", b"\x18"): # ctrl-] or ctrl-x, quit
+ break
+ elif c == b"\x04": # ctrl-D
+ # special handling needed for ctrl-D if filesystem is mounted
+ state.transport.write_ctrl_d(console_out_write)
+ elif c == b"\x0a" and code_to_inject is not None: # ctrl-j, inject code
+ state.transport.serial.write(code_to_inject)
+ elif c == b"\x0b" and file_to_inject is not None: # ctrl-k, inject script
+ console_out_write(bytes("Injecting %s\r\n" % file_to_inject, "utf8"))
+ state.transport.enter_raw_repl(soft_reset=False)
+ with open(file_to_inject, "rb") as f:
+ pyfile = f.read()
+ try:
+ state.transport.exec_raw_no_follow(pyfile)
+ except TransportError as er:
+ console_out_write(b"Error:\r\n")
+ console_out_write(er)
+ state.transport.exit_raw_repl()
+ else:
+ state.transport.serial.write(c)
+
n = state.transport.serial.inWaiting()
- except OSError as er:
- if er.args[0] == 5: # IO error, device disappeared
- print("device disconnected")
- break
+ if n > 0:
+ dev_data_in = state.transport.serial.read(n)
+ if dev_data_in is not None:
+ if escape_non_printable:
+ # Pass data through to the console, with escaping of non-printables.
+ console_data_out = bytearray()
+ for c in dev_data_in:
+ if c in (8, 9, 10, 13, 27) or 32 <= c <= 126:
+ console_data_out.append(c)
+ else:
+ console_data_out.extend(b"[%02x]" % c)
+ else:
+ console_data_out = dev_data_in
+ console_out_write(console_data_out)
- if n > 0:
- dev_data_in = state.transport.serial.read(n)
- if dev_data_in is not None:
- if escape_non_printable:
- # Pass data through to the console, with escaping of non-printables.
- console_data_out = bytearray()
- for c in dev_data_in:
- if c in (8, 9, 10, 13, 27) or 32 <= c <= 126:
- console_data_out.append(c)
- else:
- console_data_out.extend(b"[%02x]" % c)
- else:
- console_data_out = dev_data_in
- console_out_write(console_data_out)
+ except OSError as er:
+ if _is_disconnect_exception(er):
+ return True
+ else:
+ raise
+ return False
def do_repl(state, args):
@@ -86,7 +88,7 @@ def do_repl(state, args):
capture_file.flush()
try:
- do_repl_main_loop(
+ return do_repl_main_loop(
state,
console,
console_out_write,
@@ -98,3 +100,22 @@ def do_repl(state, args):
console.exit()
if capture_file is not None:
capture_file.close()
+
+
+def _is_disconnect_exception(exception):
+ """
+ Check if an exception indicates device disconnect.
+
+ Returns True if the exception indicates the device has disconnected,
+ False otherwise.
+ """
+ if isinstance(exception, OSError):
+ if hasattr(exception, 'args') and len(exception.args) > 0:
+ # IO error, device disappeared
+ if exception.args[0] == 5:
+ return True
+ # Check for common disconnect messages in the exception string
+ exception_str = str(exception)
+ disconnect_indicators = ["Write timeout", "Device disconnected", "ClearCommError failed"]
+ return any(indicator in exception_str for indicator in disconnect_indicators)
+ return False
diff --git a/tools/mpremote/mpremote/transport.py b/tools/mpremote/mpremote/transport.py
index 1b70f9b2ed..d7568b281b 100644
--- a/tools/mpremote/mpremote/transport.py
+++ b/tools/mpremote/mpremote/transport.py
@@ -24,8 +24,9 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-import ast, errno, hashlib, os, sys
+import ast, errno, hashlib, os, re, sys
from collections import namedtuple
+from .mp_errno import MP_ERRNO_TABLE
def stdout_write_bytes(b):
@@ -62,6 +63,16 @@ def _convert_filesystem_error(e, info):
]:
if estr in e.error_output:
return OSError(code, info)
+
+ # Some targets don't render OSError with the name of the errno, so in these
+ # cases support an explicit mapping of errnos to known numeric codes.
+ error_lines = e.error_output.splitlines()
+ match = re.match(r"OSError: (\d+)$", error_lines[-1])
+ if match:
+ value = int(match.group(1), 10)
+ if value in MP_ERRNO_TABLE:
+ return OSError(MP_ERRNO_TABLE[value], info)
+
return e
diff --git a/tools/mpremote/tests/test_filesystem.sh b/tools/mpremote/tests/test_filesystem.sh
index a29015e987..13c5394f71 100755
--- a/tools/mpremote/tests/test_filesystem.sh
+++ b/tools/mpremote/tests/test_filesystem.sh
@@ -237,3 +237,6 @@ echo -----
# try to delete existing folder in mounted filesystem
$MPREMOTE mount "${TMP}" + rm -rv :package || echo "expect error"
echo -----
+# fs without command should raise error
+$MPREMOTE fs 2>/dev/null || echo "expect error: $?"
+echo -----
diff --git a/tools/mpremote/tests/test_filesystem.sh.exp b/tools/mpremote/tests/test_filesystem.sh.exp
index 3d9d0fe9ae..63411580b7 100644
--- a/tools/mpremote/tests/test_filesystem.sh.exp
+++ b/tools/mpremote/tests/test_filesystem.sh.exp
@@ -272,3 +272,5 @@ rm :package
mpremote: rm -r not permitted on /remote directory
expect error
-----
+expect error: 2
+-----
diff --git a/tools/mpremote/tests/test_fs_tree.sh b/tools/mpremote/tests/test_fs_tree.sh
new file mode 100755
index 0000000000..d7fc433ae6
--- /dev/null
+++ b/tools/mpremote/tests/test_fs_tree.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+set -e
+
+# Creates a RAM disk big enough to hold two copies of the test directory
+# structure.
+cat << EOF > "${TMP}/ramdisk.py"
+class RAMBlockDev:
+ def __init__(self, block_size, num_blocks):
+ self.block_size = block_size
+ self.data = bytearray(block_size * num_blocks)
+
+ def readblocks(self, block_num, buf):
+ for i in range(len(buf)):
+ buf[i] = self.data[block_num * self.block_size + i]
+
+ def writeblocks(self, block_num, buf):
+ for i in range(len(buf)):
+ self.data[block_num * self.block_size + i] = buf[i]
+
+ def ioctl(self, op, arg):
+ if op == 4: # get number of blocks
+ return len(self.data) // self.block_size
+ if op == 5: # get block size
+ return self.block_size
+
+import os
+
+bdev = RAMBlockDev(512, 50)
+os.VfsFat.mkfs(bdev)
+os.mount(bdev, '/ramdisk')
+os.chdir('/ramdisk')
+EOF
+
+# setup
+echo -----
+$MPREMOTE run "${TMP}/ramdisk.py"
+$MPREMOTE resume ls
+
+echo -----
+echo "empty tree"
+$MPREMOTE resume tree :
+
+echo -----
+$MPREMOTE resume touch :a.py + touch :b.py
+$MPREMOTE resume mkdir :foo + touch :foo/aa.py + touch :foo/ba.py
+
+echo "small tree - :"
+$MPREMOTE resume tree :
+
+echo -----
+echo "no path"
+$MPREMOTE resume tree
+
+echo -----
+echo "path = '.'"
+$MPREMOTE resume tree .
+
+echo -----
+echo "path = ':.'"
+$MPREMOTE resume tree :.
+
+
+echo -----
+echo "multiple trees"
+$MPREMOTE resume mkdir :bar + touch :bar/aaa.py + touch :bar/bbbb.py
+$MPREMOTE resume mkdir :bar/baz + touch :bar/baz/aaa.py + touch :bar/baz/bbbb.py
+$MPREMOTE resume mkdir :bar/baz/quux + touch :bar/baz/quux/aaa.py + touch :bar/baz/quux/bbbb.py
+$MPREMOTE resume mkdir :bar/baz/quux/xen + touch :bar/baz/quux/xen/aaa.py
+
+$MPREMOTE resume tree
+
+echo -----
+echo single path
+$MPREMOTE resume tree :foo
+
+echo -----
+echo "multiple paths"
+$MPREMOTE resume tree :foo :bar
+
+echo -----
+echo "subtree"
+$MPREMOTE resume tree bar/baz
+
+echo -----
+echo mountpoint
+$MPREMOTE resume tree :/ramdisk
+
+echo -----
+echo non-existent folder : error
+$MPREMOTE resume tree :not_there || echo "expect error: $?"
+
+echo -----
+echo file : error
+$MPREMOTE resume tree :a.py || echo "expect error: $?"
+
+echo -----
+echo "tree -s :"
+mkdir -p "${TMP}/data"
+dd if=/dev/zero of="${TMP}/data/file1.txt" bs=1 count=20 > /dev/null 2>&1
+dd if=/dev/zero of="${TMP}/data/file2.txt" bs=1 count=204 > /dev/null 2>&1
+dd if=/dev/zero of="${TMP}/data/file3.txt" bs=1 count=1096 > /dev/null 2>&1
+dd if=/dev/zero of="${TMP}/data/file4.txt" bs=1 count=2192 > /dev/null 2>&1
+
+$MPREMOTE resume cp -r "${TMP}/data" :
+$MPREMOTE resume tree -s :
+echo -----
+echo "tree -s"
+$MPREMOTE resume tree -s
+echo -----
+$MPREMOTE resume tree --human :
+echo -----
+$MPREMOTE resume tree -s --human : || echo "expect error: $?"
+echo -----
+
diff --git a/tools/mpremote/tests/test_fs_tree.sh.exp b/tools/mpremote/tests/test_fs_tree.sh.exp
new file mode 100644
index 0000000000..9a67883b1c
--- /dev/null
+++ b/tools/mpremote/tests/test_fs_tree.sh.exp
@@ -0,0 +1,225 @@
+-----
+ls :
+-----
+empty tree
+tree :
+:/ramdisk
+-----
+touch :a.py
+touch :b.py
+mkdir :foo
+touch :foo/aa.py
+touch :foo/ba.py
+small tree - :
+tree :
+:/ramdisk
+├── a.py
+├── b.py
+└── foo
+ ├── aa.py
+ └── ba.py
+-----
+no path
+tree :
+:/ramdisk
+├── a.py
+├── b.py
+└── foo
+ ├── aa.py
+ └── ba.py
+-----
+path = '.'
+tree :.
+:/ramdisk
+├── a.py
+├── b.py
+└── foo
+ ├── aa.py
+ └── ba.py
+-----
+path = ':.'
+tree :.
+:/ramdisk
+├── a.py
+├── b.py
+└── foo
+ ├── aa.py
+ └── ba.py
+-----
+multiple trees
+mkdir :bar
+touch :bar/aaa.py
+touch :bar/bbbb.py
+mkdir :bar/baz
+touch :bar/baz/aaa.py
+touch :bar/baz/bbbb.py
+mkdir :bar/baz/quux
+touch :bar/baz/quux/aaa.py
+touch :bar/baz/quux/bbbb.py
+mkdir :bar/baz/quux/xen
+touch :bar/baz/quux/xen/aaa.py
+tree :
+:/ramdisk
+├── a.py
+├── b.py
+├── bar
+│ ├── aaa.py
+│ ├── baz
+│ │ ├── aaa.py
+│ │ ├── bbbb.py
+│ │ └── quux
+│ │ ├── aaa.py
+│ │ ├── bbbb.py
+│ │ └── xen
+│ │ └── aaa.py
+│ └── bbbb.py
+└── foo
+ ├── aa.py
+ └── ba.py
+-----
+single path
+tree :foo
+:foo
+├── aa.py
+└── ba.py
+-----
+multiple paths
+tree :foo
+:foo
+├── aa.py
+└── ba.py
+tree :bar
+:bar
+├── aaa.py
+├── baz
+│ ├── aaa.py
+│ ├── bbbb.py
+│ └── quux
+│ ├── aaa.py
+│ ├── bbbb.py
+│ └── xen
+│ └── aaa.py
+└── bbbb.py
+-----
+subtree
+tree :bar/baz
+:bar/baz
+├── aaa.py
+├── bbbb.py
+└── quux
+ ├── aaa.py
+ ├── bbbb.py
+ └── xen
+ └── aaa.py
+-----
+mountpoint
+tree :/ramdisk
+:/ramdisk
+├── a.py
+├── b.py
+├── bar
+│ ├── aaa.py
+│ ├── baz
+│ │ ├── aaa.py
+│ │ ├── bbbb.py
+│ │ └── quux
+│ │ ├── aaa.py
+│ │ ├── bbbb.py
+│ │ └── xen
+│ │ └── aaa.py
+│ └── bbbb.py
+└── foo
+ ├── aa.py
+ └── ba.py
+-----
+non-existent folder : error
+tree :not_there
+mpremote: tree: 'not_there' is not a directory
+expect error: 1
+-----
+file : error
+tree :a.py
+mpremote: tree: 'a.py' is not a directory
+expect error: 1
+-----
+tree -s :
+cp ${TMP}/data :
+tree :
+:/ramdisk
+├── [ 0] a.py
+├── [ 0] b.py
+├── bar
+│ ├── [ 0] aaa.py
+│ ├── baz
+│ │ ├── [ 0] aaa.py
+│ │ ├── [ 0] bbbb.py
+│ │ └── quux
+│ │ ├── [ 0] aaa.py
+│ │ ├── [ 0] bbbb.py
+│ │ └── xen
+│ │ └── [ 0] aaa.py
+│ └── [ 0] bbbb.py
+├── data
+│ ├── [ 20] file1.txt
+│ ├── [ 204] file2.txt
+│ ├── [ 1096] file3.txt
+│ └── [ 2192] file4.txt
+└── foo
+ ├── [ 0] aa.py
+ └── [ 0] ba.py
+-----
+tree -s
+tree :
+:/ramdisk
+├── [ 0] a.py
+├── [ 0] b.py
+├── bar
+│ ├── [ 0] aaa.py
+│ ├── baz
+│ │ ├── [ 0] aaa.py
+│ │ ├── [ 0] bbbb.py
+│ │ └── quux
+│ │ ├── [ 0] aaa.py
+│ │ ├── [ 0] bbbb.py
+│ │ └── xen
+│ │ └── [ 0] aaa.py
+│ └── [ 0] bbbb.py
+├── data
+│ ├── [ 20] file1.txt
+│ ├── [ 204] file2.txt
+│ ├── [ 1096] file3.txt
+│ └── [ 2192] file4.txt
+└── foo
+ ├── [ 0] aa.py
+ └── [ 0] ba.py
+-----
+tree :
+:/ramdisk
+├── [ 0] a.py
+├── [ 0] b.py
+├── bar
+│ ├── [ 0] aaa.py
+│ ├── baz
+│ │ ├── [ 0] aaa.py
+│ │ ├── [ 0] bbbb.py
+│ │ └── quux
+│ │ ├── [ 0] aaa.py
+│ │ ├── [ 0] bbbb.py
+│ │ └── xen
+│ │ └── [ 0] aaa.py
+│ └── [ 0] bbbb.py
+├── data
+│ ├── [ 20] file1.txt
+│ ├── [ 204] file2.txt
+│ ├── [ 1.1K] file3.txt
+│ └── [ 2.1K] file4.txt
+└── foo
+ ├── [ 0] aa.py
+ └── [ 0] ba.py
+-----
+usage: fs [--help] [--recursive | --no-recursive] [--force | --no-force]
+ [--verbose | --no-verbose] [--size | --human]
+ command path [path ...] ...
+fs: error: argument --human/-h: not allowed with argument --size/-s
+expect error: 2
+-----
diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py
index a47653f900..a600ec12c3 100755
--- a/tools/mpy_ld.py
+++ b/tools/mpy_ld.py
@@ -402,6 +402,7 @@ class LinkEnv:
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 = "<I"
+ struct_type = "<i"
elif env.arch.word_size == 8:
- struct_type = "<Q"
+ struct_type = "<q"
if hasattr(s, "resolved"):
s = s.resolved
sec = s.section
@@ -1207,6 +1216,9 @@ def link_objects(env, native_qstr_vals_len):
sym.section = env.obj_table_section
elif sym.name in env.known_syms:
sym.resolved = env.known_syms[sym.name]
+ elif sym.name in env.externs:
+ # Fixed-address symbols do not need pre-processing.
+ continue
else:
if sym.name in fun_table:
sym.section = mp_fun_table_sec
@@ -1214,6 +1226,15 @@ def link_objects(env, native_qstr_vals_len):
else:
undef_errors.append("{}: undefined symbol: {}".format(sym.filename, sym.name))
+ for sym in env.externs:
+ if sym in env.known_syms:
+ log(
+ LOG_LEVEL_1,
+ "Symbol {} is a fixed-address symbol at {:08x} and is also provided from an object file".format(
+ sym, env.externs[sym]
+ ),
+ )
+
if undef_errors:
raise LinkError("\n".join(undef_errors))
@@ -1456,6 +1477,9 @@ def do_link(args):
log(LOG_LEVEL_2, "qstr vals: " + ", ".join(native_qstr_vals))
env = LinkEnv(args.arch)
try:
+ if args.externs:
+ env.externs = parse_linkerscript(args.externs)
+
# Load object files
for fn in args.files:
with open(fn, "rb") as f:
@@ -1484,6 +1508,50 @@ def do_link(args):
sys.exit(1)
+def parse_linkerscript(source):
+ # This extracts fixed-address symbol lists from linkerscripts, only parsing
+ # a small subset of all possible directives. Right now the only
+ # linkerscript file this is really tested against is the ESP8266's builtin
+ # ROM functions list ($SDK/ld/eagle.rom.addr.v6.ld).
+ #
+ # The parser should be able to handle symbol entries inside ESP-IDF's ROM
+ # symbol lists for the ESP32 range of MCUs as well (see *.ld files in
+ # $SDK/components/esp_rom/<name>/).
+
+ symbols = {}
+
+ LINE_REGEX = re.compile(
+ r'^(?P<weak>PROVIDE\()?' # optional weak marker start
+ r'(?P<symbol>[a-zA-Z_]\w*)' # symbol name
+ r'=0x(?P<address>[\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
@@ -1500,6 +1568,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 d49365f617..40928e8bbb 100755
--- a/tools/pyboard.py
+++ b/tools/pyboard.py
@@ -267,7 +267,15 @@ class ProcessPtyToTerminal:
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
@@ -283,7 +291,12 @@ class Pyboard:
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
@@ -323,14 +336,25 @@ class Pyboard:
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
@@ -341,15 +365,25 @@ class Pyboard:
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())
@@ -361,7 +395,9 @@ class Pyboard:
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")
@@ -371,12 +407,12 @@ class Pyboard:
# 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")
diff --git a/tools/verifygitlog.py b/tools/verifygitlog.py
index 67215d5c5d..dba6ebd6de 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)