summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-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.rst23
-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/reference/mpremote.rst8
-rw-r--r--docs/samd/pinout.rst118
-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/Makefile6
-rw-r--r--extmod/btstack/modbluetooth_btstack.c7
-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.c140
-rw-r--r--extmod/modmachine.c12
-rw-r--r--extmod/modmachine.h2
-rw-r--r--extmod/modtls_axtls.c2
-rw-r--r--extmod/modtls_mbedtls.c2
-rw-r--r--extmod/moductypes.c2
-rw-r--r--extmod/network_cyw43.c3
-rw-r--r--extmod/network_ppp_lwip.c9
-rw-r--r--extmod/nimble/modbluetooth_nimble.c21
-rw-r--r--extmod/nimble/modbluetooth_nimble.h2
-rw-r--r--extmod/vfs_lfsx.c2
m---------lib/libhydrogen0
-rw-r--r--lib/littlefs/lfs2.c80
-rw-r--r--lib/littlefs/lfs2.h8
-rw-r--r--lib/littlefs/lfs2_util.h16
-rw-r--r--mpy-cross/main.c11
-rw-r--r--mpy-cross/mpconfigport.h2
-rw-r--r--ports/alif/alif.mk4
-rw-r--r--ports/alif/main.c2
-rw-r--r--ports/alif/modmachine.c6
-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/pybsleep.c4
-rw-r--r--ports/esp32/README.md8
-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/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/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/sdkconfig.base2
-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_pwm.c974
-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.c4
-rw-r--r--ports/esp32/main/idf_component.yml5
-rw-r--r--ports/esp32/modesp32.c47
-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/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
-rwxr-xr-xports/esp32/tools/metrics_esp32.py2
-rw-r--r--ports/esp8266/esp_init_data.c2
-rw-r--r--ports/esp8266/main.c62
-rw-r--r--ports/esp8266/modmachine.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/modmachine.c6
-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/main.c2
-rw-r--r--ports/renesas-ra/modmachine.c6
-rw-r--r--ports/renesas-ra/mphalport.c2
-rw-r--r--ports/renesas-ra/mphalport.h2
-rw-r--r--ports/renesas-ra/powerctrl.c6
-rw-r--r--ports/renesas-ra/powerctrl.h6
-rw-r--r--ports/renesas-ra/uart.c2
-rw-r--r--ports/rp2/CMakeLists.txt15
-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/machine_pin.c25
-rw-r--r--ports/rp2/memmap_mp_rp2040.ld2
-rw-r--r--ports/rp2/memmap_mp_rp2350.ld2
-rw-r--r--ports/rp2/modmachine.c22
-rw-r--r--ports/rp2/mphalport.h17
-rw-r--r--ports/rp2/mpnetworkport.c39
-rw-r--r--ports/samd/Makefile2
-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.json2
-rw-r--r--ports/samd/modmachine.c6
-rw-r--r--ports/stm32/boardctrl.c2
-rw-r--r--ports/stm32/boardctrl.h2
-rw-r--r--ports/stm32/boards/ARDUINO_GIGA/board.json2
-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/HYDRABUS/board.json2
-rw-r--r--ports/stm32/boards/LIMIFROG/board.json2
-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/SPARKFUN_MICROMOD_STM32/board.json4
-rw-r--r--ports/stm32/boards/stm32g474_af.csv58
-rw-r--r--ports/stm32/machine_adc.c11
-rw-r--r--ports/stm32/mboot/main.c4
-rw-r--r--ports/stm32/mboot/mboot.h2
-rw-r--r--ports/stm32/modmachine.c4
-rw-r--r--ports/stm32/mphalport.c2
-rw-r--r--ports/stm32/mphalport.h2
-rw-r--r--ports/stm32/mpu.h2
-rw-r--r--ports/stm32/powerctrl.c8
-rw-r--r--ports/stm32/powerctrl.h6
-rw-r--r--ports/stm32/qspi.c2
-rw-r--r--ports/unix/coverage.c25
-rw-r--r--ports/unix/main.c2
-rw-r--r--ports/unix/modsocket.c1
-rw-r--r--ports/unix/variants/coverage/mpconfigvariant.h1
-rw-r--r--ports/webassembly/main.c2
-rw-r--r--ports/windows/mpconfigport.h2
-rw-r--r--ports/zephyr/boards/beagleplay_cc1352p7.conf13
-rw-r--r--ports/zephyr/main.c2
-rw-r--r--ports/zephyr/modbluetooth_zephyr.c5
-rw-r--r--py/argcheck.c4
-rw-r--r--py/asmarm.c16
-rw-r--r--py/asmarm.h15
-rw-r--r--py/asmrv32.h26
-rw-r--r--py/asmthumb.c6
-rw-r--r--py/asmthumb.h69
-rw-r--r--py/asmx64.h2
-rw-r--r--py/asmx86.h2
-rw-r--r--py/asmxtensa.c67
-rw-r--r--py/asmxtensa.h28
-rw-r--r--py/bc.c2
-rw-r--r--py/dynruntime.h4
-rw-r--r--py/dynruntime.mk3
-rw-r--r--py/emitinlinextensa.c211
-rw-r--r--py/emitnative.c264
-rw-r--r--py/emitndebug.c4
-rw-r--r--py/misc.h2
-rw-r--r--py/modmath.c2
-rw-r--r--py/mpconfig.h13
-rw-r--r--py/mpprint.c11
-rw-r--r--py/mpprint.h14
-rw-r--r--py/mpz.c4
-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/objint.c5
-rw-r--r--py/objstr.c13
-rw-r--r--py/parsenum.c2
-rw-r--r--py/persistentcode.c14
-rw-r--r--py/runtime.c34
-rw-r--r--py/runtime.h36
-rw-r--r--py/scheduler.c26
-rw-r--r--shared/libc/abort_.c4
-rw-r--r--shared/tinyusb/mp_usbd.c13
-rw-r--r--tests/basics/string_format.py10
-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/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_str_formatsep.py19
-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/vfs_lfs_error.py46
-rw-r--r--tests/extmod/vfs_lfs_error.py.exp44
-rw-r--r--tests/float/math_constants.py27
-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.py25
-rw-r--r--tests/micropython/viper_ptr16_load_boundary.py.exp12
-rw-r--r--tests/micropython/viper_ptr16_store_boundary.py41
-rw-r--r--tests/micropython/viper_ptr16_store_boundary.py.exp18
-rw-r--r--tests/micropython/viper_ptr32_load_boundary.py25
-rw-r--r--tests/micropython/viper_ptr32_load_boundary.py.exp12
-rw-r--r--tests/micropython/viper_ptr32_store_boundary.py35
-rw-r--r--tests/micropython/viper_ptr32_store_boundary.py.exp18
-rw-r--r--tests/micropython/viper_ptr8_load_boundary.py25
-rw-r--r--tests/micropython/viper_ptr8_load_boundary.py.exp12
-rw-r--r--tests/micropython/viper_ptr8_store_boundary.py30
-rw-r--r--tests/micropython/viper_ptr8_store_boundary.py.exp12
-rw-r--r--tests/misc/print_exception.py2
-rw-r--r--tests/multi_net/tcp_recv_peek.py46
-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/ports/rp2/rp2_lightsleep_thread.py23
-rw-r--r--tests/ports/unix/extra_coverage.py.exp7
-rwxr-xr-xtests/run-natmodtests.py15
-rwxr-xr-xtests/run-tests.py128
-rwxr-xr-xtools/ci.sh42
-rw-r--r--tools/gen-cpydiff.py32
-rw-r--r--tools/mpremote/mpremote/commands.py49
-rw-r--r--tools/mpremote/mpremote/main.py34
-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.py82
-rwxr-xr-xtools/pyboard.py66
-rwxr-xr-xtools/verifygitlog.py12
291 files changed, 5601 insertions, 1878 deletions
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..24831c58d6 100644
--- a/docs/library/esp32.rst
+++ b/docs/library/esp32.rst
@@ -80,6 +80,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/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/samd/pinout.rst b/docs/samd/pinout.rst
index 1ad8f55884..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
----------------------------------------
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..5d23eac1e9 100644
--- a/examples/natmod/random/Makefile
+++ b/examples/natmod/random/Makefile
@@ -8,6 +8,10 @@ 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
include $(MPY_DIR)/py/dynruntime.mk
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/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 961803f5e8..65a3412ecb 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))
@@ -673,13 +677,13 @@ 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) {
lwip_incoming_packet_t *slot = &socket->incoming.udp_raw.array[socket->incoming.udp_raw.iget];
if (slot->pbuf == NULL) {
- if (socket->timeout == 0) {
- // Non-blocking socket.
+ // Non-blocking socket or flag
+ if (socket->timeout == 0 || (flags & MSG_DONTWAIT)) {
*_errno = MP_EAGAIN;
return -1;
}
@@ -705,9 +709,11 @@ static mp_uint_t lwip_raw_udp_receive(lwip_socket_obj_t *socket, byte *buf, mp_u
MICROPY_PY_LWIP_ENTER
u16_t result = pbuf_copy_partial(p, buf, ((p->tot_len > len) ? len : p->tot_len), 0);
- pbuf_free(p);
- slot->pbuf = NULL;
- socket->incoming.udp_raw.iget = (socket->incoming.udp_raw.iget + 1) % LWIP_INCOMING_PACKET_QUEUE_LEN;
+ 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
@@ -815,14 +821,14 @@ 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.tcp.pbuf == NULL) {
- // Non-blocking socket
- if (socket->timeout == 0) {
+ // Non-blocking socket or flag
+ if (socket->timeout == 0 || (flags & MSG_DONTWAIT)) {
if (socket->state == STATE_PEER_CLOSED) {
return 0;
}
@@ -867,19 +873,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.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;
+ 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
@@ -1271,40 +1279,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);
@@ -1339,50 +1365,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: {
- ip = socket->tcp_peer_addr;
- port = (mp_uint_t)socket->tcp_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);
@@ -1542,12 +1524,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;
@@ -1919,6 +1901,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/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 54abce79e0..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"));
}
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_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/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_lfsx.c b/extmod/vfs_lfsx.c
index 4b10ca3aa5..404eab84f4 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);
}
}
diff --git a/lib/libhydrogen b/lib/libhydrogen
-Subproject 5c5d513093075f7245ea522101b17c50aa579af
+Subproject bbca575b62510bfdc6dd927a4bfa7df4a51cb84
diff --git a/lib/littlefs/lfs2.c b/lib/littlefs/lfs2.c
index f9ce2cf290..abdec19d7c 100644
--- a/lib/littlefs/lfs2.c
+++ b/lib/littlefs/lfs2.c
@@ -3932,7 +3932,9 @@ static int lfs2_remove_(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) {
@@ -4076,8 +4078,10 @@ static int lfs2_rename_(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) {
@@ -5233,40 +5237,64 @@ static int lfs2_fs_gc_(lfs2_t *lfs2) {
#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
+
static int lfs2_fs_grow_(lfs2_t *lfs2, lfs2_size_t block_count) {
- // shrinking is not supported
- LFS2_ASSERT(block_count >= lfs2->block_count);
+ int err;
- if (block_count > lfs2->block_count) {
- lfs2->block_count = block_count;
+ if (block_count == lfs2->block_count) {
+ return 0;
+ }
- // fetch the root
- lfs2_mdir_t root;
- int err = lfs2_dir_fetch(lfs2, &root, lfs2->root);
+
+#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
- // 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);
+ lfs2->block_count = block_count;
- superblock.block_count = lfs2->block_count;
+ // fetch the root
+ lfs2_mdir_t root;
+ err = lfs2_dir_fetch(lfs2, &root, lfs2->root);
+ if (err) {
+ return err;
+ }
- lfs2_superblock_tole32(&superblock);
- err = lfs2_dir_commit(lfs2, &root, LFS2_MKATTRS(
- {tag, &superblock}));
- 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
diff --git a/lib/littlefs/lfs2.h b/lib/littlefs/lfs2.h
index f503fd00bc..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 0x0002000a
+#define LFS2_VERSION 0x0002000b
#define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16))
#define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >> 0))
@@ -766,7 +766,11 @@ int lfs2_fs_gc(lfs2_t *lfs2);
// 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.h b/lib/littlefs/lfs2_util.h
index 3b191f6885..12c82a630b 100644
--- a/lib/littlefs/lfs2_util.h
+++ b/lib/littlefs/lfs2_util.h
@@ -195,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
}
@@ -218,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
}
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/alif.mk b/ports/alif/alif.mk
index eee27b3b7d..bb07a3aa20 100644
--- a/ports/alif/alif.mk
+++ b/ports/alif/alif.mk
@@ -103,10 +103,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/main.c b/ports/alif/main.c
index fd35604cbc..4783611357 100644
--- a/ports/alif/main.c
+++ b/ports/alif/main.c
@@ -56,7 +56,7 @@
extern uint8_t __StackTop, __StackLimit;
extern uint8_t __GcHeapStart, __GcHeapEnd;
-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 (;;) {
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/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/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..e11c64ad70 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
```
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/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/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/sdkconfig.base b/ports/esp32/boards/sdkconfig.base
index 530db42711..823b916d96 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
@@ -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_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_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..b8f49a33ba 100644
--- a/ports/esp32/main.c
+++ b/ports/esp32/main.c
@@ -216,7 +216,7 @@ void boardctrl_startup(void) {
}
}
-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.
MICROPY_BOARD_STARTUP();
@@ -225,7 +225,7 @@ void app_main(void) {
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..4572e7b68b 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"
@@ -212,6 +211,49 @@ 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) },
@@ -228,6 +270,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/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/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/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..6ac17da457 100644
--- a/ports/esp8266/modmachine.c
+++ b/ports/esp8266/modmachine.c
@@ -71,7 +71,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 +114,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;
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/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/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/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/mphalport.c b/ports/renesas-ra/mphalport.c
index 1c62c23dd7..b2587af06d 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]);
}
diff --git a/ports/renesas-ra/mphalport.h b/ports/renesas-ra/mphalport.h
index 0819abeaf6..1dab67f9a7 100644
--- a/ports/renesas-ra/mphalport.h
+++ b/ports/renesas-ra/mphalport.h
@@ -49,7 +49,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/uart.c b/ports/renesas-ra/uart.c
index c319b4c0a8..4800e0ea03 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};
diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt
index 7cb0c60aa2..cf9f180792 100644
--- a/ports/rp2/CMakeLists.txt
+++ b/ports/rp2/CMakeLists.txt
@@ -525,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
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/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/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 fd2912c80f..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);
@@ -173,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.
@@ -320,9 +330,15 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) {
#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/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/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/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..b7ccac725d
--- /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..b2e6553b2d
--- /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 51d124e751..6e89cebadd 100644
--- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json
+++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json
@@ -13,7 +13,7 @@
"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"
diff --git a/ports/samd/modmachine.c b/ports/samd/modmachine.c
index 82e7b7c062..c20ce8d641 100644
--- a/ports/samd/modmachine.c
+++ b/ports/samd/modmachine.c
@@ -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/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..1a03925ef4 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);
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_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/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/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/SPARKFUN_MICROMOD_STM32/board.json b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json
index 01ed363cf3..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": "",
+ "url": "https://www.sparkfun.com/products/17713",
"vendor": "SparkFun"
}
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/machine_adc.c b/ports/stm32/machine_adc.c
index 8f07075914..c3211ea4f4 100644
--- a/ports/stm32/machine_adc.c
+++ b/ports/stm32/machine_adc.c
@@ -445,10 +445,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/mboot/main.c b/ports/stm32/mboot/main.c
index 01f8892a51..ff44dac630 100644
--- a/ports/stm32/mboot/main.c
+++ b/ports/stm32/mboot/main.c
@@ -176,7 +176,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 (;;) {
}
@@ -1443,7 +1443,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
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/modmachine.c b/ports/stm32/modmachine.c
index 620ae468cb..f3bdf1bc50 100644
--- a/ports/stm32/modmachine.c
+++ b/ports/stm32/modmachine.c
@@ -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
diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c
index dfd50cebd3..b4b2267fa0 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]);
}
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..5756cb0560 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)
diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c
index eea009e2d7..e3e2fcdd44 100644
--- a/ports/stm32/powerctrl.c
+++ b/ports/stm32/powerctrl.c
@@ -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,7 @@ 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 MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET
// Enter the bootloader via a reset, so everything is reset (including WDT).
@@ -1016,7 +1016,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/stm32/powerctrl.h b/ports/stm32/powerctrl.h
index 5b92405611..05a70e52c6 100644
--- a/ports/stm32/powerctrl.h
+++ b/ports/stm32/powerctrl.h
@@ -39,14 +39,14 @@ static inline void stm32_system_init(void) {
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/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/unix/coverage.c b/ports/unix/coverage.c
index 2b65b47fc5..29e1457cb7 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)
@@ -621,6 +633,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
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/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h
index 04b5b8ae1a..cfefeb4672 100644
--- a/ports/unix/variants/coverage/mpconfigvariant.h
+++ b/ports/unix/variants/coverage/mpconfigvariant.h
@@ -44,6 +44,7 @@
#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/windows/mpconfigport.h b/ports/windows/mpconfigport.h
index fabc9072d6..6e32d5fe5e 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
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/main.c b/ports/zephyr/main.c
index d4498c1079..45af7b0c72 100644
--- a/ports/zephyr/main.c
+++ b/ports/zephyr/main.c
@@ -219,7 +219,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 cdbeb7fc35..c1b8ddfd8b 100644
--- a/ports/zephyr/modbluetooth_zephyr.c
+++ b/ports/zephyr/modbluetooth_zephyr.c
@@ -305,11 +305,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_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();
@@ -332,6 +332,7 @@ void mp_bluetooth_deinit(void) {
MP_STATE_PORT(bluetooth_zephyr_root_pointers) = NULL;
mp_bt_zephyr_next_conn = NULL;
+ return 0;
}
bool mp_bluetooth_is_active(void) {
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..d304567882 100644
--- a/py/asmarm.c
+++ b/py/asmarm.c
@@ -343,6 +343,12 @@ void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint 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 | (ASM_ARM_REG_R8 << 12) | rn); // mov r8, rn, lsl #1
+ emit_al(as, 0x19000b0 | (rm << 16) | (rd << 12) | ASM_ARM_REG_R8); // ldrh rd, [rm, r8];
+}
+
void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) {
if (byte_offset < 0x100) {
// ldrh rd, [rn, #off]
@@ -360,6 +366,16 @@ void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint 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_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_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);
diff --git a/py/asmarm.h b/py/asmarm.h
index 4a4253aef6..42fa1ea212 100644
--- a/py/asmarm.h
+++ b/py/asmarm.h
@@ -116,6 +116,12 @@ 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);
+
+// 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);
@@ -202,19 +208,24 @@ 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_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
#endif // MICROPY_INCLUDED_PY_ASMARM_H
diff --git a/py/asmrv32.h b/py/asmrv32.h
index b09f48eb12..6453b0a3d4 100644
--- a/py/asmrv32.h
+++ b/py/asmrv32.h
@@ -737,7 +737,6 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_
#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_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)
@@ -754,10 +753,33 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_
#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_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_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_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..06021f2bc9 100644
--- a/py/asmthumb.c
+++ b/py/asmthumb.c
@@ -450,12 +450,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)));
}
diff --git a/py/asmthumb.h b/py/asmthumb.h
index a9e68d7adb..cc4213503b 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
@@ -418,19 +462,40 @@ 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_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
#endif // MICROPY_INCLUDED_PY_ASMTHUMB_H
diff --git a/py/asmx64.h b/py/asmx64.h
index c63e31797e..30c6efd6d0 100644
--- a/py/asmx64.h
+++ b/py/asmx64.h
@@ -205,14 +205,12 @@ 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)
diff --git a/py/asmx86.h b/py/asmx86.h
index 027d44151e..af73c163b4 100644
--- a/py/asmx86.h
+++ b/py/asmx86.h
@@ -200,14 +200,12 @@ 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)
diff --git a/py/asmxtensa.c b/py/asmxtensa.c
index 0fbe351dcf..ab2e7aeaeb 100644
--- a/py/asmxtensa.c
+++ b/py/asmxtensa.c
@@ -37,6 +37,10 @@
#define WORD_SIZE (4)
#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 +51,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 +66,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;
@@ -150,7 +156,7 @@ 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");
+ mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_bccz);
}
asm_xtensa_op_bccz(as, cond, reg, rel);
}
@@ -159,7 +165,7 @@ void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint r
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");
+ mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_bcc);
}
asm_xtensa_op_bcc(as, cond, reg1, reg2, rel);
}
@@ -179,6 +185,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;
@@ -264,4 +272,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..d0fd2dcc18 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)
@@ -295,6 +297,10 @@ void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, u
void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset);
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
@@ -411,12 +417,32 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx);
#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_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_xtensa_op_l32i_n((as), (reg_dest), (reg_base), 0)
+#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_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_xtensa_op_s32i_n((as), (reg_src), (reg_base), 0)
+#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/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..84c78d6225 100644
--- a/py/dynruntime.mk
+++ b/py/dynruntime.mk
@@ -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/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..4b470f3c93 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -1537,25 +1537,24 @@ 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]
+ #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
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
@@ -1566,24 +1565,24 @@ static void emit_native_load_subscr(emit_t *emit) {
}
case VTYPE_PTR16: {
// pointer to 16-bit memory
+ #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
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
@@ -1594,24 +1593,24 @@ static void emit_native_load_subscr(emit_t *emit) {
}
case VTYPE_PTR32: {
// pointer to 32-bit memory
+ #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
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
@@ -1638,40 +1637,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:
@@ -1815,28 +1810,28 @@ static void emit_native_store_subscr(emit_t *emit) {
case VTYPE_PTR8: {
// pointer to 8-bit memory
// TODO optimise to use thumb strb r1, [r2, r3]
+ #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_value, reg_base, index_value);
+ break;
+ }
+ #endif
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;
+ break;
#endif
ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add index to base
reg_base = reg_index;
@@ -1846,24 +1841,24 @@ static void emit_native_store_subscr(emit_t *emit) {
}
case VTYPE_PTR16: {
// pointer to 16-bit memory
+ #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_value, reg_base, index_value);
+ break;
+ }
+ #endif
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;
@@ -1873,27 +1868,28 @@ static void emit_native_store_subscr(emit_t *emit) {
}
case VTYPE_PTR32: {
// pointer to 32-bit memory
+ #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_value, reg_base, index_value);
+ break;
+ }
+ #endif
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
+ #if 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;
+ break;
#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
@@ -1930,50 +1926,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..c068a9a9a1 100644
--- a/py/emitndebug.c
+++ b/py/emitndebug.c
@@ -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/misc.h b/py/misc.h
index e05fbe61a9..49f2f87111 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
diff --git a/py/modmath.c b/py/modmath.c
index 4d51a28d0b..b792d8581d 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"));
}
diff --git a/py/mpconfig.h b/py/mpconfig.h
index 9001b8983b..01712bd5b4 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)
@@ -2115,8 +2120,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..00a5f944c6 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;
diff --git a/py/mpprint.h b/py/mpprint.h
index 8383ea8579..511af329ba 100644
--- a/py/mpprint.h
+++ b/py/mpprint.h
@@ -33,11 +33,11 @@
#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_PAD_AFTER_SIGN (0x020)
+#define PF_FLAG_CENTER_ADJUST (0x040)
+#define PF_FLAG_ADD_PERCENT (0x080)
+#define PF_FLAG_SHOW_OCTAL_LETTER (0x100)
+#define PF_FLAG_SEP_POS (9) // must be above all the above PF_FLAGs
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
#define MP_PYTHON_PRINTER &mp_sys_stdout_print
@@ -69,9 +69,9 @@ extern const mp_print_t mp_sys_stdout_print;
#endif
int mp_print_str(const mp_print_t *print, const char *str);
-int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width);
+int mp_print_strn(const mp_print_t *print, const char *str, size_t len, unsigned int flags, char fill, int width);
#if MICROPY_PY_BUILTINS_FLOAT
-int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec);
+int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, unsigned int flags, char fill, int width, int prec);
#endif
int mp_printf(const mp_print_t *print, const char *fmt, ...);
diff --git a/py/mpz.c b/py/mpz.c
index 084aebda9e..471bd15981 100644
--- a/py/mpz.c
+++ b/py/mpz.c
@@ -1672,6 +1672,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 +1719,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/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/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..a38ce563f9 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) {
diff --git a/py/persistentcode.c b/py/persistentcode.c
index 2a42b904bc..43207a0cc8 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);
}
diff --git a/py/runtime.c b/py/runtime.c
index 58819819ad..7979e520da 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
@@ -1649,7 +1649,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 +1662,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 +1688,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 +1696,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 +1722,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 +1732,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 +1746,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 ffbc3972a3..a93488e2cd 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);
@@ -246,10 +246,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()
@@ -257,19 +257,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/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/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/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/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/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_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/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/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/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/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..ccaaa0909a
--- /dev/null
+++ b/tests/micropython/viper_ptr16_load_boundary.py
@@ -0,0 +1,25 @@
+# Test boundary conditions for various architectures
+
+GET_TEMPLATE = """
+@micropython.viper
+def get{off}(src: ptr16) -> int:
+ return src[{off}]
+print(b[{off} * 2:({off} + 1) * 2])
+"""
+
+
+@micropython.viper
+def get_index(src: ptr16, i: int) -> int:
+ return src[i]
+
+
+b = bytearray(5000)
+b[28:38] = b"0123456789"
+b[252:262] = b"ABCDEFGHIJ"
+b[4092:4102] = b"KLMNOPQRST"
+
+for pre, idx, post in (15, 16, 17), (127, 128, 129), (2047, 2048, 2049):
+ print(get_index(b, pre), get_index(b, idx), get_index(b, 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..4b8c184c13
--- /dev/null
+++ b/tests/micropython/viper_ptr16_load_boundary.py.exp
@@ -0,0 +1,12 @@
+13106 13620 14134
+bytearray(b'23')
+bytearray(b'45')
+bytearray(b'67')
+17475 17989 18503
+bytearray(b'CD')
+bytearray(b'EF')
+bytearray(b'GH')
+20045 20559 21073
+bytearray(b'MN')
+bytearray(b'OP')
+bytearray(b'QR')
diff --git a/tests/micropython/viper_ptr16_store_boundary.py b/tests/micropython/viper_ptr16_store_boundary.py
new file mode 100644
index 0000000000..e0a4f84557
--- /dev/null
+++ b/tests/micropython/viper_ptr16_store_boundary.py
@@ -0,0 +1,41 @@
+# Test boundary conditions for various architectures
+
+SET_TEMPLATE = """
+@micropython.viper
+def set{off}(dest: ptr16):
+ dest[{off}] = {val}
+set{off}(b)
+print(b[{off} * 2:({off} + 1) * 2])
+"""
+
+TEST_DATA = (
+ (15, (0x4241, 0x4443, 0x4645)),
+ (127, (0x4847, 0x4A49, 0x4C4B)),
+ (2047, (0x4E4D, 0x504F, 0x5251)),
+)
+
+
+@micropython.viper
+def set_index(dest: ptr16, i: int, val: int):
+ dest[i] = val
+
+
+@micropython.viper
+def set_index(dest: ptr16, i: int, val: int):
+ dest[i] = val
+
+
+b = bytearray(5000)
+for start, vals in TEST_DATA:
+ for i, v in enumerate(vals):
+ set_index(b, start + i, v)
+ print(b[(start + i) * 2 : (start + i + 1) * 2])
+
+
+for i in range(len(b)):
+ b[i] = 0
+
+
+for start, vals in TEST_DATA:
+ for i, v in enumerate(vals):
+ exec(SET_TEMPLATE.format(off=start + i, val=v + 0x0101))
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..b56fe6695f
--- /dev/null
+++ b/tests/micropython/viper_ptr16_store_boundary.py.exp
@@ -0,0 +1,18 @@
+bytearray(b'AB')
+bytearray(b'CD')
+bytearray(b'EF')
+bytearray(b'GH')
+bytearray(b'IJ')
+bytearray(b'KL')
+bytearray(b'MN')
+bytearray(b'OP')
+bytearray(b'QR')
+bytearray(b'BC')
+bytearray(b'DE')
+bytearray(b'FG')
+bytearray(b'HI')
+bytearray(b'JK')
+bytearray(b'LM')
+bytearray(b'NO')
+bytearray(b'PQ')
+bytearray(b'RS')
diff --git a/tests/micropython/viper_ptr32_load_boundary.py b/tests/micropython/viper_ptr32_load_boundary.py
new file mode 100644
index 0000000000..6954bd46b2
--- /dev/null
+++ b/tests/micropython/viper_ptr32_load_boundary.py
@@ -0,0 +1,25 @@
+# Test boundary conditions for various architectures
+
+GET_TEMPLATE = """
+@micropython.viper
+def get{off}(src: ptr32) -> int:
+ return src[{off}]
+print(b[{off} * 4:({off} + 1) * 4])
+"""
+
+
+@micropython.viper
+def get_index(src: ptr32, i: int) -> int:
+ return src[i]
+
+
+b = bytearray(5000)
+b[24:43] = b"0123456789ABCDEFGHIJ"
+b[248:268] = b"KLMNOPQRSTUVWXYZabcd"
+b[4088:4108] = b"efghijklmnopqrstuvwx"
+
+for pre, idx, post in (7, 8, 9), (63, 64, 65), (1023, 1024, 1025):
+ print(get_index(b, pre), get_index(b, idx), get_index(b, 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..a58e703f91
--- /dev/null
+++ b/tests/micropython/viper_ptr32_load_boundary.py.exp
@@ -0,0 +1,12 @@
+926299444 1111570744 1178944579
+bytearray(b'4567')
+bytearray(b'89AB')
+bytearray(b'CDEF')
+1381060687 1448432723 1515804759
+bytearray(b'OPQR')
+bytearray(b'STUV')
+bytearray(b'WXYZ')
+1818978921 1886350957 1953722993
+bytearray(b'ijkl')
+bytearray(b'mnop')
+bytearray(b'qrst')
diff --git a/tests/micropython/viper_ptr32_store_boundary.py b/tests/micropython/viper_ptr32_store_boundary.py
new file mode 100644
index 0000000000..243ff5cd9c
--- /dev/null
+++ b/tests/micropython/viper_ptr32_store_boundary.py
@@ -0,0 +1,35 @@
+# Test boundary conditions for various architectures
+
+TEST_DATA = (
+ (3, (0x04030201, 0x08070605, 0x0C0B0A09)),
+ (63, (0x100F0E0D, 0x14131211, 0x18171615)),
+ (1023, (0x1C1B1A19, 0x201F1E1D, 0x24232221)),
+)
+
+SET_TEMPLATE = """
+@micropython.viper
+def set{off}(dest: ptr32):
+ dest[{off}] = {val} & 0x3FFFFFFF
+set{off}(b)
+print(b[{off} * 4:({off} + 1) * 4])
+"""
+
+
+@micropython.viper
+def set_index(dest: ptr32, i: int, val: int):
+ dest[i] = val
+
+
+b = bytearray(5000)
+for start, vals in TEST_DATA:
+ for i, v in enumerate(vals):
+ set_index(b, start + i, v)
+ print(b[(start + i) * 4 : (start + i + 1) * 4])
+
+for i in range(len(b)):
+ b[i] = 0
+
+
+for start, vals in TEST_DATA:
+ for i, v in enumerate(vals):
+ exec(SET_TEMPLATE.format(off=start + i, val=v + 0x01010101))
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..89f09fbc7a
--- /dev/null
+++ b/tests/micropython/viper_ptr32_store_boundary.py.exp
@@ -0,0 +1,18 @@
+bytearray(b'\x01\x02\x03\x04')
+bytearray(b'\x05\x06\x07\x08')
+bytearray(b'\t\n\x0b\x0c')
+bytearray(b'\r\x0e\x0f\x10')
+bytearray(b'\x11\x12\x13\x14')
+bytearray(b'\x15\x16\x17\x18')
+bytearray(b'\x19\x1a\x1b\x1c')
+bytearray(b'\x1d\x1e\x1f ')
+bytearray(b'!"#$')
+bytearray(b'\x02\x03\x04\x05')
+bytearray(b'\x06\x07\x08\t')
+bytearray(b'\n\x0b\x0c\r')
+bytearray(b'\x0e\x0f\x10\x11')
+bytearray(b'\x12\x13\x14\x15')
+bytearray(b'\x16\x17\x18\x19')
+bytearray(b'\x1a\x1b\x1c\x1d')
+bytearray(b'\x1e\x1f !')
+bytearray(b'"#$%')
diff --git a/tests/micropython/viper_ptr8_load_boundary.py b/tests/micropython/viper_ptr8_load_boundary.py
new file mode 100644
index 0000000000..bcb17a1e1f
--- /dev/null
+++ b/tests/micropython/viper_ptr8_load_boundary.py
@@ -0,0 +1,25 @@
+# Test boundary conditions for various architectures
+
+GET_TEMPLATE = """
+@micropython.viper
+def get{off}(src: ptr8) -> int:
+ return src[{off}]
+print(get{off}(b))
+"""
+
+
+@micropython.viper
+def get_index(src: ptr8, i: int) -> int:
+ return src[i]
+
+
+b = bytearray(5000)
+b[30:32] = b"123"
+b[254:256] = b"456"
+b[4094:4096] = b"789"
+
+for pre, idx, post in (30, 31, 32), (254, 255, 256), (4094, 4095, 4096):
+ print(get_index(b, pre), get_index(b, idx), get_index(b, 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..7cbd1ac78c
--- /dev/null
+++ b/tests/micropython/viper_ptr8_load_boundary.py.exp
@@ -0,0 +1,12 @@
+49 50 51
+49
+50
+51
+52 53 54
+52
+53
+54
+55 56 57
+55
+56
+57
diff --git a/tests/micropython/viper_ptr8_store_boundary.py b/tests/micropython/viper_ptr8_store_boundary.py
new file mode 100644
index 0000000000..ad51268454
--- /dev/null
+++ b/tests/micropython/viper_ptr8_store_boundary.py
@@ -0,0 +1,30 @@
+# Test boundary conditions for various architectures
+
+TEST_DATA = ((49, 30, 3), (52, 254, 3), (55, 4094, 3))
+
+SET_TEMPLATE = """
+@micropython.viper
+def set{off}(dest: ptr8):
+ dest[{off}] = {val}
+set{off}(b)
+print(b[{off}])
+"""
+
+
+@micropython.viper
+def set_index(dest: ptr8, i: int, val: int):
+ dest[i] = val
+
+
+b = bytearray(5000)
+for val, start, count in TEST_DATA:
+ for i in range(count):
+ set_index(b, start + i, val + i)
+ print(b[start : start + count])
+
+for i in range(len(b)):
+ b[i] = 0
+
+for val, start, count in TEST_DATA:
+ for i in range(count):
+ exec(SET_TEMPLATE.format(off=start + i, val=val + i + 16))
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..a35cb3ac9e
--- /dev/null
+++ b/tests/micropython/viper_ptr8_store_boundary.py.exp
@@ -0,0 +1,12 @@
+bytearray(b'123')
+bytearray(b'456')
+bytearray(b'789')
+65
+66
+67
+68
+69
+70
+71
+72
+73
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/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_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/ports/rp2/rp2_lightsleep_thread.py b/tests/ports/rp2/rp2_lightsleep_thread.py
index 2aaa34bbe3..494ead4223 100644
--- a/tests/ports/rp2/rp2_lightsleep_thread.py
+++ b/tests/ports/rp2/rp2_lightsleep_thread.py
@@ -42,12 +42,25 @@ class LightSleepInThread(unittest.TestCase):
def test_cpu0_also_lightsleep(self):
_thread.start_new_thread(self.thread_entry, ())
- time.sleep(0.050) # account for any delay in starting the thread
+ 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
- self.assertTrue(self.thread_done)
- # only one thread can actually be in lightsleep at a time to avoid races, so the total
- # runtime is doubled by doing it on both CPUs
- self.assertAlmostEqual(self.elapsed_ms(), IDEAL_RUNTIME * 2, delta=IDEAL_RUNTIME)
+ 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__":
diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp
index 5ff947e883..19b32f1c9d 100644
--- a/tests/ports/unix/extra_coverage.py.exp
+++ b/tests/ports/unix/extra_coverage.py.exp
@@ -122,6 +122,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/run-natmodtests.py b/tests/run-natmodtests.py
index b858989daa..073e0b053e 100755
--- a/tests/run-natmodtests.py
+++ b/tests/run-natmodtests.py
@@ -73,6 +73,7 @@ class __FS:
return __File()
vfs.mount(__FS(), '/__remote')
sys.path.insert(0, '/__remote')
+{import_prelude}
sys.modules['{}'] = __import__('__injected')
"""
@@ -133,6 +134,13 @@ def detect_architecture(target):
def run_tests(target_truth, target, args, stats, 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)
+
for test_file in args.files:
# Find supported test
test_file_basename = os.path.basename(test_file)
@@ -212,6 +220,13 @@ 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("files", nargs="*", help="input test files")
args = cmd_parser.parse_args()
diff --git a/tests/run-tests.py b/tests/run-tests.py
index ac411a0be6..cb7a8b7705 100755
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -105,14 +105,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": (
@@ -272,22 +269,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(
@@ -303,8 +295,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:
@@ -315,9 +306,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
@@ -328,9 +331,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
@@ -474,7 +487,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
@@ -605,9 +618,7 @@ 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
@@ -896,7 +907,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_name, test_file, "skip", ""))
return
# Run the test on the MicroPython target.
@@ -911,7 +922,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_name, test_file, "skip", ""))
+ return
+ elif output_mupy == b"SKIP-TOO-LARGE\n":
+ print("lrge ", test_file)
+ test_results.append((test_name, test_file, "skip", "too large"))
return
# Look at the output of the test to see if unittest was used.
@@ -994,7 +1009,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_name, test_file, "pass", ""))
rm_f(filename_expected)
rm_f(filename_mupy)
else:
@@ -1006,7 +1021,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_results.append((test_name, test_file, "fail", ""))
test_count.increment()
@@ -1035,17 +1050,41 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
print(line)
sys.exit(1)
+ test_results = test_results.value
+ passed_tests = list(r for r in test_results if r[2] == "pass")
+ skipped_tests = list(r for r in test_results if r[2] == "skip" and r[3] != "too large")
+ skipped_tests_too_large = list(
+ r for r in test_results if r[2] == "skip" and r[3] == "too large"
+ )
+ failed_tests = list(r for r in test_results if r[2] == "fail")
+
print(
"{} tests performed ({} individual testcases)".format(
test_count.value, testcase_count.value
)
)
- print("{} tests passed".format(passed_count.value))
+ 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):
@@ -1055,21 +1094,20 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
with open(os.path.join(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[1:] for test in test_results),
+ # A list of failed tests. This is deprecated, use the "results" above instead.
+ "failed_tests": [test[1] 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):
@@ -1238,7 +1276,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:
diff --git a/tools/ci.sh b/tools/ci.sh
index a4bd43567c..ea67e2c104 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)
@@ -524,38 +531,25 @@ 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
+ make -C examples/natmod/features2 ARCH=$arch clean
if [ $arch = "rv32imc" ] || [ $arch = "armv7m" ] || [ $arch = "xtensa" ]; then
make -C examples/natmod/features2 ARCH=$arch MICROPY_FLOAT_IMPL=float
elif [ $arch != "armv6m" ]; then
make -C examples/natmod/features2 ARCH=$arch
fi
- # btree requires thread local storage support on rv32imc, whilst on xtensa
- # it relies on symbols that are provided from ROM but not exposed to
- # natmods at the moment.
- if [ $arch != "rv32imc" ] && [ $arch != "xtensa" ]; then
- make -C examples/natmod/btree clean
+ # 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
}
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..f8c913d26d 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,6 +375,7 @@ _BUILTIN_COMMAND_EXPANSIONS = {
"rmdir": "fs rmdir",
"sha256sum": "fs sha256sum",
"touch": "fs touch",
+ "tree": "fs tree",
# Disk used/free.
"df": [
"exec",
@@ -552,8 +573,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.
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..6518037f2e 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"):
@@ -1207,6 +1215,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 +1225,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 +1476,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 +1507,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 +1567,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 5234611983..dba6ebd6de 100755
--- a/tools/verifygitlog.py
+++ b/tools/verifygitlog.py
@@ -105,8 +105,12 @@ def verify_message_body(raw_body, err):
# 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]:
@@ -116,8 +120,8 @@ def verify_message_body(raw_body, err):
def verify_subject_line_prefix(prefix, err):
ext = (".c", ".h", ".cpp", ".js", ".rst", ".md")
- if prefix.startswith("."):
- err.error('Subject prefix cannot begin with ".".')
+ if prefix.startswith((".", "/")):
+ err.error('Subject prefix cannot begin with "." or "/".')
if prefix.endswith("/"):
err.error('Subject prefix cannot end with "/".')