From 8f5467087586e443283944b40be34ae21116f09e Mon Sep 17 00:00:00 2001 From: pmp-p Date: Tue, 23 Mar 2021 09:45:03 +0100 Subject: [PATCH] prepare header only --- LICENSE | 2 +- docs/conf.py | 4 +- docs/develop/compiler.rst | 317 ++++++ docs/develop/extendingmicropython.rst | 19 + docs/develop/gettingstarted.rst | 324 ++++++ docs/develop/img/bitmap.png | Bin 0 -> 6388 bytes docs/develop/img/collision.png | Bin 0 -> 3464 bytes docs/develop/img/linprob.png | Bin 0 -> 3655 bytes docs/develop/index.rst | 31 +- docs/develop/library.rst | 86 ++ docs/develop/maps.rst | 63 ++ docs/develop/memorymgt.rst | 141 +++ docs/develop/natmod.rst | 2 +- docs/develop/optimizations.rst | 72 ++ docs/develop/porting.rst | 310 ++++++ docs/develop/publiccapi.rst | 25 + docs/develop/qstr.rst | 7 +- docs/develop/writingtests.rst | 70 ++ docs/esp8266/quickref.rst | 7 + docs/library/esp32.rst | 48 + docs/library/machine.Pin.rst | 58 +- docs/library/machine.RTC.rst | 2 +- docs/library/pyb.RTC.rst | 2 +- docs/library/uasyncio.rst | 32 +- docs/library/ussl.rst | 22 +- docs/reference/pyboard.py.rst | 4 +- examples/embedding/hello-embed.c | 2 +- examples/rp2/pio_1hz.py | 35 + examples/rp2/pio_exec.py | 27 + examples/rp2/pio_pinchange.py | 46 + examples/rp2/pio_pwm.py | 45 + examples/rp2/pio_uart_rx.py | 104 ++ examples/rp2/pio_uart_tx.py | 44 + examples/rp2/pio_ws2812.py | 59 ++ examples/rp2/pwm_fade.py | 25 + extmod/btstack/btstack.mk | 2 + extmod/btstack/btstack_hci_uart.c | 23 +- extmod/btstack/modbluetooth_btstack.c | 78 +- extmod/extmod.cmake | 44 + extmod/modbluetooth.c | 52 +- extmod/modbluetooth.h | 34 +- extmod/modframebuf.c | 70 +- extmod/modonewire.c | 16 +- extmod/modure.c | 2 +- extmod/moduselect.c | 4 +- extmod/modussl_axtls.c | 31 +- extmod/modussl_mbedtls.c | 3 +- extmod/nimble/hal/hal_uart.c | 4 +- extmod/nimble/modbluetooth_nimble.c | 450 +++++--- extmod/uasyncio/__init__.py | 1 + extmod/uasyncio/core.py | 4 + extmod/uasyncio/event.py | 31 + extmod/vfs.c | 37 +- extmod/vfs_posix_file.c | 6 +- lib/mp-readline/readline.h | 2 + lib/oofatfs/ff.c | 212 ++-- lib/oofatfs/ff.h | 24 +- lib/timeutils/timeutils.c | 2 +- lib/timeutils/timeutils.h | 12 +- lib/utils/pyexec.c | 3 + mpy-cross/main.c | 6 +- mpy-cross/mpconfigport.h | 59 +- ports/cc3200/application.mk | 2 +- ports/cc3200/ftp/ftp.c | 5 +- ports/cc3200/mods/modmachine.c | 1 + ports/cc3200/mpconfigport.h | 2 - ports/esp32/CMakeLists.txt | 38 + ports/esp32/Makefile | 979 +----------------- ports/esp32/README.md | 246 ++--- .../esp32/boards/GENERIC/mpconfigboard.cmake | 6 + .../boards/GENERIC_D2WD/mpconfigboard.cmake | 7 + .../esp32/boards/GENERIC_D2WD/sdkconfig.board | 5 + .../boards/GENERIC_OTA/mpconfigboard.cmake | 7 + .../esp32/boards/GENERIC_OTA/sdkconfig.board | 5 +- .../boards/GENERIC_SPIRAM/mpconfigboard.cmake | 7 + .../esp32/boards/TINYPICO/mpconfigboard.cmake | 9 + ports/esp32/boards/sdkconfig.base | 24 +- ports/esp32/boards/sdkconfig.ble | 10 - ports/esp32/boards/sdkconfig.spiram | 3 - ports/esp32/esp32_nvs.c | 151 +++ ports/esp32/esp32_rmt.c | 2 +- ports/esp32/esp32_ulp.c | 4 - ports/esp32/machine_i2c.c | 2 +- ports/esp32/machine_uart.c | 38 +- ports/esp32/main.c | 13 +- ports/esp32/main/CMakeLists.txt | 196 ++++ ports/esp32/modesp.c | 3 - ports/esp32/modesp32.c | 9 +- ports/esp32/modesp32.h | 1 + ports/esp32/modmachine.c | 20 +- ports/esp32/modmachine.h | 2 + ports/esp32/modnetwork.c | 20 +- ports/esp32/modsocket.c | 54 +- ports/esp32/mpconfigport.h | 14 +- ports/esp32/mphalport.c | 7 +- ports/esp32/mpthreadport.c | 3 - ports/esp32/uart.c | 1 + ports/esp8266/gccollect.c | 1 + ports/esp8266/gccollect.h | 2 + ports/esp8266/modules/inisetup.py | 11 +- ports/esp8266/modules/neopixel.py | 5 +- ports/javascript/mpconfigport.h | 2 - ports/mimxrt/board_init.c | 6 +- ports/mimxrt/mpconfigport.h | 3 +- ports/mimxrt/mphalport.c | 17 +- ports/mimxrt/tusb_config.h | 1 - ports/mimxrt/tusb_port.c | 4 +- ports/nrf/README.md | 5 +- ports/nrf/drivers/usb/usb_cdc.c | 4 + ports/nrf/drivers/usb/usb_descriptors.c | 4 +- ports/nrf/mpconfigport.h | 4 - ports/pic16bit/mpconfigport.h | 2 - ports/powerpc/mpconfigport.h | 2 - ports/rp2/CMakeLists.txt | 166 +++ ports/rp2/Makefile | 14 + ports/rp2/README.md | 100 ++ ports/rp2/machine_adc.c | 123 +++ ports/rp2/machine_i2c.c | 161 +++ ports/rp2/machine_pin.c | 449 ++++++++ ports/rp2/machine_pwm.c | 197 ++++ ports/rp2/machine_spi.c | 278 +++++ ports/rp2/machine_timer.c | 165 +++ ports/rp2/machine_uart.c | 246 +++++ ports/rp2/machine_wdt.c | 78 ++ ports/rp2/main.c | 215 ++++ ports/rp2/manifest.py | 3 + ports/rp2/memmap_mp.ld | 253 +++++ ports/rp2/micropy_extmod.cmake | 40 + ports/rp2/micropy_py.cmake | 134 +++ ports/rp2/micropy_rules.cmake | 90 ++ ports/rp2/modmachine.c | 166 +++ ports/rp2/modmachine.h | 18 + ports/rp2/modrp2.c | 41 + ports/rp2/modrp2.h | 38 + ports/rp2/modules/_boot.py | 15 + ports/rp2/modules/rp2.py | 294 ++++++ ports/rp2/moduos.c | 103 ++ ports/rp2/modutime.c | 127 +++ ports/rp2/mpconfigport.h | 198 ++++ ports/rp2/mphalport.c | 142 +++ ports/rp2/mphalport.h | 113 ++ ports/rp2/mpthreadport.c | 105 ++ ports/rp2/mpthreadport.h | 64 ++ ports/rp2/qstrdefsport.h | 3 + ports/rp2/rp2_flash.c | 146 +++ ports/rp2/rp2_pio.c | 787 ++++++++++++++ ports/rp2/tusb_config.h | 34 + ports/rp2/tusb_port.c | 115 ++ ports/rp2/uart.c | 63 ++ ports/rp2/uart.h | 32 + ports/samd/mpconfigport.h | 1 - ports/samd/mphalport.c | 15 +- ports/samd/tusb_port.c | 14 +- ports/stm32/Makefile | 24 +- .../boards/B_L072Z_LRWAN1/mpconfigboard.h | 1 + .../boards/NUCLEO_L073RZ/mpconfigboard.h | 1 + .../boards/NUCLEO_L432KC/mpconfigboard.h | 1 + ports/stm32/boards/NUCLEO_WB55/mboot_keys.h | 5 + .../stm32/boards/NUCLEO_WB55/mpconfigboard.h | 2 + .../stm32/boards/NUCLEO_WB55/mpconfigboard.mk | 7 + ports/stm32/boards/PYBD_SF2/mpconfigboard.h | 1 + ports/stm32/boards/make-pins.py | 2 + ports/stm32/boards/stm32f0xx_hal_conf_base.h | 1 + ports/stm32/boards/stm32f4xx_hal_conf_base.h | 2 + ports/stm32/boards/stm32f7xx_hal_conf_base.h | 2 + ports/stm32/boards/stm32h7xx_hal_conf_base.h | 2 + ports/stm32/boards/stm32l0xx_hal_conf_base.h | 1 + ports/stm32/boards/stm32l4xx_hal_conf_base.h | 1 + ports/stm32/boards/stm32wbxx_hal_conf_base.h | 1 + ports/stm32/machine_uart.c | 29 +- ports/stm32/main.c | 52 +- ports/stm32/mboot/Makefile | 18 +- ports/stm32/mboot/Particle.h | 10 + ports/stm32/mboot/README.md | 28 + ports/stm32/mboot/dfu.h | 10 +- ports/stm32/mboot/fsload.c | 98 +- ports/stm32/mboot/fwupdate.py | 43 +- ports/stm32/mboot/gzstream.c | 34 +- ports/stm32/mboot/gzstream.h | 3 +- ports/stm32/mboot/main.c | 83 +- ports/stm32/mboot/mboot.h | 39 + ports/stm32/mboot/mboot_pack_dfu.py | 286 +++++ ports/stm32/mboot/pack.c | 292 ++++++ ports/stm32/mboot/pack.h | 82 ++ ports/stm32/mboot/stm32_generic.ld | 4 +- ports/stm32/mboot/vfs.h | 4 +- ports/stm32/mboot/vfs_fat.c | 6 +- ports/stm32/mboot/vfs_lfs.c | 41 +- ports/stm32/modmachine.c | 4 + ports/stm32/mpbthciport.c | 35 +- ports/stm32/mpbtstackport.c | 8 +- ports/stm32/mpconfigboard_common.h | 39 +- ports/stm32/mpconfigport.h | 72 +- ports/stm32/mpconfigport.mk | 7 +- ports/stm32/pin_defs_stm32.h | 5 + ports/stm32/rfcore.c | 55 +- ports/stm32/rtc.c | 2 +- ports/stm32/stm32_it.c | 18 + ports/stm32/uart.c | 61 +- ports/stm32/uart.h | 5 + ports/stm32/usb.h | 2 + ports/stm32/usbd_cdc_interface.c | 4 + ports/teensy/mpconfigport.h | 2 - ports/unix/main.c | 14 +- ports/unix/modtime.c | 4 +- ports/unix/moduselect.c | 4 + ports/unix/mpbtstackport_common.c | 5 + ports/unix/mphalport.h | 2 +- ports/unix/mpthreadport.c | 2 +- ports/wapy-unix/main.c | 6 +- ports/wapy-unix/mphalport.h | 2 +- ports/wapy-unix/mpthreadport.c | 2 +- ports/wapy-unix/unix_mphal.c | 2 +- ports/wapy-wasi/Makefile | 2 +- ports/wapy-wasi/main.c | 14 +- ports/wapy-wasi/mpconfigport.h | 2 +- ports/wapy-wasm/mod/modtime.c | 1 + ports/wapy-wasm/mphalport.h | 2 +- ports/wapy/cmod/wasi/embed/modembed.c | 43 +- ports/wapy/core/ringbuf_o.c | 12 +- ports/wapy/core/ringbuf_o.h | 29 +- ports/wapy/qstrdefsport.h | 1 + ports/wapy/runtime_no_nlr.c | 8 +- ports/wapy/runtime_no_nlr.h | 10 +- ports/wapy/wapy.c | 60 +- ports/windows/README.md | 4 + ports/windows/msvc/genhdr.targets | 1 + ports/zephyr/CMakeLists.txt | 134 ++- ports/zephyr/Kconfig | 46 + ports/zephyr/README.md | 62 +- ports/zephyr/boards/nucleo_h743zi.conf | 7 + ports/zephyr/boards/nucleo_h743zi.overlay | 23 + ports/zephyr/machine_uart.c | 168 +++ ports/zephyr/modmachine.c | 1 + ports/zephyr/modmachine.h | 1 + ports/zephyr/modusocket.c | 7 +- ports/zephyr/mpconfigport.h | 2 +- ports/zephyr/prj.conf | 5 + py/asmthumb.c | 189 +++- py/asmthumb.h | 47 + py/binary.c | 4 +- py/compile.c | 173 ++-- py/emitbc.c | 2 +- py/emitinlinethumb.c | 16 +- py/emitnative.c | 53 +- py/gc.c | 18 +- py/gc.h | 8 +- py/grammar.h | 208 ++-- py/lexer.c | 4 +- py/makeversionhdr.py | 8 +- py/misc.h | 2 + py/mkrules.cmake | 135 +++ py/modgc.c | 2 +- py/modio.c | 5 + py/modmicropython.c | 3 + py/mpconfig.h | 28 +- py/mpstate.c | 2 +- py/mpz.c | 59 +- py/objexcept.c | 2 +- py/objfloat.c | 2 +- py/objfun.c | 4 + py/objint.c | 2 +- py/parse.c | 16 +- py/persistentcode.c | 15 +- py/py.cmake | 124 +++ py/pystack.c | 6 +- py/qstr.c | 2 +- py/qstr.h | 3 +- py/runtime.c | 8 +- py/runtime.h | 2 +- py/smallint.h | 10 +- py/vstr.c | 3 + tests/basics/fun_globals.py | 21 + tests/basics/int_big_div.py | 4 + tests/esp32/esp32_nvs.py | 67 ++ tests/esp32/esp32_nvs.py.exp | 14 + tests/extmod/uasyncio_current_task.py | 25 + tests/extmod/uasyncio_current_task.py.exp | 1 + tests/extmod/uasyncio_threadsafeflag.py | 79 ++ tests/extmod/uasyncio_threadsafeflag.py.exp | 21 + tests/extmod/utime_res.py | 65 ++ tests/extmod/utime_res.py.exp | 9 + tests/extmod/utime_time_ns.py | 6 +- tests/extmod/vfs_lfs_superblock.py | 47 + tests/extmod/vfs_lfs_superblock.py.exp | 2 + tests/extmod/vfs_posix.py | 65 ++ tests/extmod/vfs_posix.py.exp | 12 + tests/io/file1.py | 3 + tests/micropython/native_for.py | 19 + tests/micropython/native_for.py.exp | 8 + tests/multi_bluetooth/perf_gatt_notify.py | 133 +++ tests/multi_bluetooth/perf_gatt_notify.py.exp | 0 tests/multi_bluetooth/perf_l2cap.py | 148 +++ tests/multi_bluetooth/perf_l2cap.py.exp | 0 tests/net_hosted/accept_timeout.py | 6 +- tests/net_hosted/connect_nonblock.py | 5 +- tests/net_hosted/connect_nonblock_xfer.py | 147 +++ tests/net_inet/ssl_errors.py | 51 + tests/net_inet/test_tls_nonblock.py | 116 +++ tests/net_inet/test_tls_sites.py | 6 +- tests/net_inet/test_tls_sites.py.exp | 3 +- tests/run-tests | 2 +- tools/ci.sh | 97 +- tools/makemanifest.py | 32 +- tools/mpy-tool.py | 11 + tools/pyboard.py | 6 +- tools/pydfu.py | 2 +- tools/verifygitlog.py | 123 +++ 308 files changed, 12799 insertions(+), 2507 deletions(-) create mode 100644 docs/develop/compiler.rst create mode 100644 docs/develop/extendingmicropython.rst create mode 100644 docs/develop/gettingstarted.rst create mode 100644 docs/develop/img/bitmap.png create mode 100644 docs/develop/img/collision.png create mode 100644 docs/develop/img/linprob.png create mode 100644 docs/develop/library.rst create mode 100644 docs/develop/maps.rst create mode 100644 docs/develop/memorymgt.rst create mode 100644 docs/develop/optimizations.rst create mode 100644 docs/develop/porting.rst create mode 100644 docs/develop/publiccapi.rst create mode 100644 docs/develop/writingtests.rst create mode 100644 examples/rp2/pio_1hz.py create mode 100644 examples/rp2/pio_exec.py create mode 100644 examples/rp2/pio_pinchange.py create mode 100644 examples/rp2/pio_pwm.py create mode 100644 examples/rp2/pio_uart_rx.py create mode 100644 examples/rp2/pio_uart_tx.py create mode 100644 examples/rp2/pio_ws2812.py create mode 100644 examples/rp2/pwm_fade.py create mode 100644 extmod/extmod.cmake create mode 100644 ports/esp32/CMakeLists.txt create mode 100644 ports/esp32/boards/GENERIC/mpconfigboard.cmake create mode 100644 ports/esp32/boards/GENERIC_D2WD/mpconfigboard.cmake create mode 100644 ports/esp32/boards/GENERIC_D2WD/sdkconfig.board create mode 100644 ports/esp32/boards/GENERIC_OTA/mpconfigboard.cmake create mode 100644 ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.cmake create mode 100644 ports/esp32/boards/TINYPICO/mpconfigboard.cmake create mode 100644 ports/esp32/esp32_nvs.c create mode 100644 ports/esp32/main/CMakeLists.txt create mode 100644 ports/rp2/CMakeLists.txt create mode 100644 ports/rp2/Makefile create mode 100644 ports/rp2/README.md create mode 100644 ports/rp2/machine_adc.c create mode 100644 ports/rp2/machine_i2c.c create mode 100644 ports/rp2/machine_pin.c create mode 100644 ports/rp2/machine_pwm.c create mode 100644 ports/rp2/machine_spi.c create mode 100644 ports/rp2/machine_timer.c create mode 100644 ports/rp2/machine_uart.c create mode 100644 ports/rp2/machine_wdt.c create mode 100644 ports/rp2/main.c create mode 100644 ports/rp2/manifest.py create mode 100644 ports/rp2/memmap_mp.ld create mode 100644 ports/rp2/micropy_extmod.cmake create mode 100644 ports/rp2/micropy_py.cmake create mode 100644 ports/rp2/micropy_rules.cmake create mode 100644 ports/rp2/modmachine.c create mode 100644 ports/rp2/modmachine.h create mode 100644 ports/rp2/modrp2.c create mode 100644 ports/rp2/modrp2.h create mode 100644 ports/rp2/modules/_boot.py create mode 100644 ports/rp2/modules/rp2.py create mode 100644 ports/rp2/moduos.c create mode 100644 ports/rp2/modutime.c create mode 100644 ports/rp2/mpconfigport.h create mode 100644 ports/rp2/mphalport.c create mode 100644 ports/rp2/mphalport.h create mode 100644 ports/rp2/mpthreadport.c create mode 100644 ports/rp2/mpthreadport.h create mode 100644 ports/rp2/qstrdefsport.h create mode 100644 ports/rp2/rp2_flash.c create mode 100644 ports/rp2/rp2_pio.c create mode 100644 ports/rp2/tusb_config.h create mode 100644 ports/rp2/tusb_port.c create mode 100644 ports/rp2/uart.c create mode 100644 ports/rp2/uart.h create mode 100644 ports/stm32/boards/NUCLEO_WB55/mboot_keys.h create mode 100644 ports/stm32/mboot/Particle.h create mode 100644 ports/stm32/mboot/mboot_pack_dfu.py create mode 100644 ports/stm32/mboot/pack.c create mode 100644 ports/stm32/mboot/pack.h create mode 100644 ports/zephyr/Kconfig create mode 100644 ports/zephyr/boards/nucleo_h743zi.conf create mode 100644 ports/zephyr/boards/nucleo_h743zi.overlay create mode 100644 ports/zephyr/machine_uart.c create mode 100644 py/mkrules.cmake create mode 100644 py/py.cmake create mode 100644 tests/basics/fun_globals.py create mode 100644 tests/esp32/esp32_nvs.py create mode 100644 tests/esp32/esp32_nvs.py.exp create mode 100644 tests/extmod/uasyncio_current_task.py create mode 100644 tests/extmod/uasyncio_current_task.py.exp create mode 100644 tests/extmod/uasyncio_threadsafeflag.py create mode 100644 tests/extmod/uasyncio_threadsafeflag.py.exp create mode 100644 tests/extmod/utime_res.py create mode 100644 tests/extmod/utime_res.py.exp create mode 100644 tests/extmod/vfs_lfs_superblock.py create mode 100644 tests/extmod/vfs_lfs_superblock.py.exp create mode 100644 tests/micropython/native_for.py create mode 100644 tests/micropython/native_for.py.exp create mode 100644 tests/multi_bluetooth/perf_gatt_notify.py create mode 100644 tests/multi_bluetooth/perf_gatt_notify.py.exp create mode 100644 tests/multi_bluetooth/perf_l2cap.py create mode 100644 tests/multi_bluetooth/perf_l2cap.py.exp create mode 100644 tests/net_hosted/connect_nonblock_xfer.py create mode 100644 tests/net_inet/ssl_errors.py create mode 100644 tests/net_inet/test_tls_nonblock.py create mode 100755 tools/verifygitlog.py diff --git a/LICENSE b/LICENSE index 8c5e4fe4c..3193eb8ce 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2020 Damien P. George +Copyright (c) 2013-2021 Damien P. George Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/conf.py b/docs/conf.py index 460886b4d..dd6cc31fb 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -66,7 +66,7 @@ # General information about the project. project = 'MicroPython' -copyright = '2014-2020, Damien P. George, Paul Sokolovsky, and contributors' +copyright = '2014-2021, Damien P. George, Paul Sokolovsky, and contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -74,7 +74,7 @@ # # We don't follow "The short X.Y version" vs "The full version, including alpha/beta/rc tags" # breakdown, so use the same version identifier for both to avoid confusion. -version = release = '1.13' +version = release = '1.14' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/develop/compiler.rst b/docs/develop/compiler.rst new file mode 100644 index 000000000..200765749 --- /dev/null +++ b/docs/develop/compiler.rst @@ -0,0 +1,317 @@ +.. _compiler: + +The Compiler +============ + +The compilation process in MicroPython involves the following steps: + +* The lexer converts the stream of text that makes up a MicroPython program into tokens. +* The parser then converts the tokens into an abstract syntax (parse tree). +* Then bytecode or native code is emitted based on the parse tree. + +For purposes of this discussion we are going to add a simple language feature ``add1`` +that can be use in Python as: + +.. code-block:: bash + + >>> add1 3 + 4 + >>> + +The ``add1`` statement takes an integer as argument and adds ``1`` to it. + +Adding a grammar rule +---------------------- + +MicroPython's grammar is based on the `CPython grammar `_ +and is defined in `py/grammar.h `_. +This grammar is what is used to parse MicroPython source files. + +There are two macros you need to know to define a grammar rule: ``DEF_RULE`` and ``DEF_RULE_NC``. +``DEF_RULE`` allows you to define a rule with an associated compile function, +while ``DEF_RULE_NC`` has no compile (NC) function for it. + +A simple grammar definition with a compile function for our new ``add1`` statement +looks like the following: + +.. code-block:: c + + DEF_RULE(add1_stmt, c(add1_stmt), and(2), tok(KW_ADD1), rule(testlist)) + +The second argument ``c(add1_stmt)`` is the corresponding compile function that should be implemented +in ``py/compile.c`` to turn this rule into executable code. + +The third required argument can be ``or`` or ``and``. This specifies the number of nodes associated +with a statement. For example, in this case, our ``add1`` statement is similar to ADD1 in assembly +language. It takes one numeric argument. Therefore, the ``add1_stmt`` has two nodes associated with it. +One node is for the statement itself, i.e the literal ``add1`` corresponding to ``KW_ADD1``, +and the other for its argument, a ``testlist`` rule which is the top-level expression rule. + +.. note:: + The ``add1`` rule here is just an example and not part of the standard + MicroPython grammar. + +The fourth argument in this example is the token associated with the rule, ``KW_ADD1``. This token should be +defined in the lexer by editing ``py/lexer.h``. + +Defining the same rule without a compile function is achieved by using the ``DEF_RULE_NC`` macro +and omitting the compile function argument: + +.. code-block:: c + + DEF_RULE_NC(add1_stmt, and(2), tok(KW_ADD1), rule(testlist)) + +The remaining arguments take on the same meaning. A rule without a compile function must +be handled explicitly by all rules that may have this rule as a node. Such NC-rules are usually +used to express sub-parts of a complicated grammar structure that cannot be expressed in a +single rule. + +.. note:: + The macros ``DEF_RULE`` and ``DEF_RULE_NC`` take other arguments. For an in-depth understanding of + supported parameters, see `py/grammar.h `_. + +Adding a lexical token +---------------------- + +Every rule defined in the grammar should have a token associated with it that is defined in ``py/lexer.h``. +Add this token by editing the ``_mp_token_kind_t`` enum: + +.. code-block:: c + :emphasize-lines: 12 + + typedef enum _mp_token_kind_t { + ... + MP_TOKEN_KW_OR, + MP_TOKEN_KW_PASS, + MP_TOKEN_KW_RAISE, + MP_TOKEN_KW_RETURN, + MP_TOKEN_KW_TRY, + MP_TOKEN_KW_WHILE, + MP_TOKEN_KW_WITH, + MP_TOKEN_KW_YIELD, + MP_TOKEN_KW_ADD1, + ... + } mp_token_kind_t; + +Then also edit ``py/lexer.c`` to add the new keyword literal text: + +.. code-block:: c + :emphasize-lines: 12 + + STATIC const char *const tok_kw[] = { + ... + "or", + "pass", + "raise", + "return", + "try", + "while", + "with", + "yield", + "add1", + ... + }; + +Notice the keyword is named depending on what you want it to be. For consistency, maintain the +naming standard accordingly. + +.. note:: + The order of these keywords in ``py/lexer.c`` must match the order of tokens in the enum + defined in ``py/lexer.h``. + +Parsing +------- + +In the parsing stage the parser takes the tokens produced by the lexer and converts them to an abstract syntax tree (AST) or +*parse tree*. The implementation for the parser is defined in `py/parse.c `_. + +The parser also maintains a table of constants for use in different aspects of parsing, similar to what a +`symbol table `_ +does. + +Several optimizations like `constant folding `_ +on integers for most operations e.g. logical, binary, unary, etc, and optimizing enhancements on parenthesis +around expressions are performed during this phase, along with some optimizations on strings. + +It's worth noting that *docstrings* are discarded and not accessible to the compiler. +Even optimizations like `string interning `_ are +not applied to *docstrings*. + +Compiler passes +--------------- + +Like many compilers, MicroPython compiles all code to MicroPython bytecode or native code. The functionality +that achieves this is implemented in `py/compile.c `_. +The most relevant method you should know about is this: + +.. code-block:: c + + mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) { + // Compile the input parse_tree to a raw-code structure. + mp_raw_code_t *rc = mp_compile_to_raw_code(parse_tree, source_file, is_repl); + // Create and return a function object that executes the outer module. + return mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL); + } + +The compiler compiles the code in four passes: scope, stack size, code size and emit. +Each pass runs the same C code over the same AST data structure, with different things +being computed each time based on the results of the previous pass. + +First pass +~~~~~~~~~~ + +In the first pass, the compiler learns about the known identifiers (variables) and +their scope, being global, local, closed over, etc. In the same pass the emitter +(bytecode or native code) also computes the number of labels needed for the emitted +code. + +.. code-block:: c + + // Compile pass 1. + comp->emit = emit_bc; + comp->emit_method_table = &emit_bc_method_table; + + uint max_num_labels = 0; + for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) { + if (s->emit_options == MP_EMIT_OPT_ASM) { + compile_scope_inline_asm(comp, s, MP_PASS_SCOPE); + } else { + compile_scope(comp, s, MP_PASS_SCOPE); + + // Check if any implicitly declared variables should be closed over. + for (size_t i = 0; i < s->id_info_len; ++i) { + id_info_t *id = &s->id_info[i]; + if (id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) { + scope_check_to_close_over(s, id); + } + } + } + ... + } + +Second and third passes +~~~~~~~~~~~~~~~~~~~~~~~ + +The second and third passes involve computing the Python stack size and code size +for the bytecode or native code. After the third pass the code size cannot change, +otherwise jump labels will be incorrect. + +.. code-block:: c + + for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) { + ... + + // Pass 2: Compute the Python stack size. + compile_scope(comp, s, MP_PASS_STACK_SIZE); + + // Pass 3: Compute the code size. + if (comp->compile_error == MP_OBJ_NULL) { + compile_scope(comp, s, MP_PASS_CODE_SIZE); + } + + ... + } + +Just before pass two there is a selection for the type of code to be emitted, which can +either be native or bytecode. + +.. code-block:: c + + // Choose the emitter type. + switch (s->emit_options) { + case MP_EMIT_OPT_NATIVE_PYTHON: + case MP_EMIT_OPT_VIPER: + if (emit_native == NULL) { + emit_native = NATIVE_EMITTER(new)(&comp->compile_error, &comp->next_label, max_num_labels); + } + comp->emit_method_table = NATIVE_EMITTER_TABLE; + comp->emit = emit_native; + break; + + default: + comp->emit = emit_bc; + comp->emit_method_table = &emit_bc_method_table; + break; + } + +The bytecode option is the default but something unique to note for the native +code option is that there is another option via ``VIPER``. See the +:ref:`Emitting native code ` section for more details on +viper annotations. + +There is also support for *inline assembly code*, where assembly instructions are +written as Python function calls but are emitted directly as the corresponding +machine code. This assembler has only three passes (scope, code size, emit) +and uses a different implementation, not the ``compile_scope`` function. +See the `inline assembler tutorial `_ +for more details. + +Fourth pass +~~~~~~~~~~~ + +The fourth pass emits the final code that can be executed, either bytecode in +the virtual machine, or native code directly by the CPU. + +.. code-block:: c + + for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) { + ... + + // Pass 4: Emit the compiled bytecode or native code. + if (comp->compile_error == MP_OBJ_NULL) { + compile_scope(comp, s, MP_PASS_EMIT); + } + } + +Emitting bytecode +----------------- + +Statements in Python code usually correspond to emitted bytecode, for example ``a + b`` +generates "push a" then "push b" then "binary op add". Some statements do not emit +anything but instead affect other things like the scope of variables, for example +``global a``. + +The implementation of a function that emits bytecode looks similar to this: + +.. code-block:: c + + void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op) { + emit_write_bytecode_byte(emit, 0, MP_BC_UNARY_OP_MULTI + op); + } + +We use the unary operator expressions for an example here but the implementation +details are similar for other statements/expressions. The method ``emit_write_bytecode_byte()`` +is a wrapper around the main function ``emit_get_cur_to_write_bytecode()`` that all +functions must call to emit bytecode. + +.. _emitting_native_code: + +Emitting native code +--------------------- + +Similar to how bytecode is generated, there should be a corresponding function in ``py/emitnative.c`` for each +code statement: + +.. code-block:: c + + STATIC void emit_native_unary_op(emit_t *emit, mp_unary_op_t op) { + vtype_kind_t vtype; + emit_pre_pop_reg(emit, &vtype, REG_ARG_2); + if (vtype == VTYPE_PYOBJ) { + emit_call_with_imm_arg(emit, MP_F_UNARY_OP, op, REG_ARG_1); + emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); + } else { + adjust_stack(emit, 1); + EMIT_NATIVE_VIPER_TYPE_ERROR(emit, + MP_ERROR_TEXT("unary op %q not implemented"), mp_unary_op_method_name[op]); + } + } + +The difference here is that we have to handle *viper typing*. Viper annotations allow +us to handle more than one type of variable. By default all variables are Python objects, +but with viper a variable can also be declared as a machine-typed variable like a native +integer or pointer. Viper can be thought of as a superset of Python, where normal Python +objects are handled as usual, while native machine variables are handled in an optimised +way by using direct machine instructions for the operations. Viper typing may break +Python equivalence because, for example, integers become native integers and can overflow +(unlike Python integers which extend automatically to arbitrary precision). diff --git a/docs/develop/extendingmicropython.rst b/docs/develop/extendingmicropython.rst new file mode 100644 index 000000000..7fb1ae47a --- /dev/null +++ b/docs/develop/extendingmicropython.rst @@ -0,0 +1,19 @@ +.. _extendingmicropython: + +Extending MicroPython in C +========================== + +This chapter describes options for implementing additional functionality in C, but from code +written outside of the main MicroPython repository. The first approach is useful for building +your own custom firmware with some project-specific additional modules or functions that can +be accessed from Python. The second approach is for building modules that can be loaded at runtime. + +Please see the :ref:`library section ` for more information on building core modules that +live in the main MicroPython repository. + +.. toctree:: + :maxdepth: 3 + + cmodules.rst + natmod.rst + \ No newline at end of file diff --git a/docs/develop/gettingstarted.rst b/docs/develop/gettingstarted.rst new file mode 100644 index 000000000..3dd00a579 --- /dev/null +++ b/docs/develop/gettingstarted.rst @@ -0,0 +1,324 @@ +.. _gettingstarted: + +Getting Started +=============== + +This guide covers a step-by-step process on setting up version control, obtaining and building +a copy of the source code for a port, building the documentation, running tests, and a description of the +directory structure of the MicroPython code base. + +Source control with git +----------------------- + +MicroPython is hosted on `GitHub `_ and uses +`Git `_ for source control. The workflow is such that +code is pulled and pushed to and from the main repository. Install the respective version +of Git for your operating system to follow through the rest of the steps. + +.. note:: + For a reference on the installation instructions, please refer to + the `Git installation instructions `_. + Learn about the basic git commands in this `Git Handbook `_ + or any other sources on the internet. + +Get the code +------------ + +It is recommended that you maintain a fork of the MicroPython repository for your development purposes. +The process of obtaining the source code includes the following: + +#. Fork the repository https://github.com/micropython/micropython +#. You will now have a fork at /micropython>. +#. Clone the forked repository using the following command: + +.. code-block:: bash + + $ git clone https://github.com//micropython + +Then, `configure the remote repositories `_ to be able to +collaborate on the MicroPython project. + +Configure remote upstream: + +.. code-block:: bash + + $ cd micropython + $ git remote add upstream https://github.com/micropython/micropython + +It is common to configure ``upstream`` and ``origin`` on a forked repository +to assist with sharing code changes. You can maintain your own mapping but +it is recommended that ``origin`` maps to your fork and ``upstream`` to the main +MicroPython repository. + +After the above configuration, your setup should be similar to this: + +.. code-block:: bash + + $ git remote -v + origin https://github.com//micropython (fetch) + origin https://github.com//micropython (push) + upstream https://github.com/micropython/micropython (fetch) + upstream https://github.com/micropython/micropython (push) + +You should now have a copy of the source code. By default, you are pointing +to the master branch. To prepare for further development, it is recommended +to work on a development branch. + +.. code-block:: bash + + $ git checkout -b dev-branch + +You can give it any name. You will have to compile MicroPython whenever you change +to a different branch. + +Compile and build the code +-------------------------- + +When compiling MicroPython, you compile a specific :term:`port`, usually +targeting a specific :ref:`board `. Start by installing the required dependencies. +Then build the MicroPython cross-compiler before you can successfully compile and build. +This applies specifically when using Linux to compile. +The Windows instructions are provided in a later section. + +.. _required_dependencies: + +Required dependencies +~~~~~~~~~~~~~~~~~~~~~ + +Install the required dependencies for Linux: + +.. code-block:: bash + + $ sudo apt-get install build-essential libffi-dev git pkg-config + +For the stm32 port, the ARM cross-compiler is required: + +.. code-block:: bash + + $ sudo apt-get install arm-none-eabi-gcc arm-none-eabi-binutils arm-none-eabi-newlib + +See the `ARM GCC +toolchain `_ +for the latest details. + +Python is also required. Python 2 is supported for now, but we recommend using Python 3. +Check that you have Python available on your system: + +.. code-block:: bash + + $ python3 + Python 3.5.0 (default, Jul 17 2020, 14:04:10) + [GCC 5.4.0 20160609] on linux + Type "help", "copyright", "credits" or "license" for more information. + >>> + +All supported ports have different dependency requirements, see their respective +`readme files `_. + +Building the MicroPython cross-compiler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Almost all ports require building ``mpy-cross`` first to perform pre-compilation +of Python code that will be included in the port firmware: + +.. code-block:: bash + + $ cd mpy-cross + $ make + +.. note:: + Note that, ``mpy-cross`` must be built for the host architecture + and not the target architecture. + +If it built successfully, you should see a message similar to this: + +.. code-block:: bash + + LINK mpy-cross + text data bss dec hex filename + 279328 776 880 280984 44998 mpy-cross + +.. note:: + + Use ``make -C mpy-cross`` to build the cross-compiler in one statement + without moving to the ``mpy-cross`` directory otherwise, you will need + to do ``cd ..`` for the next steps. + +Building the Unix port of MicroPython +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Unix port is a version of MicroPython that runs on Linux, macOS, and other Unix-like operating systems. +It's extremely useful for developing MicroPython as it avoids having to deploy your code to a device to test it. +In many ways, it works a lot like CPython's python binary. + +To build for the Unix port, make sure all Linux related dependencies are installed as detailed in the +required dependencies section. See the :ref:`required_dependencies` +to make sure that all dependencies are installed for this port. Also, make sure you have a working +environment for ``gcc`` and ``GNU make``. Ubuntu 20.04 has been used for the example +below but other unixes ought to work with little modification: + +.. code-block:: bash + + $ gcc --version + gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0 + Copyright (C) 2019 Free Software Foundation, Inc. + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.then build: + +.. code-block:: bash + + $ cd ports/unix + $ make submodules + $ make + +If MicroPython built correctly, you should see the following: + +.. code-block:: bash + + LINK micropython + text data bss dec hex filename + 412033 5680 2496 420209 66971 micropython + +Now run it: + +.. code-block:: bash + + $ ./micropython + MicroPython v1.13-38-gc67012d-dirty on 2020-09-13; linux version + Use Ctrl-D to exit, Ctrl-E for paste mode + >>> print("hello world") + hello world + >>> + +Building the Windows port +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Windows port includes a Visual Studio project file micropython.vcxproj that you can use to build micropython.exe. +It can be opened in Visual Studio or built from the command line using msbuild. Alternatively, it can be built using mingw, +either in Windows with Cygwin, or on Linux. +See `windows port documentation `_ for more information. + +Building the STM32 port +~~~~~~~~~~~~~~~~~~~~~~~ + +Like the Unix port, you need to install some required dependencies +as detailed in the :ref:`required_dependencies` section, then build: + +.. code-block:: bash + + $ cd ports/stm32 + $ make submodules + $ make + +Please refer to the `stm32 documentation `_ +for more details on flashing the firmware. + +.. note:: + See the :ref:`required_dependencies` to make sure that all dependencies are installed for this port. + The cross-compiler is needed. ``arm-none-eabi-gcc`` should also be in the $PATH or specified manually + via CROSS_COMPILE, either by setting the environment variable or in the ``make`` command line arguments. + +You can also specify which board to use: + +.. code-block:: bash + + $ cd ports/stm32 + $ make submodules + $ make BOARD= + +See `ports/stm32/boards `_ +for the available boards. e.g. "PYBV11" or "NUCLEO_WB55". + +Building the documentation +-------------------------- + +MicroPython documentation is created using ``Sphinx``. If you have already +installed Python, then install ``Sphinx`` using ``pip``. It is recommended +that you use a virtual environment: + +.. code-block:: bash + + $ python3 -m venv env + $ source env/bin/activate + $ pip install sphinx + +Navigate to the ``docs`` directory: + +.. code-block:: bash + + $ cd docs + +Build the docs: + +.. code-block:: bash + + $ make html + +Open ``docs/build/html/index.html`` in your browser to view the docs locally. Refer to the +documentation on `importing your documentation +`_ to use Read the Docs. + +Running the tests +----------------- + +To run all tests in the test suite on the Unix port use: + +.. code-block:: bash + + $ cd ports/unix + $ make test + +To run a selection of tests on a board/device connected over USB use: + +.. code-block:: bash + + $ cd tests + $ ./run-tests --target minimal --device /dev/ttyACM0 + +See also :ref:`writingtests`. + +Folder structure +---------------- + +There are a couple of directories to take note of in terms of where certain implementation details +are. The following is a break down of the top-level folders in the source code. + +py + + Contains the compiler, runtime, and core library implementation. + +mpy-cross + + Has the MicroPython cross-compiler which pre-compiles the Python scripts to bytecode. + +ports + + Code for all the versions of MicroPython for the supported ports. + +lib + + Low-level C libraries used by any port which are mostly 3rd-party libraries. + +drivers + + Has drivers for specific hardware and intended to work across multiple ports. + +extmod + + Contains a C implementation of more non-core modules. + +docs + + Has the standard documentation found at https://docs.micropython.org/. + +tests + + An implementation of the test suite. + +tools + + Contains helper tools including the ``upip`` and the ``pyboard.py`` module. + +examples + + Example code for building MicroPython as a library as well as native modules. diff --git a/docs/develop/img/bitmap.png b/docs/develop/img/bitmap.png new file mode 100644 index 0000000000000000000000000000000000000000..87de81d769431782a9cb77c0bc7e11f6e3c07044 GIT binary patch literal 6388 zcmcIpbx>Ptn}>2+thoD4inK^+OK~XDP^?ICDNu?#0fL4UD8=rD6bZ$-Sc6mCf>T_I z6n6~??hOPscXwy!+kbXv_WSnCoH^%tW}bKEJ@5Pe=Z;qL{#c(AUz@? zVrIfEN_K~kp)m^xCOk;ImDLT%$jIhbbyf%=n~#c#kG_Y!kDs-d9g%~(hnt;v%T9wimKSp9E5wfYkoH{}5`)Lk~ym{uy+`k-^j40(xDEHRbQTHAtIZE*n zFJvevwkOwhlBnNnJ81K1@NK`kO1?s)4gu0gx74Es@^`mW~vC?`}*j%2HHFPXg;P@Ci#PuZf4cd zx>s(o==-VU)++o&ZUs$g&CUC>^8{7OTYtOWVw#6Qx5h|Fv!RJBwMW~lkY)bTaGwB< zS29 z)eEcrL#I@~HlsmPvwBW_5J{QJ94R{&a?I4U4qCOEwVHmbi%3W1L@y*$JwJlWy$ZNr zo|>AvDk147(jmvyDb1f@rSw8OnnuCFnc-VEBntRbqicI)i40t$4 zMK!Q6^V~cTV&I@!7737w?b`>yD%i-_(I-ENaE!0(oL_anNf1#>_&4nh9|3yXIC4;M zR5kcCEul`@|KZqha_YB<;m+!zS26hJI}0La$s!CVKsumWbhB6v_lkPPmAj z_!XZ7y?=O^k_xg7H-D<^0NIRaUlo0fJ_on!a5hv;%)AT{2xE}`r;nw&aqA`3k39ks zJUgc~8&W@55E1%Jn-bGciT_jnHJ$qR zxaJb@V4xFca`TFof}Ri#u7gfEG+<101n+`*t0M}xwmc4zQb{eBji74D2%!f8-LKEX zG$mVGeI6Vn`ibZW&w_EV`tqBtobVJ6%elbg@z_Lj{)GUAS74;MF-=RtH+tvqR1_(P z33W}l%5a})xN8Ph+I0Vy^E<_iHI0*}`8ep>wJyB><}pC?#l!I$t84cu!BIBZ5e3Ke zabxDBD*GizA)wG~{+o~cZ`&qpLxPE|861_Hu&Gv1A&#?S@4c@Go){gJ*nWHeZs+KE zgu+at@Vd;Kv-s`{4NKU;b^pS|ff?6q!_`{eC+xua5I%)jpatvpvo2!$)c}btCmr2l zqi#N!jjsznyi!kwmCu!J>pnoR0yFoOcp{z5zffT`Ptl5REc`8Z=r~stLAFn%D;F18p1EZ<#8B^|8*;ygI+pT(-1A2v8GEX|aNwCXTY0F>p<(^68 z(9qDIO~L*^!pQ_L9c)`%I&^&Eu(FcDOSok+WdHPKKVKou?3=6F)Eb>P1Ir8tFMhnD zN;b+(W-<31R!upmu0v*BFC9ypm*=@HNV7`{tX}P0O%G;c9qWj9Vsnswf&v0aq@?w` zPK`pU?I$lBQRoAQK{KQYexx|()x@!rsySY<*YqZ?x{BEe=Ggz*?*f;31p;LZ&IiF; zi_~ZHFJ~brOd?p**PAkqcaTeFm(t`@c=(}Qc2bh+@x1FzZ!{g30)zqFyN*-_l$b|rlJD)7MKQ72cG{#cSfO{n$ zS=%?$h*5j+^WK*cyZW%fotVc+!VG<~7}|GUI|JcXd79OF)5=kTEKikeT3m~FQHHRk zpD(*-=1QkSZUV=GvfTe3?p|Yn)R6Il*vg1Jm;G?s_Y;&ha>BW`3?MEc!5;M1!eMH5 zV%cyTc$6edBIRl4Fg{}uv0T1dVeQGu!p@Fa^s9=#{_5+{w)=Z&9xbb~j#cHi+FG|I z+Fr2SXm4-t!EAMJL-UZb2}`Zg;j?GYYRp7>!S?p{U@-XU)2BV$bV9tp-K%vR%kAmu zxxBozq}354BoL{o(%?0a4LnyC0C-YpwS%OzYLd9CM)vws^xJssuBN97^?%X-|CPeW zcSK>++}9V{b4v^_L$ldkc8OW1^nDJ(%gpP2ucTH4a`WTz{Zrc$edG8{PBwlX1$A!L)C|bWX_0m2WE4s+n5O#g1!n%o1r8ckRwSp3 zcRVf5xSl}jWRJ{=FFsRvB%(S|Rw8<9+qIuYj*ZKB0pT6n%yu%b&XRiVugAv5W>!`w zo`u8VQc_ss)ogdckj%faQ5~u(`=Yd{$VH*@7b`t0J2N|bqtcrt=>4Kc3$>TAMWLag z9;JN`VWm=r+O8%bvnwa&@`))BsjL~jsmxWwx$$CQfMZB-K$EapO<`qar8u_@4U&WG zIl$ukm+gVbfF^2J$-SP+jm2W}bU2T+{{~5;{fF^*irlo_vi{}RCC`=1BZc9E>`YYs zC9*HK9g2l5$t=>*PQOlO@7aEXpY8p^;kGu< zRv4K1wKHbCg(NAg`FPn|_(EwpOvrxQD2I1n>4a2TIV^SY^+0ud3VwjsCO z<<^n*XIacVyi8vo_84iR0_r~Vqs~|<#Wk@`GY~jM9AIH+tsUR9l(YA`wC&2Vx2iOQ zLk6f9oazmkahwj#sBW$n6vbvM&=*lEylp!6lqT%1kZxG_5F#`UZ_ZTg%W@x7d|k8A zA}z=juMLs8E$)NO5WPHLA&VnOEB43yT8HYuH$3ru%4>2?n*)mEZ&U_{IwsbDhFBYW z?tsEP;w*3DyR!^q`Diq@-=%AV9dp20<5ec~)567c0frk`sbCi1xwu}bO{SL4{th14 zD2rF>KeGjZZ@SVDSzK8d;u@Q&S%&@kWf+G}qgV(UjfN}S%pA^(i(yaAbInU-Se$@u zMVZfijggkXNrJv%2qk!x{{*Y(Z{>YT6^22F%d$?*m8wc0kd17;6#X52YYOAQyu3Ww zEBcJS|6P#1)0LK%?%`IJe5w3S*T0H{h(T_aID|ZFm#8J9GHbW}1Z6ETx97_5^LI1~?TwS_9sjIH!0QpC4LPf0N5A}{qqr?CPn{>rn{}F)H{%Pp z6CdBMElkYwYjHVjitYcB=D5IXPIGldSZe?c~{{xm(~D zb(YofSLm5_nqxQ*o423cfWv^hh5V@oK{+r9oSktLloUaQ0T!md1NlWtqdHW0d`9e5 ztILas$qcnWVA2mA(Ip9Qe^%TS8x>d)WS=A4Z_UOt^0Y1uqtCY90S|-pzmxpmf zIE0a2%QVpff_rUv^NaLqIXC#t$?=?am-A1vrSV*Xb~~$It-&7cxjt$1S6w#U3`0<}~X2d_E))7aGeY6Ljth-#5_K)`piaABH1z zIbc!CuA*<=jQN%6cS{(VK`aK5To$orz4J3suEHwL5Ka5JAdP~EsHhLCW%^EHlR^6D z_+1%GyPKXLUAd6?X&sJEws)dg%Ku2OuL{YX4uTx{1t4qmjZr0rCf5}8ZqpSgZ z&(dJM!nQP~plU&X`C2W%73DQn48KtU@g}R;X|M9Uc2m>1F_^sxQyMN(nJ;-D`8a!g zxjpEFakkFQybXpY(C^UjaFD7RVHd5=;Es%JP-Re5K&wm*`qnAoW?b8}IPjg9T$Hl9x1Im2nUteo;}d_Cl- zZCPr(ygah9<$yKAU@&<+7%dH^F; zdynLJczD_zM!DDb#VM^VChv6&_VxX6uI20{c|rHF!=j+5FMn}IFU$0zWuo?j;* z5T-LYhCVTxoIKQN6}ck=Eq>Bc6h%|{Sp(#pohf^Mg^XWwhLqIZbjSIh)+bhTJya`I6x zd5^d4y-y-&YQw~)jl?`YUt%w~FM~)J&4w_f@-uR|csR|a_k!U!}SF@KGrw7gHvG0YG@wAlYuoj+S- zJ^C`f{F&Y(4~BTH!~JJ=TY4VkplLJA_GAL2ENM0GoDtM)rZnFS5$sCmllPoJ+pxQ+Di!2_;MKF^+*R~;9J|U zs6v9!`-S#E6l)~RQ0)&e6Iv6jZeyUo3UftadJ9UB{kH%)z;YjIvOEQ$nWQ9OV1BsdQcmyfUD?&ZH!H&8h8duKeBTohGyI(% z8BIA}QI{cFBI-(h;Y(|tmQLe&nD)g|PZg9e&-^%9L~Z`5Lq=p`7cJ@Zx0$@)C?{v_4Dep%Y41aJE zHj&Aod0|p9%}G_A_*nkdF$Y%~)#3Zbick=Yzo@N^6-lJbEs&UiPhry;5)KS&U$@<~ zI?T%uXT7$v;knCpKfdnTB+Wldzw+oUuTSz5rT#c{o&UYckc0{N0qsDunGPJ_*=R&Q*SpkCVARku$vN| zTx?KmUc9R|>rp|v(tlbWes3_t-_FSt3;&}AQU4Q^-)f+1F90WtKLUXe1;+DujdC;7 zb9B9`LA~K~6JQ5`OeW8dulp)ei$zG{yS=evk-9~d(88dYMh-vh+vL}#{1tN%aYN>> z!iG>$07B#EbwXCz~ny}3A|Eo%Zy|Gk%vLZ-lv4}`kiD)l+2vden!U3psj5jvSu z^NwQ;pX{D$nT>hGthrgFMLF)L5@7MiS)hDNLX;~>r(Mhn%lECSs4y1&3F8LVAQr`U z;kbG7#q|@PD%a?>UWY#`GG22T~{7)KqhRYZ0EiZLbuUBpkUcR<)2mlImbA^a`+QT6bn5Uzg z7nz}5@q+8Nwwf}?FLQeV`67jq#rVtcIxC-yl-U($ZX@1?n3Gh7j@bvVbFH35K2-2x zXAb7p{f>rR2{{t&Tu$n=y_EOJNb2TC+1qSS=>~b*#l!0TLqI%^O&*e?;(5dLtV6Jx zYx08~?#TZ{2rl`cZQETu$E-&CeEZYEpiz6f8wW=HdaC?ppF!P-i@HOJULxyql9e8gMNCkdDm#;~Q${;~zsiJ@STg0^on~U~KRae~`hqe9NvY|F12UHrrrsWUmsd<=MEL6lKG%`h zYdw&c#lj+S5e`o9cpVLaOZnu*VmC)3XfK{FeNZ1tSfkHr>P5T~5n(o7y2nA^(0hqz zheu8Pi+Jt^X~45nAsSB)k_?V8jA;6XlLXxBN)}I+TU+Vy*H`*yJmpeZLItU%)Qp&AQIx_XcXP|8eg?!5(od^2D&cxE2m7dd zB(7TOamt8tUnjx%`~h2%%#6p*SfYr77|IlgUa{eqA(@#Tp;Ih?>}f-V{fo&NAuU9( z5#HhGIH+|li~yH2y7a#839O;;1d*_7ScyZi4e+_fIE>J|)paWIZX1c`MC^DW7Xn5G(*mzd2rOP1#Kr)|5LM8r37_ zE>c+7*RvWAcp2}Ecnk(QAL3abLKX1L~d1RP}nqFqUt*XudgVmP54<}!^r)|*S$7w%6D+^ zURD&9!Bkm3%^+s@ruU6Pm_Ah*c4p59tE?Im)02mhKpt{M@m9=>UU>Vk`~9{YRFJ|U zzWx5bm~uzCTv|}Eiju)o1^mk;+dY;w4?ukBlPlxjLvwQ(RaILHZksrzE0q`=)&+_u zogt2U0=bSo&>p9lJ%8m}gV1!rbCMsG#zi{Q%->i!-XG??1ta@n&9G9lR0sHxNZOFl z-`>?uKalUbr-XDDE-BxI3n;UNRd>!?qBF+w*B&5N7{eRieZ%3*73Gt;#VIK|3J^L; zS@Nx;eDOzK;PssG{_WFxgdY*=sZ=E|zr7X7ZH=j|MYiP6HtC2Qq=dJD=9~y9npfMC z*z;r_fa#V$gbtv4^&cY9?gpAemJ>c*{w3BxRmz}V@;Gffh`fARewBD~I3u|Rq|B!b ziZP2a|8Ju5f9TGvhg_<6$(JOH+dbUz(F%~a=pZm+L@=1p+7oBB33-_`$niANy9cAJ z8erztSqMc-QMW}(o1yjUmDI!a3kcVMz9>fx|GN3X@cmIH$-$J2lf#q=);R%GU#Xj@ zS)=S@8`DQ?einOO4e$MaR`Agh4kQ{D22xz&xJ z3~8}fMbP?T{vP)tpSMOsHDa6Iw0g0n6VQCbLPKal0RhoCp5sQf+m@*O!C7D+#gvJ8 zm1!e;LfEBES0WK_>UZAPm_JF{-OCAiRUO5*S$PuB_jjQfJo@)DO`lDH6-DL|lBw%inR(V=p{Q8YhT7iWg3(7(;YN%PY*DZX_+JDAB7jaNwvNSPE z`jqZ7$7(l1B}7`ptI5~8S66A2S>$|Jv(4yp>QCUn zcn{?au2mhogq_XOidkh2ptk84_VCe??|c*z<=zm1Xh{o{4c$4i5w0 zzn509jxsY?tI#qh>#IGrb#ZL5HFmjb_S+ITtAqJHC?U#ZifP&wjqca-bbDCEWA;dD z9JzJCqazzYT4>%uM+J|4C}js~LYt?bl*^c!fIY-ksS%rCh9pddOA!vcyxpucaq2t* zGS*m_&QTFF-Z3$~#&Cm=aBxyAP;aAixZ-n<{T3f$v&~Ymy|G$thkUs4wX4Xmnm52D zvW=(0APM7Jupc66Z~U(8JR8+z>B{dm0cj1ohdAe34?%eP&f=~lgy>s-1D zi$!$DI#PPo-D~A{)85b5?1;pZ!86o|SY-GzuL&ECy3q(jNWSCS8r4_J7gmVUGQ~jV z2NJwa!9ZY%oo;bh)Fn5!*`ptQarI^U;J>$63&Q$3RCz1@+}NQWqc$!MmDX+dc&S-7 zYs_^1d)95di8;tgpv*falTTtX^AbsTRFPZR#tymtNJ;aTg`dJTX_T>!jku}A7w;xN zGMsSc8x}VXVYhj6YcA%j1mR+%4I4x2H?;#rC@b%OBKxklr6cqAI!;VY9TrIKu9)BG zU9EiT>OBvD+nv-3$zIp}NP}i(THXntGFkZBD0}oL$o|U*>{1X@&-Hy?m?<% z-|V1Dj`*~I$@9LAkVJ`6+avRrb``Ib-@)SEXZyw-OdpPY8df%9gkpeyX`dOqZ9gc8 z#({O+KXis>Ds*xXrdq7>Zs)SO&EEkqR>n+^ttpik?^5mof&EWL4F1&LM@l=Se}>*4 z;_c0PW7PP|3Odh`eK;XuyrW{|#@~OG82)1vIxri&Hkgxh(&~|_rJ?4q`gLMoiDDjB ztPb_{WPE4W-F5hhrY}axFyEYjLx^(i^R8lp*)-xsI0H@Dro z7?zc}TS>-|J>$}aQBeqB%7^UILkvG=d*HLmt$`Nr=aHH%k7gEUq?Ogcf%axo$w=>7 z5Qf=WFNssq|I2aDuWQl1ixsUm%CEt>IR{c{?Il~aY-V1QC`KUqW2)RtJ!5lhU_@J+ z>%L90jEt-3(PujT{cqNMyLeAE)z%x(Y+o{OwiF>7FCG}jrQqjU_zlK5k zv)*=VXBXBu?S_D#=poUe`{7wp?~4}EdQ-!v_FMtLs>JbzP9k%MtHli+uU7)*6($5= zZ6+tAzG#das<56_7+pDMrLVTOOfe5K2lp!~ZF6FdUF0%RMCEVC28PYDx&f4L5=Ij_ z>iob#Ud`};m0o^$TG_jkYC5BJ;;zZ++3eM{(sD ze2PS=i-VaBg3GOIh1QX$v;+2s{^h4-hvhC zk?oLCwUKQ_ZFJeG=^aDi{j9Kvii$JW`C0OhEA7>(^Ast*PoU;MGGVjfGKKd&xKf0X zBXuUS?Cir{9vTJb-8jlrlsM=S1egG&ugs*ChVCcz8{`(a@$gW}b};+yMXd*GnIV)_^e6a~C?^r;?(Lkjv}+bgXItPWju2aw233?9VGz$)1407n zVlRTT4agjpp!POpM8w#0b#*9@w5U{z;QFs-&G>S(a7X7}Zu&B^1ZT{9LIQE_d=MY2 z5Y8ue%OUhaT|{abl@T(9tb~N|?u7lCgm>{-S@%_dK%vi%0#HqnTj&?6x;I&z!@#(3 zwOz`(a7SxcbZUC$OsM@5FEKnbKQ~9C8Oa%7`0Lxk zJma3MlN{h3QLWyL<`qJm`I|#czd+}cb1bh9HhR7Cx_l}i{GFhhcTJ&>-XnYtjI2Ez z5iSrZmz;pT733g&?r5R;gq%7bQ@98^>T$8+39(yr8~HgoMfj7n*k)euR&99_OD}ka zS9{bA$x5^$;@B%M-9n(ln?e+ zyl*M&y0*-KZfu6?V~q9asMq5XV<|r#3fJ4=47FQd!itJ~bP@Z{VlAEsTL!tBm%}Fq z(|lM$)X&+rDM(pa6MmR%dP%KlX>>X1_QPeLp`SJJ=SKy@&c^v;svl`}I&LZwXno+a zztq07nNgAM4$UR@EWEtr%0)Gl@ZDQ}#&&*65$VKU$v_@SbnElG`~BILdhik%r8m;( z>Ph!?_OFkJ*dofpby{W}El!XVFk>qU zM4ryZrQ8m1txR8jslmOxCYv(6=-qe0E6sFn634w=kExG23dsic`tNFE~BeI1EU% z1p&SZunzq{pmmqhy>L$ZWh>USOE(RK09Vu{=l>Mxw!-~CWtXYIo zPmZ1Vq!^agP2P)-@kGr@uDMQB-KN2-y@S!saI!NDAr6$PcBpRi!aN8h-Sv;_9DUyy z__irkYqR1$VEU_1HY+d^_9 zqk#aC?5lciP&f-#XP#rf6TClomt?UqQtMr;uSD~Hs;OFCpU}|EZ0ffS$!(g((=WA+ z+uM$wx0ERp(G9GZD@3mkh^F{bBAy0h%oArGD-d`~lX*5#Rha9vz}eq?TtcG@@dvhx z*n%Y|ttoS@Ha9@0f^_Qa-8{fl1f>dIueRUr(UpXE?tAN*1PA*J^bjQ>u|s>adQjq~ zXZ6HIW2UqRI1d?j=gnZ@7Z_>MH{9hd&1c|j;Aw&#cI*3uwzHG$H8)k~Ku#A@r3vE7`l)m%2eWyi89#WBvyq>t7X7AF$m10q!9!DxzW)?Fmhwef$n`4zLrn?1 z%IL~ump&ma71V+x#j${~?@Xj_WdET8!3Oj>g&4Yvf_!S(ai<_57VD0)KCa!TV{$nS zm~)tbrd=IgO_&6NA>W>tneK)unx#&yJJm-2F+JO@rlq0oJzw0^ z?N>It7pvwQ`EByj2B0Da+Simw*jpD-sQtu$xZ?5MRhaYmcFo zqZf311H&GNROQ4jL~K*%8v7V}ev8G-8T(#sP0i=Pu4;wp4Zl*4S@3TZMX)%xIOld( zqXy2rRR(hJ`VhGvM|=hWZP@$)4huAeFYZ;g8(bFK7Z^Uvo0bzr+q=l z-vA8WF!8E!y}!d{$(>IDs>P!53$S zLPdZ{i>Sw#w`ZgV?_JNd_;D*pgA#GBJ8qK5HbB9q`>ETuJwaM33W##0>))UL>?HYYBBwnv-U?+=+-0Ix_c5?7sCIr## zs`2sZ*RA!(ZaREck(3|6OQ+4Ro{R7xunw_RPd`);g%P_-||R?b(-$HoNh#)O@tsygw5S@3pVK< zzt#7re_7R!7(O+#WMyNOZ)SGtl(nV@J6Q{2CK~m=hn-Mt^q7+3vVxs4Zf?0rx(vee z^Ixd>Hq>tf1sCJdwfMCz*{Te7<^?I5UIjV7ymY_Yc&x|5EYI8Q)9yAk7k~X9jyHl+ z_}uP7oP}00v?Szm;d|Zw-VrXvpvreg)-bJRzGGFqHUs3e@)s=ijCTQqYhe?3&Sykg zj)G0Uv!wv}2 z4=eu7o)U}WRNSQtJMihcVDQx+dq!IF7Kh+VgMm2ATW)2`&j+y87NyxFA^akG82HLa zbK!60x~KXhm86GX7C6a^oNV59@APqh_GoH4aoXl^L56XsK_x6h=VoM~JHj#K9fR&h zjdX<^bIDVw8kTq(CH+Fe_6w~es4>Fw!)tjJyX_8C zJ=$JF8$zjsbei_{I+&BShvN0@XXIbi-lgoNF~R<3aTx!bj=yi4J*06Nb1xScy>o)Q zjP1Di8W6uf0hI)YeZ7;Or0xdz#?CbYJOeHr0f_&fp$ELzXwt*=lfg#W( m?$!Q}V*Iab{O2Km{}8-$!#4BCvxS3K5CAc^HY4A-ANk)54-ZcO literal 0 HcmV?d00001 diff --git a/docs/develop/index.rst b/docs/develop/index.rst index f1fd0692e..7a6a6be67 100644 --- a/docs/develop/index.rst +++ b/docs/develop/index.rst @@ -1,14 +1,27 @@ -Developing and building MicroPython -=================================== +MicroPython Internals +===================== -This chapter describes some options for extending MicroPython in C. Note -that it doesn't aim to be a complete guide for developing with MicroPython. -See the `getting started guide -`_ for further information. +This chapter covers a tour of MicroPython from the perspective of a developer, contributing +to MicroPython. It acts as a comprehensive resource on the implementation details of MicroPython +for both novice and expert contributors. + +Development around MicroPython usually involves modifying the core runtime, porting or +maintaining a new library. This guide describes at great depth, the implementation +details of MicroPython including a getting started guide, compiler internals, porting +MicroPython to a new platform and implementing a core MicroPython library. .. toctree:: - :maxdepth: 1 + :maxdepth: 3 - cmodules.rst + gettingstarted.rst + writingtests.rst + compiler.rst + memorymgt.rst + library.rst + optimizations.rst qstr.rst - natmod.rst + maps.rst + publiccapi.rst + extendingmicropython.rst + porting.rst + \ No newline at end of file diff --git a/docs/develop/library.rst b/docs/develop/library.rst new file mode 100644 index 000000000..bebddcc8a --- /dev/null +++ b/docs/develop/library.rst @@ -0,0 +1,86 @@ +.. _internals_library: + +Implementing a Module +===================== + +This chapter details how to implement a core module in MicroPython. +MicroPython modules can be one of the following: + +- Built-in module: A general module that is be part of the MicroPython repository. +- User module: A module that is useful for your specific project that you maintain + in your own repository or private codebase. +- Dynamic module: A module that can be deployed and imported at runtime to your device. + +A module in MicroPython can be implemented in one of the following locations: + +- py/: A core library that mirrors core CPython functionality. +- extmod/: A CPython or MicroPython-specific module that is shared across multiple ports. +- ports//: A port-specific module. + +.. note:: + This chapter describes modules implemented in ``py/`` or core modules. + See :ref:`extendingmicropython` for details on implementing an external module. + For details on port-specific modules, see :ref:`porting_to_a_board`. + +Implementing a core module +-------------------------- + +Like CPython, MicroPython has core builtin modules that can be accessed through import statements. +An example is the ``gc`` module discussed in :ref:`memorymanagement`. + +.. code-block:: bash + + >>> import gc + >>> gc.enable() + >>> + +MicroPython has several other builtin standard/core modules like ``io``, ``uarray`` etc. +Adding a new core module involves several modifications. + +First, create the ``C`` file in the ``py/`` directory. In this example we are adding a +hypothetical new module ``subsystem`` in the file ``modsubsystem.c``: + +.. code-block:: c + + #include "py/builtin.h" + #include "py/runtime.h" + + #if MICROPY_PY_SUBSYSTEM + + // info() + STATIC mp_obj_t py_subsystem_info(void) { + return MP_OBJ_NEW_SMALL_INT(42); + } + MP_DEFINE_CONST_FUN_OBJ_0(subsystem_info_obj, py_subsystem_info); + + STATIC const mp_rom_map_elem_t mp_module_subsystem_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_subsystem) }, + { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&subsystem_info_obj) }, + }; + STATIC MP_DEFINE_CONST_DICT(mp_module_subsystem_globals, mp_module_subsystem_globals_table); + + const mp_obj_module_t mp_module_subsystem = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_subsystem_globals, + }; + + MP_REGISTER_MODULE(MP_QSTR_subsystem, mp_module_subsystem, MICROPY_PY_SUBSYSTEM); + + #endif + +The implementation includes a definition of all functions related to the module and adds the +functions to the module's global table in ``mp_module_subsystem_globals_table``. It also +creates the module object with ``mp_module_subsystem``. The module is then registered with +the wider system via the ``MP_REGISTER_MODULE`` macro. + +After building and running the modified MicroPython, the module should now be importable: + +.. code-block:: bash + + >>> import subsystem + >>> subsystem.info() + 42 + >>> + +Our ``info()`` function currently returns just a single number but can be extended +to do anything. Similarly, more functions can be added to this new module. diff --git a/docs/develop/maps.rst b/docs/develop/maps.rst new file mode 100644 index 000000000..8f899fa1d --- /dev/null +++ b/docs/develop/maps.rst @@ -0,0 +1,63 @@ +.. _maps: + +Maps and Dictionaries +===================== + +MicroPython dictionaries and maps use techniques called open addressing and linear probing. +This chapter details both of these methods. + +Open addressing +--------------- + +`Open addressing `_ is used to resolve collisions. +Collisions are very common occurrences and happen when two items happen to hash to the same +slot or location. For example, given a hash setup as this: + +.. image:: img/collision.png + +If there is a request to fill slot ``0`` with ``70``, since the slot ``0`` is not empty, open addressing +finds the next available slot in the dictionary to service this request. This sequential search for an alternate +location is called *probing*. There are several sequence probing algorithms but MicroPython uses +linear probing that is described in the next section. + +Linear probing +-------------- + +Linear probing is one of the methods for finding an available address or slot in a dictionary. In MicroPython, +it is used with open addressing. To service the request described above, unlike other probing algorithms, +linear probing assumes a fixed interval of ``1`` between probes. The request will therefore be serviced by +placing the item in the next free slot which is slot ``4`` in our example: + +.. image:: img/linprob.png + +The same methods i.e open addressing and linear probing are used to search for an item in a dictionary. +Assume we want to search for the data item ``33``. The computed hash value will be 2. Looking at slot 2 +reveals ``33``, at this point, we return ``True``. Searching for ``70`` is quite different as there was a +collision at the time of insertion. Therefore computing the hash value is ``0`` which is currently +holding ``44``. Instead of simply returning ``False``, we perform a sequential search starting at point +``1`` until the item ``70`` is found or we encounter a free slot. This is the general way of performing +look-ups in hashes: + +.. code-block:: c + + // not yet found, keep searching in this table + pos = (pos + 1) % set->alloc; + + if (pos == start_pos) { + // search got back to starting position, so index is not in table + if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) { + if (avail_slot != NULL) { + // there was an available slot, so use that + set->used++; + *avail_slot = index; + return index; + } else { + // not enough room in table, rehash it + mp_set_rehash(set); + // restart the search for the new element + start_pos = pos = hash % set->alloc; + } + } + } else { + return MP_OBJ_NULL; + } diff --git a/docs/develop/memorymgt.rst b/docs/develop/memorymgt.rst new file mode 100644 index 000000000..5b1690cc8 --- /dev/null +++ b/docs/develop/memorymgt.rst @@ -0,0 +1,141 @@ +.. _memorymanagement: + +Memory Management +================= + +Unlike programming languages such as C/C++, MicroPython hides memory management +details from the developer by supporting automatic memory management. +Automatic memory management is a technique used by operating systems or applications to automatically manage +the allocation and deallocation of memory. This eliminates challenges such as forgetting to +free the memory allocated to an object. Automatic memory management also avoids the critical issue of using memory +that is already released. Automatic memory management takes many forms, one of them being +garbage collection (GC). + +The garbage collector usually has two responsibilities; + +#. Allocate new objects in available memory. +#. Free unused memory. + +There are many GC algorithms but MicroPython uses the +`Mark and Sweep `_ +policy for managing memory. This algorithm has a mark phase that traverses the heap marking all +live objects while the sweep phase goes through the heap reclaiming all unmarked objects. + +Garbage collection functionality in MicroPython is available through the ``gc`` built-in +module: + +.. code-block:: bash + + >>> x = 5 + >>> x + 5 + >>> import gc + >>> gc.enable() + >>> gc.mem_alloc() + 1312 + >>> gc.mem_free() + 2071392 + >>> gc.collect() + 19 + >>> gc.disable() + >>> + +Even when ``gc.disable()`` is invoked, collection can be triggered with ``gc.collect()``. + +The object model +---------------- + +All MicroPython objects are referred to by the ``mp_obj_t`` data type. +This is usually word-sized (i.e. the same size as a pointer on the target architecture), +and can be typically 32-bit (STM32, nRF, ESP32, Unix x86) or 64-bit (Unix x64). +It can also be greater than a word-size for certain object representations, for +example ``OBJ_REPR_D`` has a 64-bit sized ``mp_obj_t`` on a 32-bit architecture. + +An ``mp_obj_t`` represents a MicroPython object, for example an integer, float, type, dict or +class instance. Some objects, like booleans and small integers, have their value stored directly +in the ``mp_obj_t`` value and do not require additional memory. Other objects have their value +store elsewhere in memory (for example on the garbage-collected heap) and their ``mp_obj_t`` contains +a pointer to that memory. A portion of ``mp_obj_t`` is the tag which tells what type of object it is. + +See ``py/mpconfig.h`` for the specific details of the available representations. + +**Pointer tagging** + +Because pointers are word-aligned, when they are stored in an ``mp_obj_t`` the +lower bits of this object handle will be zero. For example on a 32-bit architecture +the lower 2 bits will be zero: + +``********|********|********|******00`` + +These bits are reserved for purposes of storing a tag. The tag stores extra information as +opposed to introducing a new field to store that information in the object, which may be +inefficient. In MicroPython the tag tells if we are dealing with a small integer, interned +(small) string or a concrete object, and different semantics apply to each of these. + +For small integers the mapping is this: + +``********|********|********|*******1`` + +Where the asterisks hold the actual integer value. For an interned string or an immediate +object (e.g. ``True``) the layout of the ``mp_obj_t`` value is, respectively: + +``********|********|********|*****010`` + +``********|********|********|*****110`` + +While a concrete object that is none of the above takes the form: + +``********|********|********|******00`` + +The stars here correspond to the address of the concrete object in memory. + +Allocation of objects +---------------------- + +The value of a small integer is stored directly in the ``mp_obj_t`` and will be +allocated in-place, not on the heap or elsewhere. As such, creation of small +integers does not affect the heap. Similarly for interned strings that already have +their textual data stored elsewhere, and immediate values like ``None``, ``False`` +and ``True``. + +Everything else which is a concrete object is allocated on the heap and its object structure is such that +a field is reserved in the object header to store the type of the object. + +.. code-block:: bash + + +++++++++++ + + + + + type + object header + + + + +++++++++++ + + + object items + + + + + + + +++++++++++ + +The heap's smallest unit of allocation is a block, which is four machine words in +size (16 bytes on a 32-bit machine, 32 bytes on a 64-bit machine). +Another structure also allocated on the heap tracks the allocation of +objects in each block. This structure is called a *bitmap*. + +.. image:: img/bitmap.png + +The bitmap tracks whether a block is "free" or "in use" and use two bits to track this state +for each block. + +The mark-sweep garbage collector manages the objects allocated on the heap, and also +utilises the bitmap to mark objects that are still in use. +See `py/gc.c `_ +for the full implementation of these details. + +**Allocation: heap layout** + +The heap is arranged such that it consists of blocks in pools. A block +can have different properties: + +- *ATB(allocation table byte):* If set, then the block is a normal block +- *FREE:* Free block +- *HEAD:* Head of a chain of blocks +- *TAIL:* In the tail of a chain of blocks +- *MARK :* Marked head block +- *FTB(finaliser table byte):* If set, then the block has a finaliser diff --git a/docs/develop/natmod.rst b/docs/develop/natmod.rst index a4c188719..8ffe49591 100644 --- a/docs/develop/natmod.rst +++ b/docs/develop/natmod.rst @@ -202,7 +202,7 @@ Module usage in MicroPython Once the module is built there should be a file called ``factorial.mpy``. Copy this so it is accessible on the filesystem of your MicroPython system and can be -found in the import path. The module con now be accessed in Python just like any +found in the import path. The module can now be accessed in Python just like any other module, for example:: import factorial diff --git a/docs/develop/optimizations.rst b/docs/develop/optimizations.rst new file mode 100644 index 000000000..d972cde66 --- /dev/null +++ b/docs/develop/optimizations.rst @@ -0,0 +1,72 @@ +.. _optimizations: + +Optimizations +============= + +MicroPython uses several optimizations to save RAM but also ensure the efficient +execution of programs. This chapter discusses some of these optimizations. + +.. note:: + :ref:`qstr` and :ref:`maps` details other optimizations on strings and + dictionaries. + +Frozen bytecode +--------------- + +When MicroPython loads Python code from the filesystem, it first has to parse the file into +a temporary in-memory representation, and then generate bytecode for execution, both of which +are stored in the heap (in RAM). This can lead to significant amounts of memory being used. +The MicroPython cross compiler can be used to generate +a ``.mpy`` file, containing the pre-compiled bytecode for a Python module. This will still +be loaded into RAM, but it avoids the additional overhead of the parsing stage. + +As a further optimisation, the pre-compiled bytecode from a ``.mpy`` file can be "frozen" +into the firmware image as part of the main firmware compilation process, which means that +the bytecode will be executed from ROM. This can lead to a significant memory saving, and +reduce heap fragmentation. + +Variables +--------- + +MicroPython processes local and global variables differently. Global variables +are stored and looked up from a global dictionary that is allocated on the heap +(note that each module has its own separate dict, so separate namespace). +Local variables on the other hand are are stored on the Python value stack, which may +live on the C stack or on the heap. They are accessed directly by their offset +within the Python stack, which is more efficient than a global lookup in a dict. + +The length of global variable names also affects how much RAM is used as identifiers +are stored in RAM. The shorter the identifier, the less memory is used. + +The other aspect is that ``const`` variables that start with an underscore are treated as +proper constants and are not allocated or added in a dictionary, hence saving some memory. +These variables use ``const()`` from the MicroPython library. Therefore: + +.. code-block:: python + + from micropython import const + + X = const(1) + _Y = const(2) + foo(X, _Y) + +Compiles to: + +.. code-block:: python + + X = 1 + foo(1, 2) + +Allocation of memory +-------------------- + +Most of the common MicroPython constructs are not allocated on the heap. +However the following are: + +- Dynamic data structures like lists, mappings, etc; +- Functions, classes and object instances; +- imports; and +- First-time assignment of global variables (to create the slot in the global dict). + +For a detailed discussion on a more user-centric perspective on optimization, +see `Maximising MicroPython speed `_ diff --git a/docs/develop/porting.rst b/docs/develop/porting.rst new file mode 100644 index 000000000..59dd57000 --- /dev/null +++ b/docs/develop/porting.rst @@ -0,0 +1,310 @@ +.. _porting_to_a_board: + +Porting MicroPython +=================== + +The MicroPython project contains several ports to different microcontroller families and +architectures. The project repository has a `ports `_ +directory containing a subdirectory for each supported port. + +A port will typically contain definitions for multiple "boards", each of which is a specific piece of +hardware that that port can run on, e.g. a development kit or device. + +The `minimal port `_ is +available as a simplified reference implementation of a MicroPython port. It can run on both the +host system and an STM32F4xx MCU. + +In general, starting a port requires: + +- Setting up the toolchain (configuring Makefiles, etc). +- Implementing boot configuration and CPU initialization. +- Initialising basic drivers required for development and debugging (e.g. GPIO, UART). +- Performing the board-specific configurations. +- Implementing the port-specific modules. + +Minimal MicroPython firmware +---------------------------- + +The best way to start porting MicroPython to a new board is by integrating a minimal +MicroPython interpreter. For this walkthrough, create a subdirectory for the new +port in the ``ports`` directory: + +.. code-block:: bash + + $ cd ports + $ mkdir example_port + +The basic MicroPython firmware is implemented in the main port file, e.g ``main.c``: + +.. code-block:: c + + #include "py/compile.h" + #include "py/gc.h" + #include "py/mperrno.h" + #include "py/stackctrl.h" + #include "lib/utils/gchelper.h" + #include "lib/utils/pyexec.h" + + // Allocate memory for the MicroPython GC heap. + static char heap[4096]; + + int main(int argc, char **argv) { + // Initialise the MicroPython runtime. + mp_stack_ctrl_init(); + gc_init(heap, heap + sizeof(heap)); + mp_init(); + mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_path), 0); + mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), 0); + + // Start a normal REPL; will exit when ctrl-D is entered on a blank line. + pyexec_friendly_repl(); + + // Deinitialise the runtime. + gc_sweep_all(); + mp_deinit(); + return 0; + } + + // Handle uncaught exceptions (should never be reached in a correct C implementation). + void nlr_jump_fail(void *val) { + for (;;) { + } + } + + // Do a garbage collection cycle. + void gc_collect(void) { + gc_collect_start(); + gc_helper_collect_regs_and_stack(); + gc_collect_end(); + } + + // There is no filesystem so stat'ing returns nothing. + mp_import_stat_t mp_import_stat(const char *path) { + return MP_IMPORT_STAT_NO_EXIST; + } + + // There is no filesystem so opening a file raises an exception. + mp_lexer_t *mp_lexer_new_from_file(const char *filename) { + mp_raise_OSError(MP_ENOENT); + } + +We also need a Makefile at this point for the port: + +.. code-block:: Makefile + + # Include the core environment definitions; this will set $(TOP). + include ../../py/mkenv.mk + + # Include py core make definitions. + include $(TOP)/py/py.mk + + # Set CFLAGS and libraries. + CFLAGS = -I. -I$(BUILD) -I$(TOP) + LIBS = -lm + + # Define the required source files. + SRC_C = \ + main.c \ + mphalport.c \ + lib/mp-readline/readline.c \ + lib/utils/gchelper_generic.c \ + lib/utils/pyexec.c \ + lib/utils/stdout_helpers.c \ + + # Define the required object files. + OBJ = $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) + + # Define the top-level target, the main firmware. + all: $(BUILD)/firmware.elf + + # Define how to build the firmware. + $(BUILD)/firmware.elf: $(OBJ) + $(ECHO) "LINK $@" + $(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + $(Q)$(SIZE) $@ + + # Include remaining core make rules. + include $(TOP)/py/mkrules.mk + +Remember to use proper tabs to indent the Makefile. + +MicroPython Configurations +-------------------------- + +After integrating the minimal code above, the next step is to create the MicroPython +configuration files for the port. The compile-time configurations are specified in +``mpconfigport.h`` and additional hardware-abstraction functions, such as time keeping, +in ``mphalport.h``. + +The following is an example of an ``mpconfigport.h`` file: + +.. code-block:: c + + #include + + // Python internal features. + #define MICROPY_ENABLE_GC (1) + #define MICROPY_HELPER_REPL (1) + #define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) + #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) + + // Fine control over Python builtins, classes, modules, etc. + #define MICROPY_PY_ASYNC_AWAIT (0) + #define MICROPY_PY_BUILTINS_SET (0) + #define MICROPY_PY_ATTRTUPLE (0) + #define MICROPY_PY_COLLECTIONS (0) + #define MICROPY_PY_MATH (0) + #define MICROPY_PY_IO (0) + #define MICROPY_PY_STRUCT (0) + + // Type definitions for the specific machine. + + typedef intptr_t mp_int_t; // must be pointer size + typedef uintptr_t mp_uint_t; // must be pointer size + typedef long mp_off_t; + + // We need to provide a declaration/definition of alloca(). + #include + + // Define the port's name and hardware. + #define MICROPY_HW_BOARD_NAME "example-board" + #define MICROPY_HW_MCU_NAME "unknown-cpu" + + #define MP_STATE_PORT MP_STATE_VM + + #define MICROPY_PORT_ROOT_POINTERS \ + const char *readline_hist[8]; + +This configuration file contains machine-specific configurations including aspects like if different +MicroPython features should be enabled e.g. ``#define MICROPY_ENABLE_GC (1)``. Making this Setting +``(0)`` disables the feature. + +Other configurations include type definitions, root pointers, board name, microcontroller name +etc. + +Similarly, an minimal example ``mphalport.h`` file looks like this: + +.. code-block:: c + + static inline void mp_hal_set_interrupt_char(char c) {} + +Support for standard input/output +--------------------------------- + +MicroPython requires at least a way to output characters, and to have a REPL it also +requires a way to input characters. Functions for this can be implemented in the file +``mphalport.c``, for example: + +.. code-block:: c + + #include + #include "py/mpconfig.h" + + // Receive single character, blocking until one is available. + int mp_hal_stdin_rx_chr(void) { + unsigned char c = 0; + int r = read(STDIN_FILENO, &c, 1); + (void)r; + return c; + } + + // Send the string of given length. + void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + int r = write(STDOUT_FILENO, str, len); + (void)r; + } + +These input and output functions have to be modified depending on the +specific board API. This example uses the standard input/output stream. + +Building and running +-------------------- + +At this stage the directory of the new port should contain:: + + ports/example_port/ + ├── main.c + ├── Makefile + ├── mpconfigport.h + ├── mphalport.c + └── mphalport.h + +The port can now be built by running ``make`` (or otherwise, depending on your system). + +If you are using the default compiler settings in the Makefile given above then this +will create an executable called ``build/firmware.elf`` which can be executed directly. +To get a functional REPL you may need to first configure the terminal to raw mode: + +.. code-block:: bash + + $ stty raw opost -echo + $ ./build/firmware.elf + +That should give a MicroPython REPL. You can then run commands like: + +.. code-block:: bash + + MicroPython v1.13 on 2021-01-01; example-board with unknown-cpu + >>> import usys + >>> usys.implementation + ('micropython', (1, 13, 0)) + >>> + +Use Ctrl-D to exit, and then run ``reset`` to reset the terminal. + +Adding a module to the port +--------------------------- + +To add a custom module like ``myport``, first add the module definition in a file +``modmyport.c``: + +.. code-block:: c + + #include "py/runtime.h" + + STATIC mp_obj_t myport_info(void) { + mp_printf(&mp_plat_print, "info about my port\n"); + return mp_const_none; + } + STATIC MP_DEFINE_CONST_FUN_OBJ_0(myport_info_obj, myport_info); + + STATIC const mp_rom_map_elem_t myport_module_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_myport) }, + { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&myport_info_obj) }, + }; + STATIC MP_DEFINE_CONST_DICT(myport_module_globals, myport_module_globals_table); + + const mp_obj_module_t myport_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&myport_module_globals, + }; + + MP_REGISTER_MODULE(MP_QSTR_myport, myport_module, 1); + +Note: the "1" as the third argument in ``MP_REGISTER_MODULE`` enables this new module +unconditionally. To allow it to be conditionally enabled, replace the "1" by +``MICROPY_PY_MYPORT`` and then add ``#define MICROPY_PY_MYPORT (1)`` in ``mpconfigport.h`` +accordingly. + +You will also need to edit the Makefile to add ``modmyport.c`` to the ``SRC_C`` list, and +a new line adding the same file to ``SRC_QSTR`` (so qstrs are searched for in this new file), +like this: + +.. code-block:: Makefile + + SRC_C = \ + main.c \ + modmyport.c \ + mphalport.c \ + ... + + SRC_QSTR += modport.c + +If all went correctly then, after rebuilding, you should be able to import the new module: + +.. code-block:: bash + + >>> import myport + >>> myport.info() + info about my port + >>> diff --git a/docs/develop/publiccapi.rst b/docs/develop/publiccapi.rst new file mode 100644 index 000000000..132c7b136 --- /dev/null +++ b/docs/develop/publiccapi.rst @@ -0,0 +1,25 @@ +.. _publiccapi: + +The public C API +================ + +The public C-API comprises functions defined in all C header files in the ``py/`` +directory. Most of the important core runtime C APIs are exposed in ``runtime.h`` and +``obj.h``. + +The following is an example of public API functions from ``obj.h``: + +.. code-block:: c + + mp_obj_t mp_obj_new_list(size_t n, mp_obj_t *items); + mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg); + mp_obj_t mp_obj_list_remove(mp_obj_t self_in, mp_obj_t value); + void mp_obj_list_get(mp_obj_t self_in, size_t *len, mp_obj_t **items); + +At its core, any functions and macros in header files make up the public +API and can be used to access very low-level details of MicroPython. Static +inline functions in header files are fine too, such functions will be +inlined in the code when used. + +Header files in the ``ports`` directory are only exposed to the functionality +specific to a given port. diff --git a/docs/develop/qstr.rst b/docs/develop/qstr.rst index 3550a8bd4..cd1fc4786 100644 --- a/docs/develop/qstr.rst +++ b/docs/develop/qstr.rst @@ -1,3 +1,5 @@ +.. _qstr: + MicroPython string interning ============================ @@ -57,6 +59,7 @@ Processing happens in the following stages: information. Note that this step only uses files that have changed, which means that ``qstr.i.last`` will only contain data from files that have changed since the last compile. + 2. ``qstr.split`` is an empty file created after running ``makeqstrdefs.py split`` on qstr.i.last. It's just used as a dependency to indicate that the step ran. This script outputs one file per input C file, ``genhdr/qstr/...file.c.qstr``, @@ -71,8 +74,8 @@ Processing happens in the following stages: data is written to another file (``qstrdefs.collected.h.hash``) which allows it to track changes across builds. -4. ``qstrdefs.preprocessed.h`` adds in the QSTRs from qstrdefs*. It - concatenates ``qstrdefs.collected.h`` with ``qstrdefs*.h``, then it transforms +4. Generate an enumeration, each entry of which maps a ``MP_QSTR_Foo`` to it's corresponding index. + It concatenates ``qstrdefs.collected.h`` with ``qstrdefs*.h``, then it transforms each line from ``Q(Foo)`` to ``"Q(Foo)"`` so they pass through the preprocessor unchanged. Then the preprocessor is used to deal with any conditional compilation in ``qstrdefs*.h``. Then the transformation is undone back to diff --git a/docs/develop/writingtests.rst b/docs/develop/writingtests.rst new file mode 100644 index 000000000..4bdf4dd7a --- /dev/null +++ b/docs/develop/writingtests.rst @@ -0,0 +1,70 @@ +.. _writingtests: + +Writing tests +============= + +Tests in MicroPython are located at the path ``tests/``. The following is a listing of +key directories and the run-tests runner script: + +.. code-block:: bash + + . + ├── basics + ├── extmod + ├── float + ├── micropython + ├── run-tests + ... + +There are subfolders maintained to categorize the tests. Add a test by creating a new file in one of the +existing folders or in a new folder. It's also possible to make custom tests outside this tests folder, +which would be recommended for a custom port. + +For example, add the following code in a file ``print.py`` in the ``tests/unix/`` subdirectory: + +.. code-block:: python + + def print_one(): + print(1) + + print_one() + +If you run your tests, this test should appear in the test output: + +.. code-block:: bash + + $ cd ports/unix + $ make tests + skip unix/extra_coverage.py + pass unix/ffi_callback.py + pass unix/ffi_float.py + pass unix/ffi_float2.py + pass unix/print.py + pass unix/time.py + pass unix/time2.py + +Tests are run by comparing the output from the test target against the output from CPython. +So any test should use print statements to indicate test results. + +For tests that can't be compared to CPython (i.e. micropython-specific functionality), +you can provide a ``.py.exp`` file which will be used as the truth for comparison. + +The other way to run tests, which is useful when running on targets other than the Unix port, is: + +.. code-block:: bash + + $ cd tests + $ ./run-tests + +Then to run on a board: + +.. code-block:: bash + + $ ./run-tests --target minimal --device /dev/ttyACM0 + +And to run only a certain set of tests (eg a directory): + +.. code-block:: bash + + $ ./run-tests -d basics + $ ./run-tests float/builtin*.py diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index 1a22bd50a..a478b6658 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -364,6 +364,13 @@ For low-level driving of a NeoPixel:: import esp esp.neopixel_write(pin, grb_buf, is800khz) +.. Warning:: + By default ``NeoPixel`` is configured to control the more popular *800kHz* + units. It is possible to use alternative timing to control other (typically + 400kHz) devices by passing ``timing=0`` when constructing the + ``NeoPixel`` object. + + APA102 driver ------------- diff --git a/docs/library/esp32.rst b/docs/library/esp32.rst index c6777b8a7..f179a31ef 100644 --- a/docs/library/esp32.rst +++ b/docs/library/esp32.rst @@ -269,3 +269,51 @@ Constants esp32.WAKEUP_ANY_HIGH Selects the wake level for pins. + +Non-Volatile Storage +-------------------- + +This class gives access to the Non-Volatile storage managed by ESP-IDF. The NVS is partitioned +into namespaces and each namespace contains typed key-value pairs. The keys are strings and the +values may be various integer types, strings, and binary blobs. The driver currently only +supports 32-bit signed integers and blobs. + +.. warning:: + + Changes to NVS need to be committed to flash by calling the commit method. Failure + to call commit results in changes being lost at the next reset. + +.. class:: NVS(namespace) + + Create an object providing access to a namespace (which is automatically created if not + present). + +.. method:: NVS.set_i32(key, value) + + Sets a 32-bit signed integer value for the specified key. Remember to call *commit*! + +.. method:: NVS.get_i32(key) + + Returns the signed integer value for the specified key. Raises an OSError if the key does not + exist or has a different type. + +.. method:: NVS.set_blob(key, value) + + Sets a binary blob value for the specified key. The value passed in must support the buffer + protocol, e.g. bytes, bytearray, str. (Note that esp-idf distinguishes blobs and strings, this + method always writes a blob even if a string is passed in as value.) + Remember to call *commit*! + +.. method:: NVS.get_blob(key, buffer) + + Reads the value of the blob for the specified key into the buffer, which must be a bytearray. + Returns the actual length read. Raises an OSError if the key does not exist, has a different + type, or if the buffer is too small. + +.. method:: NVS.erase_key(key) + + Erases a key-value pair. + +.. method:: NVS.commit() + + Commits changes made by *set_xxx* methods to flash. diff --git a/docs/library/machine.Pin.rst b/docs/library/machine.Pin.rst index 095240fe1..f8e9e1054 100644 --- a/docs/library/machine.Pin.rst +++ b/docs/library/machine.Pin.rst @@ -33,8 +33,8 @@ Usage Model:: # read and print the pin value print(p2.value()) - # reconfigure pin #0 in input mode - p0.mode(p0.IN) + # reconfigure pin #0 in input mode with a pull down resistor + p0.init(p0.IN, p0.PULL_DOWN) # configure an irq callback p0.irq(lambda p:print(p)) @@ -160,25 +160,6 @@ Methods Set pin to "0" output level. -.. method:: Pin.mode([mode]) - - Get or set the pin mode. - See the constructor documentation for details of the ``mode`` argument. - -.. method:: Pin.pull([pull]) - - Get or set the pin pull state. - See the constructor documentation for details of the ``pull`` argument. - -.. method:: Pin.drive([drive]) - - Get or set the pin drive strength. - See the constructor documentation for details of the ``drive`` argument. - - Not all ports implement this method. - - Availability: WiPy. - .. method:: Pin.irq(handler=None, trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING), *, priority=1, wake=None, hard=False) Configure an interrupt handler to be called when the trigger source of the @@ -220,6 +201,41 @@ Methods This method returns a callback object. +The following methods are not part of the core Pin API and only implemented on certain ports. + +.. method:: Pin.low() + + Set pin to "0" output level. + + Availability: nrf, rp2, stm32 ports. + +.. method:: Pin.high() + + Set pin to "1" output level. + + Availability: nrf, rp2, stm32 ports. + +.. method:: Pin.mode([mode]) + + Get or set the pin mode. + See the constructor documentation for details of the ``mode`` argument. + + Availability: cc3200, stm32 ports. + +.. method:: Pin.pull([pull]) + + Get or set the pin pull state. + See the constructor documentation for details of the ``pull`` argument. + + Availability: cc3200, stm32 ports. + +.. method:: Pin.drive([drive]) + + Get or set the pin drive strength. + See the constructor documentation for details of the ``drive`` argument. + + Availability: cc3200 port. + Constants --------- diff --git a/docs/library/machine.RTC.rst b/docs/library/machine.RTC.rst index d5a4f390b..ae5446ef7 100644 --- a/docs/library/machine.RTC.rst +++ b/docs/library/machine.RTC.rst @@ -4,7 +4,7 @@ class RTC -- real time clock ============================ -The RTC is and independent clock that keeps track of the date +The RTC is an independent clock that keeps track of the date and time. Example usage:: diff --git a/docs/library/pyb.RTC.rst b/docs/library/pyb.RTC.rst index 4c736597c..c477e7f03 100644 --- a/docs/library/pyb.RTC.rst +++ b/docs/library/pyb.RTC.rst @@ -4,7 +4,7 @@ class RTC -- real time clock ============================ -The RTC is and independent clock that keeps track of the date +The RTC is an independent clock that keeps track of the date and time. Example usage:: diff --git a/docs/library/uasyncio.rst b/docs/library/uasyncio.rst index a81e532d7..1efee56e9 100644 --- a/docs/library/uasyncio.rst +++ b/docs/library/uasyncio.rst @@ -40,6 +40,10 @@ Core functions Returns the corresponding `Task` object. +.. function:: current_task() + + Return the `Task` object associated with the currently running task. + .. function:: run(coro) Create a new task from the given coroutine and run it until it completes. @@ -121,6 +125,9 @@ class Event Set the event. Any tasks waiting on the event will be scheduled to run. + Note: This must be called from within a task. It is not safe to call this + from an IRQ, scheduler callback, or other thread. See `ThreadSafeFlag`. + .. method:: Event.clear() Clear the event. @@ -132,6 +139,29 @@ class Event This is a coroutine. +class ThreadSafeFlag +-------------------- + +.. class:: ThreadSafeFlag() + + Create a new flag which can be used to synchronise a task with code running + outside the asyncio loop, such as other threads, IRQs, or scheduler + callbacks. Flags start in the cleared state. + +.. method:: ThreadSafeFlag.set() + + Set the flag. If there is a task waiting on the event, it will be scheduled + to run. + +.. method:: ThreadSafeFlag.wait() + + Wait for the flag to be set. If the flag is already set then it returns + immediately. + + A flag may only be waited on by a single task at a time. + + This is a coroutine. + class Lock ---------- @@ -184,7 +214,7 @@ TCP stream connections This is a coroutine. .. class:: Stream() - + This represents a TCP stream connection. To minimise code this class implements both a reader and a writer, and both ``StreamReader`` and ``StreamWriter`` alias to this class. diff --git a/docs/library/ussl.rst b/docs/library/ussl.rst index ffe146331..14e3f3ad1 100644 --- a/docs/library/ussl.rst +++ b/docs/library/ussl.rst @@ -13,16 +13,23 @@ facilities for network sockets, both client-side and server-side. Functions --------- -.. function:: ussl.wrap_socket(sock, server_side=False, keyfile=None, certfile=None, cert_reqs=CERT_NONE, ca_certs=None) - +.. function:: ussl.wrap_socket(sock, server_side=False, keyfile=None, certfile=None, cert_reqs=CERT_NONE, ca_certs=None, do_handshake=True) Takes a `stream` *sock* (usually usocket.socket instance of ``SOCK_STREAM`` type), and returns an instance of ssl.SSLSocket, which wraps the underlying stream in an SSL context. Returned object has the usual `stream` interface methods like - ``read()``, ``write()``, etc. In MicroPython, the returned object does not expose - socket interface and methods like ``recv()``, ``send()``. In particular, a - server-side SSL socket should be created from a normal socket returned from + ``read()``, ``write()``, etc. + A server-side SSL socket should be created from a normal socket returned from :meth:`~usocket.socket.accept()` on a non-SSL listening server socket. + - *do_handshake* determines whether the handshake is done as part of the ``wrap_socket`` + or whether it is deferred to be done as part of the initial reads or writes + (there is no ``do_handshake`` method as in CPython). + For blocking sockets doing the handshake immediately is standard. For non-blocking + sockets (i.e. when the *sock* passed into ``wrap_socket`` is in non-blocking mode) + the handshake should generally be deferred because otherwise ``wrap_socket`` blocks + until it completes. Note that in AXTLS the handshake can be deferred until the first + read or write but it then blocks until completion. + Depending on the underlying module implementation in a particular :term:`MicroPython port`, some or all keyword arguments above may be not supported. @@ -31,6 +38,11 @@ Functions Some implementations of ``ussl`` module do NOT validate server certificates, which makes an SSL connection established prone to man-in-the-middle attacks. + CPython's ``wrap_socket`` returns an ``SSLSocket`` object which has methods typical + for sockets, such as ``send``, ``recv``, etc. MicroPython's ``wrap_socket`` + returns an object more similar to CPython's ``SSLObject`` which does not have + these socket methods. + Exceptions ---------- diff --git a/docs/reference/pyboard.py.rst b/docs/reference/pyboard.py.rst index 30230eebc..4fedbb7aa 100644 --- a/docs/reference/pyboard.py.rst +++ b/docs/reference/pyboard.py.rst @@ -48,7 +48,9 @@ Running ``pyboard.py --help`` gives the following output: available --follow follow the output after running the scripts [default if no scripts given] - -f, --filesystem perform a filesystem action + -f, --filesystem perform a filesystem action: cp local :device | cp + :device local | cat path | ls [path] | rm path | mkdir + path | rmdir path Running a command on the device ------------------------------- diff --git a/examples/embedding/hello-embed.c b/examples/embedding/hello-embed.c index 2000b703c..9a90288cf 100644 --- a/examples/embedding/hello-embed.c +++ b/examples/embedding/hello-embed.c @@ -53,7 +53,7 @@ mp_obj_t execute_from_str(const char *str) { int main() { // Initialized stack limit - mp_stack_set_limit(40000 * (BYTES_PER_WORD / 4)); + mp_stack_set_limit(40000 * (sizeof(void *) / 4)); // Initialize heap gc_init(heap, heap + sizeof(heap)); // Initialize interpreter diff --git a/examples/rp2/pio_1hz.py b/examples/rp2/pio_1hz.py new file mode 100644 index 000000000..c18aa22fc --- /dev/null +++ b/examples/rp2/pio_1hz.py @@ -0,0 +1,35 @@ +# Example using PIO to blink an LED and raise an IRQ at 1Hz. + +import time +from machine import Pin +import rp2 + + +@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW) +def blink_1hz(): + # fmt: off + # Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000 + irq(rel(0)) + set(pins, 1) + set(x, 31) [5] + label("delay_high") + nop() [29] + jmp(x_dec, "delay_high") + + # Cycles: 1 + 7 + 32 * (30 + 1) = 1000 + set(pins, 0) + set(x, 31) [6] + label("delay_low") + nop() [29] + jmp(x_dec, "delay_low") + # fmt: on + + +# Create the StateMachine with the blink_1hz program, outputting on Pin(25). +sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25)) + +# Set the IRQ handler to print the millisecond timestamp. +sm.irq(lambda p: print(time.ticks_ms())) + +# Start the StateMachine. +sm.active(1) diff --git a/examples/rp2/pio_exec.py b/examples/rp2/pio_exec.py new file mode 100644 index 000000000..d8cbc33ef --- /dev/null +++ b/examples/rp2/pio_exec.py @@ -0,0 +1,27 @@ +# Example using PIO to turn on an LED via an explicit exec. +# +# Demonstrates: +# - using set_init and set_base +# - using StateMachine.exec + +import time +from machine import Pin +import rp2 + +# Define an empty program that uses a single set pin. +@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW) +def prog(): + pass + + +# Construct the StateMachine, binding Pin(25) to the set pin. +sm = rp2.StateMachine(0, prog, set_base=Pin(25)) + +# Turn on the set pin via an exec instruction. +sm.exec("set(pins, 1)") + +# Sleep for 500ms. +time.sleep(0.5) + +# Turn off the set pin via an exec instruction. +sm.exec("set(pins, 0)") diff --git a/examples/rp2/pio_pinchange.py b/examples/rp2/pio_pinchange.py new file mode 100644 index 000000000..767c8e78c --- /dev/null +++ b/examples/rp2/pio_pinchange.py @@ -0,0 +1,46 @@ +# Example using PIO to wait for a pin change and raise an IRQ. +# +# Demonstrates: +# - PIO wrapping +# - PIO wait instruction, waiting on an input pin +# - PIO irq instruction, in blocking mode with relative IRQ number +# - setting the in_base pin for a StateMachine +# - setting an irq handler for a StateMachine +# - instantiating 2x StateMachine's with the same program and different pins + +import time +from machine import Pin +import rp2 + + +@rp2.asm_pio() +def wait_pin_low(): + wrap_target() + + wait(0, pin, 0) + irq(block, rel(0)) + wait(1, pin, 0) + + wrap() + + +def handler(sm): + # Print a (wrapping) timestamp, and the state machine object. + print(time.ticks_ms(), sm) + + +# Instantiate StateMachine(0) with wait_pin_low program on Pin(16). +pin16 = Pin(16, Pin.IN, Pin.PULL_UP) +sm0 = rp2.StateMachine(0, wait_pin_low, in_base=pin16) +sm0.irq(handler) + +# Instantiate StateMachine(1) with wait_pin_low program on Pin(17). +pin17 = Pin(17, Pin.IN, Pin.PULL_UP) +sm1 = rp2.StateMachine(1, wait_pin_low, in_base=pin17) +sm1.irq(handler) + +# Start the StateMachine's running. +sm0.active(1) +sm1.active(1) + +# Now, when Pin(16) or Pin(17) is pulled low a message will be printed to the REPL. diff --git a/examples/rp2/pio_pwm.py b/examples/rp2/pio_pwm.py new file mode 100644 index 000000000..8e87448ca --- /dev/null +++ b/examples/rp2/pio_pwm.py @@ -0,0 +1,45 @@ +# Example of using PIO for PWM, and fading the brightness of an LED + +from machine import Pin +from rp2 import PIO, StateMachine, asm_pio +from time import sleep + + +@asm_pio(sideset_init=PIO.OUT_LOW) +def pwm_prog(): + # fmt: off + pull(noblock) .side(0) + mov(x, osr) # Keep most recent pull data stashed in X, for recycling by noblock + mov(y, isr) # ISR must be preloaded with PWM count max + label("pwmloop") + jmp(x_not_y, "skip") + nop() .side(1) + label("skip") + jmp(y_dec, "pwmloop") + # fmt: on + + +class PIOPWM: + def __init__(self, sm_id, pin, max_count, count_freq): + self._sm = StateMachine(sm_id, pwm_prog, freq=2 * count_freq, sideset_base=Pin(pin)) + # Use exec() to load max count into ISR + self._sm.put(max_count) + self._sm.exec("pull()") + self._sm.exec("mov(isr, osr)") + self._sm.active(1) + self._max_count = max_count + + def set(self, value): + # Minimum value is -1 (completely turn off), 0 actually still produces narrow pulse + value = max(value, -1) + value = min(value, self._max_count) + self._sm.put(value) + + +# Pin 25 is LED on Pico boards +pwm = PIOPWM(0, 25, max_count=(1 << 16) - 1, count_freq=10_000_000) + +while True: + for i in range(256): + pwm.set(i ** 2) + sleep(0.01) diff --git a/examples/rp2/pio_uart_rx.py b/examples/rp2/pio_uart_rx.py new file mode 100644 index 000000000..41dfde3da --- /dev/null +++ b/examples/rp2/pio_uart_rx.py @@ -0,0 +1,104 @@ +# Example using PIO to create a UART RX interface. +# +# To make it work you'll need a wire connecting GPIO4 and GPIO3. +# +# Demonstrates: +# - PIO shifting in data on a pin +# - PIO jmp(pin) instruction +# - PIO irq handler +# - using the second core via _thread + +import _thread +from machine import Pin, UART +from rp2 import PIO, StateMachine, asm_pio + +UART_BAUD = 9600 + +HARD_UART_TX_PIN = Pin(4, Pin.OUT) +PIO_RX_PIN = Pin(3, Pin.IN, Pin.PULL_UP) + + +@asm_pio( + autopush=True, + push_thresh=8, + in_shiftdir=rp2.PIO.SHIFT_RIGHT, +) +def uart_rx_mini(): + # fmt: off + # Wait for start bit + wait(0, pin, 0) + # Preload bit counter, delay until eye of first data bit + set(x, 7) [10] + # Loop 8 times + label("bitloop") + # Sample data + in_(pins, 1) + # Each iteration is 8 cycles + jmp(x_dec, "bitloop") [6] + # fmt: on + + +@asm_pio( + in_shiftdir=rp2.PIO.SHIFT_RIGHT, +) +def uart_rx(): + # fmt: off + label("start") + # Stall until start bit is asserted + wait(0, pin, 0) + # Preload bit counter, then delay until halfway through + # the first data bit (12 cycles incl wait, set). + set(x, 7) [10] + label("bitloop") + # Shift data bit into ISR + in_(pins, 1) + # Loop 8 times, each loop iteration is 8 cycles + jmp(x_dec, "bitloop") [6] + # Check stop bit (should be high) + jmp(pin, "good_stop") + # Either a framing error or a break. Set a sticky flag + # and wait for line to return to idle state. + irq(block, 4) + wait(1, pin, 0) + # Don't push data if we didn't see good framing. + jmp("start") + # No delay before returning to start; a little slack is + # important in case the TX clock is slightly too fast. + label("good_stop") + push(block) + # fmt: on + + +# The handler for a UART break detected by the PIO. +def handler(sm): + print("break", time.ticks_ms(), end=" ") + + +# Function for core1 to execute to write to the given UART. +def core1_task(uart, text): + uart.write(text) + + +# Set up the hard UART we're going to use to print characters. +uart = UART(1, UART_BAUD, tx=HARD_UART_TX_PIN) + +for pio_prog in ("uart_rx_mini", "uart_rx"): + # Set up the state machine we're going to use to receive the characters. + sm = StateMachine( + 0, + globals()[pio_prog], + freq=8 * UART_BAUD, + in_base=PIO_RX_PIN, # For WAIT, IN + jmp_pin=PIO_RX_PIN, # For JMP + ) + sm.irq(handler) + sm.active(1) + + # Tell core 1 to print some text to UART 1 + text = "Hello, world from PIO, using {}!".format(pio_prog) + _thread.start_new_thread(core1_task, (uart, text)) + + # Echo characters received from PIO to the console. + for i in range(len(text)): + print(chr(sm.get() >> 24), end="") + print() diff --git a/examples/rp2/pio_uart_tx.py b/examples/rp2/pio_uart_tx.py new file mode 100644 index 000000000..0f8c1260b --- /dev/null +++ b/examples/rp2/pio_uart_tx.py @@ -0,0 +1,44 @@ +# Example using PIO to create a UART TX interface + +from machine import Pin +from rp2 import PIO, StateMachine, asm_pio + +UART_BAUD = 115200 +PIN_BASE = 10 +NUM_UARTS = 8 + + +@asm_pio(sideset_init=PIO.OUT_HIGH, out_init=PIO.OUT_HIGH, out_shiftdir=PIO.SHIFT_RIGHT) +def uart_tx(): + # fmt: off + # Block with TX deasserted until data available + pull() + # Initialise bit counter, assert start bit for 8 cycles + set(x, 7) .side(0) [7] + # Shift out 8 data bits, 8 execution cycles per bit + label("bitloop") + out(pins, 1) [6] + jmp(x_dec, "bitloop") + # Assert stop bit for 8 cycles total (incl 1 for pull()) + nop() .side(1) [6] + # fmt: on + + +# Now we add 8 UART TXs, on pins 10 to 17. Use the same baud rate for all of them. +uarts = [] +for i in range(NUM_UARTS): + sm = StateMachine( + i, uart_tx, freq=8 * UART_BAUD, sideset_base=Pin(PIN_BASE + i), out_base=Pin(PIN_BASE + i) + ) + sm.active(1) + uarts.append(sm) + +# We can print characters from each UART by pushing them to the TX FIFO +def pio_uart_print(sm, s): + for c in s: + sm.put(ord(c)) + + +# Print a different message from each UART +for i, u in enumerate(uarts): + pio_uart_print(u, "Hello from UART {}!\n".format(i)) diff --git a/examples/rp2/pio_ws2812.py b/examples/rp2/pio_ws2812.py new file mode 100644 index 000000000..dd021a9d5 --- /dev/null +++ b/examples/rp2/pio_ws2812.py @@ -0,0 +1,59 @@ +# Example using PIO to drive a set of WS2812 LEDs. + +import array, time +from machine import Pin +import rp2 + +# Configure the number of WS2812 LEDs. +NUM_LEDS = 8 + + +@rp2.asm_pio( + sideset_init=rp2.PIO.OUT_LOW, + out_shiftdir=rp2.PIO.SHIFT_LEFT, + autopull=True, + pull_thresh=24, +) +def ws2812(): + # fmt: off + T1 = 2 + T2 = 5 + T3 = 3 + wrap_target() + label("bitloop") + out(x, 1) .side(0) [T3 - 1] + jmp(not_x, "do_zero") .side(1) [T1 - 1] + jmp("bitloop") .side(1) [T2 - 1] + label("do_zero") + nop() .side(0) [T2 - 1] + wrap() + # fmt: on + + +# Create the StateMachine with the ws2812 program, outputting on Pin(22). +sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(22)) + +# Start the StateMachine, it will wait for data on its FIFO. +sm.active(1) + +# Display a pattern on the LEDs via an array of LED RGB values. +ar = array.array("I", [0 for _ in range(NUM_LEDS)]) + +# Cycle colours. +for i in range(4 * NUM_LEDS): + for j in range(NUM_LEDS): + r = j * 100 // (NUM_LEDS - 1) + b = 100 - j * 100 // (NUM_LEDS - 1) + if j != i % NUM_LEDS: + r >>= 3 + b >>= 3 + ar[j] = r << 16 | b + sm.put(ar, 8) + time.sleep_ms(50) + +# Fade out. +for i in range(24): + for j in range(NUM_LEDS): + ar[j] = ar[j] >> 1 & 0x7F7F7F + sm.put(ar, 8) + time.sleep_ms(50) diff --git a/examples/rp2/pwm_fade.py b/examples/rp2/pwm_fade.py new file mode 100644 index 000000000..7264edaa2 --- /dev/null +++ b/examples/rp2/pwm_fade.py @@ -0,0 +1,25 @@ +# Example using PWM to fade an LED. + +import time +from machine import Pin, PWM + + +# Construct PWM object, with LED on Pin(25). +pwm = PWM(Pin(25)) + +# Set the PWM frequency. +pwm.freq(1000) + +# Fade the LED in and out a few times. +duty = 0 +direction = 1 +for _ in range(8 * 256): + duty += direction + if duty > 255: + duty = 255 + direction = -1 + elif duty < 0: + duty = 0 + direction = 1 + pwm.duty_u16(duty * duty) + time.sleep(0.001) diff --git a/extmod/btstack/btstack.mk b/extmod/btstack/btstack.mk index e3309b61d..17bbb16f8 100644 --- a/extmod/btstack/btstack.mk +++ b/extmod/btstack/btstack.mk @@ -11,6 +11,8 @@ EXTMOD_SRC_C += extmod/btstack/modbluetooth_btstack.c INC += -I$(TOP)/$(BTSTACK_EXTMOD_DIR) CFLAGS_MOD += -DMICROPY_BLUETOOTH_BTSTACK=1 +CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS=1 +CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING=1 BTSTACK_DIR = $(TOP)/lib/btstack diff --git a/extmod/btstack/btstack_hci_uart.c b/extmod/btstack/btstack_hci_uart.c index ae18628a9..7205dba06 100644 --- a/extmod/btstack/btstack_hci_uart.c +++ b/extmod/btstack/btstack_hci_uart.c @@ -38,6 +38,11 @@ #include "mpbtstackport.h" +#define HCI_TRACE (0) +#define COL_OFF "\033[0m" +#define COL_GREEN "\033[0;32m" +#define COL_BLUE "\033[0;34m" + // Implements a btstack btstack_uart_block_t on top of the mphciuart.h // interface to an HCI UART provided by the port. @@ -62,8 +67,7 @@ STATIC int btstack_uart_init(const btstack_uart_config_t *uart_config) { send_handler = NULL; // Set up the UART peripheral, attach IRQ and power up the HCI controller. - // We haven't been told the baud rate yet, so defer that until btstack_uart_set_baudrate. - if (mp_bluetooth_hci_uart_init(MICROPY_HW_BLE_UART_ID, 0)) { + if (mp_bluetooth_hci_uart_init(MICROPY_HW_BLE_UART_ID, MICROPY_HW_BLE_UART_BAUDRATE)) { init_success = false; return -1; } @@ -114,6 +118,14 @@ STATIC void btstack_uart_receive_block(uint8_t *buf, uint16_t len) { } STATIC void btstack_uart_send_block(const uint8_t *buf, uint16_t len) { + #if HCI_TRACE + printf(COL_GREEN "< [% 8d] %02x", (int)mp_hal_ticks_ms(), buf[0]); + for (size_t i = 1; i < len; ++i) { + printf(":%02x", buf[i]); + } + printf(COL_OFF "\n"); + #endif + mp_bluetooth_hci_uart_write(buf, len); send_done = true; } @@ -165,6 +177,13 @@ void mp_bluetooth_btstack_hci_uart_process(void) { while (recv_idx < recv_len && (chr = mp_bluetooth_hci_uart_readchar()) >= 0) { recv_buf[recv_idx++] = chr; if (recv_idx == recv_len) { + #if HCI_TRACE + printf(COL_BLUE "> [% 8d] %02x", (int)mp_hal_ticks_ms(), recv_buf[0]); + for (size_t i = 1; i < recv_len; ++i) { + printf(":%02x", recv_buf[i]); + } + printf(COL_OFF "\n"); + #endif recv_idx = 0; recv_len = 0; if (recv_handler) { diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index f6af664a4..5f047e325 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -91,7 +91,7 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uu } return result; } -#endif +#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // Notes on supporting background ops (e.g. an attempt to gatts_notify while // an existing notification is in progress): @@ -218,7 +218,7 @@ STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_ty return pending_op; } -#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT // Cleans up a pending op of the specified type for this conn_handle (and if specified, value_handle). // Used by MP_BLUETOOTH_BTSTACK_PENDING_WRITE and MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE. @@ -282,7 +282,7 @@ STATIC void btstack_packet_handler_att_server(uint8_t packet_type, uint16_t chan } } -#if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS +#if MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS // During startup, the controller (e.g. Zephyr) might give us a static address that we can use. STATIC uint8_t controller_static_addr[6] = {0}; STATIC bool controller_static_addr_available = false; @@ -349,13 +349,13 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t DEBUG_printf(" --> hci transport packet sent\n"); } else if (event_type == HCI_EVENT_COMMAND_COMPLETE) { DEBUG_printf(" --> hci command complete\n"); - #if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + #if MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS if (memcmp(packet, read_static_address_command_complete_prefix, sizeof(read_static_address_command_complete_prefix)) == 0) { DEBUG_printf(" --> static address available\n"); reverse_48(&packet[7], controller_static_addr); controller_static_addr_available = true; } - #endif // MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + #endif // MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS } else if (event_type == HCI_EVENT_COMMAND_STATUS) { DEBUG_printf(" --> hci command status\n"); } else if (event_type == HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS) { @@ -418,6 +418,8 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t uint8_t length = gap_event_advertising_report_get_data_length(packet); const uint8_t *data = gap_event_advertising_report_get_data(packet); mp_bluetooth_gap_on_scan_result(address_type, address, adv_event_type, rssi, data, length); + #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); uint16_t status = gatt_event_query_complete_get_att_status(packet); @@ -487,7 +489,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t // Note: Can't "del" the pending_op from IRQ context. Leave it for the GC. } - #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT } else { DEBUG_printf(" --> hci event type: unknown (0x%02x)\n", event_type); } @@ -506,7 +508,7 @@ STATIC btstack_packet_callback_registration_t hci_event_callback_registration = .callback = &btstack_packet_handler_generic }; -#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT // For when the handler is being used for service discovery. STATIC void btstack_packet_handler_discover_services(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { (void)channel; @@ -541,7 +543,7 @@ STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint (void)size; btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE); } -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT STATIC btstack_timer_source_t btstack_init_deinit_timeout; @@ -575,12 +577,12 @@ STATIC bool set_public_address(void) { } STATIC void set_random_address(void) { - #if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + #if MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS if (controller_static_addr_available) { DEBUG_printf("set_random_address: Using static address supplied by controller.\n"); gap_random_address_set(controller_static_addr); } else - #endif // MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + #endif // MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS { bd_addr_t static_addr; @@ -635,7 +637,7 @@ int mp_bluetooth_init(void) { btstack_memory_init(); - #if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + #if MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS controller_static_addr_available = false; #endif @@ -662,12 +664,12 @@ int mp_bluetooth_init(void) { sm_set_er(dummy_key); sm_set_ir(dummy_key); - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT gatt_client_init(); // We always require explicitly exchanging MTU with ble.gattc_exchange_mtu(). gatt_client_mtu_enable_auto_negotiation(false); - #endif + #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT // Register for HCI events. hci_add_event_handler(&hci_event_callback_registration); @@ -719,10 +721,10 @@ int mp_bluetooth_init(void) { set_random_address(); } - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT // Enable GATT_EVENT_NOTIFICATION/GATT_EVENT_INDICATION for all connections and handles. gatt_client_listen_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification, &btstack_packet_handler_generic, GATT_CLIENT_ANY_CONNECTION, NULL); - #endif + #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT return 0; } @@ -737,10 +739,10 @@ void mp_bluetooth_deinit(void) { mp_bluetooth_gap_advertise_stop(); - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT // Remove our registration for notify/indicate. gatt_client_stop_listening_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification); - #endif + #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT // Set a timer that will forcibly set the state to TIMEOUT, which will stop the loop below. btstack_run_loop_set_timer(&btstack_init_deinit_timeout, BTSTACK_INIT_DEINIT_TIMEOUT_MS); @@ -1168,11 +1170,18 @@ int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { } #if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING + int mp_bluetooth_gap_pair(uint16_t conn_handle) { DEBUG_printf("mp_bluetooth_gap_pair: conn_handle=%d\n", conn_handle); sm_request_pairing(conn_handle); return 0; } + +int mp_bluetooth_gap_passkey(uint16_t conn_handle, uint8_t action, mp_int_t passkey) { + DEBUG_printf("mp_bluetooth_gap_passkey: conn_handle=%d action=%d passkey=%d\n", conn_handle, action, (int)passkey); + return MP_EOPNOTSUPP; +} + #endif // MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE @@ -1225,6 +1234,10 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, return btstack_error_to_errno(gap_connect(btstack_addr, addr_type)); } +#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT + int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) { DEBUG_printf("mp_bluetooth_gattc_discover_primary_services\n"); uint8_t err; @@ -1339,6 +1352,35 @@ int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) { return 0; } -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT + +#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS + +int mp_bluetooth_l2cap_listen(uint16_t psm, uint16_t mtu) { + DEBUG_printf("mp_bluetooth_l2cap_listen: psm=%d, mtu=%d\n", psm, mtu); + return MP_EOPNOTSUPP; +} + +int mp_bluetooth_l2cap_connect(uint16_t conn_handle, uint16_t psm, uint16_t mtu) { + DEBUG_printf("mp_bluetooth_l2cap_connect: conn_handle=%d, psm=%d, mtu=%d\n", conn_handle, psm, mtu); + return MP_EOPNOTSUPP; +} + +int mp_bluetooth_l2cap_disconnect(uint16_t conn_handle, uint16_t cid) { + DEBUG_printf("mp_bluetooth_l2cap_disconnect: conn_handle=%d, cid=%d\n", conn_handle, cid); + return MP_EOPNOTSUPP; +} + +int mp_bluetooth_l2cap_send(uint16_t conn_handle, uint16_t cid, const uint8_t *buf, size_t len, bool *stalled) { + DEBUG_printf("mp_bluetooth_l2cap_send: conn_handle=%d, cid=%d, len=%d\n", conn_handle, cid, (int)len); + return MP_EOPNOTSUPP; +} + +int mp_bluetooth_l2cap_recvinto(uint16_t conn_handle, uint16_t cid, uint8_t *buf, size_t *len) { + DEBUG_printf("mp_bluetooth_l2cap_recvinto: conn_handle=%d, cid=%d, len=%d\n", conn_handle, cid, (int)*len); + return MP_EOPNOTSUPP; +} + +#endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake new file mode 100644 index 000000000..6668a2d8a --- /dev/null +++ b/extmod/extmod.cmake @@ -0,0 +1,44 @@ +# CMake fragment for MicroPython extmod component + +set(MICROPY_EXTMOD_DIR "${MICROPY_DIR}/extmod") +set(MICROPY_OOFATFS_DIR "${MICROPY_DIR}/lib/oofatfs") + +set(MICROPY_SOURCE_EXTMOD + ${MICROPY_EXTMOD_DIR}/machine_i2c.c + ${MICROPY_EXTMOD_DIR}/machine_mem.c + ${MICROPY_EXTMOD_DIR}/machine_pulse.c + ${MICROPY_EXTMOD_DIR}/machine_signal.c + ${MICROPY_EXTMOD_DIR}/machine_spi.c + ${MICROPY_EXTMOD_DIR}/modbluetooth.c + ${MICROPY_EXTMOD_DIR}/modbtree.c + ${MICROPY_EXTMOD_DIR}/modframebuf.c + ${MICROPY_EXTMOD_DIR}/moduasyncio.c + ${MICROPY_EXTMOD_DIR}/modubinascii.c + ${MICROPY_EXTMOD_DIR}/moducryptolib.c + ${MICROPY_EXTMOD_DIR}/moductypes.c + ${MICROPY_EXTMOD_DIR}/moduhashlib.c + ${MICROPY_EXTMOD_DIR}/moduheapq.c + ${MICROPY_EXTMOD_DIR}/modujson.c + ${MICROPY_EXTMOD_DIR}/modurandom.c + ${MICROPY_EXTMOD_DIR}/modure.c + ${MICROPY_EXTMOD_DIR}/moduselect.c + ${MICROPY_EXTMOD_DIR}/modussl_axtls.c + ${MICROPY_EXTMOD_DIR}/modussl_mbedtls.c + ${MICROPY_EXTMOD_DIR}/modutimeq.c + ${MICROPY_EXTMOD_DIR}/moduwebsocket.c + ${MICROPY_EXTMOD_DIR}/moduzlib.c + ${MICROPY_EXTMOD_DIR}/modwebrepl.c + ${MICROPY_EXTMOD_DIR}/uos_dupterm.c + ${MICROPY_EXTMOD_DIR}/utime_mphal.c + ${MICROPY_EXTMOD_DIR}/vfs.c + ${MICROPY_EXTMOD_DIR}/vfs_blockdev.c + ${MICROPY_EXTMOD_DIR}/vfs_fat.c + ${MICROPY_EXTMOD_DIR}/vfs_fat_diskio.c + ${MICROPY_EXTMOD_DIR}/vfs_fat_file.c + ${MICROPY_EXTMOD_DIR}/vfs_lfs.c + ${MICROPY_EXTMOD_DIR}/vfs_posix.c + ${MICROPY_EXTMOD_DIR}/vfs_posix_file.c + ${MICROPY_EXTMOD_DIR}/vfs_reader.c + ${MICROPY_EXTMOD_DIR}/virtpin.c + ${MICROPY_EXTMOD_DIR}/nimble/modbluetooth_nimble.c +) diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index caaf872e7..269493f0a 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -761,7 +761,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_set_buffer_obj, 3 // Bluetooth object: GATTC (Central/Scanner role) // ---------------------------------------------------------------------------- -#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT STATIC mp_obj_t bluetooth_ble_gattc_discover_services(size_t n_args, const mp_obj_t *args) { mp_int_t conn_handle = mp_obj_get_int(args[1]); @@ -830,7 +830,7 @@ STATIC mp_obj_t bluetooth_ble_gattc_exchange_mtu(mp_obj_t self_in, mp_obj_t conn } STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_gattc_exchange_mtu_obj, bluetooth_ble_gattc_exchange_mtu); -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS @@ -884,6 +884,23 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_l2cap_recvinto_obj, 4, #endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS +#if MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD + +STATIC mp_obj_t bluetooth_ble_hci_cmd(size_t n_args, const mp_obj_t *args) { + mp_int_t ogf = mp_obj_get_int(args[1]); + mp_int_t ocf = mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo_request = {0}; + mp_buffer_info_t bufinfo_response = {0}; + mp_get_buffer_raise(args[3], &bufinfo_request, MP_BUFFER_READ); + mp_get_buffer_raise(args[4], &bufinfo_response, MP_BUFFER_WRITE); + uint8_t status = 0; + bluetooth_handle_errno(mp_bluetooth_hci_cmd(ogf, ocf, bufinfo_request.buf, bufinfo_request.len, bufinfo_response.buf, bufinfo_response.len, &status)); + return MP_OBJ_NEW_SMALL_INT(status); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_hci_cmd_obj, 5, 5, bluetooth_ble_hci_cmd); + +#endif // MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD + // ---------------------------------------------------------------------------- // Bluetooth object: Definition // ---------------------------------------------------------------------------- @@ -904,15 +921,15 @@ STATIC const mp_rom_map_elem_t bluetooth_ble_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_gap_pair), MP_ROM_PTR(&bluetooth_ble_gap_pair_obj) }, { MP_ROM_QSTR(MP_QSTR_gap_passkey), MP_ROM_PTR(&bluetooth_ble_gap_passkey_obj) }, #endif - // GATT Server (i.e. peripheral/advertiser role) + // GATT Server { MP_ROM_QSTR(MP_QSTR_gatts_register_services), MP_ROM_PTR(&bluetooth_ble_gatts_register_services_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_read), MP_ROM_PTR(&bluetooth_ble_gatts_read_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_write), MP_ROM_PTR(&bluetooth_ble_gatts_write_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_notify), MP_ROM_PTR(&bluetooth_ble_gatts_notify_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_indicate), MP_ROM_PTR(&bluetooth_ble_gatts_indicate_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_set_buffer), MP_ROM_PTR(&bluetooth_ble_gatts_set_buffer_obj) }, - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - // GATT Client (i.e. central/scanner role) + #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT + // GATT Client { MP_ROM_QSTR(MP_QSTR_gattc_discover_services), MP_ROM_PTR(&bluetooth_ble_gattc_discover_services_obj) }, { MP_ROM_QSTR(MP_QSTR_gattc_discover_characteristics), MP_ROM_PTR(&bluetooth_ble_gattc_discover_characteristics_obj) }, { MP_ROM_QSTR(MP_QSTR_gattc_discover_descriptors), MP_ROM_PTR(&bluetooth_ble_gattc_discover_descriptors_obj) }, @@ -927,6 +944,9 @@ STATIC const mp_rom_map_elem_t bluetooth_ble_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_l2cap_send), MP_ROM_PTR(&bluetooth_ble_l2cap_send_obj) }, { MP_ROM_QSTR(MP_QSTR_l2cap_recvinto), MP_ROM_PTR(&bluetooth_ble_l2cap_recvinto_obj) }, #endif + #if MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD + { MP_ROM_QSTR(MP_QSTR_hci_cmd), MP_ROM_PTR(&bluetooth_ble_hci_cmd_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(bluetooth_ble_locals_dict, bluetooth_ble_locals_dict_table); @@ -1047,6 +1067,8 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) { } else if (event == MP_BLUETOOTH_IRQ_SCAN_DONE) { // No params required. data_tuple->len = 0; + #endif + #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT } else if (event == MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT) { // conn_handle, start_handle, end_handle, uuid ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, &o->irq_data_uuid, NULL); @@ -1065,7 +1087,7 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) { } else if (event == MP_BLUETOOTH_IRQ_GATTC_READ_DONE || event == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { // conn_handle, value_handle, status ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, NULL, NULL); - #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT } MICROPY_PY_BLUETOOTH_EXIT @@ -1208,7 +1230,7 @@ void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value) { } #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS -mp_int_t mp_bluetooth_gattc_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) { +mp_int_t mp_bluetooth_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) { mp_int_t args[] = {conn_handle, cid, psm, our_mtu, peer_mtu}; mp_obj_t result = invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_ACCEPT, args, 5, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); // Return non-zero from IRQ handler to fail the accept. @@ -1217,22 +1239,22 @@ mp_int_t mp_bluetooth_gattc_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, return ret; } -void mp_bluetooth_gattc_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) { +void mp_bluetooth_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) { mp_int_t args[] = {conn_handle, cid, psm, our_mtu, peer_mtu}; invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_CONNECT, args, 5, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); } -void mp_bluetooth_gattc_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status) { +void mp_bluetooth_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status) { mp_int_t args[] = {conn_handle, cid, psm, status}; invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_DISCONNECT, args, 4, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); } -void mp_bluetooth_gattc_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status) { +void mp_bluetooth_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status) { mp_int_t args[] = {conn_handle, cid, status}; invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_SEND_READY, args, 3, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); } -void mp_bluetooth_gattc_on_l2cap_recv(uint16_t conn_handle, uint16_t cid) { +void mp_bluetooth_on_l2cap_recv(uint16_t conn_handle, uint16_t cid) { mp_int_t args[] = {conn_handle, cid}; invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_RECV, args, 2, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); } @@ -1247,7 +1269,9 @@ void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uin mp_int_t args[] = {addr_type, adv_type, rssi}; invoke_irq_handler(MP_BLUETOOTH_IRQ_SCAN_RESULT, args, 1, 2, addr, NULL_UUID, &data, &data_len, 1); } +#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid) { mp_int_t args[] = {conn_handle, start_handle, end_handle}; invoke_irq_handler(MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT, args, 3, 0, NULL_ADDR, service_uuid, NULL_DATA, NULL_DATA_LEN, 0); @@ -1305,7 +1329,7 @@ void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle invoke_irq_handler(event, args, 3, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); } -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT #else // !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS // Callbacks are called in interrupt context (i.e. can't allocate), so we need to push the data @@ -1451,7 +1475,9 @@ void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uin } schedule_ringbuf(atomic_state); } +#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid) { MICROPY_PY_BLUETOOTH_ENTER mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); @@ -1539,7 +1565,7 @@ void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle } schedule_ringbuf(atomic_state); } -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT #endif // MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index f14033de3..d126ad6c1 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -43,6 +43,12 @@ #define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (0) #endif +#ifndef MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT +// Enable the client by default if we're enabling central mode. It's possible +// to enable client without central though. +#define MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT (MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE) +#endif + #ifndef MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS // This can be enabled if the BLE stack runs entirely in scheduler context // and therefore is able to call directly into the VM to run Python callbacks. @@ -60,6 +66,12 @@ #define MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING (0) #endif +// Optionally enable support for the `hci_cmd` function allowing +// Python to directly low-level HCI commands. +#ifndef MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD +#define MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD (0) +#endif + // This is used to protect the ringbuffer. // A port may no-op this if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS is enabled. #ifndef MICROPY_PY_BLUETOOTH_ENTER @@ -359,7 +371,9 @@ int mp_bluetooth_gap_scan_stop(void); // Connect to a found peripheral. int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms); +#endif +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT // Find all primary services on the connected peripheral. int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid); @@ -377,7 +391,7 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const // Initiate MTU exchange for a specific connection using the preferred MTU. int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle); -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS int mp_bluetooth_l2cap_listen(uint16_t psm, uint16_t mtu); @@ -387,6 +401,10 @@ int mp_bluetooth_l2cap_send(uint16_t conn_handle, uint16_t cid, const uint8_t *b int mp_bluetooth_l2cap_recvinto(uint16_t conn_handle, uint16_t cid, uint8_t *buf, size_t *len); #endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS +#if MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD +int mp_bluetooth_hci_cmd(uint16_t ogf, uint16_t ocf, const uint8_t *req, size_t req_len, uint8_t *resp, size_t resp_len, uint8_t *status); +#endif // MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD + ///////////////////////////////////////////////////////////////////////////// // API implemented by modbluetooth (called by port-specific implementations): @@ -430,7 +448,9 @@ void mp_bluetooth_gap_on_scan_complete(void); // Notify modbluetooth of a scan result. void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uint8_t adv_type, const int8_t rssi, const uint8_t *data, size_t data_len); +#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT // Notify modbluetooth that a service was found (either by discover-all, or discover-by-uuid). void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid); @@ -448,14 +468,14 @@ void mp_bluetooth_gattc_on_data_available(uint8_t event, uint16_t conn_handle, u // Notify modbluetooth that a read or write operation has completed. void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle, uint16_t value_handle, uint16_t status); -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS -mp_int_t mp_bluetooth_gattc_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu); -void mp_bluetooth_gattc_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu); -void mp_bluetooth_gattc_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status); -void mp_bluetooth_gattc_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status); -void mp_bluetooth_gattc_on_l2cap_recv(uint16_t conn_handle, uint16_t cid); +mp_int_t mp_bluetooth_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu); +void mp_bluetooth_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu); +void mp_bluetooth_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status); +void mp_bluetooth_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status); +void mp_bluetooth_on_l2cap_recv(uint16_t conn_handle, uint16_t cid); #endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS // For stacks that don't manage attribute value data (currently all of them), helpers diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 2753acc27..18b170078 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -45,9 +45,9 @@ typedef struct _mp_obj_framebuf_t { STATIC const mp_obj_type_t mp_type_framebuf; #endif -typedef void (*setpixel_t)(const mp_obj_framebuf_t *, int, int, uint32_t); -typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t *, int, int); -typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, int, int, int, int, uint32_t); +typedef void (*setpixel_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int, uint32_t); +typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int); +typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int, unsigned int, unsigned int, uint32_t); typedef struct _mp_framebuf_p_t { setpixel_t setpixel; @@ -66,25 +66,25 @@ typedef struct _mp_framebuf_p_t { // Functions for MHLSB and MHMSB -STATIC void mono_horiz_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void mono_horiz_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { size_t index = (x + y * fb->stride) >> 3; - int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07); + unsigned int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07); ((uint8_t *)fb->buf)[index] = (((uint8_t *)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset); } -STATIC uint32_t mono_horiz_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t mono_horiz_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { size_t index = (x + y * fb->stride) >> 3; - int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07); + unsigned int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07); return (((uint8_t *)fb->buf)[index] >> (offset)) & 0x01; } -STATIC void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { - int reverse = fb->format == FRAMEBUF_MHMSB; - int advance = fb->stride >> 3; +STATIC void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + unsigned int reverse = fb->format == FRAMEBUF_MHMSB; + unsigned int advance = fb->stride >> 3; while (w--) { uint8_t *b = &((uint8_t *)fb->buf)[(x >> 3) + y * advance]; - int offset = reverse ? x & 7 : 7 - (x & 7); - for (int hh = h; hh; --hh) { + unsigned int offset = reverse ? x & 7 : 7 - (x & 7); + for (unsigned int hh = h; hh; --hh) { *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset); b += advance; } @@ -94,21 +94,21 @@ STATIC void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int // Functions for MVLSB format -STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { size_t index = (y >> 3) * fb->stride + x; uint8_t offset = y & 0x07; ((uint8_t *)fb->buf)[index] = (((uint8_t *)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset); } -STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { return (((uint8_t *)fb->buf)[(y >> 3) * fb->stride + x] >> (y & 0x07)) & 0x01; } -STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { +STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { while (h--) { uint8_t *b = &((uint8_t *)fb->buf)[(y >> 3) * fb->stride + x]; uint8_t offset = y & 0x07; - for (int ww = w; ww; --ww) { + for (unsigned int ww = w; ww; --ww) { *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset); ++b; } @@ -118,18 +118,18 @@ STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, in // Functions for RGB565 format -STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { ((uint16_t *)fb->buf)[x + y * fb->stride] = col; } -STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { return ((uint16_t *)fb->buf)[x + y * fb->stride]; } -STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { +STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; while (h--) { - for (int ww = w; ww; --ww) { + for (unsigned int ww = w; ww; --ww) { *b++ = col; } b += fb->stride - w; @@ -138,7 +138,7 @@ STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, i // Functions for GS2_HMSB format -STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 2]; uint8_t shift = (x & 0x3) << 1; uint8_t mask = 0x3 << shift; @@ -146,15 +146,15 @@ STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_ *pixel = color | (*pixel & (~mask)); } -STATIC uint32_t gs2_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t gs2_hmsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { uint8_t pixel = ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 2]; uint8_t shift = (x & 0x3) << 1; return (pixel >> shift) & 0x3; } -STATIC void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { - for (int xx = x; xx < x + w; xx++) { - for (int yy = y; yy < y + h; yy++) { +STATIC void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + for (unsigned int xx = x; xx < x + w; xx++) { + for (unsigned int yy = y; yy < y + h; yy++) { gs2_hmsb_setpixel(fb, xx, yy, col); } } @@ -162,7 +162,7 @@ STATIC void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, // Functions for GS4_HMSB format -STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1]; if (x % 2) { @@ -172,7 +172,7 @@ STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_ } } -STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { if (x % 2) { return ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1] & 0x0f; } @@ -180,16 +180,16 @@ STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { return ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1] >> 4; } -STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { +STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { col &= 0x0f; uint8_t *pixel_pair = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1]; uint8_t col_shifted_left = col << 4; uint8_t col_pixel_pair = col_shifted_left | col; - int pixel_count_till_next_line = (fb->stride - w) >> 1; + unsigned int pixel_count_till_next_line = (fb->stride - w) >> 1; bool odd_x = (x % 2 == 1); while (h--) { - int ww = w; + unsigned int ww = w; if (odd_x && ww > 0) { *pixel_pair = (*pixel_pair & 0xf0) | col; @@ -213,16 +213,16 @@ STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, // Functions for GS8 format -STATIC void gs8_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void gs8_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride)]; *pixel = col & 0xff; } -STATIC uint32_t gs8_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t gs8_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { return ((uint8_t *)fb->buf)[(x + y * fb->stride)]; } -STATIC void gs8_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { +STATIC void gs8_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride)]; while (h--) { memset(pixel, col, w); @@ -240,11 +240,11 @@ STATIC mp_framebuf_p_t formats[] = { [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect}, }; -static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { formats[fb->format].setpixel(fb, x, y, col); } -static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { return formats[fb->format].getpixel(fb, x, y); } diff --git a/extmod/modonewire.c b/extmod/modonewire.c index 210bee366..6abe3dfad 100644 --- a/extmod/modonewire.c +++ b/extmod/modonewire.c @@ -44,10 +44,10 @@ #define TIMING_WRITE3 (10) STATIC int onewire_bus_reset(mp_hal_pin_obj_t pin) { - mp_hal_pin_write(pin, 0); + mp_hal_pin_od_low(pin); mp_hal_delay_us(TIMING_RESET1); uint32_t i = mp_hal_quiet_timing_enter(); - mp_hal_pin_write(pin, 1); + mp_hal_pin_od_high(pin); mp_hal_delay_us_fast(TIMING_RESET2); int status = !mp_hal_pin_read(pin); mp_hal_quiet_timing_exit(i); @@ -56,11 +56,11 @@ STATIC int onewire_bus_reset(mp_hal_pin_obj_t pin) { } STATIC int onewire_bus_readbit(mp_hal_pin_obj_t pin) { - mp_hal_pin_write(pin, 1); + mp_hal_pin_od_high(pin); uint32_t i = mp_hal_quiet_timing_enter(); - mp_hal_pin_write(pin, 0); + mp_hal_pin_od_low(pin); mp_hal_delay_us_fast(TIMING_READ1); - mp_hal_pin_write(pin, 1); + mp_hal_pin_od_high(pin); mp_hal_delay_us_fast(TIMING_READ2); int value = mp_hal_pin_read(pin); mp_hal_quiet_timing_exit(i); @@ -70,13 +70,13 @@ STATIC int onewire_bus_readbit(mp_hal_pin_obj_t pin) { STATIC void onewire_bus_writebit(mp_hal_pin_obj_t pin, int value) { uint32_t i = mp_hal_quiet_timing_enter(); - mp_hal_pin_write(pin, 0); + mp_hal_pin_od_low(pin); mp_hal_delay_us_fast(TIMING_WRITE1); if (value) { - mp_hal_pin_write(pin, 1); + mp_hal_pin_od_high(pin); } mp_hal_delay_us_fast(TIMING_WRITE2); - mp_hal_pin_write(pin, 1); + mp_hal_pin_od_high(pin); mp_hal_delay_us_fast(TIMING_WRITE3); mp_hal_quiet_timing_exit(i); } diff --git a/extmod/modure.c b/extmod/modure.c index c568ad443..3fd6180a9 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -111,7 +111,7 @@ STATIC void match_span_helper(size_t n_args, const mp_obj_t *args, mp_obj_t span if (n_args == 2) { no = mp_obj_get_int(args[1]); if (no < 0 || no >= self->num_matches) { - mp_raise_or_return(mp_obj_new_exception_arg1(&mp_type_IndexError, args[1]), 1); + mp_raise_or_return_value(mp_obj_new_exception_arg1(&mp_type_IndexError, args[1]), 1); } } diff --git a/extmod/moduselect.c b/extmod/moduselect.c index 80beb8e09..6d8249f42 100644 --- a/extmod/moduselect.c +++ b/extmod/moduselect.c @@ -148,7 +148,7 @@ STATIC mp_obj_t select_select(size_t n_args, const mp_obj_t *args) { // poll the objects mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len); - if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) { + if (n_ready > 0 || (timeout != (mp_uint_t)-1 && mp_hal_ticks_ms() - start_tick >= timeout)) { // one or more objects are ready, or we had a timeout mp_obj_t list_array[3]; list_array[0] = mp_obj_new_list(rwx_len[0], NULL); @@ -250,7 +250,7 @@ STATIC mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) { for (;;) { // poll the objects n_ready = poll_map_poll(&self->poll_map, NULL); - if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) { + if (n_ready > 0 || (timeout != (mp_uint_t)-1 && mp_hal_ticks_ms() - start_tick >= timeout)) { break; } MICROPY_EVENT_POLL_HOOK diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 6b77b4f45..dbd491ce9 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -184,10 +184,15 @@ STATIC mp_obj_ssl_socket_t *ussl_socket_new(mp_obj_t sock, struct ssl_args *args o->ssl_sock = ssl_client_new(o->ssl_ctx, (long)sock, NULL, 0, ext); if (args->do_handshake.u_bool) { - int res = ssl_handshake_status(o->ssl_sock); - - if (res != SSL_OK) { - ussl_raise_error(res); + int r = ssl_handshake_status(o->ssl_sock); + + if (r != SSL_OK) { + if (r == SSL_CLOSE_NOTIFY) { // EOF + r = MP_ENOTCONN; + } else if (r == SSL_EAGAIN) { + r = MP_EAGAIN; + } + ussl_raise_error(r); } } @@ -263,8 +268,24 @@ STATIC mp_uint_t ussl_socket_write(mp_obj_t o_in, const void *buf, mp_uint_t siz return MP_STREAM_ERROR; } - mp_int_t r = ssl_write(o->ssl_sock, buf, size); + mp_int_t r; +eagain: + r = ssl_write(o->ssl_sock, buf, size); + if (r == 0) { + // see comment in ussl_socket_read above + if (o->blocking) { + goto eagain; + } else { + r = SSL_EAGAIN; + } + } if (r < 0) { + if (r == SSL_CLOSE_NOTIFY || r == SSL_ERROR_CONN_LOST) { + return 0; // EOF + } + if (r == SSL_EAGAIN) { + r = MP_EAGAIN; + } *errcode = r; return MP_STREAM_ERROR; } diff --git a/extmod/modussl_mbedtls.c b/extmod/modussl_mbedtls.c index 1677dc6e1..277af37c7 100644 --- a/extmod/modussl_mbedtls.c +++ b/extmod/modussl_mbedtls.c @@ -133,6 +133,7 @@ STATIC int _mbedtls_ssl_send(void *ctx, const byte *buf, size_t len) { } } +// _mbedtls_ssl_recv is called by mbedtls to receive bytes from the underlying socket STATIC int _mbedtls_ssl_recv(void *ctx, byte *buf, size_t len) { mp_obj_t sock = *(mp_obj_t *)ctx; @@ -171,7 +172,7 @@ STATIC mp_obj_ssl_socket_t *socket_new(mp_obj_t sock, struct ssl_args *args) { mbedtls_pk_init(&o->pkey); mbedtls_ctr_drbg_init(&o->ctr_drbg); #ifdef MBEDTLS_DEBUG_C - // Debug level (0-4) + // Debug level (0-4) 1=warning, 2=info, 3=debug, 4=verbose mbedtls_debug_set_threshold(0); #endif diff --git a/extmod/nimble/hal/hal_uart.c b/extmod/nimble/hal/hal_uart.c index 230970b08..cd5e49e30 100644 --- a/extmod/nimble/hal/hal_uart.c +++ b/extmod/nimble/hal/hal_uart.c @@ -66,7 +66,7 @@ void hal_uart_start_tx(uint32_t port) { } #if HCI_TRACE - printf("< [% 8d] %02x", mp_hal_ticks_ms(), mp_bluetooth_hci_cmd_buf[0]); + printf("< [% 8d] %02x", (int)mp_hal_ticks_ms(), mp_bluetooth_hci_cmd_buf[0]); for (size_t i = 1; i < len; ++i) { printf(":%02x", mp_bluetooth_hci_cmd_buf[i]); } @@ -86,7 +86,7 @@ void mp_bluetooth_nimble_hci_uart_process(bool run_events) { int chr; while ((chr = mp_bluetooth_hci_uart_readchar()) >= 0) { #if HCI_TRACE - printf("> %02x (%d)\n", chr); + printf("> %02x\n", chr); #endif hal_uart_rx_cb(hal_uart_rx_arg, chr); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 613e325a9..e3a2f872e 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -48,6 +48,11 @@ #include "nimble/host/src/ble_l2cap_priv.h" #endif +#if MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD || MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS +// For ble_hs_hci_cmd_tx +#include "nimble/host/src/ble_hs_hci_priv.h" +#endif + #ifndef MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME #define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY NIMBLE" #endif @@ -75,15 +80,80 @@ STATIC int8_t ble_hs_err_to_errno_table[] = { [BLE_HS_EBADDATA] = MP_EINVAL, }; +STATIC int ble_hs_err_to_errno(int err); + +STATIC ble_uuid_t *create_nimble_uuid(const mp_obj_bluetooth_uuid_t *uuid, ble_uuid_any_t *storage); +STATIC void reverse_addr_byte_order(uint8_t *addr_out, const uint8_t *addr_in); + +#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(const ble_uuid_any_t *uuid); +STATIC ble_addr_t create_nimble_addr(uint8_t addr_type, const uint8_t *addr); +#endif + +STATIC void reset_cb(int reason); + +STATIC bool has_public_address(void); +STATIC void set_random_address(bool nrpa); + +#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING +STATIC int load_irk(void); +#endif + +STATIC void sync_cb(void); + +#if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY +STATIC void ble_hs_shutdown_stop_cb(int status, void *arg); +#endif + +// Successfully registered service/char/desc handles. +STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); + +// Events about a connected central (we're in peripheral role). +STATIC int central_gap_event_cb(struct ble_gap_event *event, void *arg); +#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +// Events about a connected peripheral (we're in central role). +STATIC int peripheral_gap_event_cb(struct ble_gap_event *event, void *arg); +#endif +// Used by both of the above. +STATIC int commmon_gap_event_cb(struct ble_gap_event *event, void *arg); + +#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +// Scan results. +STATIC int gap_scan_cb(struct ble_gap_event *event, void *arg); +#endif + +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT +// Data available (either due to notify/indicate or successful read). +STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om); + +// Client discovery callbacks. +STATIC int ble_gattc_service_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg); +STATIC int ble_gattc_characteristic_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *characteristic, void *arg); +STATIC int ble_gattc_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t characteristic_val_handle, const struct ble_gatt_dsc *descriptor, void *arg); + +// Client read/write handlers. +STATIC int ble_gattc_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg); +STATIC int ble_gattc_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg); +#endif + +#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING +// Bonding store. +STATIC int ble_store_ram_read(int obj_type, const union ble_store_key *key, union ble_store_value *value); +STATIC int ble_store_ram_write(int obj_type, const union ble_store_value *val); +STATIC int ble_store_ram_delete(int obj_type, const union ble_store_key *key); +#endif + STATIC int ble_hs_err_to_errno(int err) { DEBUG_printf("ble_hs_err_to_errno: %d\n", err); if (!err) { return 0; } if (err >= 0 && (unsigned)err < MP_ARRAY_SIZE(ble_hs_err_to_errno_table) && ble_hs_err_to_errno_table[err]) { + // Return an MP_Exxx error code. return ble_hs_err_to_errno_table[err]; } else { - return MP_EIO; + // Pass through the BLE error code. + return -err; } } @@ -177,6 +247,14 @@ STATIC void set_random_address(bool nrpa) { // Mark it as STATIC (not RPA or NRPA). addr.val[5] |= 0xc0; } else + #elif MICROPY_BLUETOOTH_USE_ZEPHYR_STATIC_ADDRESS + if (!nrpa) { + DEBUG_printf("set_random_address: Generating static address from Zephyr controller\n"); + uint8_t buf[23]; + rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_VENDOR, 0x09), NULL, 0, buf, sizeof(buf)); + assert(rc == 0); + memcpy(addr.val, buf + 1, 6); + } else #endif { DEBUG_printf("set_random_address: Generating random static address\n"); @@ -227,7 +305,9 @@ STATIC int load_irk(void) { } DEBUG_printf("load_irk: Saving new IRK.\n"); if (!mp_bluetooth_gap_on_set_secret(SECRET_TYPE_OUR_IRK, key, sizeof(key), rand_irk, 16)) { - return BLE_HS_EINVAL; + // Code that doesn't implement pairing/bonding won't support set/get secret. + // So they'll just get the default fixed IRK. + return 0; } DEBUG_printf("load_irk: Applying new IRK.\n"); rc = ble_hs_pvcy_set_our_irk(rand_irk); @@ -317,8 +397,54 @@ STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) { } } -STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { - DEBUG_printf("gap_event_cb: type=%d\n", event->type); +STATIC int commmon_gap_event_cb(struct ble_gap_event *event, void *arg) { + struct ble_gap_conn_desc desc; + + switch (event->type) { + #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT + case BLE_GAP_EVENT_NOTIFY_RX: { + uint16_t ev = event->notify_rx.indication == 0 ? MP_BLUETOOTH_IRQ_GATTC_NOTIFY : MP_BLUETOOTH_IRQ_GATTC_INDICATE; + gattc_on_data_available(ev, event->notify_rx.conn_handle, event->notify_rx.attr_handle, event->notify_rx.om); + return 0; + } + #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT + + case BLE_GAP_EVENT_CONN_UPDATE: { + DEBUG_printf("commmon_gap_event_cb: connection update: status=%d\n", event->conn_update.status); + if (ble_gap_conn_find(event->conn_update.conn_handle, &desc) == 0) { + mp_bluetooth_gap_on_connection_update(event->conn_update.conn_handle, desc.conn_itvl, desc.conn_latency, desc.supervision_timeout, event->conn_update.status == 0 ? 0 : 1); + } + return 0; + } + + case BLE_GAP_EVENT_MTU: { + if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) { + DEBUG_printf("commmon_gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value); + mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value); + } + return 0; + } + + case BLE_GAP_EVENT_ENC_CHANGE: { + DEBUG_printf("commmon_gap_event_cb: enc change: status=%d\n", event->enc_change.status); + #if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING + if (ble_gap_conn_find(event->enc_change.conn_handle, &desc) == 0) { + mp_bluetooth_gatts_on_encryption_update(event->conn_update.conn_handle, + desc.sec_state.encrypted, desc.sec_state.authenticated, + desc.sec_state.bonded, desc.sec_state.key_size); + } + #endif + return 0; + } + + default: + DEBUG_printf("commmon_gap_event_cb: unknown type %d\n", event->type); + return 0; + } +} + +STATIC int central_gap_event_cb(struct ble_gap_event *event, void *arg) { + DEBUG_printf("central_gap_event_cb: type=%d\n", event->type); if (!mp_bluetooth_is_active()) { return 0; } @@ -327,7 +453,7 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { switch (event->type) { case BLE_GAP_EVENT_CONNECT: - DEBUG_printf("gap_event_cb: connect: status=%d\n", event->connect.status); + DEBUG_printf("central_gap_event_cb: connect: status=%d\n", event->connect.status); if (event->connect.status == 0) { // Connection established. ble_gap_conn_find(event->connect.conn_handle, &desc); @@ -337,60 +463,32 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { // Connection failed. mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, event->connect.conn_handle, 0xff, addr); } - break; + return 0; case BLE_GAP_EVENT_DISCONNECT: // Disconnect. - DEBUG_printf("gap_event_cb: disconnect: reason=%d\n", event->disconnect.reason); + DEBUG_printf("central_gap_event_cb: disconnect: reason=%d\n", event->disconnect.reason); reverse_addr_byte_order(addr, event->disconnect.conn.peer_id_addr.val); mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, event->disconnect.conn.conn_handle, event->disconnect.conn.peer_id_addr.type, addr); - break; + return 0; case BLE_GAP_EVENT_NOTIFY_TX: { - DEBUG_printf("gap_event_cb: notify_tx: %d %d\n", event->notify_tx.indication, event->notify_tx.status); + DEBUG_printf("central_gap_event_cb: notify_tx: %d %d\n", event->notify_tx.indication, event->notify_tx.status); // This event corresponds to either a sent notify/indicate (status == 0), or an indication confirmation (status != 0). if (event->notify_tx.indication && event->notify_tx.status != 0) { // Map "done/ack" to 0, otherwise pass the status directly. mp_bluetooth_gatts_on_indicate_complete(event->notify_tx.conn_handle, event->notify_tx.attr_handle, event->notify_tx.status == BLE_HS_EDONE ? 0 : event->notify_tx.status); } - break; - } - - case BLE_GAP_EVENT_MTU: { - if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) { - DEBUG_printf("gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value); - mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value); - } - break; + return 0; } case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: - DEBUG_printf("gap_event_cb: phy update: %d\n", event->phy_updated.tx_phy); - break; - - case BLE_GAP_EVENT_CONN_UPDATE: { - DEBUG_printf("gap_event_cb: connection update: status=%d\n", event->conn_update.status); - if (ble_gap_conn_find(event->conn_update.conn_handle, &desc) == 0) { - mp_bluetooth_gap_on_connection_update(event->conn_update.conn_handle, desc.conn_itvl, desc.conn_latency, desc.supervision_timeout, event->conn_update.status == 0 ? 0 : 1); - } - break; - } - - case BLE_GAP_EVENT_ENC_CHANGE: { - DEBUG_printf("gap_event_cb: enc change: status=%d\n", event->enc_change.status); - #if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING - if (ble_gap_conn_find(event->enc_change.conn_handle, &desc) == 0) { - mp_bluetooth_gatts_on_encryption_update(event->conn_update.conn_handle, - desc.sec_state.encrypted, desc.sec_state.authenticated, - desc.sec_state.bonded, desc.sec_state.key_size); - } - #endif - break; - } + DEBUG_printf("central_gap_event_cb: phy update: %d\n", event->phy_updated.tx_phy); + return 0; case BLE_GAP_EVENT_REPEAT_PAIRING: { // We recognized this peer but the peer doesn't recognize us. - DEBUG_printf("gap_event_cb: repeat pairing: conn_handle=%d\n", event->repeat_pairing.conn_handle); + DEBUG_printf("central_gap_event_cb: repeat pairing: conn_handle=%d\n", event->repeat_pairing.conn_handle); // TODO: Consider returning BLE_GAP_REPEAT_PAIRING_IGNORE (and // possibly an API to configure this). @@ -406,7 +504,7 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { } case BLE_GAP_EVENT_PASSKEY_ACTION: { - DEBUG_printf("gap_event_cb: passkey action: conn_handle=%d action=%d num=" UINT_FMT "\n", event->passkey.conn_handle, event->passkey.params.action, (mp_uint_t)event->passkey.params.numcmp); + DEBUG_printf("central_gap_event_cb: passkey action: conn_handle=%d action=%d num=" UINT_FMT "\n", event->passkey.conn_handle, event->passkey.params.action, (mp_uint_t)event->passkey.params.numcmp); #if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING mp_bluetooth_gap_on_passkey_action(event->passkey.conn_handle, event->passkey.params.action, event->passkey.params.numcmp); @@ -414,12 +512,9 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { return 0; } - - default: - DEBUG_printf("gap_event_cb: unknown type %d\n", event->type); - break; } - return 0; + + return commmon_gap_event_cb(event, arg); } #if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY @@ -476,11 +571,27 @@ void mp_bluetooth_nimble_port_shutdown(void) { #endif // !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY +void nimble_reset_gatts_bss(void) { + // NimBLE assumes that service registration only ever happens once, so + // we need to reset service registration state from a previous stack startup. + // These variables are defined in ble_hs.c and are only ever incremented + // (during service registration) and never reset. + // See https://github.com/apache/mynewt-nimble/issues/896 + extern uint16_t ble_hs_max_attrs; + extern uint16_t ble_hs_max_services; + extern uint16_t ble_hs_max_client_configs; + ble_hs_max_attrs = 0; + ble_hs_max_services = 0; + ble_hs_max_client_configs = 0; +} + int mp_bluetooth_init(void) { DEBUG_printf("mp_bluetooth_init\n"); // Clean up if necessary. mp_bluetooth_deinit(); + nimble_reset_gatts_bss(); + mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_STARTING; ble_hs_cfg.reset_cb = reset_cb; @@ -705,7 +816,7 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons .channel_map = 7, // all 3 channels. }; - ret = ble_gap_adv_start(nimble_address_mode, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); + ret = ble_gap_adv_start(nimble_address_mode, NULL, BLE_HS_FOREVER, &adv_params, central_gap_event_cb, NULL); if (ret == 0) { return 0; } @@ -775,6 +886,15 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } + + if (append) { + // Don't support append yet (modbluetooth.c doesn't support it yet anyway). + // TODO: This should be possible with NimBLE. + return MP_EOPNOTSUPP; + } + + nimble_reset_gatts_bss(); + int ret = ble_gatts_reset(); if (ret != 0) { return ble_hs_err_to_errno(ret); @@ -787,13 +907,11 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { ble_svc_gap_init(); ble_svc_gatt_init(); - if (!append) { - // Unref any previous service definitions. - for (size_t i = 0; i < MP_STATE_PORT(bluetooth_nimble_root_pointers)->n_services; ++i) { - MP_STATE_PORT(bluetooth_nimble_root_pointers)->services[i] = NULL; - } - MP_STATE_PORT(bluetooth_nimble_root_pointers)->n_services = 0; + // Unref any previous service definitions. + for (size_t i = 0; i < MP_STATE_PORT(bluetooth_nimble_root_pointers)->n_services; ++i) { + MP_STATE_PORT(bluetooth_nimble_root_pointers)->services[i] = NULL; } + MP_STATE_PORT(bluetooth_nimble_root_pointers)->n_services = 0; return 0; } @@ -985,38 +1103,6 @@ int mp_bluetooth_gap_passkey(uint16_t conn_handle, uint8_t action, mp_int_t pass #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om) { - // When the HCI data for an ATT payload arrives, the L2CAP channel will - // buffer it into its receive buffer. We set BLE_L2CAP_JOIN_RX_FRAGS=1 in - // syscfg.h so it should be rare that the mbuf is fragmented, but we do need - // to be able to handle it. We pass all the fragments up to modbluetooth.c - // which will create a temporary buffer on the MicroPython heap if necessary - // to re-assemble them. - - // Count how many links are in the mbuf chain. - size_t n = 0; - const struct os_mbuf *elem = om; - while (elem) { - n += 1; - elem = SLIST_NEXT(elem, om_next); - } - - // Grab data pointers and lengths for each of the links. - const uint8_t **data = mp_local_alloc(sizeof(uint8_t *) * n); - uint16_t *data_len = mp_local_alloc(sizeof(uint16_t) * n); - for (size_t i = 0; i < n; ++i) { - data[i] = OS_MBUF_DATA(om, const uint8_t *); - data_len[i] = om->om_len; - om = SLIST_NEXT(om, om_next); - } - - // Pass all the fragments together. - mp_bluetooth_gattc_on_data_available(event, conn_handle, value_handle, data, data_len, n); - - mp_local_free(data_len); - mp_local_free(data); -} - STATIC int gap_scan_cb(struct ble_gap_event *event, void *arg) { DEBUG_printf("gap_scan_cb: event=%d type=%d\n", event->type, event->type == BLE_GAP_EVENT_DISC ? event->disc.event_type : -1); if (!mp_bluetooth_is_active()) { @@ -1095,56 +1181,17 @@ STATIC int peripheral_gap_event_cb(struct ble_gap_event *event, void *arg) { // Connection failed. mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT, event->connect.conn_handle, 0xff, addr); } - break; + return 0; case BLE_GAP_EVENT_DISCONNECT: // Disconnect. DEBUG_printf("peripheral_gap_event_cb: reason=%d\n", event->disconnect.reason); reverse_addr_byte_order(addr, event->disconnect.conn.peer_id_addr.val); mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT, event->disconnect.conn.conn_handle, event->disconnect.conn.peer_id_addr.type, addr); - - break; - - case BLE_GAP_EVENT_NOTIFY_RX: { - uint16_t ev = event->notify_rx.indication == 0 ? MP_BLUETOOTH_IRQ_GATTC_NOTIFY : MP_BLUETOOTH_IRQ_GATTC_INDICATE; - gattc_on_data_available(ev, event->notify_rx.conn_handle, event->notify_rx.attr_handle, event->notify_rx.om); - break; - } - - case BLE_GAP_EVENT_CONN_UPDATE: { - DEBUG_printf("peripheral_gap_event_cb: connection update: status=%d\n", event->conn_update.status); - if (ble_gap_conn_find(event->conn_update.conn_handle, &desc) == 0) { - mp_bluetooth_gap_on_connection_update(event->conn_update.conn_handle, desc.conn_itvl, desc.conn_latency, desc.supervision_timeout, event->conn_update.status == 0 ? 0 : 1); - } - break; - } - - case BLE_GAP_EVENT_MTU: { - if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) { - DEBUG_printf("peripheral_gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value); - mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value); - } - break; - } - - case BLE_GAP_EVENT_ENC_CHANGE: { - DEBUG_printf("peripheral_gap_event_cb: enc change: status=%d\n", event->enc_change.status); - #if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING - if (ble_gap_conn_find(event->enc_change.conn_handle, &desc) == 0) { - mp_bluetooth_gatts_on_encryption_update(event->conn_update.conn_handle, - desc.sec_state.encrypted, desc.sec_state.authenticated, - desc.sec_state.bonded, desc.sec_state.key_size); - } - #endif - break; - } - - default: - DEBUG_printf("peripheral_gap_event_cb: unknown type %d\n", event->type); - break; + return 0; } - return 0; + return commmon_gap_event_cb(event, arg); } int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms) { @@ -1173,8 +1220,8 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, return ble_hs_err_to_errno(err); } -STATIC int peripheral_discover_service_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg) { - DEBUG_printf("peripheral_discover_service_cb: conn_handle=%d status=%d start_handle=%d\n", conn_handle, error->status, service ? service->start_handle : -1); +STATIC int ble_gattc_service_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg) { + DEBUG_printf("ble_gattc_service_cb: conn_handle=%d status=%d start_handle=%d\n", conn_handle, error->status, service ? service->start_handle : -1); if (!mp_bluetooth_is_active()) { return 0; } @@ -1187,6 +1234,42 @@ STATIC int peripheral_discover_service_cb(uint16_t conn_handle, const struct ble return 0; } +#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + +#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT + +STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om) { + // When the HCI data for an ATT payload arrives, the L2CAP channel will + // buffer it into its receive buffer. We set BLE_L2CAP_JOIN_RX_FRAGS=1 in + // syscfg.h so it should be rare that the mbuf is fragmented, but we do need + // to be able to handle it. We pass all the fragments up to modbluetooth.c + // which will create a temporary buffer on the MicroPython heap if necessary + // to re-assemble them. + + // Count how many links are in the mbuf chain. + size_t n = 0; + const struct os_mbuf *elem = om; + while (elem) { + n += 1; + elem = SLIST_NEXT(elem, om_next); + } + + // Grab data pointers and lengths for each of the links. + const uint8_t **data = mp_local_alloc(sizeof(uint8_t *) * n); + uint16_t *data_len = mp_local_alloc(sizeof(uint16_t) * n); + for (size_t i = 0; i < n; ++i) { + data[i] = OS_MBUF_DATA(om, const uint8_t *); + data_len[i] = om->om_len; + om = SLIST_NEXT(om, om_next); + } + + // Pass all the fragments together. + mp_bluetooth_gattc_on_data_available(event, conn_handle, value_handle, data, data_len, n); + + mp_local_free(data_len); + mp_local_free(data); +} + int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; @@ -1195,15 +1278,15 @@ int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_ if (uuid) { ble_uuid_any_t nimble_uuid; create_nimble_uuid(uuid, &nimble_uuid); - err = ble_gattc_disc_svc_by_uuid(conn_handle, &nimble_uuid.u, &peripheral_discover_service_cb, NULL); + err = ble_gattc_disc_svc_by_uuid(conn_handle, &nimble_uuid.u, &ble_gattc_service_cb, NULL); } else { - err = ble_gattc_disc_all_svcs(conn_handle, &peripheral_discover_service_cb, NULL); + err = ble_gattc_disc_all_svcs(conn_handle, &ble_gattc_service_cb, NULL); } return ble_hs_err_to_errno(err); } -STATIC int ble_gatt_characteristic_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *characteristic, void *arg) { - DEBUG_printf("ble_gatt_characteristic_cb: conn_handle=%d status=%d def_handle=%d val_handle=%d\n", conn_handle, error->status, characteristic ? characteristic->def_handle : -1, characteristic ? characteristic->val_handle : -1); +STATIC int ble_gattc_characteristic_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *characteristic, void *arg) { + DEBUG_printf("ble_gattc_characteristic_cb: conn_handle=%d status=%d def_handle=%d val_handle=%d\n", conn_handle, error->status, characteristic ? characteristic->def_handle : -1, characteristic ? characteristic->val_handle : -1); if (!mp_bluetooth_is_active()) { return 0; } @@ -1224,15 +1307,15 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s if (uuid) { ble_uuid_any_t nimble_uuid; create_nimble_uuid(uuid, &nimble_uuid); - err = ble_gattc_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, &nimble_uuid.u, &ble_gatt_characteristic_cb, NULL); + err = ble_gattc_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, &nimble_uuid.u, &ble_gattc_characteristic_cb, NULL); } else { - err = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle, &ble_gatt_characteristic_cb, NULL); + err = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle, &ble_gattc_characteristic_cb, NULL); } return ble_hs_err_to_errno(err); } -STATIC int ble_gatt_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t characteristic_val_handle, const struct ble_gatt_dsc *descriptor, void *arg) { - DEBUG_printf("ble_gatt_descriptor_cb: conn_handle=%d status=%d chr_handle=%d dsc_handle=%d\n", conn_handle, error->status, characteristic_val_handle, descriptor ? descriptor->handle : -1); +STATIC int ble_gattc_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t characteristic_val_handle, const struct ble_gatt_dsc *descriptor, void *arg) { + DEBUG_printf("ble_gattc_descriptor_cb: conn_handle=%d status=%d chr_handle=%d dsc_handle=%d\n", conn_handle, error->status, characteristic_val_handle, descriptor ? descriptor->handle : -1); if (!mp_bluetooth_is_active()) { return 0; } @@ -1249,19 +1332,20 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - int err = ble_gattc_disc_all_dscs(conn_handle, start_handle, end_handle, &ble_gatt_descriptor_cb, NULL); + int err = ble_gattc_disc_all_dscs(conn_handle, start_handle, end_handle, &ble_gattc_descriptor_cb, NULL); return ble_hs_err_to_errno(err); } -STATIC int ble_gatt_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) { - DEBUG_printf("ble_gatt_attr_read_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, attr ? attr->handle : -1); +STATIC int ble_gattc_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) { + uint16_t handle = attr ? attr->handle : (error ? error->att_handle : 0xffff); + DEBUG_printf("ble_gattc_attr_read_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, handle); if (!mp_bluetooth_is_active()) { return 0; } if (error->status == 0) { gattc_on_data_available(MP_BLUETOOTH_IRQ_GATTC_READ_RESULT, conn_handle, attr->handle, attr->om); } - mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_READ_DONE, conn_handle, attr ? attr->handle : -1, error->status); + mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_READ_DONE, conn_handle, handle, error->status); return 0; } @@ -1270,16 +1354,17 @@ int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - int err = ble_gattc_read(conn_handle, value_handle, &ble_gatt_attr_read_cb, NULL); + int err = ble_gattc_read(conn_handle, value_handle, &ble_gattc_attr_read_cb, NULL); return ble_hs_err_to_errno(err); } -STATIC int ble_gatt_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) { - DEBUG_printf("ble_gatt_attr_write_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, attr ? attr->handle : -1); +STATIC int ble_gattc_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) { + uint16_t handle = attr ? attr->handle : (error ? error->att_handle : 0xffff); + DEBUG_printf("ble_gattc_attr_write_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, handle); if (!mp_bluetooth_is_active()) { return 0; } - mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, conn_handle, attr->handle, error->status); + mp_bluetooth_gattc_on_read_write_status(MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, conn_handle, handle, error->status); return 0; } @@ -1292,7 +1377,7 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) { err = ble_gattc_write_no_rsp_flat(conn_handle, value_handle, value, *value_len); } else if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) { - err = ble_gattc_write_flat(conn_handle, value_handle, value, *value_len, &ble_gatt_attr_write_cb, NULL); + err = ble_gattc_write_flat(conn_handle, value_handle, value, *value_len, &ble_gattc_attr_write_cb, NULL); } else { err = BLE_HS_EINVAL; } @@ -1306,7 +1391,7 @@ int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) { return ble_hs_err_to_errno(ble_gattc_exchange_mtu(conn_handle, NULL, NULL)); } -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS @@ -1328,10 +1413,16 @@ typedef struct _mp_bluetooth_nimble_l2cap_channel_t { struct os_mbuf_pool sdu_mbuf_pool; struct os_mempool sdu_mempool; struct os_mbuf *rx_pending; + bool irq_in_progress; uint16_t mtu; os_membuf_t sdu_mem[]; } mp_bluetooth_nimble_l2cap_channel_t; +STATIC void destroy_l2cap_channel(); +STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg); +STATIC mp_bluetooth_nimble_l2cap_channel_t *get_l2cap_channel_for_conn_cid(uint16_t conn_handle, uint16_t cid); +STATIC int create_l2cap_channel(uint16_t mtu, mp_bluetooth_nimble_l2cap_channel_t **out); + STATIC void destroy_l2cap_channel() { // Only free the l2cap channel if we're the one that initiated the connection. // Listeners continue listening on the same channel. @@ -1352,9 +1443,9 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) { ble_l2cap_get_chan_info(event->connect.chan, &info); if (event->connect.status == 0) { - mp_bluetooth_gattc_on_l2cap_connect(event->connect.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu); + mp_bluetooth_on_l2cap_connect(event->connect.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu); } else { - mp_bluetooth_gattc_on_l2cap_disconnect(event->connect.conn_handle, info.scid, info.psm, event->connect.status); + mp_bluetooth_on_l2cap_disconnect(event->connect.conn_handle, info.scid, info.psm, event->connect.status); destroy_l2cap_channel(); } break; @@ -1362,7 +1453,7 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) { case BLE_L2CAP_EVENT_COC_DISCONNECTED: { DEBUG_printf("l2cap_channel_event: disconnect: conn_handle=%d\n", event->disconnect.conn_handle); ble_l2cap_get_chan_info(event->disconnect.chan, &info); - mp_bluetooth_gattc_on_l2cap_disconnect(event->disconnect.conn_handle, info.scid, info.psm, 0); + mp_bluetooth_on_l2cap_disconnect(event->disconnect.conn_handle, info.scid, info.psm, 0); destroy_l2cap_channel(); break; } @@ -1370,7 +1461,7 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) { DEBUG_printf("l2cap_channel_event: accept: conn_handle=%d peer_sdu_size=%d\n", event->accept.conn_handle, event->accept.peer_sdu_size); chan->chan = event->accept.chan; ble_l2cap_get_chan_info(event->accept.chan, &info); - int ret = mp_bluetooth_gattc_on_l2cap_accept(event->accept.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu); + int ret = mp_bluetooth_on_l2cap_accept(event->accept.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu); if (ret != 0) { return ret; } @@ -1414,14 +1505,30 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) { chan->chan->coc_rx.sdu = sdu_rx; ble_l2cap_get_chan_info(event->receive.chan, &info); - mp_bluetooth_gattc_on_l2cap_recv(event->receive.conn_handle, info.scid); + + // Don't allow granting more credits until after the IRQ is handled. + chan->irq_in_progress = true; + + mp_bluetooth_on_l2cap_recv(event->receive.conn_handle, info.scid); + chan->irq_in_progress = false; + + // If all data has been consumed by the IRQ handler, then now allow + // more credits. If the IRQ handler doesn't consume all available data + // then rx_pending will be still set. + if (!chan->rx_pending) { + struct os_mbuf *sdu_rx = chan->chan->coc_rx.sdu; + assert(sdu_rx); + if (sdu_rx) { + ble_l2cap_recv_ready(chan->chan, sdu_rx); + } + } break; } case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: { DEBUG_printf("l2cap_channel_event: tx_unstalled: conn_handle=%d status=%d\n", event->tx_unstalled.conn_handle, event->tx_unstalled.status); ble_l2cap_get_chan_info(event->receive.chan, &info); // Map status to {0,1} (i.e. "sent everything", or "partial send"). - mp_bluetooth_gattc_on_l2cap_send_ready(event->tx_unstalled.conn_handle, info.scid, event->tx_unstalled.status == 0 ? 0 : 1); + mp_bluetooth_on_l2cap_send_ready(event->tx_unstalled.conn_handle, info.scid, event->tx_unstalled.status == 0 ? 0 : 1); break; } case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED: { @@ -1481,6 +1588,7 @@ STATIC int create_l2cap_channel(uint16_t mtu, mp_bluetooth_nimble_l2cap_channel_ chan->mtu = mtu; chan->rx_pending = NULL; + chan->irq_in_progress = false; int err = os_mempool_init(&chan->sdu_mempool, buf_blocks, L2CAP_BUF_BLOCK_SIZE, chan->sdu_mem, "l2cap_sdu_pool"); if (err != 0) { @@ -1608,13 +1716,17 @@ int mp_bluetooth_l2cap_recvinto(uint16_t conn_handle, uint16_t cid, uint8_t *buf os_mbuf_free_chain(chan->rx_pending); chan->rx_pending = NULL; - // We've already given the channel a new mbuf in l2cap_channel_event above, so - // re-use that mbuf in the call to ble_l2cap_recv_ready. This will just - // give the channel more credits. - struct os_mbuf *sdu_rx = chan->chan->coc_rx.sdu; - assert(sdu_rx); - if (sdu_rx) { - ble_l2cap_recv_ready(chan->chan, sdu_rx); + // If we're in the call stack of the l2cap_channel_event handler, then don't + // re-enable receiving yet (as we need to complete the rest of IRQ handler first). + if (!chan->irq_in_progress) { + // We've already given the channel a new mbuf in l2cap_channel_event above, so + // re-use that mbuf in the call to ble_l2cap_recv_ready. This will just + // give the channel more credits. + struct os_mbuf *sdu_rx = chan->chan->coc_rx.sdu; + assert(sdu_rx); + if (sdu_rx) { + ble_l2cap_recv_ready(chan->chan, sdu_rx); + } } } else { // Trim the used bytes from the start of the mbuf. @@ -1635,6 +1747,22 @@ int mp_bluetooth_l2cap_recvinto(uint16_t conn_handle, uint16_t cid, uint8_t *buf #endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS +#if MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD + +int mp_bluetooth_hci_cmd(uint16_t ogf, uint16_t ocf, const uint8_t *req, size_t req_len, uint8_t *resp, size_t resp_len, uint8_t *status) { + int rc = ble_hs_hci_cmd_tx(BLE_HCI_OP(ogf, ocf), req, req_len, resp, resp_len); + if (rc < BLE_HS_ERR_HCI_BASE || rc >= BLE_HS_ERR_HCI_BASE + 0x100) { + // The controller didn't handle the command (e.g. HCI timeout). + return ble_hs_err_to_errno(rc); + } else { + // The command executed, but had an error (i.e. invalid parameter). + *status = rc - BLE_HS_ERR_HCI_BASE; + return 0; + } +} + +#endif // MICROPY_PY_BLUETOOTH_ENABLE_HCI_CMD + #if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING STATIC int ble_store_ram_read(int obj_type, const union ble_store_key *key, union ble_store_value *value) { diff --git a/extmod/uasyncio/__init__.py b/extmod/uasyncio/__init__.py index 08f924cf2..fa64438f6 100644 --- a/extmod/uasyncio/__init__.py +++ b/extmod/uasyncio/__init__.py @@ -10,6 +10,7 @@ "wait_for_ms": "funcs", "gather": "funcs", "Event": "event", + "ThreadSafeFlag": "event", "Lock": "lock", "open_connection": "stream", "start_server": "stream", diff --git a/extmod/uasyncio/core.py b/extmod/uasyncio/core.py index 6a84b0982..d74763f6a 100644 --- a/extmod/uasyncio/core.py +++ b/extmod/uasyncio/core.py @@ -264,6 +264,10 @@ def get_event_loop(runq_len=0, waitq_len=0): return Loop +def current_task(): + return cur_task + + def new_event_loop(): global _task_queue, _io_queue # TaskQueue of Task instances diff --git a/extmod/uasyncio/event.py b/extmod/uasyncio/event.py index 31cb00e05..c28ad1fb3 100644 --- a/extmod/uasyncio/event.py +++ b/extmod/uasyncio/event.py @@ -14,6 +14,8 @@ def is_set(self): def set(self): # Event becomes set, schedule any tasks waiting on it + # Note: This must not be called from anything except the thread running + # the asyncio loop (i.e. neither hard or soft IRQ, or a different thread). while self.waiting.peek(): core._task_queue.push_head(self.waiting.pop_head()) self.state = True @@ -29,3 +31,32 @@ async def wait(self): core.cur_task.data = self.waiting yield return True + + +# MicroPython-extension: This can be set from outside the asyncio event loop, +# such as other threads, IRQs or scheduler context. Implementation is a stream +# that asyncio will poll until a flag is set. +# Note: Unlike Event, this is self-clearing. +try: + import uio + + class ThreadSafeFlag(uio.IOBase): + def __init__(self): + self._flag = 0 + + def ioctl(self, req, flags): + if req == 3: # MP_STREAM_POLL + return self._flag * flags + return None + + def set(self): + self._flag = 1 + + async def wait(self): + if not self._flag: + yield core._io_queue.queue_read(self) + self._flag = 0 + + +except ImportError: + pass diff --git a/extmod/vfs.c b/extmod/vfs.c index 81492f8b2..04f6bd68e 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -170,27 +170,30 @@ STATIC mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) { #endif nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - mp_obj_t vfs = MP_OBJ_NULL; + // The superblock for littlefs is in both block 0 and 1, but block 0 may be erased + // or partially written, so search both blocks 0 and 1 for the littlefs signature. mp_vfs_blockdev_t blockdev; mp_vfs_blockdev_init(&blockdev, bdev_obj); uint8_t buf[44]; - mp_vfs_blockdev_read_ext(&blockdev, 0, 8, sizeof(buf), buf); - #if MICROPY_VFS_LFS1 - if (memcmp(&buf[32], "littlefs", 8) == 0) { - // LFS1 - vfs = mp_type_vfs_lfs1.make_new(&mp_type_vfs_lfs1, 1, 0, &bdev_obj); - nlr_pop(); - return vfs; - } - #endif - #if MICROPY_VFS_LFS2 - if (memcmp(&buf[0], "littlefs", 8) == 0) { - // LFS2 - vfs = mp_type_vfs_lfs2.make_new(&mp_type_vfs_lfs2, 1, 0, &bdev_obj); - nlr_pop(); - return vfs; + for (size_t block_num = 0; block_num <= 1; ++block_num) { + mp_vfs_blockdev_read_ext(&blockdev, block_num, 8, sizeof(buf), buf); + #if MICROPY_VFS_LFS1 + if (memcmp(&buf[32], "littlefs", 8) == 0) { + // LFS1 + mp_obj_t vfs = mp_type_vfs_lfs1.make_new(&mp_type_vfs_lfs1, 1, 0, &bdev_obj); + nlr_pop(); + return vfs; + } + #endif + #if MICROPY_VFS_LFS2 + if (memcmp(&buf[0], "littlefs", 8) == 0) { + // LFS2 + mp_obj_t vfs = mp_type_vfs_lfs2.make_new(&mp_type_vfs_lfs2, 1, 0, &bdev_obj); + nlr_pop(); + return vfs; + } + #endif } - #endif nlr_pop(); } else { // Ignore exception (eg block device doesn't support extended readblocks) diff --git a/extmod/vfs_posix_file.c b/extmod/vfs_posix_file.c index 5398cef45..ef0bd117e 100644 --- a/extmod/vfs_posix_file.c +++ b/extmod/vfs_posix_file.c @@ -167,7 +167,11 @@ STATIC mp_uint_t vfs_posix_file_write(mp_obj_t o_in, const void *buf, mp_uint_t STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in); - check_fd_is_open(o); + + if (request != MP_STREAM_CLOSE) { + check_fd_is_open(o); + } + switch (request) { case MP_STREAM_FLUSH: { int ret; diff --git a/lib/mp-readline/readline.h b/lib/mp-readline/readline.h index a19e1209a..411e42020 100644 --- a/lib/mp-readline/readline.h +++ b/lib/mp-readline/readline.h @@ -26,6 +26,8 @@ #ifndef MICROPY_INCLUDED_LIB_MP_READLINE_READLINE_H #define MICROPY_INCLUDED_LIB_MP_READLINE_READLINE_H +#include "py/misc.h" // for vstr_t + #define CHAR_CTRL_A (1) #define CHAR_CTRL_B (2) #define CHAR_CTRL_C (3) diff --git a/lib/oofatfs/ff.c b/lib/oofatfs/ff.c index 0c9d04fe7..34c7eccc3 100644 --- a/lib/oofatfs/ff.c +++ b/lib/oofatfs/ff.c @@ -98,16 +98,16 @@ #define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ #define BS_OEMName 3 /* OEM name (8-byte) */ -#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD16) */ #define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ -#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD16) */ #define BPB_NumFATs 16 /* Number of FATs (BYTE) */ -#define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */ -#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ +#define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD16) */ +#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD16) */ #define BPB_Media 21 /* Media descriptor byte (BYTE) */ -#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ -#define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */ -#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ +#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD16) */ +#define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD16) */ +#define BPB_NumHeads 26 /* Number of heads for int13h (WORD16) */ #define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */ #define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */ #define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */ @@ -117,14 +117,14 @@ #define BS_VolLab 43 /* Volume label string (8-byte) */ #define BS_FilSysType 54 /* Filesystem type string (8-byte) */ #define BS_BootCode 62 /* Boot code (448-byte) */ -#define BS_55AA 510 /* Signature word (WORD) */ +#define BS_55AA 510 /* Signature WORD16 (WORD16) */ #define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ -#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ -#define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */ +#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD16) */ +#define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD16) */ #define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ -#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ -#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */ +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD16) */ +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD16) */ #define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */ #define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ #define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */ @@ -142,8 +142,8 @@ #define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */ #define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */ #define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ -#define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */ -#define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */ +#define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD16) */ +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD16) */ #define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */ #define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */ #define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ @@ -157,23 +157,23 @@ #define DIR_NTres 12 /* Lower case flag (BYTE) */ #define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ #define DIR_CrtTime 14 /* Created time (DWORD) */ -#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ -#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ +#define DIR_LstAccDate 18 /* Last accessed date (WORD16) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD16) */ #define DIR_ModTime 22 /* Modified time (DWORD) */ -#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD16) */ #define DIR_FileSize 28 /* File size (DWORD) */ #define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */ #define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */ #define LDIR_Type 12 /* LFN: Entry type (BYTE) */ #define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */ -#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */ +#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD16) */ #define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */ #define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */ -#define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */ +#define XDIR_Label 2 /* exFAT: Volume label (11-WORD16) */ #define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */ #define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */ -#define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */ -#define XDIR_Attr 4 /* exFAT: File attribute (WORD) */ +#define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD16) */ +#define XDIR_Attr 4 /* exFAT: File attribute (WORD16) */ #define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */ #define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */ #define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */ @@ -184,7 +184,7 @@ #define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */ #define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */ #define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */ -#define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */ +#define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD16) */ #define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */ #define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */ #define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */ @@ -267,7 +267,7 @@ typedef struct { FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ DWORD clu; /* Object ID 2, containing directory (0:root) */ DWORD ofs; /* Object ID 3, offset in the directory */ - WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ + WORD16 ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ } FILESEM; #endif @@ -441,7 +441,7 @@ typedef struct { #if FF_VOLUMES < 1 || FF_VOLUMES > 10 #error Wrong FF_VOLUMES setting #endif -static WORD Fsid; /* Filesystem mount ID */ +static WORD16 Fsid; /* Filesystem mount ID */ #if FF_FS_LOCK != 0 static FILESEM Files[FF_FS_LOCK]; /* Open object lock semaphores */ @@ -529,7 +529,7 @@ static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ #if FF_CODE_PAGE == 0 /* Run-time code page configuration */ #define CODEPAGE CodePage -static WORD CodePage; /* Current code page */ +static WORD16 CodePage; /* Current code page */ static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS up-case table and DBCS code range table below */ static const BYTE Ct437[] = TBL_CT437; @@ -575,19 +575,19 @@ static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE); /*-----------------------------------------------------------------------*/ -/* Load/Store multi-byte word in the FAT structure */ +/* Load/Store multi-byte WORD16 in the FAT structure */ /*-----------------------------------------------------------------------*/ -static WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ +static WORD16 ld_WORD16 (const BYTE* ptr) /* Load a 2-byte little-endian WORD16 */ { - WORD rv; + WORD16 rv; rv = ptr[1]; rv = rv << 8 | ptr[0]; return rv; } -static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ +static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian WORD16 */ { DWORD rv; @@ -599,7 +599,7 @@ static DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ } #if FF_FS_EXFAT -static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ +static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian WORD16 */ { QWORD rv; @@ -616,13 +616,13 @@ static QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ #endif #if !FF_FS_READONLY -static void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */ +static void st_WORD16 (BYTE* ptr, WORD16 val) /* Store a 2-byte WORD16 in little-endian */ { *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; } -static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */ +static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte WORD16 in little-endian */ { *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; val >>= 8; @@ -631,7 +631,7 @@ static void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-end } #if FF_FS_EXFAT -static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */ +static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte WORD16 in little-endian */ { *ptr++ = (BYTE)val; val >>= 8; *ptr++ = (BYTE)val; val >>= 8; @@ -972,7 +972,7 @@ static FRESULT dec_lock ( /* Decrement object open counter */ UINT i /* Semaphore index (1..) */ ) { - WORD n; + WORD16 n; FRESULT res; @@ -1074,7 +1074,7 @@ static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */ /* Create FSInfo structure */ mem_set(fs->win, 0, sizeof fs->win); - st_word(fs->win + BS_55AA, 0xAA55); + st_WORD16(fs->win + BS_55AA, 0xAA55); st_dword(fs->win + FSI_LeadSig, 0x41615252); st_dword(fs->win + FSI_StrucSig, 0x61417272); st_dword(fs->win + FSI_Free_Count, fs->free_clst); @@ -1144,7 +1144,7 @@ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FF case FS_FAT16 : if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; - val = ld_word(fs->win + clst * 2 % SS(fs)); /* Simple WORD array */ + val = ld_WORD16(fs->win + clst * 2 % SS(fs)); /* Simple WORD16 array */ break; case FS_FAT32 : @@ -1223,7 +1223,7 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ case FS_FAT16 : res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); if (res != FR_OK) break; - st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */ + st_WORD16(fs->win + clst * 2 % SS(fs), (WORD16)val); /* Simple WORD16 array */ fs->wflag = 1; break; @@ -1808,9 +1808,9 @@ static DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ { DWORD cl; - cl = ld_word(dir + DIR_FstClusLO); + cl = ld_WORD16(dir + DIR_FstClusLO); if (fs->fs_type == FS_FAT32) { - cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; + cl |= (DWORD)ld_WORD16(dir + DIR_FstClusHI) << 16; } return cl; @@ -1824,9 +1824,9 @@ static void st_clust ( DWORD cl /* Value to be set */ ) { - st_word(dir + DIR_FstClusLO, (WORD)cl); + st_WORD16(dir + DIR_FstClusLO, (WORD16)cl); if (fs->fs_type == FS_FAT32) { - st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); + st_WORD16(dir + DIR_FstClusHI, (WORD16)(cl >> 16)); } } #endif @@ -1847,12 +1847,12 @@ static int cmp_lfn ( /* 1:matched, 0:not matched */ WCHAR wc, uc; - if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + if (ld_WORD16(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ - uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + uc = ld_WORD16(dir + LfnOfs[s]); /* Pick an LFN character */ if (wc != 0) { if (i >= FF_MAX_LFN + 1 || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ return 0; /* Not matched */ @@ -1883,12 +1883,12 @@ static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry * WCHAR wc, uc; - if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO is 0 */ + if (ld_WORD16(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO is 0 */ i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN buffer */ for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ - uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + uc = ld_WORD16(dir + LfnOfs[s]); /* Pick an LFN character */ if (wc != 0) { if (i >= FF_MAX_LFN + 1) return 0; /* Buffer overflow? */ lfnbuf[i++] = wc = uc; /* Store it */ @@ -1926,13 +1926,13 @@ static void put_lfn ( dir[LDIR_Chksum] = sum; /* Set checksum */ dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ dir[LDIR_Type] = 0; - st_word(dir + LDIR_FstClusLO, 0); + st_WORD16(dir + LDIR_FstClusLO, 0); i = (ord - 1) * 13; /* Get offset in the LFN working buffer */ s = wc = 0; do { if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ - st_word(dir + LfnOfs[s], wc); /* Put it */ + st_WORD16(dir + LfnOfs[s], wc); /* Put it */ if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */ } while (++s < 13); if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ @@ -2029,12 +2029,12 @@ static BYTE sum_sfn ( /* exFAT: Checksum */ /*-----------------------------------------------------------------------*/ -static WORD xdir_sum ( /* Get checksum of the directoly entry block */ +static WORD16 xdir_sum ( /* Get checksum of the directoly entry block */ const BYTE* dir /* Directory entry block to be calculated */ ) { UINT i, szblk; - WORD sum; + WORD16 sum; szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; /* Number of bytes of the entry block */ @@ -2050,12 +2050,12 @@ static WORD xdir_sum ( /* Get checksum of the directoly entry block */ -static WORD xname_sum ( /* Get check sum (to be used as hash) of the file name */ +static WORD16 xname_sum ( /* Get check sum (to be used as hash) of the file name */ const WCHAR* name /* File name to be calculated */ ) { WCHAR chr; - WORD sum = 0; + WORD16 sum = 0; while ((chr = *name++) != 0) { @@ -2098,7 +2098,7 @@ static void get_xfileinfo ( while (nc < dirb[XDIR_NumName]) { if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */ if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ - wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */ + wc = ld_WORD16(dirb + si); si += 2; nc++; /* Get a character */ if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ hs = wc; continue; /* Get low surrogate */ } @@ -2114,8 +2114,8 @@ static void get_xfileinfo ( fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ - fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ - fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ + fno->ftime = ld_WORD16(dirb + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_WORD16(dirb + XDIR_ModTime + 2); /* Date */ } #endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ @@ -2166,7 +2166,7 @@ static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ /* Sanity check (do it for only accessible object) */ if (i <= MAXDIRB(FF_MAX_LFN)) { - if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR; + if (xdir_sum(dirb) != ld_WORD16(dirb + XDIR_SetSum)) return FR_INT_ERR; } return FR_OK; } @@ -2232,7 +2232,7 @@ static FRESULT store_xdir ( BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ /* Create set sum */ - st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); + st_WORD16(dirb + XDIR_SetSum, xdir_sum(dirb)); nent = dirb[XDIR_NumSec] + 1; /* Store the direcotry entry block to the directory */ @@ -2277,7 +2277,7 @@ static void create_xdir ( dirb[i++] = ET_FILENAME; dirb[i++] = 0; do { /* Fill name field */ if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a character if exist */ - st_word(dirb + i, wc); /* Store it */ + st_WORD16(dirb + i, wc); /* Store it */ i += 2; } while (i % SZDIRE != 0); nc1++; @@ -2285,7 +2285,7 @@ static void create_xdir ( dirb[XDIR_NumName] = nlen; /* Set name length */ dirb[XDIR_NumSec] = 1 + nc1; /* Set secondary count (C0 + C1s) */ - st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ + st_WORD16(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ } #endif /* !FF_FS_READONLY */ @@ -2396,16 +2396,16 @@ static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ BYTE nc; UINT di, ni; - WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ + WORD16 hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an item */ #if FF_MAX_LFN < 255 if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */ #endif - if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */ + if (ld_WORD16(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */ for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ if ((di % SZDIRE) == 0) di += 2; - if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; + if (ff_wtoupper(ld_WORD16(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; } if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ } @@ -2705,8 +2705,8 @@ static void get_fileinfo ( fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ - fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ - fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ + fno->ftime = ld_WORD16(dp->dir + DIR_ModTime + 0); /* Time */ + fno->fdate = ld_WORD16(dp->dir + DIR_ModTime + 2); /* Date */ } #endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ @@ -3088,7 +3088,7 @@ static BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4 fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */ - if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always here regardless of the sector size) */ + if (ld_WORD16(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always here regardless of the sector size) */ #if FF_FS_EXFAT if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */ @@ -3115,7 +3115,7 @@ static FRESULT find_volume ( /* FR_OK(0): successful, !=0: an error occurred BYTE fmt, *pt; DSTATUS stat; DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; - WORD nrsv; + WORD16 nrsv; UINT i; @@ -3178,7 +3178,7 @@ static FRESULT find_volume ( /* FR_OK(0): successful, !=0: an error occurred for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; - if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */ + if (ld_WORD16(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */ if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ return FR_NO_FILESYSTEM; @@ -3234,9 +3234,9 @@ static FRESULT find_volume ( /* FR_OK(0): successful, !=0: an error occurred } else #endif /* FF_FS_EXFAT */ { - if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ + if (ld_WORD16(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ - fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + fasize = ld_WORD16(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); fs->fsize = fasize; @@ -3247,13 +3247,13 @@ static FRESULT find_volume ( /* FR_OK(0): successful, !=0: an error occurred fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ - fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + fs->n_rootdir = ld_WORD16(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ - tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + tsect = ld_WORD16(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); - nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + nrsv = ld_WORD16(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ /* Determine the FAT sub type */ @@ -3273,7 +3273,7 @@ static FRESULT find_volume ( /* FR_OK(0): successful, !=0: an error occurred fs->fatbase = bsect + nrsv; /* FAT start sector */ fs->database = bsect + sysect; /* Data start sector */ if (fmt == FS_FAT32) { - if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ + if (ld_WORD16(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ @@ -3291,11 +3291,11 @@ static FRESULT find_volume ( /* FR_OK(0): successful, !=0: an error occurred fs->fsi_flag = 0x80; #if (FF_FS_NOFSINFO & 3) != 3 if (fmt == FS_FAT32 /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */ - && ld_word(fs->win + BPB_FSInfo32) == 1 + && ld_WORD16(fs->win + BPB_FSInfo32) == 1 && move_window(fs, bsect + 1) == FR_OK) { fs->fsi_flag = 0; - if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSInfo data if available */ + if (ld_WORD16(fs->win + BS_55AA) == 0xAA55 /* Load FSInfo data if available */ && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) { @@ -3887,7 +3887,7 @@ FRESULT f_sync ( st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation information */ st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ st_dword(dir + DIR_ModTime, tm); /* Update modified time */ - st_word(dir + DIR_LstAccDate, 0); + st_WORD16(dir + DIR_LstAccDate, 0); fs->wflag = 1; res = sync_fs(fs); /* Restore it to the directory */ fp->flag &= (BYTE)~FA_MODIFIED; @@ -4538,7 +4538,7 @@ FRESULT f_getfree ( } while (clst); } else #endif - { /* FAT16/32: Scan WORD/DWORD FAT entries */ + { /* FAT16/32: Scan WORD16/DWORD FAT entries */ clst = fs->n_fatent; /* Number of entries */ sect = fs->fatbase; /* Top of the FAT */ i = 0; /* Offset in the sector */ @@ -4548,7 +4548,7 @@ FRESULT f_getfree ( if (res != FR_OK) break; } if (fs->fs_type == FS_FAT16) { - if (ld_word(fs->win + i) == 0) nfree++; + if (ld_WORD16(fs->win + i) == 0) nfree++; i += 2; } else { if ((ld_dword(fs->win + i) & 0x0FFFFFFF) == 0) nfree++; @@ -4830,7 +4830,7 @@ FRESULT f_rename ( #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */ BYTE nf, nn; - WORD nh; + WORD16 nh; mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ mem_cpy(&djn, &djo, sizeof djo); @@ -4842,10 +4842,10 @@ FRESULT f_rename ( res = dir_register(&djn); /* Register the new entry */ if (res == FR_OK) { nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; - nh = ld_word(fs->dirbuf + XDIR_NameHash); + nh = ld_WORD16(fs->dirbuf + XDIR_NameHash); mem_cpy(fs->dirbuf, buf, SZDIRE * 2); /* Restore 85+C0 entry */ fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; - st_word(fs->dirbuf + XDIR_NameHash, nh); + st_WORD16(fs->dirbuf + XDIR_NameHash, nh); if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ /* Start of critical section where an interruption can cause a cross-link */ res = store_xdir(&djn); @@ -5031,7 +5031,7 @@ FRESULT f_getlabel ( WCHAR hs; for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ - wc = ld_word(dj.dir + XDIR_Label + si * 2); + wc = ld_WORD16(dj.dir + XDIR_Label + si * 2); if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */ hs = wc; continue; } @@ -5128,13 +5128,13 @@ FRESULT f_setlabel ( if (dc == 0xFFFFFFFF || di >= 10) { /* Wrong surrogate or buffer overflow */ dc = 0; } else { - st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; + st_WORD16(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; } } if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) { /* Check validity of the volume label */ LEAVE_FF(fs, FR_INVALID_NAME); } - st_word(dirvn + di * 2, (WCHAR)dc); di++; + st_WORD16(dirvn + di * 2, (WCHAR)dc); di++; } } else #endif @@ -5390,10 +5390,10 @@ FRESULT f_mkfs ( { const UINT n_fats = 1; /* Number of FATs for FAT/FAT32 volume (1 or 2) */ const UINT n_rootdir = 512; /* Number of root directory entries for FAT volume */ - static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */ - static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ + static const WORD16 cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT volume (4Ks unit) */ + static const WORD16 cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ BYTE fmt, sys, *buf, *pte, part; void *pdrv; - WORD ss; /* Sector size */ + WORD16 ss; /* Sector size */ DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n; DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ @@ -5441,7 +5441,7 @@ FRESULT f_mkfs ( if (FF_MULTI_PARTITION && part != 0) { /* Get partition information from partition table in the MBR */ if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Load MBR */ - if (ld_word(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */ + if (ld_WORD16(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if MBR is valid */ pte = buf + (MBR_Table + (part - 1) * SZ_PTE); if (pte[PTE_System] == 0) LEAVE_MKFS(FR_MKFS_ABORTED); /* No partition? */ b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ @@ -5604,20 +5604,20 @@ FRESULT f_mkfs ( st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ - st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */ + st_WORD16(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */ for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ buf[BPB_NumFATsEx] = 1; /* Number of FATs */ buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ - st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ - st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ + st_WORD16(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ + st_WORD16(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ for (i = sum = 0; i < ss; i++) { /* VBR checksum */ if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); } if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Extended bootstrap record (+1..+8) */ mem_set(buf, 0, ss); - st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ + st_WORD16(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ for (j = 1; j < 9; j++) { for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); @@ -5714,37 +5714,37 @@ FRESULT f_mkfs ( /* Create FAT VBR */ mem_set(buf, 0, ss); mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ - st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ + st_WORD16(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ - st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ + st_WORD16(buf + BPB_RsvdSecCnt, (WORD16)sz_rsv); /* Size of reserved area */ buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */ - st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ + st_WORD16(buf + BPB_RootEntCnt, (WORD16)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ if (sz_vol < 0x10000) { - st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ + st_WORD16(buf + BPB_TotSec16, (WORD16)sz_vol); /* Volume size in 16-bit LBA */ } else { st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */ } buf[BPB_Media] = 0xF8; /* Media descriptor byte */ - st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ - st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ + st_WORD16(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ + st_WORD16(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ if (fmt == FS_FAT32) { st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ - st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ - st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ + st_WORD16(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ + st_WORD16(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ buf[BS_BootSig32] = 0x29; /* Extended boot signature */ mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ } else { st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ - st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ + st_WORD16(buf + BPB_FATSz16, (WORD16)sz_fat); /* FAT size [sector] */ buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ buf[BS_BootSig] = 0x29; /* Extended boot signature */ mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ } - st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ + st_WORD16(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ /* Create FSINFO record if needed */ @@ -5755,7 +5755,7 @@ FRESULT f_mkfs ( st_dword(buf + FSI_StrucSig, 0x61417272); st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ - st_word(buf + BS_55AA, 0xAA55); + st_WORD16(buf + BS_55AA, 0xAA55); disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ } @@ -5813,7 +5813,7 @@ FRESULT f_mkfs ( } else { /* Created as a new single partition */ if (!(opt & FM_SFD)) { /* Create partition table if in FDISK format */ mem_set(buf, 0, ss); - st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ + st_WORD16(buf + BS_55AA, 0xAA55); /* MBR signature */ pte = buf + MBR_Table; /* Create partition table for single partition in the drive */ pte[PTE_Boot] = 0; /* Boot indicator */ pte[PTE_StHead] = 1; /* Start head */ @@ -5904,7 +5904,7 @@ FRESULT f_fdisk ( /* Next partition */ b_cyl += p_cyl; } - st_word(p, 0xAA55); /* MBR signature (always at offset 510) */ + st_WORD16(p, 0xAA55); /* MBR signature (always at offset 510) */ /* Write it to the MBR */ res = (disk_write(pdrv, buf, 0, 1) == RES_OK && disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; @@ -5922,10 +5922,10 @@ FRESULT f_fdisk ( /*-----------------------------------------------------------------------*/ FRESULT f_setcp ( - WORD cp /* Value to be set as active code page */ + WORD16 cp /* Value to be set as active code page */ ) { - static const WORD validcp[] = { 437, 720, 737, 771, 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0}; + static const WORD16 validcp[] = { 437, 720, 737, 771, 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0}; static const BYTE* const tables[] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; UINT i; diff --git a/lib/oofatfs/ff.h b/lib/oofatfs/ff.h index a8aa00e9a..cfa2168c5 100644 --- a/lib/oofatfs/ff.h +++ b/lib/oofatfs/ff.h @@ -48,7 +48,7 @@ typedef unsigned __int64 QWORD; #include typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ typedef unsigned char BYTE; /* char must be 8-bit */ -typedef uint16_t WORD; /* 16-bit unsigned integer */ +typedef uint16_t WORD16; /* 16-bit unsigned integer */ typedef uint16_t WCHAR; /* 16-bit unsigned integer */ typedef uint32_t DWORD; /* 32-bit unsigned integer */ typedef uint64_t QWORD; /* 64-bit unsigned integer */ @@ -56,7 +56,7 @@ typedef uint64_t QWORD; /* 64-bit unsigned integer */ #define FF_INTDEF 1 typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ typedef unsigned char BYTE; /* char must be 8-bit */ -typedef unsigned short WORD; /* 16-bit unsigned integer */ +typedef unsigned short WORD16; /* 16-bit unsigned integer */ typedef unsigned short WCHAR; /* 16-bit unsigned integer */ typedef unsigned long DWORD; /* 32-bit unsigned integer */ #endif @@ -125,11 +125,11 @@ typedef struct { BYTE n_fats; /* Number of FATs (1 or 2) */ BYTE wflag; /* win[] flag (b0:dirty) */ BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ - WORD id; /* Volume mount ID */ - WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ - WORD csize; /* Cluster size [sectors] */ + WORD16 id; /* Volume mount ID */ + WORD16 n_rootdir; /* Number of root directory entries (FAT12/16) */ + WORD16 csize; /* Cluster size [sectors] */ #if FF_MAX_SS != FF_MIN_SS - WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ + WORD16 ssize; /* Sector size (512, 1024, 2048 or 4096) */ #endif #if FF_USE_LFN WCHAR* lfnbuf; /* LFN working buffer */ @@ -171,7 +171,7 @@ typedef struct { typedef struct { FATFS* fs; /* Pointer to the hosting volume of this object */ - WORD id; /* Hosting volume mount ID */ + WORD16 id; /* Hosting volume mount ID */ BYTE attr; /* Object attribute */ BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */ DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */ @@ -236,8 +236,8 @@ typedef struct { typedef struct { FSIZE_t fsize; /* File size */ - WORD fdate; /* Modified date */ - WORD ftime; /* Modified time */ + WORD16 fdate; /* Modified date */ + WORD16 ftime; /* Modified time */ BYTE fattrib; /* File attribute */ #if FF_USE_LFN TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ @@ -308,7 +308,7 @@ FRESULT f_mount (FATFS* fs); /* Mount/Unm FRESULT f_umount (FATFS* fs); /* Unmount a logical drive */ FRESULT f_mkfs (FATFS *fs, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ FRESULT f_fdisk (void *pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ -FRESULT f_setcp (WORD cp); /* Set current code page */ +FRESULT f_setcp (WORD16 cp); /* Set current code page */ #define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) #define f_error(fp) ((fp)->err) @@ -336,8 +336,8 @@ DWORD get_fattime (void); /* LFN support functions */ #if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */ -WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */ -WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */ +WCHAR ff_oem2uni (WCHAR oem, WORD16 cp); /* OEM code to Unicode conversion */ +WCHAR ff_uni2oem (DWORD uni, WORD16 cp); /* Unicode to OEM code conversion */ DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */ #endif #if FF_USE_LFN == 3 /* Dynamic memory allocation */ diff --git a/lib/timeutils/timeutils.c b/lib/timeutils/timeutils.c index fc8b5e7fc..af210d994 100644 --- a/lib/timeutils/timeutils.c +++ b/lib/timeutils/timeutils.c @@ -158,7 +158,7 @@ mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, + (year - 2000) * 31536000; } -mp_uint_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, +mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { // Normalize the tuple. This allows things like: diff --git a/lib/timeutils/timeutils.h b/lib/timeutils/timeutils.h index 14da831dc..2d40f773c 100644 --- a/lib/timeutils/timeutils.h +++ b/lib/timeutils/timeutils.h @@ -52,12 +52,21 @@ void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second); -mp_uint_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, +mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds); // Select the Epoch used by the port. #if MICROPY_EPOCH_IS_1970 +static inline void timeutils_seconds_since_epoch_to_struct_time(uint64_t t, timeutils_struct_time_t *tm) { + // TODO this will give incorrect results for dates before 2000/1/1 + return timeutils_seconds_since_2000_to_struct_time(t - TIMEUTILS_SECONDS_1970_TO_2000, tm); +} + +static inline uint64_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { + return timeutils_mktime_2000(year, month, mday, hours, minutes, seconds) + TIMEUTILS_SECONDS_1970_TO_2000; +} + static inline uint64_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { return timeutils_seconds_since_2000(year, month, date, hour, minute, second) + TIMEUTILS_SECONDS_1970_TO_2000; @@ -75,6 +84,7 @@ static inline uint64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_19 #define timeutils_seconds_since_epoch_to_struct_time timeutils_seconds_since_2000_to_struct_time #define timeutils_seconds_since_epoch timeutils_seconds_since_2000 +#define timeutils_mktime timeutils_mktime_2000 static inline uint64_t timeutils_seconds_since_epoch_to_nanoseconds_since_1970(mp_uint_t s) { return ((uint64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000ULL; diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index 764efe3df..c7cb9d61d 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -45,6 +45,9 @@ #include "lib/utils/pyexec.h" #include "genhdr/mpversion.h" +// #include "lib/utils/interrupt_char.h" // for mp_hal_set_interrupt_char +extern void mp_hal_set_interrupt_char(int c); + pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; int pyexec_system_exit = 0; STATIC bool repl_display_debugging_info = 0; diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 29fdcddfc..a93ee7d55 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -38,7 +38,7 @@ #ifdef _WIN32 #include "ports/windows/fmode.h" #endif - +mp_state_ctx_t mp_state_ctx; // Command line options, with their defaults STATIC uint emit_opt = MP_EMIT_OPT_NONE; mp_uint_t mp_verbose_flag = 0; @@ -249,7 +249,7 @@ STATIC void pre_process_options(int argc, char **argv) { heap_size *= 1024 * 1024; } if (word_adjust) { - heap_size = heap_size * BYTES_PER_WORD / 4; + heap_size = heap_size * MP_BYTES_PER_OBJ_WORD / 4; } } else { exit(usage(argv)); @@ -261,7 +261,7 @@ STATIC void pre_process_options(int argc, char **argv) { } MP_NOINLINE int main_(int argc, char **argv) { - mp_stack_set_limit(40000 * (BYTES_PER_WORD / 4)); + mp_stack_set_limit(40000 * (sizeof(void *) / 4)); pre_process_options(argc, argv); diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index 9a1c2c5f6..a0d9c000c 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -26,33 +26,54 @@ // options to control how WAPY is built -#define NO_NLR (1) +#define MICROPY_ALLOC_PATH_MAX (PATH_MAX) +#define MICROPY_PERSISTENT_CODE_LOAD (0) +#define MICROPY_PERSISTENT_CODE_SAVE (1) + +#ifndef MICROPY_PERSISTENT_CODE_SAVE_FILE + #if defined(__i386__) || defined(__x86_64__) || defined(_WIN32) || defined(__unix__) || defined(__APPLE__) + #define MICROPY_PERSISTENT_CODE_SAVE_FILE (1) + #else + #define MICROPY_PERSISTENT_CODE_SAVE_FILE (0) + #endif +#endif + +#ifndef NO_NLR + //#pragma message "NO_NLR was NOT SET !" + #define NO_NLR (1) +#endif #if NO_NLR #define MICROPY_ROM_TEXT_COMPRESSION (0) #define MICROPY_PY_FUNCTION_ATTRS (1) #define MICROPY_EMIT_WASM (1) #define MICROPY_PY_BUILTINS_NEXT2 (1) #define MICROPY_PY_FSTRING (1) -#endif - -// options to control how MicroPython is built -#define MICROPY_ALLOC_PATH_MAX (PATH_MAX) -#define MICROPY_PERSISTENT_CODE_LOAD (0) -#define MICROPY_PERSISTENT_CODE_SAVE (1) + #define MICROPY_EMIT_X64 (0) + #define MICROPY_EMIT_X86 (0) + #define MICROPY_EMIT_THUMB (0) + #define MICROPY_EMIT_INLINE_THUMB (0) + #define MICROPY_EMIT_INLINE_THUMB_ARMV7M (0) + #define MICROPY_EMIT_INLINE_THUMB_FLOAT (0) + #define MICROPY_EMIT_ARM (0) + #define MICROPY_EMIT_XTENSA (0) + #define MICROPY_EMIT_INLINE_XTENSA (0) + #define MICROPY_EMIT_XTENSAWIN (0) + #define MICROPY_DYNAMIC_COMPILER (0) +#else + #define MICROPY_EMIT_X64 (1) + #define MICROPY_EMIT_X86 (1) + #define MICROPY_EMIT_THUMB (1) + #define MICROPY_EMIT_INLINE_THUMB (1) + #define MICROPY_EMIT_INLINE_THUMB_ARMV7M (1) + #define MICROPY_EMIT_INLINE_THUMB_FLOAT (1) + #define MICROPY_EMIT_ARM (1) + #define MICROPY_EMIT_XTENSA (1) + #define MICROPY_EMIT_INLINE_XTENSA (1) + #define MICROPY_EMIT_XTENSAWIN (1) + #define MICROPY_DYNAMIC_COMPILER (1) +#endif -#define MICROPY_EMIT_X64 (0) -#define MICROPY_EMIT_X86 (0) -#define MICROPY_EMIT_THUMB (0) -#define MICROPY_EMIT_INLINE_THUMB (0) -#define MICROPY_EMIT_INLINE_THUMB_ARMV7M (0) -#define MICROPY_EMIT_INLINE_THUMB_FLOAT (0) -#define MICROPY_EMIT_ARM (0) -#define MICROPY_EMIT_XTENSA (0) -#define MICROPY_EMIT_INLINE_XTENSA (0) -#define MICROPY_EMIT_XTENSAWIN (0) - -#define MICROPY_DYNAMIC_COMPILER (0) #define MICROPY_COMP_CONST_FOLDING (1) #define MICROPY_COMP_MODULE_CONST (1) #define MICROPY_COMP_CONST (1) diff --git a/ports/cc3200/application.mk b/ports/cc3200/application.mk index b216ca16d..2125274ae 100644 --- a/ports/cc3200/application.mk +++ b/ports/cc3200/application.mk @@ -182,7 +182,7 @@ ifeq ($(BTYPE), release) CFLAGS += -DNDEBUG else ifeq ($(BTYPE), debug) -CFLAGS += -DNDEBUG +CFLAGS += -DDEBUG else $(error Invalid BTYPE specified) endif diff --git a/ports/cc3200/ftp/ftp.c b/ports/cc3200/ftp/ftp.c index ee80e51f5..37680bc93 100644 --- a/ports/cc3200/ftp/ftp.c +++ b/ports/cc3200/ftp/ftp.c @@ -684,7 +684,7 @@ static void ftp_process_cmd (void) { if (E_FTP_RESULT_OK == (result = ftp_recv_non_blocking(ftp_data.c_sd, ftp_cmd_buffer, FTP_MAX_PARAM_SIZE + FTP_CMD_SIZE_MAX, &len))) { // bufptr is moved as commands are being popped ftp_cmd_index_t cmd = ftp_pop_command(&bufptr); - if (!ftp_data.loggin.passvalid && (cmd != E_FTP_CMD_USER && cmd != E_FTP_CMD_PASS && cmd != E_FTP_CMD_QUIT)) { + if (!ftp_data.loggin.passvalid && (cmd != E_FTP_CMD_USER && cmd != E_FTP_CMD_PASS && cmd != E_FTP_CMD_QUIT && cmd != E_FTP_CMD_FEAT)) { ftp_send_reply(332, NULL); return; } @@ -718,7 +718,8 @@ static void ftp_process_cmd (void) { break; case E_FTP_CMD_PWD: case E_FTP_CMD_XPWD: - ftp_send_reply(257, ftp_path); + snprintf((char *)ftp_data.dBuffer, FTP_BUFFER_SIZE, "\"%s\"", ftp_path); + ftp_send_reply(257, (char *)ftp_data.dBuffer); break; case E_FTP_CMD_SIZE: { diff --git a/ports/cc3200/mods/modmachine.c b/ports/cc3200/mods/modmachine.c index ddecd47f2..89800810c 100644 --- a/ports/cc3200/mods/modmachine.c +++ b/ports/cc3200/mods/modmachine.c @@ -26,6 +26,7 @@ */ #include +#include #include "py/runtime.h" #include "py/mphal.h" diff --git a/ports/cc3200/mpconfigport.h b/ports/cc3200/mpconfigport.h index 6fdb278d8..87689c505 100644 --- a/ports/cc3200/mpconfigport.h +++ b/ports/cc3200/mpconfigport.h @@ -197,8 +197,6 @@ typedef int32_t mp_int_t; // must be pointer size typedef unsigned int mp_uint_t; // must be pointer size typedef long mp_off_t; -#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) - #define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq() #define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state) #define MICROPY_EVENT_POLL_HOOK __WFI(); diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt new file mode 100644 index 000000000..fa419202f --- /dev/null +++ b/ports/esp32/CMakeLists.txt @@ -0,0 +1,38 @@ +# Top-level cmake file for building MicroPython on ESP32. + +cmake_minimum_required(VERSION 3.5) + +# Set the location of this port's directory. +set(MICROPY_PORT_DIR ${CMAKE_SOURCE_DIR}) + +# Set the board if it's not already set. +if(NOT MICROPY_BOARD) + set(MICROPY_BOARD GENERIC) +endif() + +# Set the board directory and check that it exists. +if(NOT MICROPY_BOARD_DIR) + set(MICROPY_BOARD_DIR ${MICROPY_PORT_DIR}/boards/${MICROPY_BOARD}) +endif() +if(NOT EXISTS ${MICROPY_BOARD_DIR}/mpconfigboard.cmake) + message(FATAL_ERROR "Invalid MICROPY_BOARD specified: ${MICROPY_BOARD}") +endif() + +# Define the output sdkconfig so it goes in the build directory. +set(SDKCONFIG ${CMAKE_BINARY_DIR}/sdkconfig) + +# Include board config; this is expected to set SDKCONFIG_DEFAULTS (among other options). +include(${MICROPY_BOARD_DIR}/mpconfigboard.cmake) + +# Concatenate all sdkconfig files into a combined one for the IDF to use. +file(WRITE ${CMAKE_BINARY_DIR}/sdkconfig.combined.in "") +foreach(SDKCONFIG_DEFAULT ${SDKCONFIG_DEFAULTS}) + file(READ ${SDKCONFIG_DEFAULT} CONTENTS) + file(APPEND ${CMAKE_BINARY_DIR}/sdkconfig.combined.in "${CONTENTS}") +endforeach() +configure_file(${CMAKE_BINARY_DIR}/sdkconfig.combined.in ${CMAKE_BINARY_DIR}/sdkconfig.combined COPYONLY) +set(SDKCONFIG_DEFAULTS ${CMAKE_BINARY_DIR}/sdkconfig.combined) + +# Include main IDF cmake file and define the project. +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(micropython) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index 756bc8f89..124979599 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -1,972 +1,43 @@ -# Select the board to build for: if not given on the command line, -# then default to GENERIC. +# Makefile for MicroPython on ESP32. +# +# This is a simple, convenience wrapper around idf.py (which uses cmake). + +# Select the board to build for, defaulting to GENERIC. BOARD ?= GENERIC # If the build directory is not given, make it reflect the board name. BUILD ?= build-$(BOARD) -BOARD_DIR ?= boards/$(BOARD) -ifeq ($(wildcard $(BOARD_DIR)/.),) -$(error Invalid BOARD specified: $(BOARD_DIR)) -endif - -include ../../py/mkenv.mk - -# Optional (not currently used for ESP32) --include mpconfigport.mk - -ifneq ($(SDKCONFIG),) -$(error Use the BOARD variable instead of SDKCONFIG) -endif - -# Expected to set SDKCONFIG -include $(BOARD_DIR)/mpconfigboard.mk - -# qstr definitions (must come before including py.mk) -QSTR_DEFS = qstrdefsport.h -QSTR_GLOBAL_DEPENDENCIES = $(BOARD_DIR)/mpconfigboard.h -QSTR_GLOBAL_REQUIREMENTS = $(SDKCONFIG_H) - -# MicroPython feature configurations -MICROPY_ROM_TEXT_COMPRESSION ?= 1 -MICROPY_PY_USSL = 0 -MICROPY_SSL_AXTLS = 0 -MICROPY_PY_BTREE = 1 -MICROPY_VFS_FAT = 1 -MICROPY_VFS_LFS2 = 1 - -FROZEN_MANIFEST ?= boards/manifest.py - -# include py core make definitions -include $(TOP)/py/py.mk - -GIT_SUBMODULES = lib/berkeley-db-1.xx - +# Device serial settings. PORT ?= /dev/ttyUSB0 BAUD ?= 460800 -FLASH_MODE ?= dio -FLASH_FREQ ?= 40m -FLASH_SIZE ?= 4MB -CROSS_COMPILE ?= xtensa-esp32-elf- -OBJDUMP = $(CROSS_COMPILE)objdump - -SDKCONFIG_COMBINED = $(BUILD)/sdkconfig.combined -SDKCONFIG_H = $(BUILD)/sdkconfig.h - -# The git hash of the currently supported ESP IDF version. -# These correspond to v3.3.2 and v4.0.1. -ESPIDF_SUPHASH_V3 := 9e70825d1e1cbf7988cf36981774300066580ea7 -ESPIDF_SUPHASH_V4 := 4c81978a3e2220674a432a588292a4c860eef27b - -define print_supported_git_hash -$(info Supported git hash (v3.3): $(ESPIDF_SUPHASH_V3)) -$(info Supported git hash (v4.0) (experimental): $(ESPIDF_SUPHASH_V4)) -endef - -# paths to ESP IDF and its components -ifeq ($(ESPIDF),) -ifneq ($(IDF_PATH),) -ESPIDF = $(IDF_PATH) -else -$(info The ESPIDF variable has not been set, please set it to the root of the esp-idf repository.) -$(info See README.md for installation instructions.) -dummy := $(call print_supported_git_hash) -$(error ESPIDF not set) -endif -endif - -ESPCOMP = $(ESPIDF)/components -ESPTOOL ?= $(ESPCOMP)/esptool_py/esptool/esptool.py -ESPCOMP_KCONFIGS = $(shell find $(ESPCOMP) -name Kconfig) -ESPCOMP_KCONFIGS_PROJBUILD = $(shell find $(ESPCOMP) -name Kconfig.projbuild) - -# verify the ESP IDF version -ESPIDF_CURHASH := $(shell git -C $(ESPIDF) show -s --pretty=format:'%H') - -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V3)) -$(info Building with ESP IDF v3) -else ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -$(info Building with ESP IDF v4) - -PYPARSING_VERSION = $(shell python3 -c 'import pyparsing; print(pyparsing.__version__)') -ifneq ($(PYPARSING_VERSION),2.3.1) -$(info ** ERROR **) -$(info EDP IDF requires pyparsing version less than 2.4) -$(info You will need to set up a Python virtual environment with pyparsing 2.3.1) -$(info Please see README.md for more information) -$(error Incorrect pyparsing version) -endif -else -$(info ** WARNING **) -$(info The git hash of ESP IDF does not match the supported version) -$(info The build may complete and the firmware may work but it is not guaranteed) -$(info ESP IDF path: $(ESPIDF)) -$(info Current git hash: $(ESPIDF_CURHASH)) -dummy := $(call print_supported_git_hash) -endif - -# pretty format of ESP IDF version, used internally by the IDF -IDF_VER := $(shell git -C $(ESPIDF) describe) - -ifeq ($(shell which $(CC) 2> /dev/null),) -$(info ** ERROR **) -$(info Cannot find C compiler $(CC)) -$(info Add the xtensa toolchain to your PATH. See README.md) -$(error C compiler missing) -endif - -# Support BLE by default. -# Can be explicitly disabled on the command line or board config. -MICROPY_PY_BLUETOOTH ?= 1 -ifeq ($(MICROPY_PY_BLUETOOTH),1) -SDKCONFIG += boards/sdkconfig.ble - -# Use NimBLE on ESP32. -MICROPY_BLUETOOTH_NIMBLE ?= 1 -# Use Nimble bindings, but ESP32 IDF provides the Nimble library. -MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY = 1 -include $(TOP)/extmod/nimble/nimble.mk -endif - -# include sdkconfig to get needed configuration values -include $(SDKCONFIG) - -################################################################################ -# Compiler and linker flags - -INC += -I. -INC += -I$(TOP) -INC += -I$(BUILD) - -INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include -INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include_bootloader -INC_ESPCOMP += -I$(ESPCOMP)/console -INC_ESPCOMP += -I$(ESPCOMP)/driver/include -INC_ESPCOMP += -I$(ESPCOMP)/driver/include/driver -INC_ESPCOMP += -I$(ESPCOMP)/efuse/include -INC_ESPCOMP += -I$(ESPCOMP)/efuse/esp32/include -INC_ESPCOMP += -I$(ESPCOMP)/esp32/include -INC_ESPCOMP += -I$(ESPCOMP)/espcoredump/include -INC_ESPCOMP += -I$(ESPCOMP)/soc/include -INC_ESPCOMP += -I$(ESPCOMP)/soc/esp32/include -INC_ESPCOMP += -I$(ESPCOMP)/heap/include -INC_ESPCOMP += -I$(ESPCOMP)/log/include -INC_ESPCOMP += -I$(ESPCOMP)/nvs_flash/include -INC_ESPCOMP += -I$(ESPCOMP)/freertos/include -INC_ESPCOMP += -I$(ESPCOMP)/esp_ringbuf/include -INC_ESPCOMP += -I$(ESPCOMP)/esp_event/include -INC_ESPCOMP += -I$(ESPCOMP)/tcpip_adapter/include -INC_ESPCOMP += -I$(ESPCOMP)/lwip/lwip/src/include -INC_ESPCOMP += -I$(ESPCOMP)/lwip/port/esp32/include -INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/apps -INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/apps/sntp -INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/mbedtls/include -INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/port/include -INC_ESPCOMP += -I$(ESPCOMP)/mdns/include -INC_ESPCOMP += -I$(ESPCOMP)/mdns/private_include -INC_ESPCOMP += -I$(ESPCOMP)/spi_flash/include -INC_ESPCOMP += -I$(ESPCOMP)/ulp/include -INC_ESPCOMP += -I$(ESPCOMP)/vfs/include -INC_ESPCOMP += -I$(ESPCOMP)/xtensa-debug-module/include -INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/include -INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/port/include -INC_ESPCOMP += -I$(ESPCOMP)/app_trace/include -INC_ESPCOMP += -I$(ESPCOMP)/app_update/include -INC_ESPCOMP += -I$(ESPCOMP)/pthread/include -INC_ESPCOMP += -I$(ESPCOMP)/smartconfig_ack/include -INC_ESPCOMP += -I$(ESPCOMP)/sdmmc/include - -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -INC_ESPCOMP += -I$(ESPCOMP)/esp_common/include -INC_ESPCOMP += -I$(ESPCOMP)/esp_eth/include -INC_ESPCOMP += -I$(ESPCOMP)/esp_event/private_include -INC_ESPCOMP += -I$(ESPCOMP)/esp_rom/include -INC_ESPCOMP += -I$(ESPCOMP)/esp_wifi/include -INC_ESPCOMP += -I$(ESPCOMP)/esp_wifi/esp32/include -INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/apps/sntp -INC_ESPCOMP += -I$(ESPCOMP)/spi_flash/private_include -INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/include/esp_supplicant -INC_ESPCOMP += -I$(ESPCOMP)/xtensa/include -INC_ESPCOMP += -I$(ESPCOMP)/xtensa/esp32/include -ifeq ($(CONFIG_BT_NIMBLE_ENABLED),y) -INC_ESPCOMP += -I$(ESPCOMP)/bt/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/common/osi/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/common/btc/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/common/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/porting/nimble/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/port/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/ans/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/bas/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/gap/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/gatt/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/ias/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/lls/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/tps/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/util/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/store/ram/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/store/config/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/porting/npl/freertos/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/ext/tinycrypt/include -INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/esp-hci/include -endif -else -INC_ESPCOMP += -I$(ESPCOMP)/ethernet/include -INC_ESPCOMP += -I$(ESPCOMP)/expat/expat/expat/lib -INC_ESPCOMP += -I$(ESPCOMP)/expat/port/include -INC_ESPCOMP += -I$(ESPCOMP)/json/include -INC_ESPCOMP += -I$(ESPCOMP)/json/port/include -INC_ESPCOMP += -I$(ESPCOMP)/micro-ecc/micro-ecc -INC_ESPCOMP += -I$(ESPCOMP)/nghttp/port/include -INC_ESPCOMP += -I$(ESPCOMP)/nghttp/nghttp2/lib/includes -ifeq ($(CONFIG_NIMBLE_ENABLED),y) -INC_ESPCOMP += -I$(ESPCOMP)/bt/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/porting/nimble/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/port/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/services/ans/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/services/bas/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/services/gap/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/services/gatt/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/services/ias/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/services/lls/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/services/tps/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/util/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/store/ram/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/nimble/host/store/config/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/porting/npl/freertos/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/nimble/ext/tinycrypt/include -INC_ESPCOMP += -I$(ESPCOMP)/nimble/esp-hci/include -endif -endif - -INC_NEWLIB += -I$(ESPCOMP)/newlib/platform_include -INC_NEWLIB += -I$(ESPCOMP)/newlib/include - -ifeq ($(MICROPY_PY_BLUETOOTH),1) -CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1 -CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1 -endif - -# these flags are common to C and C++ compilation -CFLAGS_COMMON = -Os -ffunction-sections -fdata-sections -fstrict-volatile-bitfields \ - -mlongcalls -nostdlib \ - -Wall -Werror -Wno-error=unused-function -Wno-error=unused-but-set-variable \ - -Wno-error=unused-variable -Wno-error=deprecated-declarations \ - -DESP_PLATFORM - -CFLAGS_BASE = -std=gnu99 $(CFLAGS_COMMON) -DMBEDTLS_CONFIG_FILE='"mbedtls/esp_config.h"' -DHAVE_CONFIG_H -CFLAGS = $(CFLAGS_BASE) $(INC) $(INC_ESPCOMP) $(INC_NEWLIB) -CFLAGS += -DIDF_VER=\"$(IDF_VER)\" -CFLAGS += $(CFLAGS_MOD) $(CFLAGS_EXTRA) -CFLAGS += -I$(BOARD_DIR) - -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -CFLAGS += -DMICROPY_ESP_IDF_4=1 -endif - -# this is what ESPIDF uses for c++ compilation -CXXFLAGS = -std=gnu++11 $(CFLAGS_COMMON) $(INC) $(INC_ESPCOMP) $(CXXFLAGS_MOD) - -LDFLAGS = -nostdlib -Map=$(@:.elf=.map) --cref -LDFLAGS += --gc-sections -static -EL -LDFLAGS += -u call_user_start_cpu0 -u uxTopUsedPriority -u ld_include_panic_highint_hdl -LDFLAGS += -u __cxa_guard_dummy # so that implementation of static guards is taken from cxx_guards.o instead of libstdc++.a -LDFLAGS += -L$(ESPCOMP)/esp32/ld -LDFLAGS += -L$(ESPCOMP)/esp_rom/esp32/ld -LDFLAGS += -T $(BUILD)/esp32_out.ld -LDFLAGS += -T $(BUILD)/esp32.project.ld -LDFLAGS += -T esp32.rom.ld -LDFLAGS += -T esp32.rom.libgcc.ld -LDFLAGS += -T esp32.peripherals.ld - -LIBGCC_FILE_NAME = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) -LIBSTDCXX_FILE_NAME = $(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a) - -# Debugging/Optimization -ifeq ($(DEBUG), 1) -CFLAGS += -g -COPT = -O0 -else -#CFLAGS += -fdata-sections -ffunction-sections -COPT += -Os -DNDEBUG -#LDFLAGS += --gc-sections -endif - -# Options for mpy-cross -MPY_CROSS_FLAGS += -march=xtensawin - -# Enable SPIRAM support if CONFIG_ESP32_SPIRAM_SUPPORT=y in sdkconfig -ifeq ($(CONFIG_ESP32_SPIRAM_SUPPORT),y) -CFLAGS_COMMON += -mfix-esp32-psram-cache-issue -LIBC_LIBM = $(ESPCOMP)/newlib/lib/libc-psram-workaround.a $(ESPCOMP)/newlib/lib/libm-psram-workaround.a -else -# Additional newlib symbols that can only be used with spiram disabled. -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -LDFLAGS += -T esp32.rom.newlib-funcs.ld -LDFLAGS += -T esp32.rom.newlib-locale.ld -LDFLAGS += -T esp32.rom.newlib-data.ld -else -LDFLAGS += -T esp32.rom.spiram_incompatible_fns.ld -endif -LIBC_LIBM = $(ESPCOMP)/newlib/lib/libc.a $(ESPCOMP)/newlib/lib/libm.a -endif - -################################################################################ -# List of MicroPython source and object files - -SRC_C = \ - main.c \ - uart.c \ - gccollect.c \ - mphalport.c \ - fatfs_port.c \ - help.c \ - modutime.c \ - moduos.c \ - machine_timer.c \ - machine_pin.c \ - machine_touchpad.c \ - machine_adc.c \ - machine_dac.c \ - machine_i2c.c \ - machine_pwm.c \ - machine_uart.c \ - modmachine.c \ - modnetwork.c \ - network_lan.c \ - network_ppp.c \ - mpnimbleport.c \ - modsocket.c \ - modesp.c \ - esp32_partition.c \ - esp32_rmt.c \ - esp32_ulp.c \ - modesp32.c \ - espneopixel.c \ - machine_hw_spi.c \ - machine_wdt.c \ - mpthreadport.c \ - machine_rtc.c \ - machine_sdcard.c \ - $(wildcard $(BOARD_DIR)/*.c) \ - $(SRC_MOD) - -SRC_CXX += \ - $(SRC_MOD_CXX) - -EXTMOD_SRC_C += $(addprefix extmod/,\ - modonewire.c \ - ) - -LIB_SRC_C = $(addprefix lib/,\ - mbedtls_errors/mp_mbedtls_errors.c \ - mp-readline/readline.c \ - netutils/netutils.c \ - timeutils/timeutils.c \ - utils/pyexec.c \ - utils/interrupt_char.c \ - utils/sys_stdio_mphal.c \ - ) - -DRIVERS_SRC_C = $(addprefix drivers/,\ - bus/softspi.c \ - dht/dht.c \ - ) - -OBJ_MP = -OBJ_MP += $(PY_O) -OBJ_MP += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) -OBJ_MP += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o)) -OBJ_MP += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o)) -OBJ_MP += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) -OBJ_MP += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) - -# Only enable this for the MicroPython source: ignore warnings from esp-idf. -$(OBJ_MP): CFLAGS += -Wdouble-promotion -Wfloat-conversion - -# List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(SRC_CXX) $(EXTMOD_SRC_C) $(LIB_SRC_C) $(DRIVERS_SRC_C) -# Append any auto-generated sources that are needed by sources listed in SRC_QSTR -SRC_QSTR_AUTO_DEPS += - -################################################################################ -# Generate sdkconfig.h from sdkconfig - -$(SDKCONFIG_COMBINED): $(SDKCONFIG) - $(Q)$(MKDIR) -p $(dir $@) - $(Q)$(CAT) $^ > $@ - -$(SDKCONFIG_H): $(SDKCONFIG_COMBINED) - $(ECHO) "GEN $@" - $(Q)$(MKDIR) -p $(dir $@) - $(Q)$(PYTHON) $(ESPIDF)/tools/kconfig_new/confgen.py \ - --output header $@ \ - --config $< \ - --kconfig $(ESPIDF)/Kconfig \ - --env "IDF_TARGET=esp32" \ - --env "IDF_CMAKE=n" \ - --env "COMPONENT_KCONFIGS=$(ESPCOMP_KCONFIGS)" \ - --env "COMPONENT_KCONFIGS_PROJBUILD=$(ESPCOMP_KCONFIGS_PROJBUILD)" \ - --env "IDF_PATH=$(ESPIDF)" - $(Q)touch $@ - -$(HEADER_BUILD)/qstrdefs.generated.h: $(SDKCONFIG_H) $(BOARD_DIR)/mpconfigboard.h - -################################################################################ -# List of object files from the ESP32 IDF components - -ESPIDF_BOOTLOADER_SUPPORT_O = $(patsubst %.c,%.o,\ - $(filter-out $(ESPCOMP)/bootloader_support/src/bootloader_init.c,\ - $(wildcard $(ESPCOMP)/bootloader_support/src/*.c) \ - $(wildcard $(ESPCOMP)/bootloader_support/src/idf/*.c) \ - )) - -ESPIDF_DRIVER_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/driver/*.c)) - -ESPIDF_EFUSE_O = $(patsubst %.c,%.o,\ - $(wildcard $(ESPCOMP)/efuse/esp32/*.c)\ - $(wildcard $(ESPCOMP)/efuse/src/*.c)\ - ) - -$(BUILD)/$(ESPCOMP)/esp32/dport_access.o: CFLAGS += -Wno-array-bounds -ESPIDF_ESP32_O = \ - $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/esp32/*.c)) \ - $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/esp32/hwcrypto/*.c)) \ - $(patsubst %.S,%.o,$(wildcard $(ESPCOMP)/esp32/*.S)) \ - -ESPIDF_ESP_RINGBUF_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/esp_ringbuf/*.c)) - -ESPIDF_HEAP_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/heap/*.c)) - -ESPIDF_SOC_O = $(patsubst %.c,%.o,\ - $(wildcard $(ESPCOMP)/soc/esp32/*.c) \ - $(wildcard $(ESPCOMP)/soc/src/*.c) \ - $(wildcard $(ESPCOMP)/soc/src/hal/*.c) \ - ) - -$(BUILD)/$(ESPCOMP)/cxx/cxx_guards.o: CXXFLAGS += -Wno-error=sign-compare -ESPIDF_CXX_O = $(patsubst %.cpp,%.o,$(wildcard $(ESPCOMP)/cxx/*.cpp)) - -ESPIDF_PTHREAD_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/pthread/*.c)) - -# Assembler .S files need only basic flags, and in particular should not have -# -Os because that generates subtly different code. -# We also need custom CFLAGS for .c files because FreeRTOS has headers with -# generic names (eg queue.h) which can clash with other files in the port. -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -CFLAGS_ASM = -I$(BUILD) -I$(ESPCOMP)/esp32/include -I$(ESPCOMP)/soc/esp32/include -I$(ESPCOMP)/freertos/include/freertos -I. -I$(ESPCOMP)/xtensa/include -I$(ESPCOMP)/xtensa/esp32/include -I$(ESPCOMP)/esp_common/include -else -CFLAGS_ASM = -I$(BUILD) -I$(ESPCOMP)/esp32/include -I$(ESPCOMP)/soc/esp32/include -I$(ESPCOMP)/freertos/include/freertos -I. -endif -$(BUILD)/$(ESPCOMP)/freertos/portasm.o: CFLAGS = $(CFLAGS_ASM) -$(BUILD)/$(ESPCOMP)/freertos/xtensa_context.o: CFLAGS = $(CFLAGS_ASM) -$(BUILD)/$(ESPCOMP)/freertos/xtensa_intr_asm.o: CFLAGS = $(CFLAGS_ASM) -$(BUILD)/$(ESPCOMP)/freertos/xtensa_vectors.o: CFLAGS = $(CFLAGS_ASM) -$(BUILD)/$(ESPCOMP)/freertos/xtensa_vector_defaults.o: CFLAGS = $(CFLAGS_ASM) -$(BUILD)/$(ESPCOMP)/freertos/%.o: CFLAGS = $(CFLAGS_BASE) -I. -I$(BUILD) $(INC_ESPCOMP) $(INC_NEWLIB) -I$(ESPCOMP)/freertos/include/freertos -D_ESP_FREERTOS_INTERNAL -ESPIDF_FREERTOS_O = \ - $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/freertos/*.c)) \ - $(patsubst %.S,%.o,$(wildcard $(ESPCOMP)/freertos/*.S)) \ - -ESPIDF_VFS_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/vfs/*.c)) - -ESPIDF_LOG_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/log/*.c)) - -ESPIDF_XTENSA_DEBUG_MODULE_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/xtensa-debug-module/*.c)) - -ESPIDF_TCPIP_ADAPTER_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/tcpip_adapter/*.c)) - -ESPIDF_APP_TRACE_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/app_trace/*.c)) -ESPIDF_APP_UPDATE_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/app_update/*.c)) +PYTHON ?= python3 -ESPIDF_NEWLIB_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/newlib/*.c)) - -$(BUILD)/$(ESPCOMP)/nvs_flash/src/nvs_api.o: CXXFLAGS += -Wno-error=sign-compare -ESPIDF_NVS_FLASH_O = $(patsubst %.cpp,%.o,$(wildcard $(ESPCOMP)/nvs_flash/src/*.cpp)) - -ESPIDF_SMARTCONFIG_ACK_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/smartconfig_ack/*.c)) - -ESPIDF_SPI_FLASH_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/spi_flash/*.c)) - -ESPIDF_ULP_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/ulp/*.c)) - -$(BUILD)/$(ESPCOMP)/lwip/%.o: CFLAGS += -Wno-address -Wno-unused-variable -Wno-unused-but-set-variable -ESPIDF_LWIP_O = $(patsubst %.c,%.o,\ - $(wildcard $(ESPCOMP)/lwip/apps/dhcpserver/*.c) \ - $(wildcard $(ESPCOMP)/lwip/lwip/src/api/*.c) \ - $(wildcard $(ESPCOMP)/lwip/lwip/src/apps/sntp/*.c) \ - $(wildcard $(ESPCOMP)/lwip/lwip/src/core/*.c) \ - $(wildcard $(ESPCOMP)/lwip/lwip/src/core/*/*.c) \ - $(wildcard $(ESPCOMP)/lwip/lwip/src/netif/*.c) \ - $(wildcard $(ESPCOMP)/lwip/lwip/src/netif/*/*.c) \ - $(wildcard $(ESPCOMP)/lwip/lwip/src/netif/*/*/*.c) \ - $(wildcard $(ESPCOMP)/lwip/port/esp32/*.c) \ - $(wildcard $(ESPCOMP)/lwip/port/esp32/*/*.c) \ - ) - -# Mbedtls source files, exclude error.c in favor of lib/mbedtls_errors/mp_mbedtls_errors.c -ESPIDF_MBEDTLS_O = $(patsubst %.c,%.o, $(filter-out %/error.c,\ - $(wildcard $(ESPCOMP)/mbedtls/mbedtls/library/*.c) \ - $(wildcard $(ESPCOMP)/mbedtls/port/*.c) \ - $(wildcard $(ESPCOMP)/mbedtls/port/esp32/*.c) \ - )) - -ESPIDF_MDNS_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/mdns/*.c)) - -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -$(BUILD)/$(ESPCOMP)/wpa_supplicant/%.o: CFLAGS += -DCONFIG_WPA3_SAE -DCONFIG_IEEE80211W -DESP_SUPPLICANT -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_TLS -DEAP_TTLS -DEAP_PEAP -DEAP_MSCHAPv2 -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -DCONFIG_ECC -D__ets__ -Wno-strict-aliasing -I$(ESPCOMP)/wpa_supplicant/src -Wno-implicit-function-declaration -else -$(BUILD)/$(ESPCOMP)/wpa_supplicant/%.o: CFLAGS += -DEMBEDDED_SUPP -DIEEE8021X_EAPOL -DEAP_PEER_METHOD -DEAP_MSCHAPv2 -DEAP_TTLS -DEAP_TLS -DEAP_PEAP -DUSE_WPA2_TASK -DCONFIG_WPS2 -DCONFIG_WPS_PIN -DUSE_WPS_TASK -DESPRESSIF_USE -DESP32_WORKAROUND -DALLOW_EVEN_MOD -D__ets__ -Wno-strict-aliasing -endif -ESPIDF_WPA_SUPPLICANT_O = $(patsubst %.c,%.o,\ - $(wildcard $(ESPCOMP)/wpa_supplicant/port/*.c) \ - $(wildcard $(ESPCOMP)/wpa_supplicant/src/*/*.c) \ - ) - -ESPIDF_SDMMC_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/sdmmc/*.c)) - -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -ESPIDF_ESP_COMMON_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/esp_common/src/*.c)) - -ESPIDF_ESP_EVENT_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/esp_event/*.c)) - -ESPIDF_ESP_WIFI_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/esp_wifi/src/*.c)) - -ifeq ($(CONFIG_BT_NIMBLE_ENABLED),y) -ESPIDF_BT_NIMBLE_O = $(patsubst %.c,%.o,\ - $(wildcard $(ESPCOMP)/bt/controller/*.c) \ - $(wildcard $(ESPCOMP)/bt/common/btc/core/*.c) \ - $(wildcard $(ESPCOMP)/bt/common/osi/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/esp-hci/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/ext/tinycrypt/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/ans/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/bas/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/gap/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/gatt/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/ias/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/lls/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/tps/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/store/config/src/ble_store_config.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/store/config/src/ble_store_nvs.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/store/ram/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/host/util/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/nimble/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/porting/nimble/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/nimble/porting/npl/freertos/src/*.c) \ - $(wildcard $(ESPCOMP)/bt/host/nimble/port/src/*.c) \ - ) -endif - -$(BUILD)/$(ESPCOMP)/esp_eth/src/esp_eth_mac_dm9051.o: CFLAGS += -fno-strict-aliasing -ESPIDF_ESP_ETH_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/esp_eth/src/*.c)) - -ESPIDF_XTENSA_O = $(patsubst %.c,%.o,\ - $(wildcard $(ESPCOMP)/xtensa/*.c) \ - $(wildcard $(ESPCOMP)/xtensa/esp32/*.c) \ - ) -else -ESPIDF_JSON_O = $(patsubst %.c,%.o,$(wildcard $(ESPCOMP)/json/cJSON/cJSON*.c)) - -ESPIDF_ETHERNET_O = $(patsubst %.c,%.o,\ - $(wildcard $(ESPCOMP)/ethernet/*.c) \ - $(wildcard $(ESPCOMP)/ethernet/eth_phy/*.c) \ - ) - -ifeq ($(CONFIG_NIMBLE_ENABLED),y) -ESPIDF_BT_NIMBLE_O = $(patsubst %.c,%.o,\ - $(wildcard $(ESPCOMP)/bt/*.c) \ - $(wildcard $(ESPCOMP)/nimble/esp-hci/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/ext/tinycrypt/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/services/ans/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/services/bas/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/services/gap/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/services/gatt/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/services/ias/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/services/lls/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/services/tps/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/store/config/src/ble_store_config.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/store/config/src/ble_store_nvs.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/store/ram/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/host/util/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/nimble/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/porting/nimble/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/nimble/porting/npl/freertos/src/*.c) \ - $(wildcard $(ESPCOMP)/nimble/port/src/*.c) \ - ) -endif -endif - -OBJ_ESPIDF = -LIB_ESPIDF = -BUILD_ESPIDF_LIB = $(BUILD)/esp-idf - -define gen_espidf_lib_rule -OBJ_ESPIDF += $(addprefix $$(BUILD)/,$(2)) -LIB_ESPIDF += $(1) -$(BUILD_ESPIDF_LIB)/$(1)/lib$(1).a: $(addprefix $$(BUILD)/,$(2)) - $(ECHO) "AR $$@" - $(Q)$(AR) cru $$@ $$^ -endef - -$(eval $(call gen_espidf_lib_rule,bootloader_support,$(ESPIDF_BOOTLOADER_SUPPORT_O))) -$(eval $(call gen_espidf_lib_rule,driver,$(ESPIDF_DRIVER_O))) -$(eval $(call gen_espidf_lib_rule,efuse,$(ESPIDF_EFUSE_O))) -$(eval $(call gen_espidf_lib_rule,esp32,$(ESPIDF_ESP32_O))) -$(eval $(call gen_espidf_lib_rule,esp_ringbuf,$(ESPIDF_ESP_RINGBUF_O))) -$(eval $(call gen_espidf_lib_rule,heap,$(ESPIDF_HEAP_O))) -$(eval $(call gen_espidf_lib_rule,soc,$(ESPIDF_SOC_O))) -$(eval $(call gen_espidf_lib_rule,cxx,$(ESPIDF_CXX_O))) -$(eval $(call gen_espidf_lib_rule,pthread,$(ESPIDF_PTHREAD_O))) -$(eval $(call gen_espidf_lib_rule,freertos,$(ESPIDF_FREERTOS_O))) -$(eval $(call gen_espidf_lib_rule,vfs,$(ESPIDF_VFS_O))) -$(eval $(call gen_espidf_lib_rule,json,$(ESPIDF_JSON_O))) -$(eval $(call gen_espidf_lib_rule,log,$(ESPIDF_LOG_O))) -$(eval $(call gen_espidf_lib_rule,xtensa-debug-module,$(ESPIDF_XTENSA_DEBUG_MODULE_O))) -$(eval $(call gen_espidf_lib_rule,tcpip_adapter,$(ESPIDF_TCPIP_ADAPTER_O))) -$(eval $(call gen_espidf_lib_rule,app_trace,$(ESPIDF_APP_TRACE_O))) -$(eval $(call gen_espidf_lib_rule,app_update,$(ESPIDF_APP_UPDATE_O))) -$(eval $(call gen_espidf_lib_rule,newlib,$(ESPIDF_NEWLIB_O))) -$(eval $(call gen_espidf_lib_rule,nvs_flash,$(ESPIDF_NVS_FLASH_O))) -$(eval $(call gen_espidf_lib_rule,smartconfig_ack,$(ESPIDF_SMARTCONFIG_ACK_O))) -$(eval $(call gen_espidf_lib_rule,spi_flash,$(ESPIDF_SPI_FLASH_O))) -$(eval $(call gen_espidf_lib_rule,ulp,$(ESPIDF_ULP_O))) -$(eval $(call gen_espidf_lib_rule,lwip,$(ESPIDF_LWIP_O))) -$(eval $(call gen_espidf_lib_rule,mbedtls,$(ESPIDF_MBEDTLS_O))) -$(eval $(call gen_espidf_lib_rule,mdns,$(ESPIDF_MDNS_O))) -$(eval $(call gen_espidf_lib_rule,wpa_supplicant,$(ESPIDF_WPA_SUPPLICANT_O))) -$(eval $(call gen_espidf_lib_rule,sdmmc,$(ESPIDF_SDMMC_O))) -$(eval $(call gen_espidf_lib_rule,bt_nimble,$(ESPIDF_BT_NIMBLE_O))) - -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -$(eval $(call gen_espidf_lib_rule,esp_common,$(ESPIDF_ESP_COMMON_O))) -$(eval $(call gen_espidf_lib_rule,esp_event,$(ESPIDF_ESP_EVENT_O))) -$(eval $(call gen_espidf_lib_rule,esp_wifi,$(ESPIDF_ESP_WIFI_O))) -$(eval $(call gen_espidf_lib_rule,esp_eth,$(ESPIDF_ESP_ETH_O))) -$(eval $(call gen_espidf_lib_rule,xtensa,$(ESPIDF_XTENSA_O))) -else -$(eval $(call gen_espidf_lib_rule,ethernet,$(ESPIDF_ETHERNET_O))) -endif - -# Create all destination build dirs before compiling IDF source -OBJ_ESPIDF_DIRS = $(sort $(dir $(OBJ_ESPIDF))) $(BUILD_ESPIDF_LIB) $(addprefix $(BUILD_ESPIDF_LIB)/,$(LIB_ESPIDF)) -$(OBJ_ESPIDF): | $(OBJ_ESPIDF_DIRS) -$(OBJ_ESPIDF_DIRS): - $(MKDIR) -p $@ - -# Make all IDF object files depend on sdkconfig -$(OBJ_ESPIDF): $(SDKCONFIG_H) - -# Add all IDF components to the set of libraries -LIB = $(foreach lib,$(LIB_ESPIDF),$(BUILD_ESPIDF_LIB)/$(lib)/lib$(lib).a) - -################################################################################ -# ESP IDF ldgen - -LDGEN_FRAGMENTS = $(shell find $(ESPCOMP) -name "*.lf") - -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) - -LDGEN_LIBRARIES=$(foreach lib,$(LIB_ESPIDF),$(BUILD_ESPIDF_LIB)/$(lib)/lib$(lib).a) - -$(BUILD_ESPIDF_LIB)/ldgen_libraries: $(LDGEN_LIBRARIES) $(ESPIDF)/make/ldgen.mk - printf "$(foreach library,$(LDGEN_LIBRARIES),$(library)\n)" > $(BUILD_ESPIDF_LIB)/ldgen_libraries - -$(BUILD)/esp32.project.ld: $(ESPCOMP)/esp32/ld/esp32.project.ld.in $(LDGEN_FRAGMENTS) $(SDKCONFIG_COMBINED) $(BUILD_ESPIDF_LIB)/ldgen_libraries - $(ECHO) "GEN $@" - $(Q)$(PYTHON) $(ESPIDF)/tools/ldgen/ldgen.py \ - --input $< \ - --output $@ \ - --config $(SDKCONFIG_COMBINED) \ - --kconfig $(ESPIDF)/Kconfig \ - --fragments $(LDGEN_FRAGMENTS) \ - --libraries-file $(BUILD_ESPIDF_LIB)/ldgen_libraries \ - --env "IDF_TARGET=esp32" \ - --env "IDF_CMAKE=n" \ - --env "COMPONENT_KCONFIGS=$(ESPCOMP_KCONFIGS)" \ - --env "COMPONENT_KCONFIGS_PROJBUILD=$(ESPCOMP_KCONFIGS_PROJBUILD)" \ - --env "IDF_PATH=$(ESPIDF)" \ - --objdump $(OBJDUMP) - -else - -LDGEN_SECTIONS_INFO = $(foreach lib,$(LIB_ESPIDF),$(BUILD_ESPIDF_LIB)/$(lib)/lib$(lib).a.sections_info) -LDGEN_SECTION_INFOS = $(BUILD_ESPIDF_LIB)/ldgen.section_infos - -define gen_sections_info_rule -$(1).sections_info: $(1) - $(ECHO) "GEN $(1).sections_info" - $(Q)$(OBJDUMP) -h $(1) > $(1).sections_info -endef - -$(eval $(foreach lib,$(LIB_ESPIDF),$(eval $(call gen_sections_info_rule,$(BUILD_ESPIDF_LIB)/$(lib)/lib$(lib).a)))) - -$(LDGEN_SECTION_INFOS): $(LDGEN_SECTIONS_INFO) $(ESPIDF)/make/ldgen.mk - $(Q)printf "$(foreach info,$(LDGEN_SECTIONS_INFO),$(info)\n)" > $@ - -$(BUILD)/esp32.project.ld: $(ESPCOMP)/esp32/ld/esp32.project.ld.in $(LDGEN_FRAGMENTS) $(SDKCONFIG_COMBINED) $(LDGEN_SECTION_INFOS) - $(ECHO) "GEN $@" - $(Q)$(PYTHON) $(ESPIDF)/tools/ldgen/ldgen.py \ - --input $< \ - --output $@ \ - --config $(SDKCONFIG_COMBINED) \ - --kconfig $(ESPIDF)/Kconfig \ - --fragments $(LDGEN_FRAGMENTS) \ - --sections $(LDGEN_SECTION_INFOS) \ - --env "IDF_TARGET=esp32" \ - --env "IDF_CMAKE=n" \ - --env "COMPONENT_KCONFIGS=$(ESPCOMP_KCONFIGS)" \ - --env "COMPONENT_KCONFIGS_PROJBUILD=$(ESPCOMP_KCONFIGS_PROJBUILD)" \ - --env "IDF_PATH=$(ESPIDF)" - -endif +GIT_SUBMODULES = lib/berkeley-db-1.xx -################################################################################ -# Main targets +.PHONY: all clean deploy erase submodules FORCE -all: $(BUILD)/firmware.bin +IDFPY_FLAGS += -D MICROPY_BOARD=$(BOARD) -B $(BUILD) -.PHONY: idf-version deploy erase +all: + idf.py $(IDFPY_FLAGS) build + @$(PYTHON) makeimg.py \ + $(BUILD)/bootloader/bootloader.bin \ + $(BUILD)/partition_table/partition-table.bin \ + $(BUILD)/micropython.bin \ + $(BUILD)/firmware.bin -idf-version: - $(ECHO) "ESP IDF supported hash: $(ESPIDF_SUPHASH)" +$(BUILD)/bootloader/bootloader.bin $(BUILD)/partition_table/partition -table.bin $(BUILD)/micropython.bin: FORCE -$(BUILD)/firmware.bin: $(BUILD)/bootloader.bin $(BUILD)/partitions.bin $(BUILD)/application.bin - $(ECHO) "Create $@" - $(Q)$(PYTHON) makeimg.py $^ $@ +clean: + idf.py $(IDFPY_FLAGS) fullclean -deploy: $(BUILD)/firmware.bin - $(ECHO) "Writing $^ to the board" - $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) write_flash -z --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) 0x1000 $^ +deploy: + idf.py $(IDFPY_FLAGS) -p $(PORT) -b $(BAUD) flash erase: - $(ECHO) "Erasing flash" - $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) erase_flash - -################################################################################ -# Declarations to build the application - -OBJ = $(OBJ_MP) - -APP_LD_ARGS = -APP_LD_ARGS += $(LDFLAGS_MOD) -APP_LD_ARGS += $(addprefix -T,$(LD_FILES)) -APP_LD_ARGS += --start-group -APP_LD_ARGS += -L$(dir $(LIBGCC_FILE_NAME)) -lgcc -APP_LD_ARGS += -L$(dir $(LIBSTDCXX_FILE_NAME)) -lstdc++ -APP_LD_ARGS += $(LIBC_LIBM) -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -APP_LD_ARGS += -L$(ESPCOMP)/xtensa/esp32 -lhal -APP_LD_ARGS += -L$(ESPCOMP)/bt/controller/lib -lbtdm_app -APP_LD_ARGS += -L$(ESPCOMP)/esp_wifi/lib_esp32 -lcore -lmesh -lnet80211 -lphy -lrtc -lpp -lsmartconfig -lcoexist -else -APP_LD_ARGS += $(ESPCOMP)/esp32/libhal.a -APP_LD_ARGS += -L$(ESPCOMP)/bt/lib -lbtdm_app -APP_LD_ARGS += -L$(ESPCOMP)/esp32/lib -lcore -lmesh -lnet80211 -lphy -lrtc -lpp -lwpa -lsmartconfig -lcoexist -lwps -lwpa2 -endif -APP_LD_ARGS += $(OBJ) -APP_LD_ARGS += $(LIB) -APP_LD_ARGS += --end-group - -$(BUILD)/esp32_out.ld: $(SDKCONFIG_H) - $(Q)$(CC) -I$(BUILD) -C -P -x c -E $(ESPCOMP)/esp32/ld/esp32.ld -o $@ - -$(BUILD)/application.bin: $(BUILD)/application.elf - $(ECHO) "Create $@" - $(Q)$(ESPTOOL) --chip esp32 elf2image --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) --flash_size $(FLASH_SIZE) $< - -$(BUILD)/application.elf: $(OBJ) $(LIB) $(BUILD)/esp32_out.ld $(BUILD)/esp32.project.ld - $(ECHO) "LINK $@" - $(Q)$(LD) $(LDFLAGS) -o $@ $(APP_LD_ARGS) - $(Q)$(SIZE) $@ - -################################################################################ -# Declarations to build the bootloader - -BOOTLOADER_LIB_DIR = $(BUILD)/bootloader -BOOTLOADER_LIB_ALL = - -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -$(BUILD)/bootloader/$(ESPCOMP)/%.o: CFLAGS += -DBOOTLOADER_BUILD=1 -I$(ESPCOMP)/bootloader_support/include_priv -I$(ESPCOMP)/bootloader_support/include -I$(ESPCOMP)/efuse/include -I$(ESPCOMP)/esp_rom/include -Wno-error=format \ - -I$(ESPCOMP)/esp_common/include \ - -I$(ESPCOMP)/xtensa/include \ - -I$(ESPCOMP)/xtensa/esp32/include -else -$(BUILD)/bootloader/$(ESPCOMP)/%.o: CFLAGS += -DBOOTLOADER_BUILD=1 -I$(ESPCOMP)/bootloader_support/include_priv -I$(ESPCOMP)/bootloader_support/include -I$(ESPCOMP)/micro-ecc/micro-ecc -I$(ESPCOMP)/efuse/include -I$(ESPCOMP)/esp32 -Wno-error=format -endif - -# libbootloader_support.a -BOOTLOADER_LIB_ALL += bootloader_support -BOOTLOADER_LIB_BOOTLOADER_SUPPORT_OBJ = $(addprefix $(BUILD)/bootloader/$(ESPCOMP)/,\ - bootloader_support/src/bootloader_clock.o \ - bootloader_support/src/bootloader_common.o \ - bootloader_support/src/bootloader_flash.o \ - bootloader_support/src/bootloader_flash_config.o \ - bootloader_support/src/bootloader_init.o \ - bootloader_support/src/bootloader_random.o \ - bootloader_support/src/bootloader_utility.o \ - bootloader_support/src/flash_qio_mode.o \ - bootloader_support/src/esp_image_format.o \ - bootloader_support/src/flash_encrypt.o \ - bootloader_support/src/flash_partitions.o \ - ) - -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -BOOTLOADER_LIB_BOOTLOADER_SUPPORT_OBJ += $(addprefix $(BUILD)/bootloader/$(ESPCOMP)/,\ - bootloader_support/src/esp32/bootloader_sha.o \ - bootloader_support/src/bootloader_flash_config.o \ - bootloader_support/src/esp32/secure_boot.o \ - ) -else -BOOTLOADER_LIB_BOOTLOADER_SUPPORT_OBJ += $(addprefix $(BUILD)/bootloader/$(ESPCOMP)/,\ - bootloader_support/src/bootloader_sha.o \ - bootloader_support/src/secure_boot_signatures.o \ - bootloader_support/src/secure_boot.o \ - ) -endif - -$(BOOTLOADER_LIB_DIR)/libbootloader_support.a: $(BOOTLOADER_LIB_BOOTLOADER_SUPPORT_OBJ) - $(ECHO) "AR $@" - $(Q)$(AR) cr $@ $^ - -# liblog.a -BOOTLOADER_LIB_ALL += log -BOOTLOADER_LIB_LOG_OBJ = $(addprefix $(BUILD)/bootloader/$(ESPCOMP)/,\ - log/log.o \ - ) -$(BOOTLOADER_LIB_DIR)/liblog.a: $(BOOTLOADER_LIB_LOG_OBJ) - $(ECHO) "AR $@" - $(Q)$(AR) cr $@ $^ - -# libspi_flash.a -BOOTLOADER_LIB_ALL += spi_flash -BOOTLOADER_LIB_SPI_FLASH_OBJ = $(addprefix $(BUILD)/bootloader/$(ESPCOMP)/,\ - spi_flash/spi_flash_rom_patch.o \ - ) -$(BOOTLOADER_LIB_DIR)/libspi_flash.a: $(BOOTLOADER_LIB_SPI_FLASH_OBJ) - $(ECHO) "AR $@" - $(Q)$(AR) cr $@ $^ - -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V3)) -# libmicro-ecc.a -BOOTLOADER_LIB_ALL += micro-ecc -BOOTLOADER_LIB_MICRO_ECC_OBJ = $(addprefix $(BUILD)/bootloader/$(ESPCOMP)/,\ - micro-ecc/micro-ecc/uECC.o \ - ) -$(BOOTLOADER_LIB_DIR)/libmicro-ecc.a: $(BOOTLOADER_LIB_MICRO_ECC_OBJ) - $(ECHO) "AR $@" - $(Q)$(AR) cr $@ $^ -endif - -# libsoc.a -$(BUILD)/bootloader/$(ESPCOMP)/soc/esp32/rtc_clk.o: CFLAGS += -fno-jump-tables -fno-tree-switch-conversion -BOOTLOADER_LIB_ALL += soc -BOOTLOADER_LIB_SOC_OBJ = $(addprefix $(BUILD)/bootloader/$(ESPCOMP)/soc/,\ - esp32/cpu_util.o \ - esp32/gpio_periph.o \ - esp32/rtc_clk.o \ - esp32/rtc_clk_init.o \ - esp32/rtc_init.o \ - esp32/rtc_periph.o \ - esp32/rtc_pm.o \ - esp32/rtc_sleep.o \ - esp32/rtc_time.o \ - esp32/rtc_wdt.o \ - esp32/sdio_slave_periph.o \ - esp32/sdmmc_periph.o \ - esp32/soc_memory_layout.o \ - esp32/spi_periph.o \ - src/memory_layout_utils.o \ - ) -$(BOOTLOADER_LIB_DIR)/libsoc.a: $(BOOTLOADER_LIB_SOC_OBJ) - $(ECHO) "AR $@" - $(Q)$(AR) cr $@ $^ - -# libmain.a -BOOTLOADER_LIB_ALL += main -BOOTLOADER_LIB_MAIN_OBJ = $(addprefix $(BUILD)/bootloader/$(ESPCOMP)/,\ - bootloader/subproject/main/bootloader_start.o \ - ) -$(BOOTLOADER_LIB_DIR)/libmain.a: $(BOOTLOADER_LIB_MAIN_OBJ) - $(ECHO) "AR $@" - $(Q)$(AR) cr $@ $^ - -# all objects files -BOOTLOADER_OBJ_ALL = \ - $(BOOTLOADER_LIB_BOOTLOADER_SUPPORT_OBJ) \ - $(BOOTLOADER_LIB_LOG_OBJ) \ - $(BOOTLOADER_LIB_SPI_FLASH_OBJ) \ - $(BOOTLOADER_LIB_MICRO_ECC_OBJ) \ - $(BOOTLOADER_LIB_SOC_OBJ) \ - $(BOOTLOADER_LIB_MAIN_OBJ) - -$(BOOTLOADER_OBJ_ALL): $(SDKCONFIG_H) - -BOOTLOADER_LIBS = -BOOTLOADER_LIBS += -Wl,--start-group -BOOTLOADER_LIBS += $(BOOTLOADER_OBJ) -BOOTLOADER_LIBS += -L$(BUILD)/bootloader $(addprefix -l,$(BOOTLOADER_LIB_ALL)) -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -BOOTLOADER_LIBS += -L$(ESPCOMP)/esp_wifi/lib_esp32 -lrtc -else -BOOTLOADER_LIBS += -L$(ESPCOMP)/esp32/lib -lrtc -endif -BOOTLOADER_LIBS += -L$(dir $(LIBGCC_FILE_NAME)) -lgcc -BOOTLOADER_LIBS += -Wl,--end-group - -BOOTLOADER_LDFLAGS = -BOOTLOADER_LDFLAGS += -nostdlib -BOOTLOADER_LDFLAGS += -L$(ESPIDF)/lib -BOOTLOADER_LDFLAGS += -L$(ESPIDF)/ld -BOOTLOADER_LDFLAGS += -u call_user_start_cpu0 -BOOTLOADER_LDFLAGS += -Wl,--gc-sections -BOOTLOADER_LDFLAGS += -static -BOOTLOADER_LDFLAGS += -Wl,-EL -BOOTLOADER_LDFLAGS += -Wl,-Map=$(@:.elf=.map) -Wl,--cref -BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/bootloader/subproject/main/esp32.bootloader.ld -BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/bootloader/subproject/main/esp32.bootloader.rom.ld -ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) -BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/esp_rom/esp32/ld/esp32.rom.ld -BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/esp_rom/esp32/ld/esp32.rom.newlib-funcs.ld -else -BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/esp32/ld/esp32.rom.ld -BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/esp32/ld/esp32.rom.spiram_incompatible_fns.ld -endif -BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/esp32/ld/esp32.peripherals.ld - -BOOTLOADER_OBJ_DIRS = $(sort $(dir $(BOOTLOADER_OBJ_ALL))) -$(BOOTLOADER_OBJ_ALL): | $(BOOTLOADER_OBJ_DIRS) -$(BOOTLOADER_OBJ_DIRS): - $(MKDIR) -p $@ - -$(BUILD)/bootloader/%.o: %.c - $(call compile_c) - -$(BUILD)/bootloader.bin: $(BUILD)/bootloader.elf - $(ECHO) "Create $@" - $(Q)$(ESPTOOL) --chip esp32 elf2image --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) --flash_size $(FLASH_SIZE) $< - -$(BUILD)/bootloader.elf: $(BOOTLOADER_OBJ) $(addprefix $(BOOTLOADER_LIB_DIR)/lib,$(addsuffix .a,$(BOOTLOADER_LIB_ALL))) - $(ECHO) "LINK $@" - $(Q)$(CC) $(BOOTLOADER_LDFLAGS) -o $@ $(BOOTLOADER_LIBS) - -################################################################################ -# Declarations to build the partitions - -PYTHON2 ?= python2 - -# Can be overriden by mkconfigboard.mk. -PART_SRC ?= partitions.csv - -$(BUILD)/partitions.bin: $(PART_SRC) - $(ECHO) "Create $@" - $(Q)$(PYTHON2) $(ESPCOMP)/partition_table/gen_esp32part.py -q $< $@ - -################################################################################ + idf.py $(IDFPY_FLAGS) erase_flash -include $(TOP)/py/mkrules.mk +submodules: + git submodule update --init $(addprefix ../../,$(GIT_SUBMODULES)) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index 54fb41cf6..c349f44b2 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -1,13 +1,13 @@ MicroPython port to the ESP32 ============================= -This is an experimental port of MicroPython to the Espressif ESP32 -microcontroller. It uses the ESP-IDF framework and MicroPython runs as +This is a port of MicroPython to the Espressif ESP32 series of +microcontrollers. It uses the ESP-IDF framework and MicroPython runs as a task under FreeRTOS. Supported features include: - REPL (Python prompt) over UART0. -- 16k stack for the MicroPython task and 96k Python heap. +- 16k stack for the MicroPython task and approximately 100k Python heap. - Many of MicroPython's features are enabled: unicode, arbitrary-precision integers, single-precision floats, complex numbers, frozen bytecode, as well as many of the internal modules. @@ -15,16 +15,24 @@ Supported features include: - The machine module with GPIO, UART, SPI, software I2C, ADC, DAC, PWM, TouchPad, WDT and Timer. - The network module with WLAN (WiFi) support. +- Bluetooth low-energy (BLE) support via the bluetooth module. -Development of this ESP32 port was sponsored in part by Microbric Pty Ltd. +Initial development of this ESP32 port was sponsored in part by Microbric Pty Ltd. -Setting up the toolchain and ESP-IDF ------------------------------------- +Setting up ESP-IDF and the build environment +-------------------------------------------- -There are two main components that are needed to build the firmware: -- the Xtensa cross-compiler that targets the CPU in the ESP32 (this is - different to the compiler used by the ESP8266) -- the Espressif IDF (IoT development framework, aka SDK) +MicroPython on ESP32 requires the Espressif IDF version 4 (IoT development +framework, aka SDK). The ESP-IDF includes the libraries and RTOS needed to +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 v4.0.2, v4.1.1 and v4.2, +although other IDF v4 versions may also work. + +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/v4.0.2/get-started/index.html#installation-step-by-step). If you are on a Windows machine then the [Windows Subsystem for Linux](https://msdn.microsoft.com/en-au/commandline/wsl/install_guide) is the @@ -32,164 +40,32 @@ most efficient way to install the ESP32 toolchain and build the project. If you use WSL then follow the Linux instructions rather than the Windows instructions. -The ESP-IDF changes quickly and MicroPython only supports certain versions. -The git hash of these versions (one for 3.x, one for 4.x) can be found by -running `make` without a configured `ESPIDF`. Then you can fetch the -required IDF using the following command: - -```bash -$ cd ports/esp32 -$ make ESPIDF= # This will print the supported hashes, copy the one you want. -$ export ESPIDF=$HOME/src/github.com/espressif/esp-idf # Or any path you like. -$ mkdir -p $ESPIDF -$ cd $ESPIDF -$ git clone https://github.com/espressif/esp-idf.git $ESPIDF -$ git checkout -$ git submodule update --init --recursive -``` - -Note: The ESP IDF v4.x support is currently experimental. It does not -currently support PPP or wired Ethernet. - -Python dependencies -=================== - -You will also need other dependencies from the IDF, see -`$ESPIDF/requirements.txt`, but at a minimum you need `pyserial>=3.0` and -`pyparsing>=2.0.3,<2.4.0`. - -You can use Python 2 or Python 3. If you need to override the system default -add (for example) `PYTHON=python3` to any of the `make` commands below. - -It is recommended to use a Python virtual environment. Even if your system -package manager already provides these libraries, the IDF v4.x is currently -incompatible with pyparsing 2.4 and higher. - -For example, to set up a Python virtual environment from scratch: - -```bash -$ cd ports/esp32 -$ python3 -m venv build-venv -$ source build-venv/bin/activate -$ pip install --upgrade pip -$ pip install -r path/to/esp-idf/requirements.txt -``` +The Espressif instructions will guide you through using the `install.sh` +(or `install.bat`) script to download the toolchain and set up your environment. +The steps to take are summarised below. -To re-enter this virtual environment in future sessions, you only need to -source the `activate` script, i.e.: +To check out a copy of the IDF use git clone: ```bash -$ cd ports/esp32 -$ source build-venv/bin/activate +$ git clone -b v4.0.2 --recursive https://github.com/espressif/esp-idf.git ``` -Then, to install the toolchain (which includes the GCC compiler, linker, binutils, -etc), there are two options: - -1. Using the IDF scripts to install the toolchain (IDF 4.x only) -================================================================ - -Follow the steps at the [Espressif Getting Started guide](https://docs.espressif.com/projects/esp-idf/en/v4.0/get-started/index.html#step-3-set-up-the-tools). - -This will guide you through using the `install.sh` (or `install.bat`) script -to download the toolchain and add it to your `PATH`. The steps are summarised -below: - -After you've cloned and checked out the IDF to the correct version (see -above), run the `install.sh` script: - -```bash -$ cd $ESPIDF -$ ./install.sh # (or install.bat on Windows) -``` +You can replace `v4.0.2` with `v4.1.1` or 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.) -Then in the `ports/esp32` directory, source the `export.sh` script to set the -`PATH`. +After you've cloned and checked out the IDF to the correct version, run the +`install.sh` script: ```bash -$ cd micropython/ports/esp32 -$ source $ESPIDF/export.sh # (or path\to\esp-idf\export.bat on Windows) -$ # Run make etc, see below. +$ cd esp-idf +$ ./install.sh # (or install.bat on Windows) +$ source export.sh # (or export.bat on Windows) ``` The `install.sh` step only needs to be done once. You will need to source `export.sh` for every new session. -Note: If you get an error about `--no-site-packages`, then modify -`$ESPIDF/tools/idf_tools.py` and make the same change as [this -commit](https://github.com/espressif/esp-idf/commit/7a18f02acd7005f7c56e62175a8d1968a1a9019d). - -2. or, Downloading pre-built toolchain manually (IDF 3.x and 4.x) -============================================================= - -Note: while this works with 4.x, if you're using the 4.x IDF, it's much -simpler to use the guide above, which will also get a more recent version of -the toolchain. - -You can follow the 3.x guide at: - - * [Linux installation](https://docs.espressif.com/projects/esp-idf/en/v3.3.2/get-started/linux-setup.html) - * [MacOS installation](https://docs.espressif.com/projects/esp-idf/en/v3.3.2/get-started/macos-setup.html) - * [Windows installation](https://docs.espressif.com/projects/esp-idf/en/v3.3.2/get-started/windows-setup.html) - -You will need to update your `PATH` environment variable to include the ESP32 -toolchain. For example, you can issue the following commands on (at least) -Linux: - - $ export PATH=$PATH:$HOME/esp/crosstool-NG/builds/xtensa-esp32-elf/bin - -You can put this command in your `.profile` or `.bash_login`, or do it manually. - -Configuring the MicroPython build ---------------------------------- - -You then need to set the `ESPIDF` environment/makefile variable to point to -the root of the ESP-IDF repository. The recommended way to do this is to have -a custom `makefile` in `ports/esp32` which sets any additional variables, then -includes the main `Makefile`. Note that GNU Make will preferentially run -`GNUmakefile`, then `makefile`, then `Makefile`, which is what allows this to -work. On case-insensitive filesystems, you'll need to use `GNUmakefile` rather -than `makefile`. - -Create a new file in the esp32 directory called `makefile` (or `GNUmakefile`) -and add the following lines to that file: - -``` -ESPIDF ?= -BOARD ?= GENERIC -#PORT ?= /dev/ttyUSB0 -#FLASH_MODE ?= qio -#FLASH_SIZE ?= 4MB -#CROSS_COMPILE ?= xtensa-esp32-elf- - -include Makefile -``` - -Be sure to enter the correct path to your local copy of the IDF repository -(and use `$(HOME)`, not tilde (`~`), to reference your home directory). - -If the Xtensa cross-compiler is not in your path you can use the -`CROSS_COMPILE` variable to set its location. Other options of interest are -`PORT` for the serial port of your ESP32 module, and `FLASH_MODE` (which may -need to be `dio` for some modules) and `FLASH_SIZE`. See the Makefile for -further information. - -The default ESP IDF configuration settings are provided by the `GENERIC` -board definition in the directory `boards/GENERIC`. For a custom configuration -you can define your own board directory. - -Any of these variables can also be set on the make command line, e.g. to set -the `BOARD` variable, use: - -```bash -$ make BOARD=TINYPICO -``` - -Note the use of `?=` in the `makefile` which allows them to be overridden on -the command line. There is also a `GENERIC_SPIRAM` board for for ESP32 -modules that have external SPIRAM, but prefer to use a specific board target -(or define your own as necessary). - Building the firmware --------------------- @@ -198,8 +74,7 @@ built-in scripts to bytecode. This can be done by (from the root of this repository): ```bash -$ cd mpy-cross -$ make mpy-cross +$ make -C mpy-cross ``` Then to build MicroPython for the ESP32 run: @@ -210,16 +85,14 @@ $ make submodules $ make ``` -This will produce binary firmware images in the `build/` subdirectory -(three of them: bootloader.bin, partitions.bin and application.bin). +This will produce a combined `firmware.bin` image in the `build-GENERIC/` +subdirectory (this firmware image is made up of: bootloader.bin, partitions.bin +and micropython.bin). To flash the firmware you must have your ESP32 module in the bootloader mode and connected to a serial port on your PC. Refer to the documentation -for your particular ESP32 module for how to do this. The serial port and -flash settings are set in the `Makefile`, and can be overridden in your -local `makefile`; see above for more details. - -You will also need to have user permissions to access the /dev/ttyUSB0 device. +for your particular ESP32 module for how to do this. +You will also need to have user permissions to access the `/dev/ttyUSB0` device. On Linux, you can enable this by adding your user to the `dialout` group, and rebooting or logging out and in again. (Note: on some distributions this may be the `uucp` group, run `ls -la /dev/ttyUSB0` to check.) @@ -242,8 +115,23 @@ To flash the MicroPython firmware to your ESP32 use: $ make deploy ``` -This will use the `esptool.py` script (provided by ESP-IDF) to flash the -binary images to the device. +The default ESP32 board build by the above commands is the `GENERIC` one, which +should work on most ESP32 modules. You can specify a different board by passing +`BOARD=` to the make commands, for example: + +```bash +$ make BOARD=GENERIC_SPIRAM +``` + +Note: the above "make" commands are thin wrappers for the underlying `idf.py` +build tool that is part of the ESP-IDF. You can instead use `idf.py` directly, +for example: + +```bash +$ idf.py build +$ idf.py -D MICROPY_BOARD=GENERIC_SPIRAM build +$ idf.py flash +``` Getting a Python prompt on the device ------------------------------------- @@ -262,6 +150,8 @@ or $ miniterm.py /dev/ttyUSB0 115200 ``` +You can also use `idf.py monitor`. + Configuring the WiFi and using the board ---------------------------------------- @@ -301,9 +191,27 @@ import machine antenna = machine.Pin(16, machine.Pin.OUT, value=0) ``` +Defining a custom ESP32 board +----------------------------- + +The default ESP-IDF configuration settings are provided by the `GENERIC` +board definition in the directory `boards/GENERIC`. For a custom configuration +you can define your own board directory. Start a new board configuration by +copying an existing one (like `GENERIC`) and modifying it to suit your board. + +MicroPython specific configuration values are defined in the board-specific +`mpconfigboard.h` file, which is included by `mpconfigport.h`. Additional +settings are put in `mpconfigboard.cmake`, including a list of `sdkconfig` +files that configure ESP-IDF settings. Some standard `sdkconfig` files are +provided in the `boards/` directory, like `boards/sdkconfig.ble`. You can +also define custom ones in your board directory. + +See existing board definitions for further examples of configuration. + +Configuration Troubleshooting --------------- -* Continuous reboots after programming: Ensure FLASH_MODE is correct for your - board (e.g. ESP-WROOM-32 should be DIO). Then perform a `make clean`, rebuild, - redeploy. +* Continuous reboots after programming: Ensure `CONFIG_ESPTOOLPY_FLASHMODE` is + correct for your board (e.g. ESP-WROOM-32 should be DIO). Then perform a + `make clean`, rebuild, redeploy. diff --git a/ports/esp32/boards/GENERIC/mpconfigboard.cmake b/ports/esp32/boards/GENERIC/mpconfigboard.cmake new file mode 100644 index 000000000..f69b05f47 --- /dev/null +++ b/ports/esp32/boards/GENERIC/mpconfigboard.cmake @@ -0,0 +1,6 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_PORT_DIR}/boards/manifest.py) diff --git a/ports/esp32/boards/GENERIC_D2WD/mpconfigboard.cmake b/ports/esp32/boards/GENERIC_D2WD/mpconfigboard.cmake new file mode 100644 index 000000000..686c78df9 --- /dev/null +++ b/ports/esp32/boards/GENERIC_D2WD/mpconfigboard.cmake @@ -0,0 +1,7 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/GENERIC_D2WD/sdkconfig.board +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_PORT_DIR}/boards/manifest.py) diff --git a/ports/esp32/boards/GENERIC_D2WD/sdkconfig.board b/ports/esp32/boards/GENERIC_D2WD/sdkconfig.board new file mode 100644 index 000000000..367283ded --- /dev/null +++ b/ports/esp32/boards/GENERIC_D2WD/sdkconfig.board @@ -0,0 +1,5 @@ +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_40M=y +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-2MiB.csv" diff --git a/ports/esp32/boards/GENERIC_OTA/mpconfigboard.cmake b/ports/esp32/boards/GENERIC_OTA/mpconfigboard.cmake new file mode 100644 index 000000000..ca552d470 --- /dev/null +++ b/ports/esp32/boards/GENERIC_OTA/mpconfigboard.cmake @@ -0,0 +1,7 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/GENERIC_OTA/sdkconfig.board +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_PORT_DIR}/boards/manifest.py) diff --git a/ports/esp32/boards/GENERIC_OTA/sdkconfig.board b/ports/esp32/boards/GENERIC_OTA/sdkconfig.board index b0ed171d8..d314860cc 100644 --- a/ports/esp32/boards/GENERIC_OTA/sdkconfig.board +++ b/ports/esp32/boards/GENERIC_OTA/sdkconfig.board @@ -1,4 +1,3 @@ CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y - -# ESP-IDF v3: -CONFIG_APP_ROLLBACK_ENABLE=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-ota.csv" diff --git a/ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.cmake b/ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.cmake new file mode 100644 index 000000000..2128edb59 --- /dev/null +++ b/ports/esp32/boards/GENERIC_SPIRAM/mpconfigboard.cmake @@ -0,0 +1,7 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/sdkconfig.spiram +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_PORT_DIR}/boards/manifest.py) diff --git a/ports/esp32/boards/TINYPICO/mpconfigboard.cmake b/ports/esp32/boards/TINYPICO/mpconfigboard.cmake new file mode 100644 index 000000000..5c31f5c60 --- /dev/null +++ b/ports/esp32/boards/TINYPICO/mpconfigboard.cmake @@ -0,0 +1,9 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/sdkconfig.240mhz + boards/sdkconfig.spiram + boards/TINYPICO/sdkconfig.board +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index 67e2424a1..91e68c7bf 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -4,6 +4,12 @@ CONFIG_IDF_TARGET="esp32" CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 +# Compiler options: use -Os to reduce size, but keep full assertions +# (CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is for IDF 4.0.2) +CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y + # Application manager CONFIG_APP_EXCLUDE_PROJECT_VER_VAR=y CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR=y @@ -41,13 +47,11 @@ CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y # ULP coprocessor support CONFIG_ESP32_ULP_COPROC_ENABLED=y -# v3.3-only (renamed in 4.0) -CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y -CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=n -CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=n -CONFIG_SUPPORT_STATIC_ALLOCATION=y -CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK=y -CONFIG_PPP_SUPPORT=y -CONFIG_PPP_PAP_SUPPORT=y -CONFIG_PPP_CHAP_SUPPORT=y -CONFIG_ULP_COPROC_ENABLED=y +# For cmake build +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" + +# To reduce iRAM usage +CONFIG_ESP32_WIFI_IRAM_OPT=n +CONFIG_ESP32_WIFI_RX_IRAM_OPT=n diff --git a/ports/esp32/boards/sdkconfig.ble b/ports/esp32/boards/sdkconfig.ble index f714ce462..5565fd81a 100644 --- a/ports/esp32/boards/sdkconfig.ble +++ b/ports/esp32/boards/sdkconfig.ble @@ -15,13 +15,3 @@ CONFIG_BT_NIMBLE_MAX_CONNECTIONS=4 CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y CONFIG_BT_NIMBLE_PINNED_TO_CORE_1=n CONFIG_BT_NIMBLE_PINNED_TO_CORE=0 - -# v3.3-only (renamed in 4.0) -CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y -CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY= -CONFIG_BTDM_CONTROLLER_MODE_BTDM= -CONFIG_BLUEDROID_ENABLED=n -CONFIG_NIMBLE_ENABLED=y -CONFIG_NIMBLE_MAX_CONNECTIONS=4 -CONFIG_NIMBLE_PINNED_TO_CORE_0=n -CONFIG_NIMBLE_PINNED_TO_CORE_1=y diff --git a/ports/esp32/boards/sdkconfig.spiram b/ports/esp32/boards/sdkconfig.spiram index db1a83af8..5b4ce118b 100644 --- a/ports/esp32/boards/sdkconfig.spiram +++ b/ports/esp32/boards/sdkconfig.spiram @@ -4,6 +4,3 @@ CONFIG_ESP32_SPIRAM_SUPPORT=y CONFIG_SPIRAM_CACHE_WORKAROUND=y CONFIG_SPIRAM_IGNORE_NOTFOUND=y CONFIG_SPIRAM_USE_MEMMAP=y - -# v3.3-only (renamed in 4.0) -CONFIG_SPIRAM_SUPPORT=y diff --git a/ports/esp32/esp32_nvs.c b/ports/esp32/esp32_nvs.c new file mode 100644 index 000000000..d13151d3c --- /dev/null +++ b/ports/esp32/esp32_nvs.c @@ -0,0 +1,151 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 by Thorsten von Eicken + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "mphalport.h" +#include "modesp32.h" +#include "nvs_flash.h" +#include "nvs.h" + +// This file implements the NVS (Non-Volatile Storage) class in the esp32 module. +// It provides simple access to the NVS feature provided by ESP-IDF. + +// NVS python object that represents an NVS namespace. +typedef struct _esp32_nvs_obj_t { + mp_obj_base_t base; + nvs_handle_t namespace; +} esp32_nvs_obj_t; + +// *esp32_nvs_new allocates a python NVS object given a handle to an esp-idf namespace C obj. +STATIC esp32_nvs_obj_t *esp32_nvs_new(nvs_handle_t namespace) { + esp32_nvs_obj_t *self = m_new_obj(esp32_nvs_obj_t); + self->base.type = &esp32_nvs_type; + self->namespace = namespace; + return self; +} + +// esp32_nvs_print prints an NVS object, unfortunately it doesn't seem possible to extract the +// namespace string or anything else from the opaque handle provided by esp-idf. +STATIC void esp32_nvs_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + // esp32_nvs_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, ""); +} + +// esp32_nvs_make_new constructs a handle to an NVS namespace. +STATIC mp_obj_t esp32_nvs_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Check args + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + // Get requested nvs namespace + const char *ns_name = mp_obj_str_get_str(all_args[0]); + nvs_handle_t namespace; + check_esp_err(nvs_open(ns_name, NVS_READWRITE, &namespace)); + return MP_OBJ_FROM_PTR(esp32_nvs_new(namespace)); +} + +// esp32_nvs_set_i32 sets a 32-bit integer value +STATIC mp_obj_t esp32_nvs_set_i32(mp_obj_t self_in, mp_obj_t key_in, mp_obj_t value_in) { + esp32_nvs_obj_t *self = MP_OBJ_TO_PTR(self_in); + const char *key = mp_obj_str_get_str(key_in); + int32_t value = mp_obj_get_int(value_in); + check_esp_err(nvs_set_i32(self->namespace, key, value)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp32_nvs_set_i32_obj, esp32_nvs_set_i32); + +// esp32_nvs_get_i32 reads a 32-bit integer value +STATIC mp_obj_t esp32_nvs_get_i32(mp_obj_t self_in, mp_obj_t key_in) { + esp32_nvs_obj_t *self = MP_OBJ_TO_PTR(self_in); + const char *key = mp_obj_str_get_str(key_in); + int32_t value; + check_esp_err(nvs_get_i32(self->namespace, key, &value)); + return mp_obj_new_int(value); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp32_nvs_get_i32_obj, esp32_nvs_get_i32); + +// esp32_nvs_set_blob writes a buffer object into a binary blob value. +STATIC mp_obj_t esp32_nvs_set_blob(mp_obj_t self_in, mp_obj_t key_in, mp_obj_t value_in) { + esp32_nvs_obj_t *self = MP_OBJ_TO_PTR(self_in); + const char *key = mp_obj_str_get_str(key_in); + mp_buffer_info_t value; + mp_get_buffer_raise(value_in, &value, MP_BUFFER_READ); + check_esp_err(nvs_set_blob(self->namespace, key, value.buf, value.len)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp32_nvs_set_blob_obj, esp32_nvs_set_blob); + +// esp32_nvs_get_blob reads a binary blob value into a bytearray. Returns actual length. +STATIC mp_obj_t esp32_nvs_get_blob(mp_obj_t self_in, mp_obj_t key_in, mp_obj_t value_in) { + esp32_nvs_obj_t *self = MP_OBJ_TO_PTR(self_in); + const char *key = mp_obj_str_get_str(key_in); + // get buffer to be filled + mp_buffer_info_t value; + mp_get_buffer_raise(value_in, &value, MP_BUFFER_WRITE); + size_t length = value.len; + // fill the buffer with the value, will raise an esp-idf error if the length of + // the provided buffer (bytearray) is too small + check_esp_err(nvs_get_blob(self->namespace, key, value.buf, &length)); + return MP_OBJ_NEW_SMALL_INT(length); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp32_nvs_get_blob_obj, esp32_nvs_get_blob); + +// esp32_nvs_erase_key erases one key. +STATIC mp_obj_t esp32_nvs_erase_key(mp_obj_t self_in, mp_obj_t key_in) { + esp32_nvs_obj_t *self = MP_OBJ_TO_PTR(self_in); + const char *key = mp_obj_str_get_str(key_in); + check_esp_err(nvs_erase_key(self->namespace, key)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp32_nvs_erase_key_obj, esp32_nvs_erase_key); + +// esp32_nvs_commit commits any changes to flash. +STATIC mp_obj_t esp32_nvs_commit(mp_obj_t self_in) { + esp32_nvs_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_esp_err(nvs_commit(self->namespace)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_nvs_commit_obj, esp32_nvs_commit); + +STATIC const mp_rom_map_elem_t esp32_nvs_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_get_i32), MP_ROM_PTR(&esp32_nvs_get_i32_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_i32), MP_ROM_PTR(&esp32_nvs_set_i32_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_blob), MP_ROM_PTR(&esp32_nvs_get_blob_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_blob), MP_ROM_PTR(&esp32_nvs_set_blob_obj) }, + { MP_ROM_QSTR(MP_QSTR_erase_key), MP_ROM_PTR(&esp32_nvs_erase_key_obj) }, + { MP_ROM_QSTR(MP_QSTR_commit), MP_ROM_PTR(&esp32_nvs_commit_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(esp32_nvs_locals_dict, esp32_nvs_locals_dict_table); + +const mp_obj_type_t esp32_nvs_type = { + { &mp_type_type }, + .name = MP_QSTR_NVS, + .print = esp32_nvs_print, + .make_new = esp32_nvs_make_new, + .locals_dict = (mp_obj_dict_t *)&esp32_nvs_locals_dict, +}; diff --git a/ports/esp32/esp32_rmt.c b/ports/esp32/esp32_rmt.c index 7971ca5d1..b547af553 100644 --- a/ports/esp32/esp32_rmt.c +++ b/ports/esp32/esp32_rmt.c @@ -209,7 +209,7 @@ STATIC mp_obj_t esp32_rmt_write_pulses(size_t n_args, const mp_obj_t *pos_args, mp_obj_t pulses = args[1].u_obj; mp_uint_t start = args[2].u_int; - if (start < 0 || start > 1) { + if (start > 1) { mp_raise_ValueError(MP_ERROR_TEXT("start must be 0 or 1")); } diff --git a/ports/esp32/esp32_ulp.c b/ports/esp32/esp32_ulp.c index 4e9d76814..50244cdf2 100644 --- a/ports/esp32/esp32_ulp.c +++ b/ports/esp32/esp32_ulp.c @@ -85,11 +85,7 @@ STATIC const mp_rom_map_elem_t esp32_ulp_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_set_wakeup_period), MP_ROM_PTR(&esp32_ulp_set_wakeup_period_obj) }, { MP_ROM_QSTR(MP_QSTR_load_binary), MP_ROM_PTR(&esp32_ulp_load_binary_obj) }, { MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&esp32_ulp_run_obj) }, - #if !MICROPY_ESP_IDF_4 - { MP_ROM_QSTR(MP_QSTR_RESERVE_MEM), MP_ROM_INT(CONFIG_ULP_COPROC_RESERVE_MEM) }, - #else { MP_ROM_QSTR(MP_QSTR_RESERVE_MEM), MP_ROM_INT(CONFIG_ESP32_ULP_COPROC_RESERVE_MEM) }, - #endif }; STATIC MP_DEFINE_CONST_DICT(esp32_ulp_locals_dict, esp32_ulp_locals_dict_table); diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c index 87b08fc9b..3993c7b52 100644 --- a/ports/esp32/machine_i2c.c +++ b/ports/esp32/machine_i2c.c @@ -120,7 +120,7 @@ mp_obj_t machine_hw_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_ // Parse args enum { ARG_id, ARG_scl, ARG_sda, ARG_freq, ARG_timeout }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} }, diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index c334e694b..7fce83f2c 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -36,6 +36,20 @@ #include "py/mperrno.h" #include "modmachine.h" +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 1, 0) +#define UART_INV_TX UART_INVERSE_TXD +#define UART_INV_RX UART_INVERSE_RXD +#define UART_INV_RTS UART_INVERSE_RTS +#define UART_INV_CTS UART_INVERSE_CTS +#else +#define UART_INV_TX UART_SIGNAL_TXD_INV +#define UART_INV_RX UART_SIGNAL_RXD_INV +#define UART_INV_RTS UART_SIGNAL_RTS_INV +#define UART_INV_CTS UART_SIGNAL_CTS_INV +#endif + +#define UART_INV_MASK (UART_INV_TX | UART_INV_RX | UART_INV_RTS | UART_INV_CTS) + typedef struct _machine_uart_obj_t { mp_obj_base_t base; uart_port_t uart_num; @@ -68,28 +82,28 @@ STATIC void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_pri if (self->invert) { mp_printf(print, ", invert="); uint32_t invert_mask = self->invert; - if (invert_mask & UART_INVERSE_TXD) { + if (invert_mask & UART_INV_TX) { mp_printf(print, "INV_TX"); - invert_mask &= ~UART_INVERSE_TXD; + invert_mask &= ~UART_INV_TX; if (invert_mask) { mp_printf(print, "|"); } } - if (invert_mask & UART_INVERSE_RXD) { + if (invert_mask & UART_INV_RX) { mp_printf(print, "INV_RX"); - invert_mask &= ~UART_INVERSE_RXD; + invert_mask &= ~UART_INV_RX; if (invert_mask) { mp_printf(print, "|"); } } - if (invert_mask & UART_INVERSE_RTS) { + if (invert_mask & UART_INV_RTS) { mp_printf(print, "INV_RTS"); - invert_mask &= ~UART_INVERSE_RTS; + invert_mask &= ~UART_INV_RTS; if (invert_mask) { mp_printf(print, "|"); } } - if (invert_mask & UART_INVERSE_CTS) { + if (invert_mask & UART_INV_CTS) { mp_printf(print, "INV_CTS"); } } @@ -238,7 +252,7 @@ STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, co } // set line inversion - if (args[ARG_invert].u_int & ~UART_LINE_INV_MASK) { + if (args[ARG_invert].u_int & ~UART_INV_MASK) { mp_raise_ValueError(MP_ERROR_TEXT("invalid inversion mask")); } self->invert = args[ARG_invert].u_int; @@ -380,10 +394,10 @@ STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_sendbreak), MP_ROM_PTR(&machine_uart_sendbreak_obj) }, - { MP_ROM_QSTR(MP_QSTR_INV_TX), MP_ROM_INT(UART_INVERSE_TXD) }, - { MP_ROM_QSTR(MP_QSTR_INV_RX), MP_ROM_INT(UART_INVERSE_RXD) }, - { MP_ROM_QSTR(MP_QSTR_INV_RTS), MP_ROM_INT(UART_INVERSE_RTS) }, - { MP_ROM_QSTR(MP_QSTR_INV_CTS), MP_ROM_INT(UART_INVERSE_CTS) }, + { MP_ROM_QSTR(MP_QSTR_INV_TX), MP_ROM_INT(UART_INV_TX) }, + { MP_ROM_QSTR(MP_QSTR_INV_RX), MP_ROM_INT(UART_INV_RX) }, + { MP_ROM_QSTR(MP_QSTR_INV_RTS), MP_ROM_INT(UART_INV_RTS) }, + { MP_ROM_QSTR(MP_QSTR_INV_CTS), MP_ROM_INT(UART_INV_CTS) }, }; STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table); diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 6f9ab82d0..7413798d0 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -37,11 +37,7 @@ #include "esp_task.h" #include "soc/cpu.h" #include "esp_log.h" -#if MICROPY_ESP_IDF_4 #include "esp32/spiram.h" -#else -#include "esp_spiram.h" -#endif #include "py/stackctrl.h" #include "py/nlr.h" @@ -77,6 +73,7 @@ void mp_task(void *pvParameter) { mp_thread_init(pxTaskGetStackStart(NULL), MP_TASK_STACK_SIZE / sizeof(uintptr_t)); #endif uart_init(); + machine_init(); // TODO: CONFIG_SPIRAM_SUPPORT is for 3.3 compatibility, remove after move to 4.0. #if CONFIG_ESP32_SPIRAM_SUPPORT || CONFIG_SPIRAM_SUPPORT @@ -122,7 +119,10 @@ void mp_task(void *pvParameter) { pyexec_frozen_module("_boot.py"); pyexec_file_if_exists("boot.py"); if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { - pyexec_file_if_exists("main.py"); + int ret = pyexec_file_if_exists("main.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } } for (;;) { @@ -139,6 +139,8 @@ void mp_task(void *pvParameter) { } } +soft_reset_exit: + #if MICROPY_BLUETOOTH_NIMBLE mp_bluetooth_deinit(); #endif @@ -155,6 +157,7 @@ void mp_task(void *pvParameter) { // deinitialise peripherals machine_pins_deinit(); + machine_deinit(); usocket_events_deinit(); mp_deinit(); diff --git a/ports/esp32/main/CMakeLists.txt b/ports/esp32/main/CMakeLists.txt new file mode 100644 index 000000000..bd25d76ee --- /dev/null +++ b/ports/esp32/main/CMakeLists.txt @@ -0,0 +1,196 @@ +# Set location of base MicroPython directory. +get_filename_component(MICROPY_DIR ${PROJECT_DIR}/../.. ABSOLUTE) + +# Include core source components. +include(${MICROPY_DIR}/py/py.cmake) +include(${MICROPY_DIR}/extmod/extmod.cmake) + +set(MICROPY_QSTRDEFS_PORT + ${PROJECT_DIR}/qstrdefsport.h +) + +set(MICROPY_SOURCE_EXTMOD_EXTRA + ${MICROPY_DIR}/extmod/modonewire.c +) + +set(MICROPY_SOURCE_LIB + ${MICROPY_DIR}/lib/littlefs/lfs1.c + ${MICROPY_DIR}/lib/littlefs/lfs1_util.c + ${MICROPY_DIR}/lib/littlefs/lfs2.c + ${MICROPY_DIR}/lib/littlefs/lfs2_util.c + ${MICROPY_DIR}/lib/mbedtls_errors/mp_mbedtls_errors.c + ${MICROPY_DIR}/lib/mp-readline/readline.c + ${MICROPY_DIR}/lib/netutils/netutils.c + ${MICROPY_DIR}/lib/oofatfs/ff.c + ${MICROPY_DIR}/lib/oofatfs/ffunicode.c + ${MICROPY_DIR}/lib/timeutils/timeutils.c + ${MICROPY_DIR}/lib/utils/interrupt_char.c + ${MICROPY_DIR}/lib/utils/stdout_helpers.c + ${MICROPY_DIR}/lib/utils/sys_stdio_mphal.c + ${MICROPY_DIR}/lib/utils/pyexec.c +) + +set(MICROPY_SOURCE_DRIVERS + ${MICROPY_DIR}/drivers/bus/softspi.c + ${MICROPY_DIR}/drivers/dht/dht.c +) + +set(MICROPY_SOURCE_PORT + ${PROJECT_DIR}/main.c + ${PROJECT_DIR}/uart.c + ${PROJECT_DIR}/gccollect.c + ${PROJECT_DIR}/mphalport.c + ${PROJECT_DIR}/fatfs_port.c + ${PROJECT_DIR}/help.c + ${PROJECT_DIR}/modutime.c + ${PROJECT_DIR}/moduos.c + ${PROJECT_DIR}/machine_timer.c + ${PROJECT_DIR}/machine_pin.c + ${PROJECT_DIR}/machine_touchpad.c + ${PROJECT_DIR}/machine_adc.c + ${PROJECT_DIR}/machine_dac.c + ${PROJECT_DIR}/machine_i2c.c + ${PROJECT_DIR}/machine_pwm.c + ${PROJECT_DIR}/machine_uart.c + ${PROJECT_DIR}/modmachine.c + ${PROJECT_DIR}/modnetwork.c + ${PROJECT_DIR}/network_lan.c + ${PROJECT_DIR}/network_ppp.c + ${PROJECT_DIR}/mpnimbleport.c + ${PROJECT_DIR}/modsocket.c + ${PROJECT_DIR}/modesp.c + ${PROJECT_DIR}/esp32_nvs.c + ${PROJECT_DIR}/esp32_partition.c + ${PROJECT_DIR}/esp32_rmt.c + ${PROJECT_DIR}/esp32_ulp.c + ${PROJECT_DIR}/modesp32.c + ${PROJECT_DIR}/espneopixel.c + ${PROJECT_DIR}/machine_hw_spi.c + ${PROJECT_DIR}/machine_wdt.c + ${PROJECT_DIR}/mpthreadport.c + ${PROJECT_DIR}/machine_rtc.c + ${PROJECT_DIR}/machine_sdcard.c +) + +set(MICROPY_SOURCE_QSTR + ${MICROPY_SOURCE_PY} + ${MICROPY_SOURCE_EXTMOD} + ${MICROPY_SOURCE_EXTMOD_EXTRA} + ${MICROPY_SOURCE_LIB} + ${MICROPY_SOURCE_PORT} +) + +set(IDF_COMPONENTS + app_update + bootloader_support + bt + driver + esp32 + esp_common + esp_eth + esp_event + esp_ringbuf + esp_rom + esp_wifi + freertos + heap + log + lwip + mbedtls + mdns + newlib + nvs_flash + sdmmc + soc + spi_flash + tcpip_adapter + ulp + vfs + xtensa +) + +if(IDF_VERSION_MINOR GREATER_EQUAL 1) + list(APPEND IDF_COMPONENTS esp_netif) +endif() + +if(IDF_VERSION_MINOR GREATER_EQUAL 2) + list(APPEND IDF_COMPONENTS esp_system) + list(APPEND IDF_COMPONENTS esp_timer) +endif() + +if(IDF_VERSION_MINOR GREATER_EQUAL 3) + list(APPEND IDF_COMPONENTS esp_hw_support) + list(APPEND IDF_COMPONENTS esp_pm) + list(APPEND IDF_COMPONENTS hal) +endif() + +# Register the main IDF component. +idf_component_register( + SRCS + ${MICROPY_SOURCE_PY} + ${MICROPY_SOURCE_EXTMOD} + ${MICROPY_SOURCE_EXTMOD_EXTRA} + ${MICROPY_SOURCE_LIB} + ${MICROPY_SOURCE_DRIVERS} + ${MICROPY_SOURCE_PORT} + INCLUDE_DIRS + ${MICROPY_DIR} + ${MICROPY_PORT_DIR} + ${MICROPY_BOARD_DIR} + ${CMAKE_BINARY_DIR} + REQUIRES + ${IDF_COMPONENTS} +) + +# Set the MicroPython target as the current (main) IDF component target. +set(MICROPY_TARGET ${COMPONENT_TARGET}) + +# Define mpy-cross flags, for use with frozen code. +set(MICROPY_CROSS_FLAGS -march=xtensawin) + +# Set compile options for this port. +target_compile_definitions(${MICROPY_TARGET} PUBLIC + MICROPY_ESP_IDF_4=1 + MICROPY_VFS_FAT=1 + MICROPY_VFS_LFS2=1 + FFCONF_H=\"${MICROPY_OOFATFS_DIR}/ffconf.h\" + LFS1_NO_MALLOC LFS1_NO_DEBUG LFS1_NO_WARN LFS1_NO_ERROR LFS1_NO_ASSERT + LFS2_NO_MALLOC LFS2_NO_DEBUG LFS2_NO_WARN LFS2_NO_ERROR LFS2_NO_ASSERT +) + +# Disable some warnings to keep the build output clean. +target_compile_options(${MICROPY_TARGET} PUBLIC + -Wno-clobbered + -Wno-deprecated-declarations + -Wno-missing-field-initializers +) + +# Collect all of the include directories and compile definitions for the IDF components. +foreach(comp ${IDF_COMPONENTS}) + get_target_property(type __idf_${comp} TYPE) + set(_inc OFF) + set(_def OFF) + if(${type} STREQUAL STATIC_LIBRARY) + get_target_property(_inc __idf_${comp} INCLUDE_DIRECTORIES) + get_target_property(_def __idf_${comp} COMPILE_DEFINITIONS) + elseif(${type} STREQUAL INTERFACE_LIBRARY) + get_target_property(_inc __idf_${comp} INTERFACE_INCLUDE_DIRECTORIES) + get_target_property(_def __idf_${comp} INTERFACE_COMPILE_DEFINITIONS) + endif() + if(_inc) + list(APPEND MICROPY_CPP_INC_EXTRA ${_inc}) + endif() + if(_def) + list(APPEND MICROPY_CPP_DEF_EXTRA ${_def}) + endif() +endforeach() + +if(IDF_VERSION_MINOR GREATER_EQUAL 2) + # These paths cannot currently be found by the IDF_COMPONENTS search loop above, + # so add them explicitly. + list(APPEND MICROPY_CPP_INC_EXTRA ${IDF_PATH}/components/soc/soc/${IDF_TARGET}/include) + list(APPEND MICROPY_CPP_INC_EXTRA ${IDF_PATH}/components/soc/soc/include) +endif() + +# Include the main MicroPython cmake rules. +include(${MICROPY_DIR}/py/mkrules.cmake) diff --git a/ports/esp32/modesp.c b/ports/esp32/modesp.c index 369649c11..59a261e8c 100644 --- a/ports/esp32/modesp.c +++ b/ports/esp32/modesp.c @@ -29,9 +29,6 @@ #include -#if !MICROPY_ESP_IDF_4 -#include "rom/gpio.h" -#endif #include "esp_log.h" #include "esp_spi_flash.h" diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 28d1762d2..53ca7fdc6 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -35,7 +35,6 @@ #include "driver/adc.h" #include "esp_heap_caps.h" #include "multi_heap.h" -#include "../heap_private.h" #include "py/nlr.h" #include "py/obj.h" @@ -46,6 +45,13 @@ #include "machine_rtc.h" #include "modesp32.h" +// These private includes are needed for idf_heap_info. +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) +#define MULTI_HEAP_FREERTOS +#include "../multi_heap_platform.h" +#endif +#include "../heap_private.h" + STATIC mp_obj_t esp32_wake_on_touch(const mp_obj_t wake) { if (machine_rtc_config.ext0_pin != -1) { @@ -180,6 +186,7 @@ STATIC const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_hall_sensor), MP_ROM_PTR(&esp32_hall_sensor_obj) }, { MP_ROM_QSTR(MP_QSTR_idf_heap_info), MP_ROM_PTR(&esp32_idf_heap_info_obj) }, + { MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) }, { MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) }, { MP_ROM_QSTR(MP_QSTR_RMT), MP_ROM_PTR(&esp32_rmt_type) }, { MP_ROM_QSTR(MP_QSTR_ULP), MP_ROM_PTR(&esp32_ulp_type) }, diff --git a/ports/esp32/modesp32.h b/ports/esp32/modesp32.h index f4c0491f7..18bd62ee4 100644 --- a/ports/esp32/modesp32.h +++ b/ports/esp32/modesp32.h @@ -26,6 +26,7 @@ #define RTC_LAST_EXT_PIN 39 #define RTC_IS_VALID_EXT_PIN(pin_id) ((1ll << (pin_id)) & RTC_VALID_EXT_PINS) +extern const mp_obj_type_t esp32_nvs_type; extern const mp_obj_type_t esp32_partition_type; extern const mp_obj_type_t esp32_rmt_type; extern const mp_obj_type_t esp32_ulp_type; diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 303d25ee8..3925bcb64 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -32,15 +32,9 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#if MICROPY_ESP_IDF_4 #include "esp32/rom/rtc.h" #include "esp32/clk.h" #include "esp_sleep.h" -#else -#include "rom/ets_sys.h" -#include "rom/rtc.h" -#include "esp_clk.h" -#endif #include "esp_pm.h" #include "driver/touch_pad.h" @@ -65,6 +59,8 @@ typedef enum { MP_SOFT_RESET } reset_reason_t; +STATIC bool is_soft_reset = 0; + STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { if (n_args == 0) { // get @@ -146,6 +142,9 @@ STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *pos_args, mp_ma STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_deepsleep_obj, 0, machine_deepsleep); STATIC mp_obj_t machine_reset_cause(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + if (is_soft_reset) { + return MP_OBJ_NEW_SMALL_INT(MP_SOFT_RESET); + } switch (esp_reset_reason()) { case ESP_RST_POWERON: case ESP_RST_BROWNOUT: @@ -177,6 +176,15 @@ STATIC mp_obj_t machine_reset_cause(size_t n_args, const mp_obj_t *pos_args, mp_ } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_reset_cause_obj, 0, machine_reset_cause); +void machine_init(void) { + is_soft_reset = 0; +} + +void machine_deinit(void) { + // we are doing a soft-reset so change the reset_cause + is_soft_reset = 1; +} + STATIC mp_obj_t machine_wake_reason(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { return MP_OBJ_NEW_SMALL_INT(esp_sleep_get_wakeup_cause()); } diff --git a/ports/esp32/modmachine.h b/ports/esp32/modmachine.h index 98b36e32b..3e99e1120 100644 --- a/ports/esp32/modmachine.h +++ b/ports/esp32/modmachine.h @@ -22,6 +22,8 @@ extern const mp_obj_type_t machine_uart_type; extern const mp_obj_type_t machine_rtc_type; extern const mp_obj_type_t machine_sdcard_type; +void machine_init(void); +void machine_deinit(void); void machine_pins_init(void); void machine_pins_deinit(void); void machine_timer_deinit_all(void); diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c index 442633504..f772fa2cf 100644 --- a/ports/esp32/modnetwork.c +++ b/ports/esp32/modnetwork.c @@ -45,7 +45,6 @@ #include "esp_wifi.h" #include "esp_log.h" #include "lwip/dns.h" -#include "tcpip_adapter.h" #include "mdns.h" #if !MICROPY_ESP_IDF_4 @@ -55,6 +54,12 @@ #include "modnetwork.h" +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 1, 0) +#define DNS_MAIN TCPIP_ADAPTER_DNS_MAIN +#else +#define DNS_MAIN ESP_NETIF_DNS_MAIN +#endif + #define MODNETWORK_INCLUDE_CONSTANTS (1) NORETURN void _esp_exceptions(esp_err_t e) { @@ -331,7 +336,7 @@ STATIC mp_obj_t esp_connect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *k mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - wifi_config_t wifi_sta_config = {{{0}}}; + wifi_config_t wifi_sta_config = {0}; // configure any parameters that are given if (n_args > 1) { @@ -491,7 +496,7 @@ STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { tcpip_adapter_ip_info_t info; tcpip_adapter_dns_info_t dns_info; tcpip_adapter_get_ip_info(self->if_id, &info); - tcpip_adapter_get_dns_info(self->if_id, TCPIP_ADAPTER_DNS_MAIN, &dns_info); + tcpip_adapter_get_dns_info(self->if_id, DNS_MAIN, &dns_info); if (n_args == 1) { // get mp_obj_t tuple[4] = { @@ -526,14 +531,14 @@ STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { _esp_exceptions(e); } ESP_EXCEPTIONS(tcpip_adapter_set_ip_info(self->if_id, &info)); - ESP_EXCEPTIONS(tcpip_adapter_set_dns_info(self->if_id, TCPIP_ADAPTER_DNS_MAIN, &dns_info)); + ESP_EXCEPTIONS(tcpip_adapter_set_dns_info(self->if_id, DNS_MAIN, &dns_info)); } else if (self->if_id == WIFI_IF_AP) { esp_err_t e = tcpip_adapter_dhcps_stop(WIFI_IF_AP); if (e != ESP_OK && e != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) { _esp_exceptions(e); } ESP_EXCEPTIONS(tcpip_adapter_set_ip_info(WIFI_IF_AP, &info)); - ESP_EXCEPTIONS(tcpip_adapter_set_dns_info(WIFI_IF_AP, TCPIP_ADAPTER_DNS_MAIN, &dns_info)); + ESP_EXCEPTIONS(tcpip_adapter_set_dns_info(WIFI_IF_AP, DNS_MAIN, &dns_info)); ESP_EXCEPTIONS(tcpip_adapter_dhcps_start(WIFI_IF_AP)); } } else { @@ -772,6 +777,11 @@ STATIC const mp_rom_map_elem_t mp_module_network_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_AUTH_WPA_PSK), MP_ROM_INT(WIFI_AUTH_WPA_PSK) }, { MP_ROM_QSTR(MP_QSTR_AUTH_WPA2_PSK), MP_ROM_INT(WIFI_AUTH_WPA2_PSK) }, { MP_ROM_QSTR(MP_QSTR_AUTH_WPA_WPA2_PSK), MP_ROM_INT(WIFI_AUTH_WPA_WPA2_PSK) }, + { MP_ROM_QSTR(MP_QSTR_AUTH_WPA2_ENTERPRISE), MP_ROM_INT(WIFI_AUTH_WPA2_ENTERPRISE) }, + #if 0 // TODO: Remove this #if/#endif when lastest ISP IDF will be used + { MP_ROM_QSTR(MP_QSTR_AUTH_WPA3_PSK), MP_ROM_INT(WIFI_AUTH_WPA3_PSK) }, + { MP_ROM_QSTR(MP_QSTR_AUTH_WPA2_WPA3_PSK), MP_ROM_INT(WIFI_AUTH_WPA2_WPA3_PSK) }, + #endif { MP_ROM_QSTR(MP_QSTR_AUTH_MAX), MP_ROM_INT(WIFI_AUTH_MAX) }, { MP_ROM_QSTR(MP_QSTR_PHY_LAN8720), MP_ROM_INT(PHY_LAN8720) }, diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 85433e575..5135e3163 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -46,7 +46,6 @@ #include "py/stream.h" #include "py/mperrno.h" #include "lib/netutils/netutils.h" -#include "tcpip_adapter.h" #include "mdns.h" #include "modnetwork.h" @@ -56,18 +55,6 @@ #include "lwip/igmp.h" #include "esp_log.h" -#if !MICROPY_ESP_IDF_4 -#define lwip_bind lwip_bind_r -#define lwip_listen lwip_listen_r -#define lwip_accept lwip_accept_r -#define lwip_setsockopt lwip_setsockopt_r -#define lwip_fnctl lwip_fnctl_r -#define lwip_recvfrom lwip_recvfrom_r -#define lwip_write lwip_write_r -#define lwip_sendto lwip_sendto_r -#define lwip_close lwip_close_r -#endif - #define SOCKET_POLL_US (100000) #define MDNS_QUERY_TIMEOUT_MS (5000) #define MDNS_LOCAL_SUFFIX ".local" @@ -153,16 +140,6 @@ void usocket_events_handler(void) { #endif // MICROPY_PY_USOCKET_EVENTS -NORETURN static void exception_from_errno(int _errno) { - // Here we need to convert from lwip errno values to MicroPython's standard ones - if (_errno == EADDRINUSE) { - _errno = MP_EADDRINUSE; - } else if (_errno == EINPROGRESS) { - _errno = MP_EINPROGRESS; - } - mp_raise_OSError(_errno); -} - static inline void check_for_exceptions(void) { mp_handle_pending(true); } @@ -181,7 +158,12 @@ static int _socket_getaddrinfo3(const char *nodename, const char *servname, memcpy(nodename_no_local, nodename, nodename_len - local_len); nodename_no_local[nodename_len - local_len] = '\0'; + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 1, 0) struct ip4_addr addr = {0}; + #else + esp_ip4_addr_t addr = {0}; + #endif + esp_err_t err = mdns_query_a(nodename_no_local, MDNS_QUERY_TIMEOUT_MS, &addr); if (err != ESP_OK) { if (err == ESP_ERR_NOT_FOUND) { @@ -285,7 +267,7 @@ STATIC mp_obj_t socket_make_new(const mp_obj_type_t *type_in, size_t n_args, siz sock->fd = lwip_socket(sock->domain, sock->type, sock->proto); if (sock->fd < 0) { - exception_from_errno(errno); + mp_raise_OSError(errno); } _socket_settimeout(sock, UINT64_MAX); @@ -299,7 +281,7 @@ STATIC mp_obj_t socket_bind(const mp_obj_t arg0, const mp_obj_t arg1) { int r = lwip_bind(self->fd, res->ai_addr, res->ai_addrlen); lwip_freeaddrinfo(res); if (r < 0) { - exception_from_errno(errno); + mp_raise_OSError(errno); } return mp_const_none; } @@ -310,7 +292,7 @@ STATIC mp_obj_t socket_listen(const mp_obj_t arg0, const mp_obj_t arg1) { int backlog = mp_obj_get_int(arg1); int r = lwip_listen(self->fd, backlog); if (r < 0) { - exception_from_errno(errno); + mp_raise_OSError(errno); } return mp_const_none; } @@ -331,7 +313,7 @@ STATIC mp_obj_t socket_accept(const mp_obj_t arg0) { break; } if (errno != EAGAIN) { - exception_from_errno(errno); + mp_raise_OSError(errno); } check_for_exceptions(); } @@ -373,7 +355,7 @@ STATIC mp_obj_t socket_connect(const mp_obj_t arg0, const mp_obj_t arg1) { MP_THREAD_GIL_ENTER(); lwip_freeaddrinfo(res); if (r != 0) { - exception_from_errno(errno); + mp_raise_OSError(errno); } return mp_const_none; @@ -392,7 +374,7 @@ STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { int val = mp_obj_get_int(args[3]); int ret = lwip_setsockopt(self->fd, SOL_SOCKET, opt, &val, sizeof(int)); if (ret != 0) { - exception_from_errno(errno); + mp_raise_OSError(errno); } break; } @@ -543,7 +525,7 @@ mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, int errcode; mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, from, from_len, &errcode); if (ret == MP_STREAM_ERROR) { - exception_from_errno(errcode); + mp_raise_OSError(errcode); } vstr.len = ret; @@ -576,8 +558,9 @@ int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { MP_THREAD_GIL_EXIT(); int r = lwip_write(sock->fd, data + sentlen, datalen - sentlen); MP_THREAD_GIL_ENTER(); - if (r < 0 && errno != EWOULDBLOCK) { - exception_from_errno(errno); + // lwip returns EINPROGRESS when trying to send right after a non-blocking connect + if (r < 0 && errno != EWOULDBLOCK && errno != EINPROGRESS) { + mp_raise_OSError(errno); } if (r > 0) { sentlen += r; @@ -585,7 +568,7 @@ int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { check_for_exceptions(); } if (sentlen == 0) { - mp_raise_OSError(MP_ETIMEDOUT); + mp_raise_OSError(sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT); } return sentlen; } @@ -635,7 +618,7 @@ STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_ return mp_obj_new_int_from_uint(ret); } if (ret == -1 && errno != EWOULDBLOCK) { - exception_from_errno(errno); + mp_raise_OSError(errno); } check_for_exceptions(); } @@ -668,7 +651,8 @@ STATIC mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_ if (r > 0) { return r; } - if (r < 0 && errno != EWOULDBLOCK) { + // lwip returns MP_EINPROGRESS when trying to write right after a non-blocking connect + if (r < 0 && errno != EWOULDBLOCK && errno != EINPROGRESS) { *errcode = errno; return MP_STREAM_ERROR; } diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index f170d7070..392f8c749 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -8,10 +8,6 @@ #include #include "esp_system.h" -#if !MICROPY_ESP_IDF_4 -#include "rom/ets_sys.h" -#endif - // object representation and NLR handling #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_A) #define MICROPY_NLR_SETJMP (1) @@ -55,7 +51,7 @@ #define MICROPY_MODULE_FROZEN_MPY (1) #define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool #define MICROPY_CAN_OVERRIDE_BUILTINS (1) -#define MICROPY_USE_INTERNAL_ERRNO (1) +#define MICROPY_USE_INTERNAL_ERRNO (0) // errno.h from xtensa-esp32-elf/sys-include/sys #define MICROPY_USE_INTERNAL_PRINTF (0) // ESP32 SDK requires its own printf #define MICROPY_ENABLE_SCHEDULER (1) #define MICROPY_SCHEDULER_DEPTH (8) @@ -126,6 +122,12 @@ #define MICROPY_PY_THREAD_GIL_VM_DIVISOR (32) // extended modules +#ifndef MICROPY_PY_BLUETOOTH +#define MICROPY_PY_BLUETOOTH (1) +#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) +#define MICROPY_BLUETOOTH_NIMBLE (1) +#define MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY (1) +#endif #define MICROPY_PY_UASYNCIO (1) #define MICROPY_PY_UCTYPES (1) #define MICROPY_PY_UZLIB (1) @@ -221,9 +223,7 @@ struct mp_bluetooth_nimble_root_pointers_t; // type definitions for the specific machine -#define BYTES_PER_WORD (4) #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p))) -#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) void *esp_native_code_commit(void *, size_t, void *); #define MP_PLAT_COMMIT_EXEC(buf, len, reloc) esp_native_code_commit(buf, len, reloc) #define MP_SSIZE_MAX (0x7fffffff) diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c index ad571bf96..65fa88e4d 100644 --- a/ports/esp32/mphalport.c +++ b/ports/esp32/mphalport.c @@ -32,12 +32,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" - -#if MICROPY_ESP_IDF_4 #include "esp32/rom/uart.h" -#else -#include "rom/uart.h" -#endif #include "py/obj.h" #include "py/objstr.h" @@ -52,7 +47,7 @@ TaskHandle_t mp_main_task_handle; STATIC uint8_t stdin_ringbuf_array[256]; -ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array)}; +ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0}; // Check the ESP-IDF error code and raise an OSError if it's not ESP_OK. void check_esp_err(esp_err_t code) { diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index d294c9272..140a76464 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -34,9 +34,6 @@ #include "mpthreadport.h" #include "esp_task.h" -#if !MICROPY_ESP_IDF_4 -#include "freertos/semphr.h" -#endif #if MICROPY_PY_THREAD diff --git a/ports/esp32/uart.c b/ports/esp32/uart.c index 381be7f4f..c837c8dcf 100644 --- a/ports/esp32/uart.c +++ b/ports/esp32/uart.c @@ -29,6 +29,7 @@ #include #include "driver/uart.h" +#include "soc/uart_periph.h" #include "py/runtime.h" #include "py/mphal.h" diff --git a/ports/esp8266/gccollect.c b/ports/esp8266/gccollect.c index 3618a5664..bca0e030c 100644 --- a/ports/esp8266/gccollect.c +++ b/ports/esp8266/gccollect.c @@ -26,6 +26,7 @@ #include +#include "py/mpconfig.h" #include "py/gc.h" #include "gccollect.h" diff --git a/ports/esp8266/gccollect.h b/ports/esp8266/gccollect.h index 4323e9507..b86d3d6e1 100644 --- a/ports/esp8266/gccollect.h +++ b/ports/esp8266/gccollect.h @@ -26,6 +26,8 @@ #ifndef MICROPY_INCLUDED_ESP8266_GCCOLLECT_H #define MICROPY_INCLUDED_ESP8266_GCCOLLECT_H +#include + extern uint32_t _text_start; extern uint32_t _text_end; extern uint32_t _irom0_text_start; diff --git a/ports/esp8266/modules/inisetup.py b/ports/esp8266/modules/inisetup.py index e711a57d4..9500b8048 100644 --- a/ports/esp8266/modules/inisetup.py +++ b/ports/esp8266/modules/inisetup.py @@ -30,13 +30,12 @@ def fs_corrupted(): while 1: print( """\ -The filesystem starting at sector %d with size %d sectors appears to -be corrupted. If you had important data there, you may want to make a flash -snapshot to try to recover it. Otherwise, perform factory reprogramming -of MicroPython firmware (completely erase flash, followed by firmware -programming). +The filesystem starting at sector %d with size %d sectors looks corrupt. +You may want to make a flash snapshot and try to recover it. Otherwise, +format it with uos.VfsLfs2.mkfs(bdev), or completely erase the flash and +reprogram MicroPython. """ - % (bdev.START_SEC, bdev.blocks) + % (bdev.start_sec, bdev.blocks) ) time.sleep(3) diff --git a/ports/esp8266/modules/neopixel.py b/ports/esp8266/modules/neopixel.py index be56ed1b0..501a2689e 100644 --- a/ports/esp8266/modules/neopixel.py +++ b/ports/esp8266/modules/neopixel.py @@ -7,12 +7,13 @@ class NeoPixel: ORDER = (1, 0, 2, 3) - def __init__(self, pin, n, bpp=3): + def __init__(self, pin, n, bpp=3, timing=1): self.pin = pin self.n = n self.bpp = bpp self.buf = bytearray(n * bpp) self.pin.init(pin.OUT) + self.timing = timing def __setitem__(self, index, val): offset = index * self.bpp @@ -28,4 +29,4 @@ def fill(self, color): self[i] = color def write(self): - neopixel_write(self.pin, self.buf, True) + neopixel_write(self.pin, self.buf, self.timing) diff --git a/ports/javascript/mpconfigport.h b/ports/javascript/mpconfigport.h index 0101e10c8..d1bfbbd60 100644 --- a/ports/javascript/mpconfigport.h +++ b/ports/javascript/mpconfigport.h @@ -180,8 +180,6 @@ typedef int mp_int_t; // must be pointer size typedef unsigned mp_uint_t; // must be pointer size typedef long mp_off_t; -#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) - // extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, diff --git a/ports/mimxrt/board_init.c b/ports/mimxrt/board_init.c index 6f5235d3b..cd7dc9a7d 100644 --- a/ports/mimxrt/board_init.c +++ b/ports/mimxrt/board_init.c @@ -90,11 +90,13 @@ void SysTick_Handler(void) { } void USB_OTG1_IRQHandler(void) { - tud_isr(0); + tud_int_handler(0); tud_task(); + __SEV(); } void USB_OTG2_IRQHandler(void) { - tud_isr(1); + tud_int_handler(1); tud_task(); + __SEV(); } diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 344867fb8..68c7a8942 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -94,11 +94,10 @@ extern const struct _mp_obj_module_t mp_module_utime; do { \ extern void mp_handle_pending(bool); \ mp_handle_pending(true); \ - __WFI(); \ + __WFE(); \ } while (0); #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) -#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) #define MP_SSIZE_MAX (0x7fffffff) typedef int mp_int_t; // must be pointer size diff --git a/ports/mimxrt/mphalport.c b/ports/mimxrt/mphalport.c index d7642d6b6..3032464d3 100644 --- a/ports/mimxrt/mphalport.c +++ b/ports/mimxrt/mphalport.c @@ -75,7 +75,7 @@ int mp_hal_stdin_rx_chr(void) { return buf[0]; } } - __WFI(); + MICROPY_EVENT_POLL_HOOK } } @@ -83,17 +83,16 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { if (tud_cdc_connected()) { for (size_t i = 0; i < len;) { uint32_t n = len - i; - uint32_t n2 = tud_cdc_write(str + i, n); - if (n2 < n) { - while (!tud_cdc_write_flush()) { - __WFI(); - } + if (n > CFG_TUD_CDC_EP_BUFSIZE) { + n = CFG_TUD_CDC_EP_BUFSIZE; } + while (n > tud_cdc_write_available()) { + __WFE(); + } + uint32_t n2 = tud_cdc_write(str + i, n); + tud_cdc_write_flush(); i += n2; } - while (!tud_cdc_write_flush()) { - __WFI(); - } } // TODO // while (len--) { diff --git a/ports/mimxrt/tusb_config.h b/ports/mimxrt/tusb_config.h index c7ec05e63..862fb6c52 100644 --- a/ports/mimxrt/tusb_config.h +++ b/ports/mimxrt/tusb_config.h @@ -32,6 +32,5 @@ #define CFG_TUD_CDC (1) #define CFG_TUD_CDC_RX_BUFSIZE (512) #define CFG_TUD_CDC_TX_BUFSIZE (512) -#define CFG_TUD_CDC_EPSIZE (512) #endif // MICROPY_INCLUDED_MIMXRT_TUSB_CONFIG_H diff --git a/ports/mimxrt/tusb_port.c b/ports/mimxrt/tusb_port.c index f6b09a362..0d73b0923 100644 --- a/ports/mimxrt/tusb_port.c +++ b/ports/mimxrt/tusb_port.c @@ -67,7 +67,7 @@ static const tusb_desc_device_t usbd_desc_device = { }; static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { - TUD_CONFIG_DESCRIPTOR(USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, + TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA), TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, @@ -90,7 +90,7 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { return usbd_desc_cfg; } -const uint16_t *tud_descriptor_string_cb(uint8_t index) { +const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { #define DESC_STR_MAX (20) static uint16_t desc_str[DESC_STR_MAX]; diff --git a/ports/nrf/README.md b/ports/nrf/README.md index 22bb4af51..0341cd81c 100644 --- a/ports/nrf/README.md +++ b/ports/nrf/README.md @@ -62,8 +62,9 @@ By default, the PCA10040 (nrf52832) is used as compile target. To build and flas Alternatively the target board could be defined: - make BOARD=pca10040 - make BOARD=pca10040 deploy + make submodules + make BOARD=pca10040 + make BOARD=pca10040 deploy ## Compile without LTO enabled diff --git a/ports/nrf/drivers/usb/usb_cdc.c b/ports/nrf/drivers/usb/usb_cdc.c index c90bced6b..9d7c832e2 100644 --- a/ports/nrf/drivers/usb/usb_cdc.c +++ b/ports/nrf/drivers/usb/usb_cdc.c @@ -223,4 +223,8 @@ void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) { } } +void USBD_IRQHandler(void) { + tud_int_handler(0); +} + #endif // MICROPY_HW_USB_CDC diff --git a/ports/nrf/drivers/usb/usb_descriptors.c b/ports/nrf/drivers/usb/usb_descriptors.c index f6724c1bc..e9436ded5 100644 --- a/ports/nrf/drivers/usb/usb_descriptors.c +++ b/ports/nrf/drivers/usb/usb_descriptors.c @@ -67,7 +67,7 @@ static const tusb_desc_device_t usbd_desc_device = { }; static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { - TUD_CONFIG_DESCRIPTOR(USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, + TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA), TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, @@ -90,7 +90,7 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { return usbd_desc_cfg; } -const uint16_t *tud_descriptor_string_cb(uint8_t index) { +const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { #define DESC_STR_MAX (20) static uint16_t desc_str[DESC_STR_MAX]; diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index a025feb2e..c246c53dd 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -201,8 +201,6 @@ // type definitions for the specific machine -#define BYTES_PER_WORD (4) - #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) #define MP_SSIZE_MAX (0x7fffffff) @@ -332,8 +330,6 @@ extern const struct _mp_obj_module_t ble_module; __WFI(); \ } while (0); -#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) - // We need to provide a declaration/definition of alloca() #include diff --git a/ports/pic16bit/mpconfigport.h b/ports/pic16bit/mpconfigport.h index f121081e6..43300d176 100644 --- a/ports/pic16bit/mpconfigport.h +++ b/ports/pic16bit/mpconfigport.h @@ -84,8 +84,6 @@ typedef unsigned int mp_uint_t; // must be pointer size typedef int mp_off_t; -#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) - // extra builtin names to add to the global namespace #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, diff --git a/ports/powerpc/mpconfigport.h b/ports/powerpc/mpconfigport.h index 7a9277736..0b868e3da 100644 --- a/ports/powerpc/mpconfigport.h +++ b/ports/powerpc/mpconfigport.h @@ -103,8 +103,6 @@ typedef unsigned long mp_uint_t; // must be pointer size typedef long mp_off_t; -#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) - // extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt new file mode 100644 index 000000000..bd58d8f70 --- /dev/null +++ b/ports/rp2/CMakeLists.txt @@ -0,0 +1,166 @@ +cmake_minimum_required(VERSION 3.12) + +# Set build type to reduce firmware size +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE MinSizeRel) +endif() + +# Set main target and component locations +set(MICROPYTHON_TARGET firmware) +get_filename_component(MPY_DIR "../.." ABSOLUTE) +if (PICO_SDK_PATH_OVERRIDE) + set(PICO_SDK_PATH ${PICO_SDK_PATH_OVERRIDE}) +else() + set(PICO_SDK_PATH ../../lib/pico-sdk) +endif() + +# Use the local tinyusb instead of the one in pico-sdk +set(PICO_TINYUSB_PATH ${MPY_DIR}/lib/tinyusb) + +# Include component cmake fragments +include(micropy_py.cmake) +include(micropy_extmod.cmake) +include(${PICO_SDK_PATH}/pico_sdk_init.cmake) + +# Define the top-level project +project(${MICROPYTHON_TARGET}) + +pico_sdk_init() + +add_executable(${MICROPYTHON_TARGET}) + +set(SOURCE_LIB + ${MPY_DIR}/lib/littlefs/lfs1.c + ${MPY_DIR}/lib/littlefs/lfs1_util.c + ${MPY_DIR}/lib/littlefs/lfs2.c + ${MPY_DIR}/lib/littlefs/lfs2_util.c + ${MPY_DIR}/lib/mp-readline/readline.c + ${MPY_DIR}/lib/oofatfs/ff.c + ${MPY_DIR}/lib/oofatfs/ffunicode.c + ${MPY_DIR}/lib/timeutils/timeutils.c + ${MPY_DIR}/lib/utils/gchelper_m0.s + ${MPY_DIR}/lib/utils/gchelper_native.c + ${MPY_DIR}/lib/utils/mpirq.c + ${MPY_DIR}/lib/utils/stdout_helpers.c + ${MPY_DIR}/lib/utils/sys_stdio_mphal.c + ${MPY_DIR}/lib/utils/pyexec.c +) + +set(SOURCE_DRIVERS + ${MPY_DIR}/drivers/bus/softspi.c +) + +set(SOURCE_PORT + machine_adc.c + machine_i2c.c + machine_pin.c + machine_pwm.c + machine_spi.c + machine_timer.c + machine_uart.c + machine_wdt.c + main.c + modmachine.c + modrp2.c + moduos.c + modutime.c + mphalport.c + mpthreadport.c + rp2_flash.c + rp2_pio.c + tusb_port.c + uart.c +) + +set(SOURCE_QSTR + ${SOURCE_PY} + ${SOURCE_EXTMOD} + ${MPY_DIR}/lib/utils/mpirq.c + ${MPY_DIR}/lib/utils/sys_stdio_mphal.c + ${PROJECT_SOURCE_DIR}/machine_adc.c + ${PROJECT_SOURCE_DIR}/machine_i2c.c + ${PROJECT_SOURCE_DIR}/machine_pin.c + ${PROJECT_SOURCE_DIR}/machine_pwm.c + ${PROJECT_SOURCE_DIR}/machine_spi.c + ${PROJECT_SOURCE_DIR}/machine_timer.c + ${PROJECT_SOURCE_DIR}/machine_uart.c + ${PROJECT_SOURCE_DIR}/machine_wdt.c + ${PROJECT_SOURCE_DIR}/modmachine.c + ${PROJECT_SOURCE_DIR}/modrp2.c + ${PROJECT_SOURCE_DIR}/moduos.c + ${PROJECT_SOURCE_DIR}/modutime.c + ${PROJECT_SOURCE_DIR}/rp2_flash.c + ${PROJECT_SOURCE_DIR}/rp2_pio.c +) + +set(MPY_QSTR_DEFS ${PROJECT_SOURCE_DIR}/qstrdefsport.h) + +# Define mpy-cross flags and frozen manifest +set(MPY_CROSS_FLAGS -march=armv7m) +set(FROZEN_MANIFEST ${PROJECT_SOURCE_DIR}/manifest.py) + +include(micropy_rules.cmake) + +target_sources(${MICROPYTHON_TARGET} PRIVATE + ${SOURCE_PY} + ${SOURCE_EXTMOD} + ${SOURCE_LIB} + ${SOURCE_DRIVERS} + ${SOURCE_PORT} +) + +target_include_directories(${MICROPYTHON_TARGET} PRIVATE + "${PROJECT_SOURCE_DIR}" + "${MPY_DIR}" + "${CMAKE_BINARY_DIR}" + ) + +target_compile_options(${MICROPYTHON_TARGET} PRIVATE + -Wall + #-Werror + -DFFCONF_H=\"${MPY_DIR}/lib/oofatfs/ffconf.h\" + -DLFS1_NO_MALLOC -DLFS1_NO_DEBUG -DLFS1_NO_WARN -DLFS1_NO_ERROR -DLFS1_NO_ASSERT + -DLFS2_NO_MALLOC -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR -DLFS2_NO_ASSERT +) + +target_compile_definitions(${MICROPYTHON_TARGET} PRIVATE + PICO_FLOAT_PROPAGATE_NANS=1 + PICO_STACK_SIZE=0x2000 + PICO_CORE1_STACK_SIZE=0 + PICO_PROGRAM_NAME="MicroPython" + PICO_NO_PROGRAM_VERSION_STRING=1 # do it ourselves in main.c + MICROPY_BUILD_TYPE="${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION} ${CMAKE_BUILD_TYPE}" + PICO_NO_BI_STDIO_UART=1 # we call it UART REPL +) + +target_link_libraries(${MICROPYTHON_TARGET} + hardware_adc + hardware_dma + hardware_flash + hardware_i2c + hardware_pio + hardware_pwm + hardware_rtc + hardware_spi + hardware_sync + pico_multicore + pico_stdlib_headers + pico_stdlib + pico_unique_id + tinyusb_device +) + +# todo this is a bit brittle, but we want to move a few source files into RAM (which requires +# a linker script modification) until we explicitly add macro calls around the function +# defs to move them into RAM. +if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM) + pico_set_linker_script(${MICROPYTHON_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp.ld) +endif() + +pico_add_extra_outputs(${MICROPYTHON_TARGET}) + +add_custom_command(TARGET ${MICROPYTHON_TARGET} + POST_BUILD + COMMAND arm-none-eabi-size --format=berkeley ${PROJECT_BINARY_DIR}/${MICROPYTHON_TARGET}.elf + VERBATIM +) diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile new file mode 100644 index 000000000..08cd53dcc --- /dev/null +++ b/ports/rp2/Makefile @@ -0,0 +1,14 @@ +# Makefile for micropython on Raspberry Pi RP2 +# +# This is a simple wrapper around cmake + +BUILD = build + +$(VERBOSE)MAKESILENT = -s + +all: + [ -d $(BUILD) ] || cmake -S . -B $(BUILD) -DPICO_BUILD_DOCS=0 + $(MAKE) $(MAKESILENT) -C $(BUILD) + +clean: + $(RM) -rf $(BUILD) diff --git a/ports/rp2/README.md b/ports/rp2/README.md new file mode 100644 index 000000000..d49550c5c --- /dev/null +++ b/ports/rp2/README.md @@ -0,0 +1,100 @@ +# The RP2 port + +This is a port of MicroPython to the Raspberry Pi RP2 series of microcontrollers. +Currently supported features are: + +- REPL over USB VCP, and optionally over UART (on GP0/GP1). +- Filesystem on the internal flash, using littlefs2. +- Support for native code generation and inline assembler. +- `utime` module with sleep, time and ticks functions. +- `uos` module with VFS support. +- `machine` module with the following classes: `Pin`, `ADC`, `PWM`, `I2C`, `SPI`, + `SoftI2C`, `SoftSPI`, `Timer`, `UART`, `WDT`. +- `rp2` module with programmable IO (PIO) support. + +See the `examples/rp2/` directory for some example code. + +## Building + +The MicroPython cross-compiler must be built first, which will be used to +pre-compile (freeze) built-in Python code. This cross-compiler is built and +run on the host machine using: + + $ make -C mpy-cross + +This command should be executed from the root directory of this repository. +All other commands below should be executed from the ports/rp2/ directory. + +Building of the RP2 firmware is done entirely using CMake, although a simple +Makefile is also provided as a convenience. To build the firmware run (from +this directory): + + $ make clean + $ make + +You can also build the standard CMake way. The final firmware is found in +the top-level of the CMake build directory (`build` by default) and is +called `firmware.uf2`. + +## Deploying firmware to the device + +Firmware can be deployed to the device by putting it into bootloader mode +(hold down BOOTSEL while powering on or resetting) and then copying +`firmware.uf2` to the USB mass storage device that appears. + +If MicroPython is already installed then the bootloader can be entered by +executing `import machine; machine.bootloader()` at the REPL. + +## Sample code + +The following samples can be easily run on the board by entering paste mode +with Ctrl-E at the REPL, then cut-and-pasting the sample code to the REPL, then +executing the code with Ctrl-D. + +### Blinky + +This blinks the on-board LED on the Pico board at 1.25Hz, using a Timer object +with a callback. + +```python +from machine import Pin, Timer +led = Pin(25, Pin.OUT) +tim = Timer() +def tick(timer): + global led + led.toggle() + +tim.init(freq=2.5, mode=Timer.PERIODIC, callback=tick) +``` + +### PIO blinky + +This blinks the on-board LED on the Pico board at 1Hz, using a PIO peripheral and +PIO assembler to directly toggle the LED at the required rate. + +```python +from machine import Pin +import rp2 + +@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW) +def blink_1hz(): + # Turn on the LED and delay, taking 1000 cycles. + set(pins, 1) + set(x, 31) [6] + label("delay_high") + nop() [29] + jmp(x_dec, "delay_high") + + # Turn off the LED and delay, taking 1000 cycles. + set(pins, 0) + set(x, 31) [6] + label("delay_low") + nop() [29] + jmp(x_dec, "delay_low") + +# Create StateMachine(0) with the blink_1hz program, outputting on Pin(25). +sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25)) +sm.active(1) +``` + +See the `examples/rp2/` directory for further example code. diff --git a/ports/rp2/machine_adc.c b/ports/rp2/machine_adc.c new file mode 100644 index 000000000..f8925e5aa --- /dev/null +++ b/ports/rp2/machine_adc.c @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "hardware/adc.h" + +#define ADC_IS_VALID_GPIO(gpio) ((gpio) >= 26 && (gpio) <= 29) +#define ADC_CHANNEL_FROM_GPIO(gpio) ((gpio) - 26) +#define ADC_CHANNEL_TEMPSENSOR (4) + +STATIC uint16_t adc_config_and_read_u16(uint32_t channel) { + adc_select_input(channel); + uint32_t raw = adc_read(); + const uint32_t bits = 12; + // Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16) + return raw << (16 - bits) | raw >> (2 * bits - 16); +} + +/******************************************************************************/ +// MicroPython bindings for machine.ADC + +const mp_obj_type_t machine_adc_type; + +typedef struct _machine_adc_obj_t { + mp_obj_base_t base; + uint32_t channel; +} machine_adc_obj_t; + +STATIC void machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "", self->channel); +} + +// ADC(id) +STATIC mp_obj_t machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Check number of arguments + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_t source = all_args[0]; + + uint32_t channel; + if (mp_obj_is_int(source)) { + // Get and validate channel number. + channel = mp_obj_get_int(source); + if (!((channel >= 0 && channel <= ADC_CHANNEL_TEMPSENSOR) || ADC_IS_VALID_GPIO(channel))) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid channel")); + } + + } else { + // Get GPIO and check it has ADC capabilities. + channel = mp_hal_get_pin_obj(source); + if (!ADC_IS_VALID_GPIO(channel)) { + mp_raise_ValueError(MP_ERROR_TEXT("Pin doesn't have ADC capabilities")); + } + } + + // Initialise the ADC peripheral if it's not already running. + if (!(adc_hw->cs & ADC_CS_EN_BITS)) { + adc_init(); + } + + if (ADC_IS_VALID_GPIO(channel)) { + // Configure the GPIO pin in ADC mode. + adc_gpio_init(channel); + channel = ADC_CHANNEL_FROM_GPIO(channel); + } else if (channel == ADC_CHANNEL_TEMPSENSOR) { + // Enable temperature sensor. + adc_set_temp_sensor_enabled(1); + } + + // Create ADC object. + machine_adc_obj_t *o = m_new_obj(machine_adc_obj_t); + o->base.type = &machine_adc_type; + o->channel = channel; + + return MP_OBJ_FROM_PTR(o); +} + +// read_u16() +STATIC mp_obj_t machine_adc_read_u16(mp_obj_t self_in) { + machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(adc_config_and_read_u16(self->channel)); +} +MP_DEFINE_CONST_FUN_OBJ_1(machine_adc_read_u16_obj, machine_adc_read_u16); + +STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&machine_adc_read_u16_obj) }, + + { MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMPSENSOR) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_adc_locals_dict, machine_adc_locals_dict_table); + +const mp_obj_type_t machine_adc_type = { + { &mp_type_type }, + .name = MP_QSTR_ADC, + .print = machine_adc_print, + .make_new = machine_adc_make_new, + .locals_dict = (mp_obj_dict_t *)&machine_adc_locals_dict, +}; diff --git a/ports/rp2/machine_i2c.c b/ports/rp2/machine_i2c.c new file mode 100644 index 000000000..0852cc199 --- /dev/null +++ b/ports/rp2/machine_i2c.c @@ -0,0 +1,161 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "extmod/machine_i2c.h" +#include "modmachine.h" + +#include "hardware/i2c.h" + +#define DEFAULT_I2C_FREQ (400000) +#define DEFAULT_I2C0_SCL (9) +#define DEFAULT_I2C0_SDA (8) +#define DEFAULT_I2C1_SCL (7) +#define DEFAULT_I2C1_SDA (6) + +// SDA/SCL on even/odd pins, I2C0/I2C1 on even/odd pairs of pins. +#define IS_VALID_SCL(i2c, pin) (((pin) & 1) == 1 && (((pin) & 2) >> 1) == (i2c)) +#define IS_VALID_SDA(i2c, pin) (((pin) & 1) == 0 && (((pin) & 2) >> 1) == (i2c)) + +typedef struct _machine_i2c_obj_t { + mp_obj_base_t base; + i2c_inst_t *const i2c_inst; + uint8_t i2c_id; + uint8_t scl; + uint8_t sda; + uint32_t freq; +} machine_i2c_obj_t; + +STATIC machine_i2c_obj_t machine_i2c_obj[] = { + {{&machine_hw_i2c_type}, i2c0, 0, DEFAULT_I2C0_SCL, DEFAULT_I2C0_SDA, 0}, + {{&machine_hw_i2c_type}, i2c1, 1, DEFAULT_I2C1_SCL, DEFAULT_I2C1_SDA, 0}, +}; + +STATIC void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "I2C(%u, freq=%u, scl=%u, sda=%u)", + self->i2c_id, self->freq, self->scl, self->sda); +} + +mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_freq, ARG_scl, ARG_sda }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_freq, MP_ARG_INT, {.u_int = DEFAULT_I2C_FREQ} }, + { MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get I2C bus. + int i2c_id = mp_obj_get_int(args[ARG_id].u_obj); + if (i2c_id < 0 || i2c_id >= MP_ARRAY_SIZE(machine_i2c_obj)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("I2C(%d) doesn't exist"), i2c_id); + } + + // Get static peripheral object. + machine_i2c_obj_t *self = (machine_i2c_obj_t *)&machine_i2c_obj[i2c_id]; + + // Set SCL/SDA pins if configured. + if (args[ARG_scl].u_obj != mp_const_none) { + int scl = mp_hal_get_pin_obj(args[ARG_scl].u_obj); + if (!IS_VALID_SCL(self->i2c_id, scl)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad SCL pin")); + } + self->scl = scl; + } + if (args[ARG_sda].u_obj != mp_const_none) { + int sda = mp_hal_get_pin_obj(args[ARG_sda].u_obj); + if (!IS_VALID_SDA(self->i2c_id, sda)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad SDA pin")); + } + self->sda = sda; + } + + // Initialise the I2C peripheral if any arguments given, or it was not initialised previously. + if (n_args > 1 || n_kw > 0 || self->freq == 0) { + self->freq = args[ARG_freq].u_int; + i2c_init(self->i2c_inst, self->freq); + self->freq = i2c_set_baudrate(self->i2c_inst, self->freq); + gpio_set_function(self->scl, GPIO_FUNC_I2C); + gpio_set_function(self->sda, GPIO_FUNC_I2C); + gpio_set_pulls(self->scl, true, 0); + gpio_set_pulls(self->sda, true, 0); + } + + return MP_OBJ_FROM_PTR(self); +} + +STATIC int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, size_t len, uint8_t *buf, unsigned int flags) { + machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in; + int ret; + bool nostop = !(flags & MP_MACHINE_I2C_FLAG_STOP); + if (flags & MP_MACHINE_I2C_FLAG_READ) { + ret = i2c_read_blocking(self->i2c_inst, addr, buf, len, nostop); + } else { + if (len <= 2) { + // Workaround issue with hardware I2C not accepting short writes. + mp_machine_soft_i2c_obj_t soft_i2c = { + .base = { &mp_machine_soft_i2c_type }, + .us_delay = 500000 / self->freq + 1, + .us_timeout = 255, + .scl = self->scl, + .sda = self->sda, + }; + mp_machine_i2c_buf_t bufs = { + .len = len, + .buf = buf, + }; + mp_hal_pin_open_drain(self->scl); + mp_hal_pin_open_drain(self->sda); + ret = mp_machine_soft_i2c_transfer(&soft_i2c.base, addr, 1, &bufs, flags); + gpio_set_function(self->scl, GPIO_FUNC_I2C); + gpio_set_function(self->sda, GPIO_FUNC_I2C); + } else { + ret = i2c_write_blocking(self->i2c_inst, addr, buf, len, nostop); + } + } + return (ret < 0) ? -MP_EIO : ret; +} + +STATIC const mp_machine_i2c_p_t machine_i2c_p = { + .transfer = mp_machine_i2c_transfer_adaptor, + .transfer_single = machine_i2c_transfer_single, +}; + +const mp_obj_type_t machine_hw_i2c_type = { + { &mp_type_type }, + .name = MP_QSTR_I2C, + .print = machine_i2c_print, + .make_new = machine_i2c_make_new, + .protocol = &machine_i2c_p, + .locals_dict = (mp_obj_dict_t *)&mp_machine_i2c_locals_dict, +}; diff --git a/ports/rp2/machine_pin.c b/ports/rp2/machine_pin.c new file mode 100644 index 000000000..0525e0f9f --- /dev/null +++ b/ports/rp2/machine_pin.c @@ -0,0 +1,449 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "py/runtime.h" +#include "py/mphal.h" +#include "lib/utils/mpirq.h" +#include "modmachine.h" +#include "extmod/virtpin.h" + +#include "hardware/irq.h" +#include "hardware/regs/intctrl.h" +#include "hardware/structs/iobank0.h" +#include "hardware/structs/padsbank0.h" + +#define GPIO_MODE_IN (0) +#define GPIO_MODE_OUT (1) +#define GPIO_MODE_OPEN_DRAIN (2) +#define GPIO_MODE_ALT (3) + +// These can be or'd together. +#define GPIO_PULL_UP (1) +#define GPIO_PULL_DOWN (2) + +#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))) + +typedef struct _machine_pin_obj_t { + mp_obj_base_t base; + uint32_t id; +} machine_pin_obj_t; + +typedef struct _machine_pin_irq_obj_t { + mp_irq_obj_t base; + uint32_t flags; + uint32_t trigger; +} machine_pin_irq_obj_t; + +STATIC const mp_irq_methods_t machine_pin_irq_methods; + +STATIC const machine_pin_obj_t machine_pin_obj[NUM_BANK0_GPIOS] = { + {{&machine_pin_type}, 0}, + {{&machine_pin_type}, 1}, + {{&machine_pin_type}, 2}, + {{&machine_pin_type}, 3}, + {{&machine_pin_type}, 4}, + {{&machine_pin_type}, 5}, + {{&machine_pin_type}, 6}, + {{&machine_pin_type}, 7}, + {{&machine_pin_type}, 8}, + {{&machine_pin_type}, 9}, + {{&machine_pin_type}, 10}, + {{&machine_pin_type}, 11}, + {{&machine_pin_type}, 12}, + {{&machine_pin_type}, 13}, + {{&machine_pin_type}, 14}, + {{&machine_pin_type}, 15}, + {{&machine_pin_type}, 16}, + {{&machine_pin_type}, 17}, + {{&machine_pin_type}, 18}, + {{&machine_pin_type}, 19}, + {{&machine_pin_type}, 20}, + {{&machine_pin_type}, 21}, + {{&machine_pin_type}, 22}, + {{&machine_pin_type}, 23}, + {{&machine_pin_type}, 24}, + {{&machine_pin_type}, 25}, + {{&machine_pin_type}, 26}, + {{&machine_pin_type}, 27}, + {{&machine_pin_type}, 28}, + {{&machine_pin_type}, 29}, +}; + +// Mask with "1" indicating that the corresponding pin is in simulated open-drain mode. +uint32_t machine_pin_open_drain_mask; + +STATIC void gpio_irq(void) { + for (int i = 0; i < 4; ++i) { + uint32_t intr = iobank0_hw->intr[i]; + if (intr) { + for (int j = 0; j < 8; ++j) { + if (intr & 0xf) { + uint32_t gpio = 8 * i + j; + gpio_acknowledge_irq(gpio, intr & 0xf); + machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[gpio]); + if (irq != NULL && (intr & irq->trigger)) { + irq->flags = intr & irq->trigger; + mp_irq_handler(&irq->base); + } + } + intr >>= 4; + } + } + } +} + +void machine_pin_init(void) { + memset(MP_STATE_PORT(machine_pin_irq_obj), 0, sizeof(MP_STATE_PORT(machine_pin_irq_obj))); + irq_set_exclusive_handler(IO_IRQ_BANK0, gpio_irq); + irq_set_enabled(IO_IRQ_BANK0, true); +} + +void machine_pin_deinit(void) { + for (int i = 0; i < NUM_BANK0_GPIOS; ++i) { + gpio_set_irq_enabled(i, GPIO_IRQ_ALL, false); + } + irq_set_enabled(IO_IRQ_BANK0, false); + irq_remove_handler(IO_IRQ_BANK0, gpio_irq); +} + +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); + qstr mode_qst; + 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)) { + mode_qst = MP_QSTR_OUT; + } else { + mode_qst = MP_QSTR_IN; + } + } else { + mode_qst = MP_QSTR_ALT; + } + mp_printf(print, "Pin(%u, mode=%q", self->id, mode_qst); + bool pull_up = false; + if (GPIO_IS_PULL_UP(self->id)) { + mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP); + pull_up = true; + } + if (GPIO_IS_PULL_DOWN(self->id)) { + if (pull_up) { + mp_printf(print, "|%q", MP_QSTR_PULL_DOWN); + } else { + mp_printf(print, ", pull=%q", MP_QSTR_PULL_DOWN); + } + } + if (funcsel != GPIO_FUNC_SIO) { + mp_printf(print, ", alt=%u", funcsel); + } + mp_printf(print, ")"); +} + +// pin.init(mode, pull=None, *, value=None, alt=FUNC_SIO) +STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_mode, ARG_pull, ARG_value, ARG_alt }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + { MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + { MP_QSTR_alt, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = GPIO_FUNC_SIO}}, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // set initial value (do this before configuring mode/pull) + if (args[ARG_value].u_obj != mp_const_none) { + gpio_put(self->id, mp_obj_is_true(args[ARG_value].u_obj)); + } + + // configure mode + if (args[ARG_mode].u_obj != mp_const_none) { + mp_int_t mode = mp_obj_get_int(args[ARG_mode].u_obj); + if (mode == GPIO_MODE_IN) { + mp_hal_pin_input(self->id); + } else if (mode == GPIO_MODE_OUT) { + mp_hal_pin_output(self->id); + } else if (mode == GPIO_MODE_OPEN_DRAIN) { + mp_hal_pin_open_drain(self->id); + } else { + // Alternate function. + gpio_set_function(self->id, args[ARG_alt].u_int); + machine_pin_open_drain_mask &= ~(1 << self->id); + } + } + + // configure pull (unconditionally because None means no-pull) + uint32_t pull = 0; + if (args[ARG_pull].u_obj != mp_const_none) { + pull = mp_obj_get_int(args[ARG_pull].u_obj); + } + gpio_set_pulls(self->id, pull & GPIO_PULL_UP, pull & GPIO_PULL_DOWN); + + return mp_const_none; +} + +// constructor(id, ...) +mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // get the wanted pin object + int wanted_pin = mp_obj_get_int(args[0]); + if (!(0 <= wanted_pin && wanted_pin < MP_ARRAY_SIZE(machine_pin_obj))) { + mp_raise_ValueError("invalid pin"); + } + const machine_pin_obj_t *self = &machine_pin_obj[wanted_pin]; + + if (n_args > 1 || n_kw > 0) { + // pin mode given, so configure this GPIO + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args); + } + + return MP_OBJ_FROM_PTR(self); +} + +// fast method for getting/setting pin value +STATIC mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + machine_pin_obj_t *self = self_in; + if (n_args == 0) { + // get pin + return MP_OBJ_NEW_SMALL_INT(gpio_get(self->id)); + } else { + // set pin + bool value = mp_obj_is_true(args[0]); + if (GPIO_IS_OPEN_DRAIN(self->id)) { + MP_STATIC_ASSERT(GPIO_IN == 0 && GPIO_OUT == 1); + gpio_set_dir(self->id, 1 - value); + } else { + gpio_put(self->id, value); + } + return mp_const_none; + } +} + +// pin.init(mode, pull) +STATIC mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init); + +// pin.value([value]) +STATIC mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) { + return machine_pin_call(args[0], n_args - 1, 0, args + 1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value); + +// pin.low() +STATIC mp_obj_t machine_pin_low(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (GPIO_IS_OPEN_DRAIN(self->id)) { + gpio_set_dir(self->id, GPIO_OUT); + } else { + gpio_clr_mask(1u << self->id); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_low_obj, machine_pin_low); + +// pin.high() +STATIC mp_obj_t machine_pin_high(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (GPIO_IS_OPEN_DRAIN(self->id)) { + gpio_set_dir(self->id, GPIO_IN); + } else { + gpio_set_mask(1u << self->id); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_high_obj, machine_pin_high); + +// pin.toggle() +STATIC mp_obj_t machine_pin_toggle(mp_obj_t self_in) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (GPIO_IS_OPEN_DRAIN(self->id)) { + if (GPIO_IS_OUT(self->id)) { + gpio_set_dir(self->id, GPIO_IN); + } else { + gpio_set_dir(self->id, GPIO_OUT); + } + } else { + gpio_xor_mask(1u << self->id); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle); + +// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False) +STATIC mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_handler, ARG_trigger, ARG_hard }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_trigger, MP_ARG_INT, {.u_int = GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE} }, + { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} }, + }; + machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get the IRQ object. + machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[self->id]); + + // Allocate the IRQ object if it doesn't already exist. + if (irq == NULL) { + irq = m_new_obj(machine_pin_irq_obj_t); + irq->base.base.type = &mp_irq_type; + irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods; + irq->base.parent = MP_OBJ_FROM_PTR(self); + irq->base.handler = mp_const_none; + irq->base.ishard = false; + MP_STATE_PORT(machine_pin_irq_obj[self->id]) = irq; + } + + if (n_args > 1 || kw_args->used != 0) { + // Configure IRQ. + + // Disable all IRQs while data is updated. + gpio_set_irq_enabled(self->id, GPIO_IRQ_ALL, false); + + // Update IRQ data. + irq->base.handler = args[ARG_handler].u_obj; + irq->base.ishard = args[ARG_hard].u_bool; + irq->flags = 0; + irq->trigger = args[ARG_trigger].u_int; + + // Enable IRQ if a handler is given. + if (args[ARG_handler].u_obj != mp_const_none) { + gpio_set_irq_enabled(self->id, args[ARG_trigger].u_int, true); + } + } + + return MP_OBJ_FROM_PTR(irq); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq); + +STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&machine_pin_low_obj) }, + { MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&machine_pin_high_obj) }, + { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_low_obj) }, + { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_high_obj) }, + { MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) }, + + // class constants + { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_MODE_IN) }, + { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_MODE_OUT) }, + { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(GPIO_MODE_OPEN_DRAIN) }, + { MP_ROM_QSTR(MP_QSTR_ALT), MP_ROM_INT(GPIO_MODE_ALT) }, + { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULL_UP) }, + { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULL_DOWN) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_IRQ_EDGE_RISE) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_IRQ_EDGE_FALL) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table); + +STATIC mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + (void)errcode; + machine_pin_obj_t *self = self_in; + + switch (request) { + case MP_PIN_READ: { + return gpio_get(self->id); + } + case MP_PIN_WRITE: { + gpio_put(self->id, arg); + return 0; + } + } + return -1; +} + +STATIC const mp_pin_p_t pin_pin_p = { + .ioctl = pin_ioctl, +}; + +const mp_obj_type_t machine_pin_type = { + { &mp_type_type }, + .name = MP_QSTR_Pin, + .print = machine_pin_print, + .make_new = mp_pin_make_new, + .call = machine_pin_call, + .protocol = &pin_pin_p, + .locals_dict = (mp_obj_t)&machine_pin_locals_dict, +}; + +STATIC mp_uint_t machine_pin_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[self->id]); + gpio_set_irq_enabled(self->id, GPIO_IRQ_ALL, false); + irq->flags = 0; + irq->trigger = new_trigger; + gpio_set_irq_enabled(self->id, new_trigger, true); + return 0; +} + +STATIC mp_uint_t machine_pin_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[self->id]); + if (info_type == MP_IRQ_INFO_FLAGS) { + return irq->flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return irq->trigger; + } + return 0; +} + +STATIC const mp_irq_methods_t machine_pin_irq_methods = { + .trigger = machine_pin_irq_trigger, + .info = machine_pin_irq_info, +}; + +mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) { + if (!mp_obj_is_type(obj, &machine_pin_type)) { + mp_raise_ValueError(MP_ERROR_TEXT("expecting a Pin")); + } + machine_pin_obj_t *pin = MP_OBJ_TO_PTR(obj); + return pin->id; +} diff --git a/ports/rp2/machine_pwm.c b/ports/rp2/machine_pwm.c new file mode 100644 index 000000000..ff40c5503 --- /dev/null +++ b/ports/rp2/machine_pwm.c @@ -0,0 +1,197 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" + +#include "hardware/clocks.h" +#include "hardware/pwm.h" + +/******************************************************************************/ +// MicroPython bindings for machine.PWM + +const mp_obj_type_t machine_pwm_type; + +typedef struct _machine_pwm_obj_t { + mp_obj_base_t base; + uint8_t slice; + uint8_t channel; +} machine_pwm_obj_t; + +STATIC machine_pwm_obj_t machine_pwm_obj[] = { + {{&machine_pwm_type}, 0, PWM_CHAN_A}, + {{&machine_pwm_type}, 0, PWM_CHAN_B}, + {{&machine_pwm_type}, 1, PWM_CHAN_A}, + {{&machine_pwm_type}, 1, PWM_CHAN_B}, + {{&machine_pwm_type}, 2, PWM_CHAN_A}, + {{&machine_pwm_type}, 2, PWM_CHAN_B}, + {{&machine_pwm_type}, 3, PWM_CHAN_A}, + {{&machine_pwm_type}, 3, PWM_CHAN_B}, + {{&machine_pwm_type}, 4, PWM_CHAN_A}, + {{&machine_pwm_type}, 4, PWM_CHAN_B}, + {{&machine_pwm_type}, 5, PWM_CHAN_A}, + {{&machine_pwm_type}, 5, PWM_CHAN_B}, + {{&machine_pwm_type}, 6, PWM_CHAN_A}, + {{&machine_pwm_type}, 6, PWM_CHAN_B}, + {{&machine_pwm_type}, 7, PWM_CHAN_A}, + {{&machine_pwm_type}, 7, PWM_CHAN_B}, +}; + +STATIC void 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, "", self->slice, self->channel); +} + +// PWM(pin) +STATIC mp_obj_t machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Check number of arguments + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + // Get GPIO to connect to PWM. + uint32_t gpio = mp_hal_get_pin_obj(all_args[0]); + + // Get static peripheral object. + uint slice = pwm_gpio_to_slice_num(gpio); + uint8_t channel = pwm_gpio_to_channel(gpio); + const machine_pwm_obj_t *self = &machine_pwm_obj[slice * 2 + channel]; + + // Select PWM function for given GPIO. + gpio_set_function(gpio, GPIO_FUNC_PWM); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t machine_pwm_deinit(mp_obj_t self_in) { + machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + pwm_set_enabled(self->slice, false); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_pwm_deinit_obj, machine_pwm_deinit); + +// PWM.freq([value]) +STATIC mp_obj_t machine_pwm_freq(size_t n_args, const mp_obj_t *args) { + machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t source_hz = clock_get_hz(clk_sys); + if (n_args == 1) { + // Get frequency. + uint32_t div16 = pwm_hw->slice[self->slice].div; + uint32_t top = pwm_hw->slice[self->slice].top; + uint32_t pwm_freq = 16 * source_hz / div16 / top; + return MP_OBJ_NEW_SMALL_INT(pwm_freq); + } else { + // Set the frequency, making "top" as large as possible for maximum resolution. + // Maximum "top" is set at 65534 to be able to achieve 100% duty with 65535. + #define TOP_MAX 65534 + mp_int_t freq = mp_obj_get_int(args[1]); + uint32_t div16_top = 16 * source_hz / freq; + uint32_t top = 1; + for (;;) { + // Try a few small prime factors to get close to the desired frequency. + if (div16_top >= 16 * 5 && div16_top % 5 == 0 && top * 5 <= TOP_MAX) { + div16_top /= 5; + top *= 5; + } else if (div16_top >= 16 * 3 && div16_top % 3 == 0 && top * 3 <= TOP_MAX) { + div16_top /= 3; + top *= 3; + } else if (div16_top >= 16 * 2 && top * 2 <= TOP_MAX) { + div16_top /= 2; + top *= 2; + } else { + break; + } + } + if (div16_top < 16) { + mp_raise_ValueError(MP_ERROR_TEXT("freq too large")); + } else if (div16_top >= 256 * 16) { + mp_raise_ValueError(MP_ERROR_TEXT("freq too small")); + } + pwm_hw->slice[self->slice].div = div16_top; + pwm_hw->slice[self->slice].top = top; + return mp_const_none; + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_freq_obj, 1, 2, machine_pwm_freq); + +// PWM.duty_u16([value]) +STATIC mp_obj_t machine_pwm_duty_u16(size_t n_args, const mp_obj_t *args) { + machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t top = pwm_hw->slice[self->slice].top; + if (n_args == 1) { + // Get duty cycle. + uint32_t cc = pwm_hw->slice[self->slice].cc; + cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff; + return MP_OBJ_NEW_SMALL_INT(cc * 65535 / (top + 1)); + } else { + // Set duty cycle. + mp_int_t duty_u16 = mp_obj_get_int(args[1]); + uint32_t cc = duty_u16 * (top + 1) / 65535; + pwm_set_chan_level(self->slice, self->channel, cc); + pwm_set_enabled(self->slice, true); + return mp_const_none; + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_u16_obj, 1, 2, machine_pwm_duty_u16); + +// PWM.duty_ns([value]) +STATIC mp_obj_t machine_pwm_duty_ns(size_t n_args, const mp_obj_t *args) { + machine_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t source_hz = clock_get_hz(clk_sys); + uint32_t slice_hz = 16 * source_hz / pwm_hw->slice[self->slice].div; + if (n_args == 1) { + // Get duty cycle. + uint32_t cc = pwm_hw->slice[self->slice].cc; + cc = (cc >> (self->channel ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB)) & 0xffff; + return MP_OBJ_NEW_SMALL_INT((uint64_t)cc * 1000000000ULL / slice_hz); + } else { + // Set duty cycle. + mp_int_t duty_ns = mp_obj_get_int(args[1]); + uint32_t cc = (uint64_t)duty_ns * slice_hz / 1000000000ULL; + if (cc > 65535) { + mp_raise_ValueError(MP_ERROR_TEXT("duty larger than period")); + } + pwm_set_chan_level(self->slice, self->channel, cc); + pwm_set_enabled(self->slice, true); + return mp_const_none; + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pwm_duty_ns_obj, 1, 2, machine_pwm_duty_ns); + +STATIC const mp_rom_map_elem_t machine_pwm_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_pwm_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_pwm_freq_obj) }, + { MP_ROM_QSTR(MP_QSTR_duty_u16), MP_ROM_PTR(&machine_pwm_duty_u16_obj) }, + { MP_ROM_QSTR(MP_QSTR_duty_ns), MP_ROM_PTR(&machine_pwm_duty_ns_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_pwm_locals_dict, machine_pwm_locals_dict_table); + +const mp_obj_type_t machine_pwm_type = { + { &mp_type_type }, + .name = MP_QSTR_PWM, + .print = machine_pwm_print, + .make_new = machine_pwm_make_new, + .locals_dict = (mp_obj_dict_t *)&machine_pwm_locals_dict, +}; diff --git a/ports/rp2/machine_spi.c b/ports/rp2/machine_spi.c new file mode 100644 index 000000000..478c06145 --- /dev/null +++ b/ports/rp2/machine_spi.c @@ -0,0 +1,278 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "extmod/machine_spi.h" +#include "modmachine.h" + +#include "hardware/spi.h" +#include "hardware/dma.h" + +#define DEFAULT_SPI_BAUDRATE (1000000) +#define DEFAULT_SPI_POLARITY (0) +#define DEFAULT_SPI_PHASE (0) +#define DEFAULT_SPI_BITS (8) +#define DEFAULT_SPI_FIRSTBIT (SPI_MSB_FIRST) +#define DEFAULT_SPI0_SCK (6) +#define DEFAULT_SPI0_MOSI (7) +#define DEFAULT_SPI0_MISO (4) +#define DEFAULT_SPI1_SCK (10) +#define DEFAULT_SPI1_MOSI (11) +#define DEFAULT_SPI1_MISO (8) + +#define IS_VALID_PERIPH(spi, pin) ((((pin) & 8) >> 3) == (spi)) +#define IS_VALID_SCK(spi, pin) (((pin) & 3) == 2 && IS_VALID_PERIPH(spi, pin)) +#define IS_VALID_MOSI(spi, pin) (((pin) & 3) == 3 && IS_VALID_PERIPH(spi, pin)) +#define IS_VALID_MISO(spi, pin) (((pin) & 3) == 0 && IS_VALID_PERIPH(spi, pin)) + +typedef struct _machine_spi_obj_t { + mp_obj_base_t base; + spi_inst_t *const spi_inst; + uint8_t spi_id; + uint8_t polarity; + uint8_t phase; + uint8_t bits; + uint8_t firstbit; + uint8_t sck; + uint8_t mosi; + uint8_t miso; + uint32_t baudrate; +} machine_spi_obj_t; + +STATIC machine_spi_obj_t machine_spi_obj[] = { + { + {&machine_spi_type}, spi0, 0, + DEFAULT_SPI_POLARITY, DEFAULT_SPI_PHASE, DEFAULT_SPI_BITS, DEFAULT_SPI_FIRSTBIT, + DEFAULT_SPI0_SCK, DEFAULT_SPI0_MOSI, DEFAULT_SPI0_MISO, + 0, + }, + { + {&machine_spi_type}, spi1, 1, + DEFAULT_SPI_POLARITY, DEFAULT_SPI_PHASE, DEFAULT_SPI_BITS, DEFAULT_SPI_FIRSTBIT, + DEFAULT_SPI1_SCK, DEFAULT_SPI1_MOSI, DEFAULT_SPI1_MISO, + 0, + }, +}; + +STATIC void machine_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "SPI(%u, baudrate=%u, polarity=%u, phase=%u, bits=%u, sck=%u, mosi=%u, miso=%u)", + self->spi_id, self->baudrate, self->polarity, self->phase, self->bits, + self->sck, self->mosi, self->miso); +} + +mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = DEFAULT_SPI_BAUDRATE} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_POLARITY} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_PHASE} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_BITS} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_SPI_FIRSTBIT} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + + // Parse the arguments. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get the SPI bus id. + int spi_id = mp_obj_get_int(args[ARG_id].u_obj); + if (spi_id < 0 || spi_id >= MP_ARRAY_SIZE(machine_spi_obj)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI(%d) doesn't exist"), spi_id); + } + + // Get static peripheral object. + machine_spi_obj_t *self = (machine_spi_obj_t *)&machine_spi_obj[spi_id]; + + // Set SCK/MOSI/MISO pins if configured. + if (args[ARG_sck].u_obj != mp_const_none) { + int sck = mp_hal_get_pin_obj(args[ARG_sck].u_obj); + if (!IS_VALID_SCK(self->spi_id, sck)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad SCK pin")); + } + self->sck = sck; + } + if (args[ARG_mosi].u_obj != mp_const_none) { + int mosi = mp_hal_get_pin_obj(args[ARG_mosi].u_obj); + if (!IS_VALID_MOSI(self->spi_id, mosi)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad MOSI pin")); + } + self->mosi = mosi; + } + if (args[ARG_miso].u_obj != mp_const_none) { + int miso = mp_hal_get_pin_obj(args[ARG_miso].u_obj); + if (!IS_VALID_MISO(self->spi_id, miso)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad MISO pin")); + } + self->miso = miso; + } + + // Initialise the SPI peripheral if any arguments given, or it was not initialised previously. + if (n_args > 1 || n_kw > 0 || self->baudrate == 0) { + self->baudrate = args[ARG_baudrate].u_int; + self->polarity = args[ARG_polarity].u_int; + self->phase = args[ARG_phase].u_int; + self->bits = args[ARG_bits].u_int; + self->firstbit = args[ARG_firstbit].u_int; + if (self->firstbit == SPI_LSB_FIRST) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("LSB")); + } + + spi_init(self->spi_inst, self->baudrate); + self->baudrate = spi_set_baudrate(self->spi_inst, self->baudrate); + spi_set_format(self->spi_inst, self->bits, self->polarity, self->phase, self->firstbit); + gpio_set_function(self->sck, GPIO_FUNC_SPI); + gpio_set_function(self->miso, GPIO_FUNC_SPI); + gpio_set_function(self->mosi, GPIO_FUNC_SPI); + } + + return MP_OBJ_FROM_PTR(self); +} + +STATIC void machine_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + }; + + // Parse the arguments. + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Reconfigure the baudrate if requested. + if (args[ARG_baudrate].u_int != -1) { + self->baudrate = spi_set_baudrate(self->spi_inst, args[ARG_baudrate].u_int); + } + + // Reconfigure the format if requested. + bool set_format = false; + if (args[ARG_polarity].u_int != -1) { + self->polarity = args[ARG_polarity].u_int; + set_format = true; + } + if (args[ARG_phase].u_int != -1) { + self->phase = args[ARG_phase].u_int; + set_format = true; + } + if (args[ARG_bits].u_int != -1) { + self->bits = args[ARG_bits].u_int; + set_format = true; + } + if (args[ARG_firstbit].u_int != -1) { + self->firstbit = args[ARG_firstbit].u_int; + if (self->firstbit == SPI_LSB_FIRST) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("LSB")); + } + } + if (set_format) { + spi_set_format(self->spi_inst, self->bits, self->polarity, self->phase, self->firstbit); + } +} + +STATIC void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { + machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; + // Use DMA for large transfers if channels are available + const size_t dma_min_size_threshold = 32; + int chan_tx = -1; + int chan_rx = -1; + if (len >= dma_min_size_threshold) { + // Use two DMA channels to service the two FIFOs + chan_tx = dma_claim_unused_channel(false); + chan_rx = dma_claim_unused_channel(false); + } + bool use_dma = chan_rx >= 0 && chan_tx >= 0; + // note src is guaranteed to be non-NULL + bool write_only = dest == NULL; + + if (use_dma) { + uint8_t dev_null; + dma_channel_config c = dma_channel_get_default_config(chan_tx); + channel_config_set_transfer_data_size(&c, DMA_SIZE_8); + channel_config_set_dreq(&c, spi_get_index(self->spi_inst) ? DREQ_SPI1_TX : DREQ_SPI0_TX); + dma_channel_configure(chan_tx, &c, + &spi_get_hw(self->spi_inst)->dr, + src, + len, + false); + + c = dma_channel_get_default_config(chan_rx); + channel_config_set_transfer_data_size(&c, DMA_SIZE_8); + channel_config_set_dreq(&c, spi_get_index(self->spi_inst) ? DREQ_SPI1_RX : DREQ_SPI0_RX); + channel_config_set_read_increment(&c, false); + channel_config_set_write_increment(&c, !write_only); + dma_channel_configure(chan_rx, &c, + write_only ? &dev_null : dest, + &spi_get_hw(self->spi_inst)->dr, + len, + false); + + dma_start_channel_mask((1u << chan_rx) | (1u << chan_tx)); + dma_channel_wait_for_finish_blocking(chan_rx); + dma_channel_wait_for_finish_blocking(chan_tx); + } + + // If we have claimed only one channel successfully, we should release immediately + if (chan_rx >= 0) { + dma_channel_unclaim(chan_rx); + } + if (chan_tx >= 0) { + dma_channel_unclaim(chan_tx); + } + + if (!use_dma) { + // Use software for small transfers, or if couldn't claim two DMA channels + if (write_only) { + spi_write_blocking(self->spi_inst, src, len); + } else { + spi_write_read_blocking(self->spi_inst, src, dest, len); + } + } +} + +STATIC const mp_machine_spi_p_t machine_spi_p = { + .init = machine_spi_init, + .transfer = machine_spi_transfer, +}; + +const mp_obj_type_t machine_spi_type = { + { &mp_type_type }, + .name = MP_QSTR_SPI, + .print = machine_spi_print, + .make_new = machine_spi_make_new, + .protocol = &machine_spi_p, + .locals_dict = (mp_obj_dict_t *)&mp_machine_spi_locals_dict, +}; diff --git a/ports/rp2/machine_timer.c b/ports/rp2/machine_timer.c new file mode 100644 index 000000000..e7e8f02d5 --- /dev/null +++ b/ports/rp2/machine_timer.c @@ -0,0 +1,165 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "pico/time.h" + +#define ALARM_ID_INVALID (-1) +#define TIMER_MODE_ONE_SHOT (0) +#define TIMER_MODE_PERIODIC (1) + +typedef struct _machine_timer_obj_t { + mp_obj_base_t base; + struct alarm_pool *pool; + alarm_id_t alarm_id; + uint32_t mode; + uint64_t delta_us; // for periodic mode + mp_obj_t callback; +} machine_timer_obj_t; + +const mp_obj_type_t machine_timer_type; + +STATIC int64_t alarm_callback(alarm_id_t id, void *user_data) { + machine_timer_obj_t *self = user_data; + mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self)); + if (self->mode == TIMER_MODE_ONE_SHOT) { + return 0; + } else { + return -self->delta_us; + } +} + +STATIC void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_timer_obj_t *self = MP_OBJ_TO_PTR(self_in); + qstr mode = self->mode == TIMER_MODE_ONE_SHOT ? MP_QSTR_ONE_SHOT : MP_QSTR_PERIODIC; + mp_printf(print, "Timer(mode=%q, period=%u, tick_hz=1000000)", mode, self->delta_us); +} + +STATIC mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_MODE_PERIODIC} }, + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + { MP_QSTR_tick_hz, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} }, + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + + // Parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self->mode = args[ARG_mode].u_int; + if (args[ARG_freq].u_obj != mp_const_none) { + // Frequency specified in Hz + #if MICROPY_PY_BUILTINS_FLOAT + self->delta_us = (uint64_t)(MICROPY_FLOAT_CONST(1000000.0) / mp_obj_get_float(args[ARG_freq].u_obj)); + #else + self->delta_us = 1000000 / mp_obj_get_int(args[ARG_freq].u_obj); + #endif + } else { + // Period specified + self->delta_us = (uint64_t)args[ARG_period].u_int * 1000000 / args[ARG_tick_hz].u_int; + } + if (self->delta_us < 1) { + self->delta_us = 1; + } + + self->callback = args[ARG_callback].u_obj; + self->alarm_id = alarm_pool_add_alarm_in_us(self->pool, self->delta_us, alarm_callback, self, true); + if (self->alarm_id == -1) { + mp_raise_OSError(MP_ENOMEM); + } + + return mp_const_none; +} + +STATIC mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + machine_timer_obj_t *self = m_new_obj_with_finaliser(machine_timer_obj_t); + self->base.type = &machine_timer_type; + self->pool = alarm_pool_get_default(); + self->alarm_id = ALARM_ID_INVALID; + + // Get timer id (only soft timer (-1) supported at the moment) + mp_int_t id = -1; + if (n_args > 0) { + id = mp_obj_get_int(args[0]); + --n_args; + ++args; + } + if (id != -1) { + mp_raise_ValueError(MP_ERROR_TEXT("Timer doesn't exist")); + } + + if (n_args > 0 || n_kw > 0) { + // Start the timer + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_timer_init_helper(self, n_args, args, &kw_args); + } + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t machine_timer_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + machine_timer_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (self->alarm_id != ALARM_ID_INVALID) { + alarm_pool_cancel_alarm(self->pool, self->alarm_id); + self->alarm_id = ALARM_ID_INVALID; + } + return machine_timer_init_helper(self, n_args - 1, args + 1, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init); + +STATIC mp_obj_t machine_timer_deinit(mp_obj_t self_in) { + machine_timer_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->alarm_id != ALARM_ID_INVALID) { + alarm_pool_cancel_alarm(self->pool, self->alarm_id); + self->alarm_id = ALARM_ID_INVALID; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_deinit_obj, machine_timer_deinit); + +STATIC const mp_rom_map_elem_t machine_timer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_timer_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_timer_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_timer_deinit_obj) }, + + { MP_ROM_QSTR(MP_QSTR_ONE_SHOT), MP_ROM_INT(TIMER_MODE_ONE_SHOT) }, + { MP_ROM_QSTR(MP_QSTR_PERIODIC), MP_ROM_INT(TIMER_MODE_PERIODIC) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_timer_locals_dict, machine_timer_locals_dict_table); + +const mp_obj_type_t machine_timer_type = { + { &mp_type_type }, + .name = MP_QSTR_Timer, + .print = machine_timer_print, + .make_new = machine_timer_make_new, + .locals_dict = (mp_obj_dict_t *)&machine_timer_locals_dict, +}; diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c new file mode 100644 index 000000000..30446e688 --- /dev/null +++ b/ports/rp2/machine_uart.c @@ -0,0 +1,246 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "modmachine.h" + +#include "hardware/uart.h" + +#define DEFAULT_UART_BAUDRATE (115200) +#define DEFAULT_UART_BITS (8) +#define DEFAULT_UART_STOP (1) +#define DEFAULT_UART0_TX (0) +#define DEFAULT_UART0_RX (1) +#define DEFAULT_UART1_TX (4) +#define DEFAULT_UART1_RX (5) + +#define IS_VALID_PERIPH(uart, pin) (((((pin) + 4) & 8) >> 3) == (uart)) +#define IS_VALID_TX(uart, pin) (((pin) & 3) == 0 && IS_VALID_PERIPH(uart, pin)) +#define IS_VALID_RX(uart, pin) (((pin) & 3) == 1 && IS_VALID_PERIPH(uart, pin)) + +typedef struct _machine_uart_obj_t { + mp_obj_base_t base; + uart_inst_t *const uart; + uint8_t uart_id; + uint32_t baudrate; + uint8_t bits; + uart_parity_t parity; + uint8_t stop; + uint8_t tx; + uint8_t rx; +} machine_uart_obj_t; + +STATIC machine_uart_obj_t machine_uart_obj[] = { + {{&machine_uart_type}, uart0, 0, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, DEFAULT_UART0_TX, DEFAULT_UART0_RX}, + {{&machine_uart_type}, uart1, 1, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, DEFAULT_UART1_TX, DEFAULT_UART1_RX}, +}; + +STATIC const char *_parity_name[] = {"None", "0", "1"}; + +/******************************************************************************/ +// MicroPython bindings for UART + +STATIC void 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); + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, tx=%d, rx=%d)", + self->uart_id, self->baudrate, self->bits, _parity_name[self->parity], + self->stop, self->tx, self->rx); +} + +STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_tx, ARG_rx }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_parity, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_INT(-1)} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + + // Parse args. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get UART bus. + int uart_id = mp_obj_get_int(args[ARG_id].u_obj); + if (uart_id < 0 || uart_id >= MP_ARRAY_SIZE(machine_uart_obj)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%d) doesn't exist"), uart_id); + } + + // Get static peripheral object. + machine_uart_obj_t *self = (machine_uart_obj_t *)&machine_uart_obj[uart_id]; + + // Set baudrate if configured. + if (args[ARG_baudrate].u_int > 0) { + self->baudrate = args[ARG_baudrate].u_int; + } + + // Set bits if configured. + if (args[ARG_bits].u_int > 0) { + self->bits = args[ARG_bits].u_int; + } + + // Set parity if configured. + if (args[ARG_parity].u_obj != MP_OBJ_NEW_SMALL_INT(-1)) { + if (args[ARG_parity].u_obj == mp_const_none) { + self->parity = UART_PARITY_NONE; + } else if (mp_obj_get_int(args[ARG_parity].u_obj) & 1) { + self->parity = UART_PARITY_ODD; + } else { + self->parity = UART_PARITY_EVEN; + } + } + + // Set stop bits if configured. + if (args[ARG_stop].u_int > 0) { + self->stop = args[ARG_stop].u_int; + } + + // Set TX/RX pins if configured. + if (args[ARG_tx].u_obj != mp_const_none) { + int tx = mp_hal_get_pin_obj(args[ARG_tx].u_obj); + if (!IS_VALID_TX(self->uart_id, tx)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad TX pin")); + } + self->tx = tx; + } + if (args[ARG_rx].u_obj != mp_const_none) { + int rx = mp_hal_get_pin_obj(args[ARG_rx].u_obj); + if (!IS_VALID_RX(self->uart_id, rx)) { + mp_raise_ValueError(MP_ERROR_TEXT("bad RX pin")); + } + self->rx = rx; + } + + // Initialise the UART peripheral if any arguments given, or it was not initialised previously. + if (n_args > 1 || n_kw > 0 || self->baudrate == 0) { + if (self->baudrate == 0) { + self->baudrate = DEFAULT_UART_BAUDRATE; + } + uart_init(self->uart, self->baudrate); + uart_set_format(self->uart, self->bits, self->stop, self->parity); + uart_set_fifo_enabled(self->uart, true); + gpio_set_function(self->tx, GPIO_FUNC_UART); + gpio_set_function(self->rx, GPIO_FUNC_UART); + } + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t machine_uart_any(mp_obj_t self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(uart_is_readable(self->uart)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_any_obj, machine_uart_any); + +STATIC mp_obj_t machine_uart_sendbreak(mp_obj_t self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + uart_set_break(self->uart, true); + mp_hal_delay_us(13000000 / self->baudrate + 1); + uart_set_break(self->uart, false); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_sendbreak_obj, machine_uart_sendbreak); + +STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_uart_any_obj) }, + + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, + + { MP_ROM_QSTR(MP_QSTR_sendbreak), MP_ROM_PTR(&machine_uart_sendbreak_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table); + +STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + // TODO support timeout + uint8_t *dest = buf_in; + for (size_t i = 0; i < size; ++i) { + while (!uart_is_readable(self->uart)) { + MICROPY_EVENT_POLL_HOOK + } + *dest++ = uart_get_hw(self->uart)->dr; + } + return size; +} + +STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + // TODO support timeout + const uint8_t *src = buf_in; + for (size_t i = 0; i < size; ++i) { + while (!uart_is_writable(self->uart)) { + MICROPY_EVENT_POLL_HOOK + } + uart_get_hw(self->uart)->dr = *src++; + } + return size; +} + +STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + machine_uart_obj_t *self = self_in; + mp_uint_t ret; + if (request == MP_STREAM_POLL) { + uintptr_t flags = arg; + ret = 0; + if ((flags & MP_STREAM_POLL_RD) && uart_is_readable(self->uart)) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR) && uart_is_writable(self->uart)) { + ret |= MP_STREAM_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +STATIC const mp_stream_p_t uart_stream_p = { + .read = machine_uart_read, + .write = machine_uart_write, + .ioctl = machine_uart_ioctl, + .is_text = false, +}; + +const mp_obj_type_t machine_uart_type = { + { &mp_type_type }, + .name = MP_QSTR_UART, + .print = machine_uart_print, + .make_new = machine_uart_make_new, + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &uart_stream_p, + .locals_dict = (mp_obj_dict_t *)&machine_uart_locals_dict, +}; diff --git a/ports/rp2/machine_wdt.c b/ports/rp2/machine_wdt.c new file mode 100644 index 000000000..38e059701 --- /dev/null +++ b/ports/rp2/machine_wdt.c @@ -0,0 +1,78 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "modmachine.h" + +#include "hardware/watchdog.h" + +typedef struct _machine_wdt_obj_t { + mp_obj_base_t base; +} machine_wdt_obj_t; + +STATIC const machine_wdt_obj_t machine_wdt = {{&machine_wdt_type}}; + +STATIC mp_obj_t machine_wdt_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_timeout }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout, MP_ARG_INT, {.u_int = 5000} }, + }; + + // Parse the arguments. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Verify the WDT id. + mp_int_t id = args[ARG_id].u_int; + if (id != 0) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("WDT(%d) doesn't exist"), id); + } + + // Start the watchdog (timeout is in milliseconds). + watchdog_enable(args[ARG_timeout].u_int, false); + + return MP_OBJ_FROM_PTR(&machine_wdt); +} + +STATIC mp_obj_t machine_wdt_feed(mp_obj_t self_in) { + (void)self_in; + watchdog_update(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_wdt_feed_obj, machine_wdt_feed); + +STATIC const mp_rom_map_elem_t machine_wdt_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_feed), MP_ROM_PTR(&machine_wdt_feed_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_wdt_locals_dict, machine_wdt_locals_dict_table); + +const mp_obj_type_t machine_wdt_type = { + { &mp_type_type }, + .name = MP_QSTR_WDT, + .make_new = machine_wdt_make_new, + .locals_dict = (mp_obj_dict_t *)&machine_wdt_locals_dict, +}; diff --git a/ports/rp2/main.c b/ports/rp2/main.c new file mode 100644 index 000000000..64218f97c --- /dev/null +++ b/ports/rp2/main.c @@ -0,0 +1,215 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/compile.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/stackctrl.h" +#include "lib/mp-readline/readline.h" +#include "lib/utils/gchelper.h" +#include "lib/utils/pyexec.h" +#include "tusb.h" +#include "uart.h" +#include "modmachine.h" +#include "modrp2.h" +#include "genhdr/mpversion.h" + +#include "pico/stdlib.h" +#include "pico/binary_info.h" +#include "hardware/rtc.h" +#include "hardware/structs/rosc.h" + +extern uint8_t __StackTop, __StackBottom; +static char gc_heap[192 * 1024]; + +// Embed version info in the binary in machine readable form +bi_decl(bi_program_version_string(MICROPY_GIT_TAG)); + +// Add a section to the picotool output similar to program features, but for frozen modules +// (it will aggregate BINARY_INFO_ID_MP_FROZEN binary info) +bi_decl(bi_program_feature_group_with_flags(BINARY_INFO_TAG_MICROPYTHON, + BINARY_INFO_ID_MP_FROZEN, "frozen modules", + BI_NAMED_GROUP_SEPARATE_COMMAS | BI_NAMED_GROUP_SORT_ALPHA)); + +int main(int argc, char **argv) { + #if MICROPY_HW_ENABLE_UART_REPL + bi_decl(bi_program_feature("UART REPL")) + setup_default_uart(); + mp_uart_init(); + #endif + + #if MICROPY_HW_ENABLE_USBDEV + bi_decl(bi_program_feature("USB REPL")) + tusb_init(); + #endif + + #if MICROPY_PY_THREAD + bi_decl(bi_program_feature("thread support")) + mp_thread_init(); + #endif + + // Start and initialise the RTC + datetime_t t = { + .year = 2021, + .month = 1, + .day = 1, + .dotw = 5, // 0 is Sunday, so 5 is Friday + .hour = 0, + .min = 0, + .sec = 0, + }; + rtc_init(); + rtc_set_datetime(&t); + + // Initialise stack extents and GC heap. + mp_stack_set_top(&__StackTop); + mp_stack_set_limit(&__StackTop - &__StackBottom - 256); + gc_init(&gc_heap[0], &gc_heap[MP_ARRAY_SIZE(gc_heap)]); + + for (;;) { + + // Initialise MicroPython runtime. + mp_init(); + mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_path), 0); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib)); + mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), 0); + + // Initialise sub-systems. + readline_init0(); + machine_pin_init(); + rp2_pio_init(); + + // Execute _boot.py to set up the filesystem. + pyexec_frozen_module("_boot.py"); + + // Execute user scripts. + int ret = pyexec_file_if_exists("boot.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { + ret = pyexec_file_if_exists("main.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + } + + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } + + soft_reset_exit: + mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); + rp2_pio_deinit(); + machine_pin_deinit(); + gc_sweep_all(); + mp_deinit(); + } + + return 0; +} + +void gc_collect(void) { + gc_collect_start(); + gc_helper_collect_regs_and_stack(); + #if MICROPY_PY_THREAD + mp_thread_gc_others(); + #endif + gc_collect_end(); +} + +void nlr_jump_fail(void *val) { + printf("FATAL: uncaught exception %p\n", val); + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(val)); + for (;;) { + __breakpoint(); + } +} + +#ifndef NDEBUG +void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { + printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); + panic("Assertion failed"); +} +#endif + +uint32_t rosc_random_u32(void) { + uint32_t value = 0; + for (size_t i = 0; i < 32; ++i) { + value = value << 1 | rosc_hw->randombit; + } + return value; +} + +const char rp2_help_text[] = + "Welcome to MicroPython!\n" + "\n" + "For online help please visit https://micropython.org/help/.\n" + "\n" + "For access to the hardware use the 'machine' module. RP2 specific commands\n" + "are in the 'rp2' module.\n" + "\n" + "Quick overview of some objects:\n" + " machine.Pin(pin) -- get a pin, eg machine.Pin(0)\n" + " machine.Pin(pin, m, [p]) -- get a pin and configure it for IO mode m, pull mode p\n" + " methods: init(..), value([v]), high(), low(), irq(handler)\n" + " machine.ADC(pin) -- make an analog object from a pin\n" + " methods: read_u16()\n" + " machine.PWM(pin) -- make a PWM object from a pin\n" + " methods: deinit(), freq([f]), duty_u16([d]), duty_ns([d])\n" + " machine.I2C(id) -- create an I2C object (id=0,1)\n" + " methods: readfrom(addr, buf, stop=True), writeto(addr, buf, stop=True)\n" + " readfrom_mem(addr, memaddr, arg), writeto_mem(addr, memaddr, arg)\n" + " machine.SPI(id, baudrate=1000000) -- create an SPI object (id=0,1)\n" + " methods: read(nbytes, write=0x00), write(buf), write_readinto(wr_buf, rd_buf)\n" + " machine.Timer(freq, callback) -- create a software timer object\n" + " eg: machine.Timer(freq=1, callback=lambda t:print(t))\n" + "\n" + "Pins are numbered 0-29, and 26-29 have ADC capabilities\n" + "Pin IO modes are: Pin.IN, Pin.OUT, Pin.ALT\n" + "Pin pull modes are: Pin.PULL_UP, Pin.PULL_DOWN\n" + "\n" + "Useful control commands:\n" + " CTRL-C -- interrupt a running program\n" + " CTRL-D -- on a blank line, do a soft reset of the board\n" + " CTRL-E -- on a blank line, enter paste mode\n" + "\n" + "For further help on a specific object, type help(obj)\n" + "For a list of available modules, type help('modules')\n" +; + diff --git a/ports/rp2/manifest.py b/ports/rp2/manifest.py new file mode 100644 index 000000000..a56d4b1b0 --- /dev/null +++ b/ports/rp2/manifest.py @@ -0,0 +1,3 @@ +freeze("modules") +freeze("$(MPY_DIR)/drivers/onewire") +include("$(MPY_DIR)/extmod/uasyncio/manifest.py") diff --git a/ports/rp2/memmap_mp.ld b/ports/rp2/memmap_mp.ld new file mode 100644 index 000000000..0dc96743e --- /dev/null +++ b/ports/rp2/memmap_mp.ld @@ -0,0 +1,253 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k + SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* Second stage bootloader is prepended to the image. It must be 256 bytes big + and checksummed. It is usually built by the boot_stage2 target + in the Raspberry Pi Pico SDK + */ + + .flash_begin : { + __flash_binary_start = .; + } > FLASH + + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH + + ASSERT(__boot2_end__ - __boot2_start__ == 256, + "ERROR: Pico second stage bootloader must be 256 bytes in size") + + /* The second stage will always enter the image at the start of .text. + The debugger will use the ELF entry point, which is the _entry_point + symbol if present, otherwise defaults to start of .text. + This can be used to transfer control back to the bootrom on debugger + launches only, to perform proper flash setup. + */ + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.reset)) + /* TODO revisit this now memset/memcpy/float in ROM */ + /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from + * FLASH ... we will include any thing excluded here in .data below by default */ + *(.init) + /* Change for MicroPython... excluse gc.c, parse.c, vm.c from flash */ + *(EXCLUDE_FILE(*libgcc.a: *libc.a: *lib_a-mem*.o *libm.a: *gc.c.obj *vm.c.obj *parse.c.obj) .text*) + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH + + .rodata : { + *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) + . = ALIGN(4); + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + /* End of .text-like segments */ + __etext = .; + + .ram_vector_table (COPY): { + *(.ram_vector_table) + } > RAM + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + } > RAM AT> FLASH + + .uninitialized_data (COPY): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + .bss : { + . = ALIGN(4); + __bss_start__ = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (COPY): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (COPY): + { + *(.stack*) + } > SCRATCH_Y + + .flash_end : { + __flash_binary_end = .; + } > FLASH + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") + /* todo assert on extra code */ +} + diff --git a/ports/rp2/micropy_extmod.cmake b/ports/rp2/micropy_extmod.cmake new file mode 100644 index 000000000..1e968bef6 --- /dev/null +++ b/ports/rp2/micropy_extmod.cmake @@ -0,0 +1,40 @@ +# CMake fragment for MicroPython extmod component + +set(SOURCE_EXTMOD + ${MPY_DIR}/extmod/machine_i2c.c + ${MPY_DIR}/extmod/machine_mem.c + ${MPY_DIR}/extmod/machine_pulse.c + ${MPY_DIR}/extmod/machine_signal.c + ${MPY_DIR}/extmod/machine_spi.c + ${MPY_DIR}/extmod/modbtree.c + ${MPY_DIR}/extmod/modframebuf.c + ${MPY_DIR}/extmod/modonewire.c + ${MPY_DIR}/extmod/moduasyncio.c + ${MPY_DIR}/extmod/modubinascii.c + ${MPY_DIR}/extmod/moducryptolib.c + ${MPY_DIR}/extmod/moductypes.c + ${MPY_DIR}/extmod/moduhashlib.c + ${MPY_DIR}/extmod/moduheapq.c + ${MPY_DIR}/extmod/modujson.c + ${MPY_DIR}/extmod/modurandom.c + ${MPY_DIR}/extmod/modure.c + ${MPY_DIR}/extmod/moduselect.c + ${MPY_DIR}/extmod/modussl_axtls.c + ${MPY_DIR}/extmod/modussl_mbedtls.c + ${MPY_DIR}/extmod/modutimeq.c + ${MPY_DIR}/extmod/moduwebsocket.c + ${MPY_DIR}/extmod/moduzlib.c + ${MPY_DIR}/extmod/modwebrepl.c + ${MPY_DIR}/extmod/uos_dupterm.c + ${MPY_DIR}/extmod/utime_mphal.c + ${MPY_DIR}/extmod/vfs.c + ${MPY_DIR}/extmod/vfs_blockdev.c + ${MPY_DIR}/extmod/vfs_fat.c + ${MPY_DIR}/extmod/vfs_fat_diskio.c + ${MPY_DIR}/extmod/vfs_fat_file.c + ${MPY_DIR}/extmod/vfs_lfs.c + ${MPY_DIR}/extmod/vfs_posix.c + ${MPY_DIR}/extmod/vfs_posix_file.c + ${MPY_DIR}/extmod/vfs_reader.c + ${MPY_DIR}/extmod/virtpin.c +) diff --git a/ports/rp2/micropy_py.cmake b/ports/rp2/micropy_py.cmake new file mode 100644 index 000000000..aeb7e2b9b --- /dev/null +++ b/ports/rp2/micropy_py.cmake @@ -0,0 +1,134 @@ +# CMake fragment for MicroPython core py component + +set(MPY_PY_DIR "${MPY_DIR}/py") +set(MPY_PY_QSTRDEFS "${MPY_PY_DIR}/qstrdefs.h") +set(MPY_GENHDR_DIR "${CMAKE_BINARY_DIR}/genhdr") +set(MPY_MPVERSION "${MPY_GENHDR_DIR}/mpversion.h") +set(MPY_MODULEDEFS "${MPY_GENHDR_DIR}/moduledefs.h") +set(MPY_QSTR_DEFS_LAST "${MPY_GENHDR_DIR}/qstr.i.last") +set(MPY_QSTR_DEFS_SPLIT "${MPY_GENHDR_DIR}/qstr.split") +set(MPY_QSTR_DEFS_COLLECTED "${MPY_GENHDR_DIR}/qstrdefs.collected.h") +set(MPY_QSTR_DEFS_PREPROCESSED "${MPY_GENHDR_DIR}/qstrdefs.preprocessed.h") +set(MPY_QSTR_DEFS_GENERATED "${MPY_GENHDR_DIR}/qstrdefs.generated.h") +set(MPY_FROZEN_CONTENT "${CMAKE_BINARY_DIR}/frozen_content.c") + +# All py/ source files +set(SOURCE_PY + ${MPY_PY_DIR}/argcheck.c + ${MPY_PY_DIR}/asmarm.c + ${MPY_PY_DIR}/asmbase.c + ${MPY_PY_DIR}/asmthumb.c + ${MPY_PY_DIR}/asmx64.c + ${MPY_PY_DIR}/asmx86.c + ${MPY_PY_DIR}/asmxtensa.c + ${MPY_PY_DIR}/bc.c + ${MPY_PY_DIR}/binary.c + ${MPY_PY_DIR}/builtinevex.c + ${MPY_PY_DIR}/builtinhelp.c + ${MPY_PY_DIR}/builtinimport.c + ${MPY_PY_DIR}/compile.c + ${MPY_PY_DIR}/emitbc.c + ${MPY_PY_DIR}/emitcommon.c + ${MPY_PY_DIR}/emitglue.c + ${MPY_PY_DIR}/emitinlinethumb.c + ${MPY_PY_DIR}/emitinlinextensa.c + ${MPY_PY_DIR}/emitnarm.c + ${MPY_PY_DIR}/emitnthumb.c + ${MPY_PY_DIR}/emitnx64.c + ${MPY_PY_DIR}/emitnx86.c + ${MPY_PY_DIR}/emitnxtensa.c + ${MPY_PY_DIR}/emitnxtensawin.c + ${MPY_PY_DIR}/formatfloat.c + ${MPY_PY_DIR}/frozenmod.c + ${MPY_PY_DIR}/gc.c + ${MPY_PY_DIR}/lexer.c + ${MPY_PY_DIR}/malloc.c + ${MPY_PY_DIR}/map.c + ${MPY_PY_DIR}/modarray.c + ${MPY_PY_DIR}/modbuiltins.c + ${MPY_PY_DIR}/modcmath.c + ${MPY_PY_DIR}/modcollections.c + ${MPY_PY_DIR}/modgc.c + ${MPY_PY_DIR}/modio.c + ${MPY_PY_DIR}/modmath.c + ${MPY_PY_DIR}/modmicropython.c + ${MPY_PY_DIR}/modstruct.c + ${MPY_PY_DIR}/modsys.c + ${MPY_PY_DIR}/modthread.c + ${MPY_PY_DIR}/moduerrno.c + ${MPY_PY_DIR}/mpprint.c + ${MPY_PY_DIR}/mpstate.c + ${MPY_PY_DIR}/mpz.c + ${MPY_PY_DIR}/nativeglue.c + ${MPY_PY_DIR}/nlr.c + ${MPY_PY_DIR}/nlrpowerpc.c + ${MPY_PY_DIR}/nlrsetjmp.c + ${MPY_PY_DIR}/nlrthumb.c + ${MPY_PY_DIR}/nlrx64.c + ${MPY_PY_DIR}/nlrx86.c + ${MPY_PY_DIR}/nlrxtensa.c + ${MPY_PY_DIR}/obj.c + ${MPY_PY_DIR}/objarray.c + ${MPY_PY_DIR}/objattrtuple.c + ${MPY_PY_DIR}/objbool.c + ${MPY_PY_DIR}/objboundmeth.c + ${MPY_PY_DIR}/objcell.c + ${MPY_PY_DIR}/objclosure.c + ${MPY_PY_DIR}/objcomplex.c + ${MPY_PY_DIR}/objdeque.c + ${MPY_PY_DIR}/objdict.c + ${MPY_PY_DIR}/objenumerate.c + ${MPY_PY_DIR}/objexcept.c + ${MPY_PY_DIR}/objfilter.c + ${MPY_PY_DIR}/objfloat.c + ${MPY_PY_DIR}/objfun.c + ${MPY_PY_DIR}/objgenerator.c + ${MPY_PY_DIR}/objgetitemiter.c + ${MPY_PY_DIR}/objint.c + ${MPY_PY_DIR}/objint_longlong.c + ${MPY_PY_DIR}/objint_mpz.c + ${MPY_PY_DIR}/objlist.c + ${MPY_PY_DIR}/objmap.c + ${MPY_PY_DIR}/objmodule.c + ${MPY_PY_DIR}/objnamedtuple.c + ${MPY_PY_DIR}/objnone.c + ${MPY_PY_DIR}/objobject.c + ${MPY_PY_DIR}/objpolyiter.c + ${MPY_PY_DIR}/objproperty.c + ${MPY_PY_DIR}/objrange.c + ${MPY_PY_DIR}/objreversed.c + ${MPY_PY_DIR}/objset.c + ${MPY_PY_DIR}/objsingleton.c + ${MPY_PY_DIR}/objslice.c + ${MPY_PY_DIR}/objstr.c + ${MPY_PY_DIR}/objstringio.c + ${MPY_PY_DIR}/objstrunicode.c + ${MPY_PY_DIR}/objtuple.c + ${MPY_PY_DIR}/objtype.c + ${MPY_PY_DIR}/objzip.c + ${MPY_PY_DIR}/opmethods.c + ${MPY_PY_DIR}/pairheap.c + ${MPY_PY_DIR}/parse.c + ${MPY_PY_DIR}/parsenum.c + ${MPY_PY_DIR}/parsenumbase.c + ${MPY_PY_DIR}/persistentcode.c + ${MPY_PY_DIR}/profile.c + ${MPY_PY_DIR}/pystack.c + ${MPY_PY_DIR}/qstr.c + ${MPY_PY_DIR}/reader.c + ${MPY_PY_DIR}/repl.c + ${MPY_PY_DIR}/ringbuf.c + ${MPY_PY_DIR}/runtime.c + ${MPY_PY_DIR}/runtime_utils.c + ${MPY_PY_DIR}/scheduler.c + ${MPY_PY_DIR}/scope.c + ${MPY_PY_DIR}/sequence.c + ${MPY_PY_DIR}/showbc.c + ${MPY_PY_DIR}/smallint.c + ${MPY_PY_DIR}/stackctrl.c + ${MPY_PY_DIR}/stream.c + ${MPY_PY_DIR}/unicode.c + ${MPY_PY_DIR}/vm.c + ${MPY_PY_DIR}/vstr.c + ${MPY_PY_DIR}/warning.c +) diff --git a/ports/rp2/micropy_rules.cmake b/ports/rp2/micropy_rules.cmake new file mode 100644 index 000000000..9eee4ac14 --- /dev/null +++ b/ports/rp2/micropy_rules.cmake @@ -0,0 +1,90 @@ +# CMake fragment for MicroPython rules + +target_sources(${MICROPYTHON_TARGET} PRIVATE + ${MPY_MPVERSION} + ${MPY_QSTR_DEFS_GENERATED} + ${MPY_FROZEN_CONTENT} +) + +# Command to force the build of another command + +add_custom_command( + OUTPUT FORCE_BUILD + COMMENT "" + COMMAND echo -n +) + +# Generate mpversion.h + +add_custom_command( + OUTPUT ${MPY_MPVERSION} + COMMAND ${CMAKE_COMMAND} -E make_directory ${MPY_GENHDR_DIR} + COMMAND python3 ${MPY_DIR}/py/makeversionhdr.py ${MPY_MPVERSION} + DEPENDS FORCE_BUILD +) + +# Generate moduledefs.h + +add_custom_command( + OUTPUT ${MPY_MODULEDEFS} + COMMAND python3 ${MPY_PY_DIR}/makemoduledefs.py --vpath="/" ${SOURCE_QSTR} > ${MPY_MODULEDEFS} + DEPENDS ${MPY_MPVERSION} + ${SOURCE_QSTR} +) + +# Generate qstrs + +# If any of the dependencies in this rule change then the C-preprocessor step must be run. +# It only needs to be passed the list of SOURCE_QSTR files that have changed since it was +# last run, but it looks like it's not possible to specify that with cmake. +add_custom_command( + OUTPUT ${MPY_QSTR_DEFS_LAST} + COMMAND ${CMAKE_C_COMPILER} -E \$\(C_INCLUDES\) \$\(C_FLAGS\) -DNO_QSTR ${SOURCE_QSTR} > ${MPY_GENHDR_DIR}/qstr.i.last + DEPENDS ${MPY_MODULEDEFS} + ${SOURCE_QSTR} + VERBATIM +) + +add_custom_command( + OUTPUT ${MPY_QSTR_DEFS_SPLIT} + COMMAND python3 ${MPY_DIR}/py/makeqstrdefs.py split qstr ${MPY_GENHDR_DIR}/qstr.i.last ${MPY_GENHDR_DIR}/qstr _ + COMMAND touch ${MPY_QSTR_DEFS_SPLIT} + DEPENDS ${MPY_QSTR_DEFS_LAST} + VERBATIM +) + +add_custom_command( + OUTPUT ${MPY_QSTR_DEFS_COLLECTED} + COMMAND python3 ${MPY_DIR}/py/makeqstrdefs.py cat qstr _ ${MPY_GENHDR_DIR}/qstr ${MPY_QSTR_DEFS_COLLECTED} + DEPENDS ${MPY_QSTR_DEFS_SPLIT} + VERBATIM +) + +add_custom_command( + OUTPUT ${MPY_QSTR_DEFS_PREPROCESSED} + COMMAND cat ${MPY_PY_QSTRDEFS} ${MPY_QSTR_DEFS} ${MPY_QSTR_DEFS_COLLECTED} | sed "s/^Q(.*)/\"&\"/" | ${CMAKE_C_COMPILER} -E \$\(C_INCLUDES\) \$\(C_FLAGS\) - | sed "s/^\\\"\\(Q(.*)\\)\\\"/\\1/" > ${MPY_QSTR_DEFS_PREPROCESSED} + DEPENDS ${MPY_PY_QSTRDEFS} ${MPY_QSTR_DEFS} ${MPY_QSTR_DEFS_COLLECTED} + VERBATIM +) + +add_custom_command( + OUTPUT ${MPY_QSTR_DEFS_GENERATED} + COMMAND python3 ${MPY_PY_DIR}/makeqstrdata.py ${MPY_QSTR_DEFS_PREPROCESSED} > ${MPY_QSTR_DEFS_GENERATED} + DEPENDS ${MPY_QSTR_DEFS_PREPROCESSED} + VERBATIM +) + +# Build frozen code + +target_compile_options(${MICROPYTHON_TARGET} PUBLIC + -DMICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool + -DMICROPY_MODULE_FROZEN_MPY=\(1\) +) + +add_custom_command( + OUTPUT ${MPY_FROZEN_CONTENT} + COMMAND python3 ${MPY_DIR}/tools/makemanifest.py -o ${MPY_FROZEN_CONTENT} -v "MPY_DIR=${MPY_DIR}" -v "PORT_DIR=${PROJECT_SOURCE_DIR}" -b "${CMAKE_BINARY_DIR}" -f${MPY_CROSS_FLAGS} ${FROZEN_MANIFEST} + DEPENDS FORCE_BUILD + ${MPY_QSTR_DEFS_GENERATED} + VERBATIM +) diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c new file mode 100644 index 000000000..dbaafabe8 --- /dev/null +++ b/ports/rp2/modmachine.c @@ -0,0 +1,166 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" +#include "lib/utils/pyexec.h" +#include "extmod/machine_i2c.h" +#include "extmod/machine_mem.h" +#include "extmod/machine_pulse.h" +#include "extmod/machine_spi.h" + +#include "modmachine.h" +#include "hardware/clocks.h" +#include "hardware/watchdog.h" +#include "pico/bootrom.h" +#include "pico/unique_id.h" + +#define RP2_RESET_PWRON (1) +#define RP2_RESET_WDT (3) + +STATIC mp_obj_t machine_unique_id(void) { + pico_unique_board_id_t id; + pico_get_unique_board_id(&id); + return mp_obj_new_bytes(id.id, sizeof(id.id)); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); + +STATIC mp_obj_t machine_soft_reset(void) { + pyexec_system_exit = PYEXEC_FORCED_EXIT; + mp_raise_type(&mp_type_SystemExit); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset); + +STATIC mp_obj_t machine_reset(void) { + watchdog_reboot(0, SRAM_END, 0); + for (;;) { + __wfi(); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset); + +STATIC mp_obj_t machine_reset_cause(void) { + int reset_cause; + if (watchdog_caused_reboot()) { + reset_cause = RP2_RESET_WDT; + } else { + reset_cause = RP2_RESET_PWRON; + } + return MP_OBJ_NEW_SMALL_INT(reset_cause); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause); + +STATIC mp_obj_t machine_bootloader(void) { + reset_usb_boot(0, 0); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_bootloader_obj, machine_bootloader); + +STATIC mp_obj_t machine_freq(void) { + return MP_OBJ_NEW_SMALL_INT(clock_get_hz(clk_sys)); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_freq_obj, machine_freq); + +STATIC mp_obj_t machine_idle(void) { + best_effort_wfe_or_timeout(make_timeout_time_ms(1)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); + +STATIC mp_obj_t machine_lightsleep(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + for (;;) { + MICROPY_EVENT_POLL_HOOK + } + } else { + mp_hal_delay_ms(mp_obj_get_int(args[0])); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_lightsleep_obj, 0, 1, machine_lightsleep); + +STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *args) { + machine_lightsleep(n_args, args); + return machine_reset(); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_deepsleep_obj, 0, 1, machine_deepsleep); + +STATIC mp_obj_t machine_disable_irq(void) { + uint32_t state = MICROPY_BEGIN_ATOMIC_SECTION(); + return mp_obj_new_int(state); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq); + +STATIC mp_obj_t machine_enable_irq(mp_obj_t state_in) { + uint32_t state = mp_obj_get_int(state_in); + MICROPY_END_ATOMIC_SECTION(state); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(machine_enable_irq_obj, machine_enable_irq); + +STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) }, + { MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) }, + { MP_ROM_QSTR(MP_QSTR_soft_reset), MP_ROM_PTR(&machine_soft_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset_cause), MP_ROM_PTR(&machine_reset_cause_obj) }, + { MP_ROM_QSTR(MP_QSTR_bootloader), MP_ROM_PTR(&machine_bootloader_obj) }, + { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) }, + + { MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&machine_idle_obj) }, + { MP_ROM_QSTR(MP_QSTR_lightsleep), MP_ROM_PTR(&machine_lightsleep_obj) }, + { MP_ROM_QSTR(MP_QSTR_deepsleep), MP_ROM_PTR(&machine_deepsleep_obj) }, + + { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, + + { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, + + { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) }, + + { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hw_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, + { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, + { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_spi_type) }, + { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, + { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, + { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&machine_wdt_type) }, + + { MP_ROM_QSTR(MP_QSTR_PWRON_RESET), MP_ROM_INT(RP2_RESET_PWRON) }, + { MP_ROM_QSTR(MP_QSTR_WDT_RESET), MP_ROM_INT(RP2_RESET_WDT) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); + +const mp_obj_module_t mp_module_machine = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&machine_module_globals, +}; diff --git a/ports/rp2/modmachine.h b/ports/rp2/modmachine.h new file mode 100644 index 000000000..d09c83aee --- /dev/null +++ b/ports/rp2/modmachine.h @@ -0,0 +1,18 @@ +#ifndef MICROPY_INCLUDED_RP2_MODMACHINE_H +#define MICROPY_INCLUDED_RP2_MODMACHINE_H + +#include "py/obj.h" + +extern const mp_obj_type_t machine_adc_type; +extern const mp_obj_type_t machine_hw_i2c_type; +extern const mp_obj_type_t machine_pin_type; +extern const mp_obj_type_t machine_pwm_type; +extern const mp_obj_type_t machine_spi_type; +extern const mp_obj_type_t machine_timer_type; +extern const mp_obj_type_t machine_uart_type; +extern const mp_obj_type_t machine_wdt_type; + +void machine_pin_init(void); +void machine_pin_deinit(void); + +#endif // MICROPY_INCLUDED_RP2_MODMACHINE_H diff --git a/ports/rp2/modrp2.c b/ports/rp2/modrp2.c new file mode 100644 index 000000000..8009fa33f --- /dev/null +++ b/ports/rp2/modrp2.c @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "modrp2.h" + +STATIC const mp_rom_map_elem_t rp2_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_rp2) }, + { MP_ROM_QSTR(MP_QSTR_Flash), MP_ROM_PTR(&rp2_flash_type) }, + { MP_ROM_QSTR(MP_QSTR_PIO), MP_ROM_PTR(&rp2_pio_type) }, + { MP_ROM_QSTR(MP_QSTR_StateMachine), MP_ROM_PTR(&rp2_state_machine_type) }, +}; +STATIC MP_DEFINE_CONST_DICT(rp2_module_globals, rp2_module_globals_table); + +const mp_obj_module_t mp_module_rp2 = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&rp2_module_globals, +}; diff --git a/ports/rp2/modrp2.h b/ports/rp2/modrp2.h new file mode 100644 index 000000000..805c785f2 --- /dev/null +++ b/ports/rp2/modrp2.h @@ -0,0 +1,38 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_RP2_MODRP2_H +#define MICROPY_INCLUDED_RP2_MODRP2_H + +#include "py/obj.h" + +extern const mp_obj_type_t rp2_flash_type; +extern const mp_obj_type_t rp2_pio_type; +extern const mp_obj_type_t rp2_state_machine_type; + +void rp2_pio_init(void); +void rp2_pio_deinit(void); + +#endif // MICROPY_INCLUDED_RP2_MODRP2_H diff --git a/ports/rp2/modules/_boot.py b/ports/rp2/modules/_boot.py new file mode 100644 index 000000000..099e5aba0 --- /dev/null +++ b/ports/rp2/modules/_boot.py @@ -0,0 +1,15 @@ +import os +import machine, rp2 + + +# Try to mount the filesystem, and format the flash if it doesn't exist. +# Note: the flash requires the programming size to be aligned to 256 bytes. +bdev = rp2.Flash() +try: + vfs = os.VfsLfs2(bdev, progsize=256) +except: + os.VfsLfs2.mkfs(bdev, progsize=256) + vfs = os.VfsLfs2(bdev, progsize=256) +os.mount(vfs, "/") + +del os, bdev, vfs diff --git a/ports/rp2/modules/rp2.py b/ports/rp2/modules/rp2.py new file mode 100644 index 000000000..a3adcdc51 --- /dev/null +++ b/ports/rp2/modules/rp2.py @@ -0,0 +1,294 @@ +# rp2 module: uses C code from _rp2, plus asm_pio decorator implemented in Python. +# MIT license; Copyright (c) 2020-2021 Damien P. George + +from _rp2 import * +from micropython import const + +_PROG_DATA = const(0) +_PROG_OFFSET_PIO0 = const(1) +_PROG_OFFSET_PIO1 = const(2) +_PROG_EXECCTRL = const(3) +_PROG_SHIFTCTRL = const(4) +_PROG_OUT_PINS = const(5) +_PROG_SET_PINS = const(6) +_PROG_SIDESET_PINS = const(7) +_PROG_MAX_FIELDS = const(8) + + +class PIOASMError(Exception): + pass + + +class PIOASMEmit: + def __init__( + self, + *, + out_init=None, + set_init=None, + sideset_init=None, + in_shiftdir=0, + out_shiftdir=0, + autopush=False, + autopull=False, + push_thresh=32, + pull_thresh=32 + ): + from array import array + + self.labels = {} + execctrl = 0 + shiftctrl = ( + (pull_thresh & 0x1F) << 25 + | (push_thresh & 0x1F) << 20 + | out_shiftdir << 19 + | in_shiftdir << 18 + | autopull << 17 + | autopush << 16 + ) + self.prog = [array("H"), -1, -1, execctrl, shiftctrl, out_init, set_init, sideset_init] + + self.wrap_used = False + + if sideset_init is None: + self.sideset_count = 0 + elif isinstance(sideset_init, int): + self.sideset_count = 1 + else: + self.sideset_count = len(sideset_init) + + def start_pass(self, pass_): + if pass_ == 1: + if not self.wrap_used and self.num_instr: + self.wrap() + self.delay_max = 31 + if self.sideset_count: + self.sideset_opt = self.num_sideset != self.num_instr + if self.sideset_opt: + self.prog[_PROG_EXECCTRL] |= 1 << 30 + self.sideset_count += 1 + self.delay_max >>= self.sideset_count + self.pass_ = pass_ + self.num_instr = 0 + self.num_sideset = 0 + + def __getitem__(self, key): + return self.delay(key) + + def delay(self, delay): + if self.pass_ > 0: + if delay > self.delay_max: + raise PIOASMError("delay too large") + self.prog[_PROG_DATA][-1] |= delay << 8 + return self + + def side(self, value): + self.num_sideset += 1 + if self.pass_ > 0: + set_bit = 13 - self.sideset_count + self.prog[_PROG_DATA][-1] |= self.sideset_opt << 12 | value << set_bit + return self + + def wrap_target(self): + self.prog[_PROG_EXECCTRL] |= self.num_instr << 7 + + def wrap(self): + assert self.num_instr + self.prog[_PROG_EXECCTRL] |= (self.num_instr - 1) << 12 + self.wrap_used = True + + def label(self, label): + if self.pass_ == 0: + if label in self.labels: + raise PIOASMError("duplicate label {}".format(label)) + self.labels[label] = self.num_instr + + def word(self, instr, label=None): + self.num_instr += 1 + if self.pass_ > 0: + if label is None: + label = 0 + else: + if not label in self.labels: + raise PIOASMError("unknown label {}".format(label)) + label = self.labels[label] + self.prog[_PROG_DATA].append(instr | label) + return self + + def nop(self): + return self.word(0xA042) + + def jmp(self, cond, label=None): + if label is None: + label = cond + cond = 0 # always + return self.word(0x0000 | cond << 5, label) + + def wait(self, polarity, src, index): + if src == 6: + src = 1 # "pin" + elif src != 0: + src = 2 # "irq" + return self.word(0x2000 | polarity << 7 | src << 5 | index) + + def in_(self, src, data): + if not 0 < data <= 32: + raise PIOASMError("invalid bit count {}".format(data)) + return self.word(0x4000 | src << 5 | data & 0x1F) + + def out(self, dest, data): + if dest == 8: + dest = 7 # exec + if not 0 < data <= 32: + raise PIOASMError("invalid bit count {}".format(data)) + return self.word(0x6000 | dest << 5 | data & 0x1F) + + def push(self, value=0, value2=0): + value |= value2 + if not value & 1: + value |= 0x20 # block by default + return self.word(0x8000 | (value & 0x60)) + + def pull(self, value=0, value2=0): + value |= value2 + if not value & 1: + value |= 0x20 # block by default + return self.word(0x8080 | (value & 0x60)) + + def mov(self, dest, src): + if dest == 8: + dest = 4 # exec + return self.word(0xA000 | dest << 5 | src) + + def irq(self, mod, index=None): + if index is None: + index = mod + mod = 0 # no modifiers + return self.word(0xC000 | (mod & 0x60) | index) + + def set(self, dest, data): + return self.word(0xE000 | dest << 5 | data) + + +_pio_funcs = { + # source constants for wait + "gpio": 0, + # "pin": see below, translated to 1 + # "irq": see below function, translated to 2 + # source/dest constants for in_, out, mov, set + "pins": 0, + "x": 1, + "y": 2, + "null": 3, + "pindirs": 4, + "pc": 5, + "status": 5, + "isr": 6, + "osr": 7, + "exec": 8, # translated to 4 for mov, 7 for out + # operation functions for mov's src + "invert": lambda x: x | 0x08, + "reverse": lambda x: x | 0x10, + # jmp condition constants + "not_x": 1, + "x_dec": 2, + "not_y": 3, + "y_dec": 4, + "x_not_y": 5, + "pin": 6, + "not_osre": 7, + # constants for push, pull + "noblock": 0x01, + "block": 0x21, + "iffull": 0x40, + "ifempty": 0x40, + # constants and modifiers for irq + # "noblock": see above + # "block": see above + "clear": 0x40, + "rel": lambda x: x | 0x10, + # functions + "wrap_target": None, + "wrap": None, + "label": None, + "word": None, + "nop": None, + "jmp": None, + "wait": None, + "in_": None, + "out": None, + "push": None, + "pull": None, + "mov": None, + "irq": None, + "set": None, +} + + +def asm_pio(**kw): + emit = PIOASMEmit(**kw) + + def dec(f): + nonlocal emit + + gl = _pio_funcs + gl["wrap_target"] = emit.wrap_target + gl["wrap"] = emit.wrap + gl["label"] = emit.label + gl["word"] = emit.word + gl["nop"] = emit.nop + gl["jmp"] = emit.jmp + gl["wait"] = emit.wait + gl["in_"] = emit.in_ + gl["out"] = emit.out + gl["push"] = emit.push + gl["pull"] = emit.pull + gl["mov"] = emit.mov + gl["irq"] = emit.irq + gl["set"] = emit.set + + old_gl = f.__globals__.copy() + f.__globals__.clear() + f.__globals__.update(gl) + + emit.start_pass(0) + f() + + emit.start_pass(1) + f() + + f.__globals__.clear() + f.__globals__.update(old_gl) + + return emit.prog + + return dec + + +# sideset_count is inclusive of enable bit +def asm_pio_encode(instr, sideset_count): + emit = PIOASMEmit() + emit.delay_max = 31 + emit.sideset_count = sideset_count + if emit.sideset_count: + emit.delay_max >>= emit.sideset_count + emit.pass_ = 1 + emit.num_instr = 0 + emit.num_sideset = 0 + + gl = _pio_funcs + gl["nop"] = emit.nop + # gl["jmp"] = emit.jmp currently not supported + gl["wait"] = emit.wait + gl["in_"] = emit.in_ + gl["out"] = emit.out + gl["push"] = emit.push + gl["pull"] = emit.pull + gl["mov"] = emit.mov + gl["irq"] = emit.irq + gl["set"] = emit.set + + exec(instr, gl) + + if len(emit.prog[_PROG_DATA]) != 1: + raise PIOASMError("expecting exactly 1 instruction") + return emit.prog[_PROG_DATA][0] diff --git a/ports/rp2/moduos.c b/ports/rp2/moduos.c new file mode 100644 index 000000000..7ee662b5a --- /dev/null +++ b/ports/rp2/moduos.c @@ -0,0 +1,103 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/objstr.h" +#include "py/runtime.h" +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "extmod/vfs_lfs.h" +#include "genhdr/mpversion.h" + +STATIC const qstr os_uname_info_fields[] = { + MP_QSTR_sysname, MP_QSTR_nodename, + MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine +}; +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, MICROPY_PY_SYS_PLATFORM); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, MICROPY_PY_SYS_PLATFORM); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE " (" MICROPY_BUILD_TYPE ")"); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); + +STATIC MP_DEFINE_ATTRTUPLE( + os_uname_info_obj, + os_uname_info_fields, + 5, + (mp_obj_t)&os_uname_info_sysname_obj, + (mp_obj_t)&os_uname_info_nodename_obj, + (mp_obj_t)&os_uname_info_release_obj, + (mp_obj_t)&os_uname_info_version_obj, + (mp_obj_t)&os_uname_info_machine_obj + ); + +STATIC mp_obj_t os_uname(void) { + return (mp_obj_t)&os_uname_info_obj; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); + +STATIC const mp_rom_map_elem_t os_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, + + { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, + + #if MICROPY_VFS + { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, + { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, + { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, + #endif + + // The following are MicroPython extensions. + + #if MICROPY_PY_OS_DUPTERM + { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, + #endif + + #if MICROPY_VFS + { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, + #if MICROPY_VFS_FAT + { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, + #endif + #if MICROPY_VFS_LFS1 + { MP_ROM_QSTR(MP_QSTR_VfsLfs1), MP_ROM_PTR(&mp_type_vfs_lfs1) }, + #endif + #if MICROPY_VFS_LFS2 + { MP_ROM_QSTR(MP_QSTR_VfsLfs2), MP_ROM_PTR(&mp_type_vfs_lfs2) }, + #endif + #endif +}; +STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); + +const mp_obj_module_t mp_module_uos = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&os_module_globals, +}; diff --git a/ports/rp2/modutime.c b/ports/rp2/modutime.c new file mode 100644 index 000000000..4835a0ee1 --- /dev/null +++ b/ports/rp2/modutime.c @@ -0,0 +1,127 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "lib/timeutils/timeutils.h" +#include "extmod/utime_mphal.h" +#include "hardware/rtc.h" + +// localtime([secs]) +// Convert a time expressed in seconds since the Epoch into an 8-tuple which +// contains: (year, month, mday, hour, minute, second, weekday, yearday) +// If secs is not provided or None, then the current time from is used. +STATIC mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { + if (n_args == 0 || args[0] == mp_const_none) { + // Get current date and time. + datetime_t t; + rtc_get_datetime(&t); + mp_obj_t tuple[8] = { + mp_obj_new_int(t.year), + mp_obj_new_int(t.month), + mp_obj_new_int(t.day), + mp_obj_new_int(t.hour), + mp_obj_new_int(t.min), + mp_obj_new_int(t.sec), + mp_obj_new_int((t.dotw + 6) % 7), // convert 0=Sunday to 6=Sunday + mp_obj_new_int(timeutils_year_day(t.year, t.month, t.day)), + }; + return mp_obj_new_tuple(8, tuple); + } else { + // Convert given seconds to tuple. + mp_int_t seconds = mp_obj_get_int(args[0]); + timeutils_struct_time_t tm; + timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); + mp_obj_t tuple[8] = { + tuple[0] = mp_obj_new_int(tm.tm_year), + tuple[1] = mp_obj_new_int(tm.tm_mon), + tuple[2] = mp_obj_new_int(tm.tm_mday), + tuple[3] = mp_obj_new_int(tm.tm_hour), + tuple[4] = mp_obj_new_int(tm.tm_min), + tuple[5] = mp_obj_new_int(tm.tm_sec), + tuple[6] = mp_obj_new_int(tm.tm_wday), + tuple[7] = mp_obj_new_int(tm.tm_yday), + }; + return mp_obj_new_tuple(8, tuple); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_localtime_obj, 0, 1, time_localtime); + +// mktime() +// 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 the Epoch. +STATIC mp_obj_t time_mktime(mp_obj_t tuple) { + size_t len; + mp_obj_t *elem; + mp_obj_get_array(tuple, &len, &elem); + + // localtime generates a tuple of len 8. CPython uses 9, so we accept both. + if (len < 8 || len > 9) { + mp_raise_TypeError(MP_ERROR_TEXT("mktime needs a tuple of length 8 or 9")); + } + + return mp_obj_new_int_from_uint(timeutils_mktime(mp_obj_get_int(elem[0]), + mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]), mp_obj_get_int(elem[3]), + mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5]))); +} +MP_DEFINE_CONST_FUN_OBJ_1(time_mktime_obj, time_mktime); + +// time() +// Return the number of seconds since the Epoch. +STATIC mp_obj_t time_time(void) { + datetime_t t; + rtc_get_datetime(&t); + return mp_obj_new_int_from_ull(timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.min, t.sec)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); + +STATIC const mp_rom_map_elem_t mp_module_time_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) }, + + { MP_ROM_QSTR(MP_QSTR_gmtime), MP_ROM_PTR(&time_localtime_obj) }, + { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&time_localtime_obj) }, + { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&time_mktime_obj) }, + + { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&time_time_obj) }, + { MP_ROM_QSTR(MP_QSTR_time_ns), MP_ROM_PTR(&mp_utime_time_ns_obj) }, + + { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_utime_sleep_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_us), MP_ROM_PTR(&mp_utime_sleep_us_obj) }, + + { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&mp_utime_ticks_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_us), MP_ROM_PTR(&mp_utime_ticks_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_cpu), MP_ROM_PTR(&mp_utime_ticks_cpu_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_add), MP_ROM_PTR(&mp_utime_ticks_add_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_utime_ticks_diff_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_time_globals, mp_module_time_globals_table); + +const mp_obj_module_t mp_module_utime = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_time_globals, +}; diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h new file mode 100644 index 000000000..b3214acfc --- /dev/null +++ b/ports/rp2/mpconfigport.h @@ -0,0 +1,198 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// Options controlling how MicroPython is built, overriding defaults in py/mpconfig.h + +#include +#include "hardware/spi.h" +#include "hardware/sync.h" +#include "pico/binary_info.h" + +// Board and hardware specific configuration +#define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico" +#define MICROPY_HW_MCU_NAME "RP2040" +#define MICROPY_HW_ENABLE_UART_REPL (0) // useful if there is no USB +#define MICROPY_HW_ENABLE_USBDEV (1) + +// Memory allocation policies +#define MICROPY_GC_STACK_ENTRY_TYPE uint16_t +#define MICROPY_ALLOC_PATH_MAX (128) +#define MICROPY_QSTR_BYTES_IN_HASH (1) + +// MicroPython emitters +#define MICROPY_PERSISTENT_CODE_LOAD (1) +#define MICROPY_EMIT_THUMB (1) +#define MICROPY_EMIT_THUMB_ARMV7M (0) +#define MICROPY_EMIT_INLINE_THUMB (1) +#define MICROPY_EMIT_INLINE_THUMB_FLOAT (0) +#define MICROPY_EMIT_INLINE_THUMB_ARMV7M (0) + +// Python internal features +#define MICROPY_READER_VFS (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_ENABLE_FINALISER (1) +#define MICROPY_STACK_CHECK (1) +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) +#define MICROPY_KBD_EXCEPTION (1) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_ENABLE_SOURCE_LINE (1) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#define MICROPY_MODULE_BUILTIN_INIT (1) +#define MICROPY_MODULE_WEAK_LINKS (1) +#define MICROPY_CAN_OVERRIDE_BUILTINS (1) +#define MICROPY_ENABLE_SCHEDULER (1) +#define MICROPY_SCHEDULER_DEPTH (8) + +// Fine control over Python builtins, classes, modules, etc +#define MICROPY_PY_FUNCTION_ATTRS (1) +#define MICROPY_PY_BUILTINS_STR_UNICODE (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BUILTINS_ROUND_INT (1) +#define MICROPY_PY_ALL_SPECIAL_METHODS (1) +#define MICROPY_PY_BUILTINS_INPUT (1) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_HELP_TEXT rp2_help_text +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#define MICROPY_PY___FILE__ (0) +#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) +#define MICROPY_PY_IO_IOBASE (1) +#define MICROPY_PY_IO_FILEIO (1) +#define MICROPY_PY_SYS_MAXSIZE (1) +#define MICROPY_PY_SYS_STDFILES (1) +#define MICROPY_PY_SYS_STDIO_BUFFER (1) +#define MICROPY_PY_SYS_PLATFORM "rp2" +#define MICROPY_PY_THREAD (1) +#define MICROPY_PY_THREAD_GIL (0) + +// Extended modules +#define MICROPY_EPOCH_IS_1970 (1) +#define MICROPY_PY_UASYNCIO (1) +#define MICROPY_PY_UCTYPES (1) +#define MICROPY_PY_UZLIB (1) +#define MICROPY_PY_UJSON (1) +#define MICROPY_PY_URE (1) +#define MICROPY_PY_URE_MATCH_GROUPS (1) +#define MICROPY_PY_URE_MATCH_SPAN_START_END (1) +#define MICROPY_PY_URE_SUB (1) +#define MICROPY_PY_UHASHLIB (1) +#define MICROPY_PY_UBINASCII (1) +#define MICROPY_PY_UBINASCII_CRC32 (1) +#define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_PY_URANDOM (1) +#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) +#define MICROPY_PY_URANDOM_SEED_INIT_FUNC (rosc_random_u32()) +#define MICROPY_PY_USELECT (1) +#define MICROPY_PY_MACHINE (1) +#define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new +#define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_SPI (1) +#define MICROPY_PY_MACHINE_SPI_MSB (SPI_MSB_FIRST) +#define MICROPY_PY_MACHINE_SPI_LSB (SPI_LSB_FIRST) +#define MICROPY_PY_FRAMEBUF (1) +#define MICROPY_VFS (1) +#define MICROPY_VFS_LFS2 (1) + +// Use VfsLfs2's types for fileio/textio +#define mp_type_fileio mp_type_vfs_lfs2_fileio +#define mp_type_textio mp_type_vfs_lfs2_textio + +// Use VFS's functions for import stat and builtin open +#define mp_import_stat mp_vfs_import_stat +#define mp_builtin_open_obj mp_vfs_open_obj + +// Hooks to add builtins + +#define MICROPY_PORT_BUILTINS \ + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, + +extern const struct _mp_obj_module_t mp_module_machine; +extern const struct _mp_obj_module_t mp_module_onewire; +extern const struct _mp_obj_module_t mp_module_rp2; +extern const struct _mp_obj_module_t mp_module_uos; +extern const struct _mp_obj_module_t mp_module_utime; + +#define MICROPY_PORT_BUILTIN_MODULES \ + { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR__rp2), (mp_obj_t)&mp_module_rp2 }, \ + { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ + { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ + +#define MICROPY_PORT_ROOT_POINTERS \ + const char *readline_hist[8]; \ + void *machine_pin_irq_obj[30]; \ + void *rp2_pio_irq_obj[2]; \ + void *rp2_state_machine_irq_obj[8]; \ + +#define MP_STATE_PORT MP_STATE_VM + +// Miscellaneous settings + +// TODO need to look and see if these could/should be spinlock/mutex +#define MICROPY_BEGIN_ATOMIC_SECTION() save_and_disable_interrupts() +#define MICROPY_END_ATOMIC_SECTION(state) restore_interrupts(state) + +#if MICROPY_HW_ENABLE_USBDEV +#define MICROPY_HW_USBDEV_TASK_HOOK extern void tud_task(void); tud_task(); +#define MICROPY_VM_HOOK_COUNT (10) +#define MICROPY_VM_HOOK_INIT static uint vm_hook_divisor = MICROPY_VM_HOOK_COUNT; +#define MICROPY_VM_HOOK_POLL if (--vm_hook_divisor == 0) { \ + vm_hook_divisor = MICROPY_VM_HOOK_COUNT; \ + MICROPY_HW_USBDEV_TASK_HOOK \ +} +#define MICROPY_VM_HOOK_LOOP MICROPY_VM_HOOK_POLL +#define MICROPY_VM_HOOK_RETURN MICROPY_VM_HOOK_POLL +#else +#define MICROPY_HW_USBDEV_TASK_HOOK +#endif + +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(bool); \ + mp_handle_pending(true); \ + best_effort_wfe_or_timeout(make_timeout_time_ms(1)); \ + MICROPY_HW_USBDEV_TASK_HOOK \ + } while (0); + +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) + +#define MP_SSIZE_MAX (0x7fffffff) +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size +typedef intptr_t mp_off_t; + +// We need to provide a declaration/definition of alloca() +#include + +#define BINARY_INFO_TAG_MICROPYTHON BINARY_INFO_MAKE_TAG('M', 'P') +#define BINARY_INFO_ID_MP_FROZEN 0x4a99d719 +#define MICROPY_FROZEN_LIST_ITEM(name, file) bi_decl(bi_string(BINARY_INFO_TAG_MICROPYTHON, BINARY_INFO_ID_MP_FROZEN, name)) + +extern uint32_t rosc_random_u32(void); diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c new file mode 100644 index 000000000..1122afcef --- /dev/null +++ b/ports/rp2/mphalport.c @@ -0,0 +1,142 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mphal.h" +#include "lib/timeutils/timeutils.h" +#include "tusb.h" +#include "uart.h" +#include "hardware/rtc.h" + +#if MICROPY_HW_ENABLE_UART_REPL + +#ifndef UART_BUFFER_LEN +// reasonably big so we can paste +#define UART_BUFFER_LEN 256 +#endif + +STATIC uint8_t stdin_ringbuf_array[UART_BUFFER_LEN]; +ringbuf_t stdin_ringbuf = { stdin_ringbuf_array, sizeof(stdin_ringbuf_array) }; + +#endif + +#if MICROPY_KBD_EXCEPTION + +int mp_interrupt_char = -1; + +void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { + (void)itf; + (void)wanted_char; + tud_cdc_read_char(); // discard interrupt char + mp_keyboard_interrupt(); +} + +void mp_hal_set_interrupt_char(int c) { + mp_interrupt_char = c; + tud_cdc_set_wanted_char(c); +} + +#endif + +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { + uintptr_t ret = 0; + #if MICROPY_HW_ENABLE_UART_REPL + if ((poll_flags & MP_STREAM_POLL_RD) && ringbuf_peek(&stdin_ringbuf) != -1) { + ret |= MP_STREAM_POLL_RD; + } + #endif + #if MICROPY_HW_ENABLE_USBDEV + if (tud_cdc_connected() && tud_cdc_available()) { + ret |= MP_STREAM_POLL_RD; + } + #endif + return ret; +} + +// Receive single character +int mp_hal_stdin_rx_chr(void) { + for (;;) { + #if MICROPY_HW_ENABLE_UART_REPL + int c = ringbuf_get(&stdin_ringbuf); + if (c != -1) { + return c; + } + #endif + #if MICROPY_HW_ENABLE_USBDEV + if (tud_cdc_connected() && tud_cdc_available()) { + uint8_t buf[1]; + uint32_t count = tud_cdc_read(buf, sizeof(buf)); + if (count) { + return buf[0]; + } + } + #endif + MICROPY_EVENT_POLL_HOOK + } +} + +// Send string of given length +void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + #if MICROPY_HW_ENABLE_UART_REPL + mp_uart_write_strn(str, len); + #endif + + #if MICROPY_HW_ENABLE_USBDEV + if (tud_cdc_connected()) { + for (size_t i = 0; i < len;) { + uint32_t n = len - i; + if (n > CFG_TUD_CDC_EP_BUFSIZE) { + n = CFG_TUD_CDC_EP_BUFSIZE; + } + while (n > tud_cdc_write_available()) { + tud_task(); + tud_cdc_write_flush(); + } + uint32_t n2 = tud_cdc_write(str + i, n); + tud_task(); + tud_cdc_write_flush(); + i += n2; + } + } + #endif +} + +void mp_hal_delay_ms(mp_uint_t ms) { + absolute_time_t t = make_timeout_time_ms(ms); + while (!time_reached(t)) { + mp_handle_pending(true); + best_effort_wfe_or_timeout(t); + MICROPY_HW_USBDEV_TASK_HOOK + } +} + +uint64_t mp_hal_time_ns(void) { + datetime_t t; + rtc_get_datetime(&t); + uint64_t s = timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.min, t.sec); + return s * 1000000000ULL; +} diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h new file mode 100644 index 000000000..40633dcf2 --- /dev/null +++ b/ports/rp2/mphalport.h @@ -0,0 +1,113 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +#ifndef MICROPY_INCLUDED_RP2_MPHALPORT_H +#define MICROPY_INCLUDED_RP2_MPHALPORT_H + +#include "py/mpconfig.h" +#include "py/ringbuf.h" +#include "pico/time.h" + +extern int mp_interrupt_char; +extern ringbuf_t stdin_ringbuf; + +void mp_hal_set_interrupt_char(int c); + +static inline void mp_hal_delay_us(mp_uint_t us) { + sleep_us(us); +} + +static inline void mp_hal_delay_us_fast(mp_uint_t us) { + busy_wait_us(us); +} + +#define mp_hal_quiet_timing_enter() MICROPY_BEGIN_ATOMIC_SECTION() +#define mp_hal_quiet_timing_exit(irq_state) MICROPY_END_ATOMIC_SECTION(irq_state) + +static inline mp_uint_t mp_hal_ticks_us(void) { + return time_us_32(); +} + +static inline mp_uint_t mp_hal_ticks_ms(void) { + return to_ms_since_boot(get_absolute_time()); +} + +static inline mp_uint_t mp_hal_ticks_cpu(void) { + // ticks_cpu() is defined as using the highest-resolution timing source + // in the system. This is usually a CPU clock, but doesn't have to be. + return time_us_32(); +} + +// C-level pin HAL + +#include "py/obj.h" +#include "hardware/gpio.h" + +#define MP_HAL_PIN_FMT "%u" +#define mp_hal_pin_obj_t uint + +extern uint32_t machine_pin_open_drain_mask; + +mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); + +static inline unsigned int mp_hal_pin_name(mp_hal_pin_obj_t pin) { + return pin; +} + +static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) { + gpio_set_function(pin, GPIO_FUNC_SIO); + gpio_set_dir(pin, GPIO_IN); + machine_pin_open_drain_mask &= ~(1 << pin); +} + +static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) { + gpio_set_function(pin, GPIO_FUNC_SIO); + gpio_set_dir(pin, GPIO_OUT); + machine_pin_open_drain_mask &= ~(1 << pin); +} + +static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) { + gpio_set_function(pin, GPIO_FUNC_SIO); + gpio_set_dir(pin, GPIO_IN); + gpio_put(pin, 0); + machine_pin_open_drain_mask |= 1 << pin; +} + +static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) { + return gpio_get(pin); +} + +static inline void mp_hal_pin_write(mp_hal_pin_obj_t pin, int v) { + gpio_put(pin, v); +} + +static inline void mp_hal_pin_od_low(mp_hal_pin_obj_t pin) { + gpio_set_dir(pin, GPIO_OUT); +} + +static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) { + gpio_set_dir(pin, GPIO_IN); +} + +#endif // MICROPY_INCLUDED_RP2_MPHALPORT_H diff --git a/ports/rp2/mpthreadport.c b/ports/rp2/mpthreadport.c new file mode 100644 index 000000000..fb4428772 --- /dev/null +++ b/ports/rp2/mpthreadport.c @@ -0,0 +1,105 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mpthread.h" +#include "pico/stdlib.h" +#include "pico/multicore.h" + +#if MICROPY_PY_THREAD + +extern uint8_t __StackTop, __StackBottom; + +void *core_state[2]; + +STATIC void *(*core1_entry)(void *) = NULL; +STATIC void *core1_arg = NULL; +STATIC uint32_t *core1_stack = NULL; +STATIC size_t core1_stack_num_words = 0; + +void mp_thread_init(void) { + mp_thread_set_state(&mp_state_ctx.thread); + core1_entry = NULL; +} + +void mp_thread_gc_others(void) { + if (get_core_num() == 0) { + // GC running on core0, trace core1's stack, if it's running. + if (core1_entry != NULL) { + gc_collect_root((void **)core1_stack, core1_stack_num_words); + } + } else { + // GC running on core1, trace core0's stack. + gc_collect_root((void **)&__StackBottom, (&__StackTop - &__StackBottom) / sizeof(uintptr_t)); + } +} + +STATIC void core1_entry_wrapper(void) { + if (core1_entry) { + core1_entry(core1_arg); + } + core1_entry = NULL; + // returning from here will loop the core forever (WFI) +} + +void mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) { + // Check if core1 is already in use. + if (core1_entry != NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("core1 in use")); + } + + core1_entry = entry; + core1_arg = arg; + + if (*stack_size == 0) { + *stack_size = 4096; // default stack size + } else if (*stack_size < 2048) { + *stack_size = 2048; // minimum stack size + } + + // Round stack size to a multiple of the word size. + core1_stack_num_words = *stack_size / sizeof(uint32_t); + *stack_size = core1_stack_num_words * sizeof(uint32_t); + + // Allocate stack. + core1_stack = m_new(uint32_t, core1_stack_num_words); + + // Create thread on core1. + multicore_reset_core1(); + multicore_launch_core1_with_stack(core1_entry_wrapper, core1_stack, *stack_size); + + // Adjust stack_size to provide room to recover from hitting the limit. + *stack_size -= 512; +} + +void mp_thread_start(void) { +} + +void mp_thread_finish(void) { +} + +#endif // MICROPY_PY_THREAD diff --git a/ports/rp2/mpthreadport.h b/ports/rp2/mpthreadport.h new file mode 100644 index 000000000..4583f6f53 --- /dev/null +++ b/ports/rp2/mpthreadport.h @@ -0,0 +1,64 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_RP2_MPTHREADPORT_H +#define MICROPY_INCLUDED_RP2_MPTHREADPORT_H + +#include "py/mpthread.h" +#include "pico/mutex.h" + +typedef struct mutex mp_thread_mutex_t; + +extern void *core_state[2]; + +void mp_thread_init(void); +void mp_thread_gc_others(void); + +static inline void mp_thread_set_state(struct _mp_state_thread_t *state) { + core_state[get_core_num()] = state; +} + +static inline struct _mp_state_thread_t *mp_thread_get_state(void) { + return core_state[get_core_num()]; +} + +static inline void mp_thread_mutex_init(mp_thread_mutex_t *m) { + mutex_init(m); +} + +static inline int mp_thread_mutex_lock(mp_thread_mutex_t *m, int wait) { + if (wait) { + mutex_enter_blocking(m); + return 1; + } else { + return mutex_try_enter(m, NULL); + } +} + +static inline void mp_thread_mutex_unlock(mp_thread_mutex_t *m) { + mutex_exit(m); +} + +#endif // MICROPY_INCLUDED_RP2_MPTHREADPORT_H diff --git a/ports/rp2/qstrdefsport.h b/ports/rp2/qstrdefsport.h new file mode 100644 index 000000000..472d05f43 --- /dev/null +++ b/ports/rp2/qstrdefsport.h @@ -0,0 +1,3 @@ +// qstrs specific to this port +// *FORMAT-OFF* +Q(/lib) diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c new file mode 100644 index 000000000..cd1bc6548 --- /dev/null +++ b/ports/rp2/rp2_flash.c @@ -0,0 +1,146 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/runtime.h" +#include "extmod/vfs.h" +#include "modrp2.h" +#include "hardware/flash.h" +#include "pico/binary_info.h" + +#define BLOCK_SIZE_BYTES (FLASH_SECTOR_SIZE) + +#ifndef MICROPY_HW_FLASH_STORAGE_BYTES +#define MICROPY_HW_FLASH_STORAGE_BYTES (1408 * 1024) +#endif + +#ifndef MICROPY_HW_FLASH_STORAGE_BASE +#define MICROPY_HW_FLASH_STORAGE_BASE (PICO_FLASH_SIZE_BYTES - MICROPY_HW_FLASH_STORAGE_BYTES) +#endif + +static_assert(MICROPY_HW_FLASH_STORAGE_BASE + MICROPY_HW_FLASH_STORAGE_BYTES <= PICO_FLASH_SIZE_BYTES, "MICROPY_HW_FLASH_STORAGE_BYTES too big"); + +typedef struct _rp2_flash_obj_t { + mp_obj_base_t base; + uint32_t flash_base; + uint32_t flash_size; +} rp2_flash_obj_t; + +STATIC rp2_flash_obj_t rp2_flash_obj = { + .base = { &rp2_flash_type }, + .flash_base = MICROPY_HW_FLASH_STORAGE_BASE, + .flash_size = MICROPY_HW_FLASH_STORAGE_BYTES, +}; + +// Tag the flash drive in the binary as readable/writable (but not reformatable) +bi_decl(bi_block_device( + BINARY_INFO_TAG_MICROPYTHON, + "MicroPython", + XIP_BASE + MICROPY_HW_FLASH_STORAGE_BASE, + MICROPY_HW_FLASH_STORAGE_BYTES, + NULL, + BINARY_INFO_BLOCK_DEV_FLAG_READ | + BINARY_INFO_BLOCK_DEV_FLAG_WRITE | + BINARY_INFO_BLOCK_DEV_FLAG_PT_UNKNOWN)); + +STATIC mp_obj_t rp2_flash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + // Check args. + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + // Return singleton object. + return MP_OBJ_FROM_PTR(&rp2_flash_obj); +} + +STATIC mp_obj_t rp2_flash_readblocks(size_t n_args, const mp_obj_t *args) { + rp2_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = mp_obj_get_int(args[1]) * BLOCK_SIZE_BYTES; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); + if (n_args == 4) { + offset += mp_obj_get_int(args[3]); + } + memcpy(bufinfo.buf, (void *)(XIP_BASE + self->flash_base + offset), bufinfo.len); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_flash_readblocks_obj, 3, 4, rp2_flash_readblocks); + +STATIC mp_obj_t rp2_flash_writeblocks(size_t n_args, const mp_obj_t *args) { + rp2_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = mp_obj_get_int(args[1]) * BLOCK_SIZE_BYTES; + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + if (n_args == 3) { + flash_range_erase(self->flash_base + offset, bufinfo.len); + // TODO check return value + } else { + offset += mp_obj_get_int(args[3]); + } + flash_range_program(self->flash_base + offset, bufinfo.buf, bufinfo.len); + // TODO check return value + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_flash_writeblocks_obj, 3, 4, rp2_flash_writeblocks); + +STATIC mp_obj_t rp2_flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) { + rp2_flash_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t cmd = mp_obj_get_int(cmd_in); + switch (cmd) { + case MP_BLOCKDEV_IOCTL_INIT: + return MP_OBJ_NEW_SMALL_INT(0); + case MP_BLOCKDEV_IOCTL_DEINIT: + return MP_OBJ_NEW_SMALL_INT(0); + case MP_BLOCKDEV_IOCTL_SYNC: + return MP_OBJ_NEW_SMALL_INT(0); + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + return MP_OBJ_NEW_SMALL_INT(self->flash_size / BLOCK_SIZE_BYTES); + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + return MP_OBJ_NEW_SMALL_INT(BLOCK_SIZE_BYTES); + case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: { + uint32_t offset = mp_obj_get_int(arg_in) * BLOCK_SIZE_BYTES; + flash_range_erase(self->flash_base + offset, BLOCK_SIZE_BYTES); + // TODO check return value + return MP_OBJ_NEW_SMALL_INT(0); + } + default: + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(rp2_flash_ioctl_obj, rp2_flash_ioctl); + +STATIC const mp_rom_map_elem_t rp2_flash_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&rp2_flash_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&rp2_flash_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&rp2_flash_ioctl_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(rp2_flash_locals_dict, rp2_flash_locals_dict_table); + +const mp_obj_type_t rp2_flash_type = { + { &mp_type_type }, + .name = MP_QSTR_Flash, + .make_new = rp2_flash_make_new, + .locals_dict = (mp_obj_dict_t *)&rp2_flash_locals_dict, +}; diff --git a/ports/rp2/rp2_pio.c b/ports/rp2/rp2_pio.c new file mode 100644 index 000000000..7d8921d0c --- /dev/null +++ b/ports/rp2/rp2_pio.c @@ -0,0 +1,787 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/binary.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "lib/utils/mpirq.h" +#include "modrp2.h" + +#include "hardware/clocks.h" +#include "hardware/irq.h" +#include "hardware/pio.h" + +#define PIO_NUM(pio) ((pio) == pio0 ? 0 : 1) + +typedef struct _rp2_pio_obj_t { + mp_obj_base_t base; + PIO pio; + uint8_t irq; +} rp2_pio_obj_t; + +typedef struct _rp2_pio_irq_obj_t { + mp_irq_obj_t base; + uint32_t flags; + uint32_t trigger; +} rp2_pio_irq_obj_t; + +typedef struct _rp2_state_machine_obj_t { + mp_obj_base_t base; + PIO pio; + uint8_t irq; + uint8_t sm; // 0-3 + uint8_t id; // 0-7 +} rp2_state_machine_obj_t; + +typedef struct _rp2_state_machine_irq_obj_t { + mp_irq_obj_t base; + uint8_t flags; + uint8_t trigger; +} rp2_state_machine_irq_obj_t; + +STATIC const rp2_state_machine_obj_t rp2_state_machine_obj[8]; + +STATIC mp_obj_t rp2_state_machine_init_helper(const rp2_state_machine_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); + +STATIC void pio_irq0(PIO pio) { + uint32_t ints = pio->ints0; + + // Acknowledge SM0-3 IRQs if they are enabled on this IRQ0. + pio->irq = ints >> 8; + + // Call handler if it is registered, for PIO irqs. + rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[PIO_NUM(pio)]); + if (irq != NULL && (ints & irq->trigger)) { + irq->flags = ints & irq->trigger; + mp_irq_handler(&irq->base); + } + + // Call handler if it is registered, for StateMachine irqs. + for (size_t i = 0; i < 4; ++i) { + rp2_state_machine_irq_obj_t *irq = MP_STATE_PORT(rp2_state_machine_irq_obj[PIO_NUM(pio) * 4 + i]); + if (irq != NULL && ((ints >> (8 + i)) & irq->trigger)) { + irq->flags = 1; + mp_irq_handler(&irq->base); + } + } +} + +STATIC void pio0_irq0(void) { + pio_irq0(pio0); +} + +STATIC void pio1_irq0(void) { + pio_irq0(pio1); +} + +void rp2_pio_init(void) { + // Reset all PIO instruction memory. + pio_clear_instruction_memory(pio0); + pio_clear_instruction_memory(pio1); + + // Set up interrupts. + memset(MP_STATE_PORT(rp2_pio_irq_obj), 0, sizeof(MP_STATE_PORT(rp2_pio_irq_obj))); + memset(MP_STATE_PORT(rp2_state_machine_irq_obj), 0, sizeof(MP_STATE_PORT(rp2_state_machine_irq_obj))); + irq_set_exclusive_handler(PIO0_IRQ_0, pio0_irq0); + irq_set_exclusive_handler(PIO1_IRQ_0, pio1_irq0); +} + +void rp2_pio_deinit(void) { + // Disable and clear interrupts. + irq_set_mask_enabled((1u << PIO0_IRQ_0) | (1u << PIO0_IRQ_1), false); + irq_remove_handler(PIO0_IRQ_0, pio0_irq0); + irq_remove_handler(PIO1_IRQ_0, pio1_irq0); +} + +/******************************************************************************/ +// Helper functions to manage asm_pio data structure. + +#define ASM_PIO_CONFIG_DEFAULT { -1, 0, 0, 0 }; + +enum { + PROG_DATA, + PROG_OFFSET_PIO0, + PROG_OFFSET_PIO1, + PROG_EXECCTRL, + PROG_SHIFTCTRL, + PROG_OUT_PINS, + PROG_SET_PINS, + PROG_SIDESET_PINS, + PROG_MAX_FIELDS, +}; + +typedef struct _asm_pio_config_t { + int8_t base; + uint8_t count; + uint8_t pindirs; + uint8_t pinvals; +} asm_pio_config_t; + +STATIC void asm_pio_override_shiftctrl(mp_obj_t arg, uint32_t bits, uint32_t lsb, pio_sm_config *config) { + if (arg != mp_const_none) { + config->shiftctrl = (config->shiftctrl & ~bits) | (mp_obj_get_int(arg) << lsb); + } +} + +STATIC void asm_pio_get_pins(const char *type, mp_obj_t prog_pins, mp_obj_t arg_base, asm_pio_config_t *config) { + if (prog_pins != mp_const_none) { + // The PIO program specified pins for initialisation on out/set/sideset. + if (mp_obj_is_integer(prog_pins)) { + // A single pin specified, set its dir and value. + config->count = 1; + mp_int_t value = mp_obj_get_int(prog_pins); + config->pindirs = value >> 1; + config->pinvals = value & 1; + } else { + // An array of pins specified, set their dirs and values. + size_t count; + mp_obj_t *items; + mp_obj_get_array(prog_pins, &count, &items); + config->count = count; + for (size_t i = 0; i < config->count; ++i) { + mp_int_t value = mp_obj_get_int(items[i]); + config->pindirs |= (value >> 1) << i; + config->pinvals |= (value & 1) << i; + } + } + } + + if (arg_base != mp_const_none) { + // The instantiation of the PIO program specified a base pin. + config->base = mp_hal_get_pin_obj(arg_base); + } +} + +STATIC void asm_pio_init_gpio(PIO pio, uint32_t sm, asm_pio_config_t *config) { + uint32_t pinmask = ((1 << config->count) - 1) << config->base; + pio_sm_set_pins_with_mask(pio, sm, config->pinvals << config->base, pinmask); + pio_sm_set_pindirs_with_mask(pio, sm, config->pindirs << config->base, pinmask); + for (size_t i = 0; i < config->count; ++i) { + gpio_set_function(config->base + i, pio == pio0 ? GPIO_FUNC_PIO0 : GPIO_FUNC_PIO1); + } +} + +/******************************************************************************/ +// PIO object + +STATIC const mp_irq_methods_t rp2_pio_irq_methods; + +STATIC rp2_pio_obj_t rp2_pio_obj[] = { + { { &rp2_pio_type }, pio0, PIO0_IRQ_0 }, + { { &rp2_pio_type }, pio1, PIO1_IRQ_0 }, +}; + +STATIC void rp2_pio_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + rp2_pio_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "PIO(%u)", self->pio == pio0 ? 0 : 1); +} + +// constructor(id) +STATIC mp_obj_t rp2_pio_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + // Get the PIO object. + int pio_id = mp_obj_get_int(args[0]); + if (!(0 <= pio_id && pio_id < MP_ARRAY_SIZE(rp2_pio_obj))) { + mp_raise_ValueError("invalid PIO"); + } + const rp2_pio_obj_t *self = &rp2_pio_obj[pio_id]; + + // Return the PIO object. + return MP_OBJ_FROM_PTR(self); +} + +// PIO.add_program(prog) +STATIC mp_obj_t rp2_pio_add_program(mp_obj_t self_in, mp_obj_t prog_in) { + rp2_pio_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // Get the program data. + mp_obj_t *prog; + mp_obj_get_array_fixed_n(prog_in, PROG_MAX_FIELDS, &prog); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(prog[PROG_DATA], &bufinfo, MP_BUFFER_READ); + + // Add the program data to the PIO instruction memory. + struct pio_program pio_program = { bufinfo.buf, bufinfo.len / 2, -1 }; + if (!pio_can_add_program(self->pio, &pio_program)) { + mp_raise_OSError(MP_ENOMEM); + } + uint offset = pio_add_program(self->pio, &pio_program); + + // Store the program offset in the program object. + prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)] = MP_OBJ_NEW_SMALL_INT(offset); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(rp2_pio_add_program_obj, rp2_pio_add_program); + +// PIO.remove_program([prog]) +STATIC mp_obj_t rp2_pio_remove_program(size_t n_args, const mp_obj_t *args) { + rp2_pio_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + // Default to remove all programs. + uint8_t length = 32; + uint offset = 0; + + if (n_args > 1) { + // Get specific program to remove. + mp_obj_t *prog; + mp_obj_get_array_fixed_n(args[1], PROG_MAX_FIELDS, &prog); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(prog[PROG_DATA], &bufinfo, MP_BUFFER_READ); + length = bufinfo.len / 2; + offset = mp_obj_get_int(prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)]); + if (offset < 0) { + mp_raise_ValueError("prog not in instruction memory"); + } + // Invalidate the program offset in the program object. + prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)] = MP_OBJ_NEW_SMALL_INT(-1); + } + + // Remove the program from the instruction memory. + struct pio_program pio_program = { NULL, length, -1 }; + pio_remove_program(self->pio, &pio_program, offset); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_pio_remove_program_obj, 1, 2, rp2_pio_remove_program); + +// PIO.state_machine(id, prog, freq=-1, *, set=None) +STATIC mp_obj_t rp2_pio_state_machine(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + rp2_pio_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + + // Get and verify the state machine id. + mp_int_t sm_id = mp_obj_get_int(pos_args[1]); + if (!(0 <= sm_id && sm_id < 4)) { + mp_raise_ValueError("invalide state machine"); + } + + // Return the correct StateMachine object. + const rp2_state_machine_obj_t *sm = &rp2_state_machine_obj[(self->pio == pio0 ? 0 : 4) + sm_id]; + + if (n_args > 2 || kw_args->used > 0) { + // Configuration arguments given so init this StateMachine. + rp2_state_machine_init_helper(sm, n_args - 2, pos_args + 2, kw_args); + } + + return MP_OBJ_FROM_PTR(sm); +} +MP_DEFINE_CONST_FUN_OBJ_KW(rp2_pio_state_machine_obj, 2, rp2_pio_state_machine); + +// PIO.irq(handler=None, trigger=IRQ_SM0|IRQ_SM1|IRQ_SM2|IRQ_SM3, hard=False) +STATIC mp_obj_t rp2_pio_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_handler, ARG_trigger, ARG_hard }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_trigger, MP_ARG_INT, {.u_int = 0xf00} }, + { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} }, + }; + + // Parse the arguments. + rp2_pio_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get the IRQ object. + rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[PIO_NUM(self->pio)]); + + // Allocate the IRQ object if it doesn't already exist. + if (irq == NULL) { + irq = m_new_obj(rp2_pio_irq_obj_t); + irq->base.base.type = &mp_irq_type; + irq->base.methods = (mp_irq_methods_t *)&rp2_pio_irq_methods; + irq->base.parent = MP_OBJ_FROM_PTR(self); + irq->base.handler = mp_const_none; + irq->base.ishard = false; + MP_STATE_PORT(rp2_pio_irq_obj[PIO_NUM(self->pio)]) = irq; + } + + if (n_args > 1 || kw_args->used != 0) { + // Configure IRQ. + + // Disable all IRQs while data is updated. + irq_set_enabled(self->irq, false); + + // Update IRQ data. + irq->base.handler = args[ARG_handler].u_obj; + irq->base.ishard = args[ARG_hard].u_bool; + irq->flags = 0; + irq->trigger = args[ARG_trigger].u_int; + + // Enable IRQ if a handler is given. + if (args[ARG_handler].u_obj != mp_const_none) { + self->pio->inte0 = irq->trigger; + irq_set_enabled(self->irq, true); + } + } + + return MP_OBJ_FROM_PTR(irq); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(rp2_pio_irq_obj, 1, rp2_pio_irq); + +STATIC const mp_rom_map_elem_t rp2_pio_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_add_program), MP_ROM_PTR(&rp2_pio_add_program_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove_program), MP_ROM_PTR(&rp2_pio_remove_program_obj) }, + { MP_ROM_QSTR(MP_QSTR_state_machine), MP_ROM_PTR(&rp2_pio_state_machine_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&rp2_pio_irq_obj) }, + + { MP_ROM_QSTR(MP_QSTR_IN_LOW), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_IN_HIGH), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_OUT_LOW), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_OUT_HIGH), MP_ROM_INT(3) }, + + { MP_ROM_QSTR(MP_QSTR_SHIFT_LEFT), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_SHIFT_RIGHT), MP_ROM_INT(1) }, + + { MP_ROM_QSTR(MP_QSTR_IRQ_SM0), MP_ROM_INT(0x100) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_SM1), MP_ROM_INT(0x200) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_SM2), MP_ROM_INT(0x400) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_SM3), MP_ROM_INT(0x800) }, +}; +STATIC MP_DEFINE_CONST_DICT(rp2_pio_locals_dict, rp2_pio_locals_dict_table); + +const mp_obj_type_t rp2_pio_type = { + { &mp_type_type }, + .name = MP_QSTR_PIO, + .print = rp2_pio_print, + .make_new = rp2_pio_make_new, + .locals_dict = (mp_obj_dict_t *)&rp2_pio_locals_dict, +}; + +STATIC mp_uint_t rp2_pio_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + rp2_pio_obj_t *self = MP_OBJ_TO_PTR(self_in); + rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[PIO_NUM(self->pio)]); + irq_set_enabled(self->irq, false); + irq->flags = 0; + irq->trigger = new_trigger; + irq_set_enabled(self->irq, true); + return 0; +} + +STATIC mp_uint_t rp2_pio_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + rp2_pio_obj_t *self = MP_OBJ_TO_PTR(self_in); + rp2_pio_irq_obj_t *irq = MP_STATE_PORT(rp2_pio_irq_obj[PIO_NUM(self->pio)]); + if (info_type == MP_IRQ_INFO_FLAGS) { + return irq->flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return irq->trigger; + } + return 0; +} + +STATIC const mp_irq_methods_t rp2_pio_irq_methods = { + .trigger = rp2_pio_irq_trigger, + .info = rp2_pio_irq_info, +}; + +/******************************************************************************/ +// StateMachine object + +STATIC const mp_irq_methods_t rp2_state_machine_irq_methods; + +STATIC const rp2_state_machine_obj_t rp2_state_machine_obj[] = { + { { &rp2_state_machine_type }, pio0, PIO0_IRQ_0, 0, 0 }, + { { &rp2_state_machine_type }, pio0, PIO0_IRQ_0, 1, 1 }, + { { &rp2_state_machine_type }, pio0, PIO0_IRQ_0, 2, 2 }, + { { &rp2_state_machine_type }, pio0, PIO0_IRQ_0, 3, 3 }, + { { &rp2_state_machine_type }, pio1, PIO1_IRQ_0, 0, 4 }, + { { &rp2_state_machine_type }, pio1, PIO1_IRQ_0, 1, 5 }, + { { &rp2_state_machine_type }, pio1, PIO1_IRQ_0, 2, 6 }, + { { &rp2_state_machine_type }, pio1, PIO1_IRQ_0, 3, 7 }, +}; + +STATIC void rp2_state_machine_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "StateMachine(%u)", self->id); +} + +// StateMachine.init(prog, freq=-1, *, +// in_base=None, out_base=None, set_base=None, jmp_pin=None, +// sideset_base=None, in_shiftdir=None, out_shiftdir=None, +// push_thresh=None, pull_thresh=None, +// ) +STATIC mp_obj_t rp2_state_machine_init_helper(const rp2_state_machine_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_prog, ARG_freq, + ARG_in_base, ARG_out_base, ARG_set_base, ARG_jmp_pin, ARG_sideset_base, + ARG_in_shiftdir, ARG_out_shiftdir, ARG_push_thresh, ARG_pull_thresh + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_prog, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_in_base, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_out_base, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_set_base, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_jmp_pin, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_sideset_base, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_in_shiftdir, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_out_shiftdir, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_push_thresh, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_pull_thresh, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + + // Parse the arguments. + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get the program. + mp_obj_t *prog; + mp_obj_get_array_fixed_n(args[ARG_prog].u_obj, PROG_MAX_FIELDS, &prog); + + // Get and the program offset, and load it into memory if it's not already there. + mp_int_t offset = mp_obj_get_int(prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)]); + if (offset < 0) { + rp2_pio_add_program(&rp2_pio_obj[PIO_NUM(self->pio)], args[ARG_prog].u_obj); + offset = mp_obj_get_int(prog[PROG_OFFSET_PIO0 + PIO_NUM(self->pio)]); + } + + // Compute the clock divider. + float div; + if (args[ARG_freq].u_int < 0) { + div = 1; + } else if (args[ARG_freq].u_int == 0) { + div = 0; + } else { + div = (float)clock_get_hz(clk_sys) / (float)args[ARG_freq].u_int; + } + + // Disable and reset the state machine. + pio_sm_init(self->pio, self->sm, offset, NULL); + + // Build the state machine config. + pio_sm_config config = pio_get_default_sm_config(); + sm_config_set_clkdiv(&config, div); + config.execctrl = mp_obj_get_int_truncated(prog[PROG_EXECCTRL]); + config.shiftctrl = mp_obj_get_int_truncated(prog[PROG_SHIFTCTRL]); + + // Adjust wrap top/bottom to account for location of program in instruction memory. + config.execctrl += (offset << PIO_SM0_EXECCTRL_WRAP_TOP_LSB) + + (offset << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB); + + // Configure in pin base, if needed. + if (args[ARG_in_base].u_obj != mp_const_none) { + sm_config_set_in_pins(&config, mp_hal_get_pin_obj(args[ARG_in_base].u_obj)); + } + + // Configure out pins, if needed. + asm_pio_config_t out_config = ASM_PIO_CONFIG_DEFAULT; + asm_pio_get_pins("out", prog[PROG_OUT_PINS], args[ARG_out_base].u_obj, &out_config); + if (out_config.base >= 0) { + sm_config_set_out_pins(&config, out_config.base, out_config.count); + } + + // Configure set pin, if needed. + asm_pio_config_t set_config = ASM_PIO_CONFIG_DEFAULT; + asm_pio_get_pins("set", prog[PROG_SET_PINS], args[ARG_set_base].u_obj, &set_config); + if (set_config.base >= 0) { + sm_config_set_set_pins(&config, set_config.base, set_config.count); + } + + // Configure jmp pin, if needed. + if (args[ARG_jmp_pin].u_obj != mp_const_none) { + sm_config_set_jmp_pin(&config, mp_hal_get_pin_obj(args[ARG_jmp_pin].u_obj)); + } + + // Configure sideset pin, if needed. + asm_pio_config_t sideset_config = ASM_PIO_CONFIG_DEFAULT; + asm_pio_get_pins("sideset", prog[PROG_SIDESET_PINS], args[ARG_sideset_base].u_obj, &sideset_config); + if (sideset_config.base >= 0) { + uint32_t count = sideset_config.count; + if (config.execctrl & (1 << PIO_SM0_EXECCTRL_SIDE_EN_LSB)) { + // When sideset is optional, count includes the option bit. + ++count; + } + config.pinctrl |= count << PIO_SM0_PINCTRL_SIDESET_COUNT_LSB; + sm_config_set_sideset_pins(&config, sideset_config.base); + } + + // Override shift state if needed. + asm_pio_override_shiftctrl(args[ARG_in_shiftdir].u_obj, PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS, PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB, &config); + asm_pio_override_shiftctrl(args[ARG_out_shiftdir].u_obj, PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS, PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB, &config); + asm_pio_override_shiftctrl(args[ARG_push_thresh].u_obj, PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS, PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB, &config); + asm_pio_override_shiftctrl(args[ARG_pull_thresh].u_obj, PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS, PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB, &config); + + // Configure the state machine. + pio_sm_set_config(self->pio, self->sm, &config); + + // Configure the GPIO. + if (out_config.base >= 0) { + asm_pio_init_gpio(self->pio, self->sm, &out_config); + } + if (set_config.base >= 0) { + asm_pio_init_gpio(self->pio, self->sm, &set_config); + } + if (sideset_config.base >= 0) { + asm_pio_init_gpio(self->pio, self->sm, &sideset_config); + } + + return mp_const_none; +} + +// StateMachine(id, ...) +STATIC mp_obj_t rp2_state_machine_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // Get the StateMachine object. + mp_int_t sm_id = mp_obj_get_int(args[0]); + if (!(0 <= sm_id && sm_id < MP_ARRAY_SIZE(rp2_state_machine_obj))) { + mp_raise_ValueError("invalid StateMachine"); + } + const rp2_state_machine_obj_t *self = &rp2_state_machine_obj[sm_id]; + + if (n_args > 1 || n_kw > 0) { + // Configuration arguments given so init this StateMachine. + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + rp2_state_machine_init_helper(self, n_args - 1, args + 1, &kw_args); + } + + // Return the StateMachine object. + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t rp2_state_machine_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return rp2_state_machine_init_helper(MP_OBJ_TO_PTR(args[0]), n_args - 1, args + 1, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(rp2_state_machine_init_obj, 1, rp2_state_machine_init); + +// StateMachine.active([value]) +STATIC mp_obj_t rp2_state_machine_active(size_t n_args, const mp_obj_t *args) { + rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args > 1) { + pio_sm_set_enabled(self->pio, self->sm, mp_obj_is_true(args[1])); + } + return mp_obj_new_bool((self->pio->ctrl >> self->sm) & 1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_state_machine_active_obj, 1, 2, rp2_state_machine_active); + +// StateMachine.exec(instr) +STATIC mp_obj_t rp2_state_machine_exec(mp_obj_t self_in, mp_obj_t instr_in) { + rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t rp2_module = mp_import_name(MP_QSTR_rp2, mp_const_none, MP_OBJ_NEW_SMALL_INT(0)); + mp_obj_t asm_pio_encode = mp_load_attr(rp2_module, MP_QSTR_asm_pio_encode); + uint32_t sideset_count = self->pio->sm[self->sm].pinctrl >> PIO_SM0_PINCTRL_SIDESET_COUNT_LSB; + mp_obj_t encoded_obj = mp_call_function_2(asm_pio_encode, instr_in, MP_OBJ_NEW_SMALL_INT(sideset_count)); + mp_int_t encoded = mp_obj_get_int(encoded_obj); + pio_sm_exec(self->pio, self->sm, encoded); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(rp2_state_machine_exec_obj, rp2_state_machine_exec); + +// StateMachine.get(buf=None, shift=0) +STATIC mp_obj_t rp2_state_machine_get(size_t n_args, const mp_obj_t *args) { + rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(args[0]); + mp_buffer_info_t bufinfo; + bufinfo.buf = NULL; + uint32_t shift = 0; + if (n_args > 1) { + if (args[1] != mp_const_none) { + mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); + if (bufinfo.typecode == BYTEARRAY_TYPECODE) { + bufinfo.typecode = 'b'; + } else { + bufinfo.typecode |= 0x20; // make lowercase to support upper and lower + } + } + if (n_args > 2) { + shift = mp_obj_get_int(args[2]); + } + } + uint8_t *dest = bufinfo.buf; + const uint8_t *dest_top = dest + bufinfo.len; + for (;;) { + while (pio_sm_is_rx_fifo_empty(self->pio, self->sm)) { + // This delay must be fast. + mp_handle_pending(true); + MICROPY_HW_USBDEV_TASK_HOOK + } + uint32_t value = pio_sm_get(self->pio, self->sm) >> shift; + if (dest == NULL) { + return mp_obj_new_int_from_uint(value); + } + if (dest >= dest_top) { + return args[1]; + } + if (bufinfo.typecode == 'b') { + *(uint8_t *)dest = value; + dest += sizeof(uint8_t); + } else if (bufinfo.typecode == 'h') { + *(uint16_t *)dest = value; + dest += sizeof(uint16_t); + } else if (bufinfo.typecode == 'i') { + *(uint32_t *)dest = value; + dest += sizeof(uint32_t); + } else { + mp_raise_ValueError("unsupported buffer type"); + } + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_state_machine_get_obj, 1, 3, rp2_state_machine_get); + +// StateMachine.put(value, shift=0) +STATIC mp_obj_t rp2_state_machine_put(size_t n_args, const mp_obj_t *args) { + rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t shift = 0; + if (n_args > 2) { + shift = mp_obj_get_int(args[2]); + } + uint32_t data; + mp_buffer_info_t bufinfo; + if (!mp_get_buffer(args[1], &bufinfo, MP_BUFFER_READ)) { + data = mp_obj_get_int_truncated(args[1]); + bufinfo.buf = &data; + bufinfo.len = sizeof(uint32_t); + bufinfo.typecode = 'I'; + } + const uint8_t *src = bufinfo.buf; + const uint8_t *src_top = src + bufinfo.len; + while (src < src_top) { + uint32_t value; + if (bufinfo.typecode == 'B' || bufinfo.typecode == BYTEARRAY_TYPECODE) { + value = *(uint8_t *)src; + src += sizeof(uint8_t); + } else if (bufinfo.typecode == 'H') { + value = *(uint16_t *)src; + src += sizeof(uint16_t); + } else if (bufinfo.typecode == 'I') { + value = *(uint32_t *)src; + src += sizeof(uint32_t); + } else { + mp_raise_ValueError("unsupported buffer type"); + } + while (pio_sm_is_tx_fifo_full(self->pio, self->sm)) { + // This delay must be fast. + mp_handle_pending(true); + MICROPY_HW_USBDEV_TASK_HOOK + } + pio_sm_put(self->pio, self->sm, value << shift); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rp2_state_machine_put_obj, 2, 3, rp2_state_machine_put); + +// StateMachine.irq(handler=None, trigger=0|1, hard=False) +STATIC mp_obj_t rp2_state_machine_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_handler, ARG_trigger, ARG_hard }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_trigger, MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} }, + }; + + // Parse the arguments. + rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Get the IRQ object. + rp2_state_machine_irq_obj_t *irq = MP_STATE_PORT(rp2_state_machine_irq_obj[self->id]); + + // Allocate the IRQ object if it doesn't already exist. + if (irq == NULL) { + irq = m_new_obj(rp2_state_machine_irq_obj_t); + irq->base.base.type = &mp_irq_type; + irq->base.methods = (mp_irq_methods_t *)&rp2_state_machine_irq_methods; + irq->base.parent = MP_OBJ_FROM_PTR(self); + irq->base.handler = mp_const_none; + irq->base.ishard = false; + MP_STATE_PORT(rp2_state_machine_irq_obj[self->id]) = irq; + } + + if (n_args > 1 || kw_args->used != 0) { + // Configure IRQ. + + // Disable all IRQs while data is updated. + irq_set_enabled(self->irq, false); + + // Update IRQ data. + irq->base.handler = args[ARG_handler].u_obj; + irq->base.ishard = args[ARG_hard].u_bool; + irq->flags = 0; + irq->trigger = args[ARG_trigger].u_int; + + // Enable IRQ if a handler is given. + if (args[ARG_handler].u_obj == mp_const_none) { + self->pio->inte0 &= ~(1 << (8 + self->sm)); + } else { + self->pio->inte0 |= 1 << (8 + self->sm); + } + + if (self->pio->inte0) { + irq_set_enabled(self->irq, true); + } + } + + return MP_OBJ_FROM_PTR(irq); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(rp2_state_machine_irq_obj, 1, rp2_state_machine_irq); + +STATIC const mp_rom_map_elem_t rp2_state_machine_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&rp2_state_machine_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&rp2_state_machine_active_obj) }, + { MP_ROM_QSTR(MP_QSTR_exec), MP_ROM_PTR(&rp2_state_machine_exec_obj) }, + { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&rp2_state_machine_get_obj) }, + { MP_ROM_QSTR(MP_QSTR_put), MP_ROM_PTR(&rp2_state_machine_put_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&rp2_state_machine_irq_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(rp2_state_machine_locals_dict, rp2_state_machine_locals_dict_table); + +const mp_obj_type_t rp2_state_machine_type = { + { &mp_type_type }, + .name = MP_QSTR_StateMachine, + .print = rp2_state_machine_print, + .make_new = rp2_state_machine_make_new, + .locals_dict = (mp_obj_dict_t *)&rp2_state_machine_locals_dict, +}; + +STATIC mp_uint_t rp2_state_machine_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { + rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(self_in); + rp2_state_machine_irq_obj_t *irq = MP_STATE_PORT(rp2_state_machine_irq_obj[PIO_NUM(self->pio)]); + irq_set_enabled(self->irq, false); + irq->flags = 0; + irq->trigger = new_trigger; + irq_set_enabled(self->irq, true); + return 0; +} + +STATIC mp_uint_t rp2_state_machine_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + rp2_state_machine_obj_t *self = MP_OBJ_TO_PTR(self_in); + rp2_state_machine_irq_obj_t *irq = MP_STATE_PORT(rp2_state_machine_irq_obj[PIO_NUM(self->pio)]); + if (info_type == MP_IRQ_INFO_FLAGS) { + return irq->flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return irq->trigger; + } + return 0; +} + +STATIC const mp_irq_methods_t rp2_state_machine_irq_methods = { + .trigger = rp2_state_machine_irq_trigger, + .info = rp2_state_machine_irq_info, +}; diff --git a/ports/rp2/tusb_config.h b/ports/rp2/tusb_config.h new file mode 100644 index 000000000..1402edf37 --- /dev/null +++ b/ports/rp2/tusb_config.h @@ -0,0 +1,34 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +#ifndef MICROPY_INCLUDED_RP2_TUSB_CONFIG_H +#define MICROPY_INCLUDED_RP2_TUSB_CONFIG_H + +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE) + +#define CFG_TUD_CDC (1) +#define CFG_TUD_CDC_RX_BUFSIZE (256) +#define CFG_TUD_CDC_TX_BUFSIZE (256) + +#endif // MICROPY_INCLUDED_RP2_TUSB_CONFIG_H diff --git a/ports/rp2/tusb_port.c b/ports/rp2/tusb_port.c new file mode 100644 index 000000000..afb566bdb --- /dev/null +++ b/ports/rp2/tusb_port.c @@ -0,0 +1,115 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "tusb.h" + +#define USBD_VID (0x2E8A) // Raspberry Pi +#define USBD_PID (0x0005) // RP2 MicroPython + +#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) +#define USBD_MAX_POWER_MA (250) + +#define USBD_ITF_CDC (0) // needs 2 interfaces +#define USBD_ITF_MAX (2) + +#define USBD_CDC_EP_CMD (0x81) +#define USBD_CDC_EP_OUT (0x02) +#define USBD_CDC_EP_IN (0x82) +#define USBD_CDC_CMD_MAX_SIZE (8) +#define USBD_CDC_IN_OUT_MAX_SIZE (64) + +#define USBD_STR_0 (0x00) +#define USBD_STR_MANUF (0x01) +#define USBD_STR_PRODUCT (0x02) +#define USBD_STR_SERIAL (0x03) +#define USBD_STR_CDC (0x04) + +// Note: descriptors returned from callbacks must exist long enough for transfer to complete + +static const tusb_desc_device_t usbd_desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = USBD_VID, + .idProduct = USBD_PID, + .bcdDevice = 0x0100, + .iManufacturer = USBD_STR_MANUF, + .iProduct = USBD_STR_PRODUCT, + .iSerialNumber = USBD_STR_SERIAL, + .bNumConfigurations = 1, +}; + +static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { + TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, + TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA), + + TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, + USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), +}; + +static const char *const usbd_desc_str[] = { + [USBD_STR_MANUF] = "MicroPython", + [USBD_STR_PRODUCT] = "Board in FS mode", + [USBD_STR_SERIAL] = "000000000000", // TODO + [USBD_STR_CDC] = "Board CDC", +}; + +const uint8_t *tud_descriptor_device_cb(void) { + return (const uint8_t *)&usbd_desc_device; +} + +const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { + (void)index; + return usbd_desc_cfg; +} + +const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { + #define DESC_STR_MAX (20) + static uint16_t desc_str[DESC_STR_MAX]; + + uint8_t len; + if (index == 0) { + desc_str[1] = 0x0409; // supported language is English + len = 1; + } else { + if (index >= sizeof(usbd_desc_str) / sizeof(usbd_desc_str[0])) { + return NULL; + } + const char *str = usbd_desc_str[index]; + for (len = 0; len < DESC_STR_MAX - 1 && str[len]; ++len) { + desc_str[1 + len] = str[len]; + } + } + + // first byte is length (including header), second byte is string type + desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * len + 2); + + return desc_str; +} diff --git a/ports/rp2/uart.c b/ports/rp2/uart.c new file mode 100644 index 000000000..b7991563a --- /dev/null +++ b/ports/rp2/uart.c @@ -0,0 +1,63 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/ringbuf.h" +#include "py/mphal.h" +#include "uart.h" + +#include "hardware/uart.h" +#include "hardware/irq.h" +#include "hardware/regs/uart.h" + +#if MICROPY_HW_ENABLE_UART_REPL + +void uart_irq(void) { + uart_get_hw(uart_default)->icr = UART_UARTICR_BITS; // clear interrupt flags + if (uart_is_readable(uart_default)) { + int c = uart_getc(uart_default); + #if MICROPY_KBD_EXCEPTION + if (c == mp_interrupt_char) { + mp_keyboard_interrupt(); + return; + } + #endif + ringbuf_put(&stdin_ringbuf, c); + } +} + +void mp_uart_init(void) { + uart_get_hw(uart_default)->imsc = UART_UARTIMSC_BITS; // enable mask + uint irq_num = uart_get_index(uart_default) ? UART1_IRQ : UART0_IRQ; + irq_set_exclusive_handler(irq_num, uart_irq); + irq_set_enabled(irq_num, true); // enable irq +} + +void mp_uart_write_strn(const char *str, size_t len) { + uart_write_blocking(uart_default, (const uint8_t *)str, len); +} + +#endif diff --git a/ports/rp2/uart.h b/ports/rp2/uart.h new file mode 100644 index 000000000..a49172f8f --- /dev/null +++ b/ports/rp2/uart.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_RP2_UART_H +#define MICROPY_INCLUDED_RP2_UART_H + +void mp_uart_init(void); +void mp_uart_write_strn(const char *str, size_t len); + +#endif // MICROPY_INCLUDED_RP2_UART_H diff --git a/ports/samd/mpconfigport.h b/ports/samd/mpconfigport.h index ffb30bf5e..852d9e9ee 100644 --- a/ports/samd/mpconfigport.h +++ b/ports/samd/mpconfigport.h @@ -98,7 +98,6 @@ extern const struct _mp_obj_module_t mp_module_utime; } while (0); #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) -#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) #define MP_SSIZE_MAX (0x7fffffff) typedef int mp_int_t; // must be pointer size diff --git a/ports/samd/mphalport.c b/ports/samd/mphalport.c index 67fc2cb0d..357ec93a6 100644 --- a/ports/samd/mphalport.c +++ b/ports/samd/mphalport.c @@ -80,17 +80,16 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { if (tud_cdc_connected()) { for (size_t i = 0; i < len;) { uint32_t n = len - i; - uint32_t n2 = tud_cdc_write(str + i, n); - if (n2 < n) { - while (!tud_cdc_write_flush()) { - __WFI(); - } + if (n > CFG_TUD_CDC_EP_BUFSIZE) { + n = CFG_TUD_CDC_EP_BUFSIZE; + } + while (n > tud_cdc_write_available()) { + __WFI(); } + uint32_t n2 = tud_cdc_write(str + i, n); + tud_cdc_write_flush(); i += n2; } - while (!tud_cdc_write_flush()) { - __WFI(); - } } while (len--) { while (!(USARTx->USART.INTFLAG.bit.DRE)) { diff --git a/ports/samd/tusb_port.c b/ports/samd/tusb_port.c index f3d417f1a..e96f33246 100644 --- a/ports/samd/tusb_port.c +++ b/ports/samd/tusb_port.c @@ -68,7 +68,7 @@ static const tusb_desc_device_t usbd_desc_device = { }; static const uint8_t usbd_desc_cfg[USBD_DESC_LEN] = { - TUD_CONFIG_DESCRIPTOR(USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, + TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_0, USBD_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, USBD_MAX_POWER_MA), TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, @@ -91,7 +91,7 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { return usbd_desc_cfg; } -const uint16_t *tud_descriptor_string_cb(uint8_t index) { +const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { #define DESC_STR_MAX (20) static uint16_t desc_str[DESC_STR_MAX]; @@ -118,29 +118,29 @@ const uint16_t *tud_descriptor_string_cb(uint8_t index) { #if defined(MCU_SAMD21) void USB_Handler_wrapper(void) { - USB_Handler(); + tud_int_handler(0); tud_task(); } #elif defined(MCU_SAMD51) void USB_0_Handler_wrapper(void) { - USB_0_Handler(); + tud_int_handler(0); tud_task(); } void USB_1_Handler_wrapper(void) { - USB_1_Handler(); + tud_int_handler(0); tud_task(); } void USB_2_Handler_wrapper(void) { - USB_2_Handler(); + tud_int_handler(0); tud_task(); } void USB_3_Handler_wrapper(void) { - USB_3_Handler(); + tud_int_handler(0); tud_task(); } diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index a9dac03d2..6e5fc1536 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -30,7 +30,7 @@ FROZEN_MANIFEST ?= boards/manifest.py # include py core make definitions include $(TOP)/py/py.mk -GIT_SUBMODULES = lib/lwip lib/mbedtls lib/mynewt-nimble lib/stm32lib +GIT_SUBMODULES = lib/libhydrogen lib/lwip lib/mbedtls lib/mynewt-nimble lib/stm32lib MCU_SERIES_UPPER = $(shell echo $(MCU_SERIES) | tr '[:lower:]' '[:upper:]') CMSIS_MCU_LOWER = $(shell echo $(CMSIS_MCU) | tr '[:upper:]' '[:lower:]') @@ -41,6 +41,7 @@ HAL_DIR=lib/stm32lib/STM32$(MCU_SERIES_UPPER)xx_HAL_Driver USBDEV_DIR=usbdev #USBHOST_DIR=usbhost DFU=$(TOP)/tools/dfu.py +MBOOT_PACK_DFU = mboot/mboot_pack_dfu.py # may need to prefix dfu-util with sudo USE_PYDFU ?= 1 PYDFU ?= $(TOP)/tools/pydfu.py @@ -546,7 +547,13 @@ $(PY_BUILD)/formatfloat.o: COPT += -Os $(PY_BUILD)/parsenum.o: COPT += -Os $(PY_BUILD)/mpprint.o: COPT += -Os -all: $(TOP)/lib/stm32lib/README.md $(BUILD)/firmware.dfu $(BUILD)/firmware.hex +all: $(TOP)/lib/stm32lib/README.md all_main $(BUILD)/firmware.hex + +ifeq ($(MBOOT_ENABLE_PACKING),1) +all_main: $(BUILD)/firmware.pack.dfu +else +all_main: $(BUILD)/firmware.dfu +endif # For convenience, automatically fetch required submodules if they don't exist $(TOP)/lib/stm32lib/README.md: @@ -607,6 +614,11 @@ define GENERATE_DFU $(1) endef +define GENERATE_PACK_DFU + $(ECHO) "GEN $(1)" + $(Q)$(PYTHON) $(MBOOT_PACK_DFU) --keys $(MBOOT_PACK_KEYS_FILE) pack-dfu --gzip $(MBOOT_PACK_CHUNKSIZE) $(2) $(1) +endef + define GENERATE_HEX $(ECHO) "GEN $(1)" $(Q)$(OBJCOPY) -O ihex $(2) $(1) @@ -614,8 +626,13 @@ endef .PHONY: deploy deploy-stlink deploy-openocd +ifeq ($(MBOOT_ENABLE_PACKING),1) +deploy: $(BUILD)/firmware.pack.dfu + $(call RUN_DFU,$^) +else deploy: $(BUILD)/firmware.dfu $(call RUN_DFU,$^) +endif # A board should specify TEXT0_ADDR if to use a different location than the # default for the firmware memory location. A board can also optionally define @@ -662,6 +679,9 @@ $(BUILD)/firmware.dfu: $(BUILD)/firmware0.bin $(BUILD)/firmware1.bin $(call GENERATE_DFU,$@,$(word 1,$^),$(TEXT0_ADDR),$(word 2,$^),$(TEXT1_ADDR)) endif +$(BUILD)/firmware.pack.dfu: $(BUILD)/firmware.dfu $(MBOOT_PACK_KEYS_FILE) + $(call GENERATE_PACK_DFU,$@,$<) + $(BUILD)/firmware.hex: $(BUILD)/firmware.elf $(call GENERATE_HEX,$@,$^) diff --git a/ports/stm32/boards/B_L072Z_LRWAN1/mpconfigboard.h b/ports/stm32/boards/B_L072Z_LRWAN1/mpconfigboard.h index 531794de6..b330e8fdc 100644 --- a/ports/stm32/boards/B_L072Z_LRWAN1/mpconfigboard.h +++ b/ports/stm32/boards/B_L072Z_LRWAN1/mpconfigboard.h @@ -13,6 +13,7 @@ #define MICROPY_PY_FRAMEBUF (0) #define MICROPY_PY_USOCKET (0) #define MICROPY_PY_NETWORK (0) +#define MICROPY_PY_ONEWIRE (0) #define MICROPY_PY_STM (0) #define MICROPY_PY_PYB_LEGACY (0) #define MICROPY_PY_UHEAPQ (0) diff --git a/ports/stm32/boards/NUCLEO_L073RZ/mpconfigboard.h b/ports/stm32/boards/NUCLEO_L073RZ/mpconfigboard.h index 2b01bd33a..a5c5dc9a0 100644 --- a/ports/stm32/boards/NUCLEO_L073RZ/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_L073RZ/mpconfigboard.h @@ -13,6 +13,7 @@ #define MICROPY_PY_FRAMEBUF (0) #define MICROPY_PY_USOCKET (0) #define MICROPY_PY_NETWORK (0) +#define MICROPY_PY_ONEWIRE (0) #define MICROPY_PY_STM (0) #define MICROPY_PY_PYB_LEGACY (0) #define MICROPY_PY_UHEAPQ (0) diff --git a/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h b/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h index 6a3a364dc..14b3bab8a 100644 --- a/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_L432KC/mpconfigboard.h @@ -7,6 +7,7 @@ #define MICROPY_PY_GENERATOR_PEND_THROW (0) #define MICROPY_PY_USOCKET (0) #define MICROPY_PY_NETWORK (0) +#define MICROPY_PY_ONEWIRE (0) #define MICROPY_PY_STM (0) #define MICROPY_PY_PYB_LEGACY (0) #define MICROPY_PY_UHEAPQ (0) diff --git a/ports/stm32/boards/NUCLEO_WB55/mboot_keys.h b/ports/stm32/boards/NUCLEO_WB55/mboot_keys.h new file mode 100644 index 000000000..773813079 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_WB55/mboot_keys.h @@ -0,0 +1,5 @@ +//const uint8_t mboot_pack_sign_secret_key[] = {0xf1,0xd8,0x66,0x08,0xbc,0x78,0x39,0x65,0x6a,0xf4,0x88,0xf4,0x43,0x4d,0x10,0xfe,0x4f,0x79,0xb9,0xc3,0x77,0x36,0x23,0xc8,0xcf,0x62,0xa1,0x90,0xf1,0xdc,0xd9,0xbc,0xb2,0x2e,0x59,0x1e,0x53,0x04,0x54,0xd5,0xd1,0x3a,0x6d,0x2e,0x79,0x3e,0xb5,0x70,0xb4,0x9f,0x33,0xff,0x90,0x1d,0xc3,0x54,0x90,0x12,0x96,0x79,0xf4,0xed,0x56,0x75}; +const uint8_t mboot_pack_sign_public_key[] = {0xb2,0x2e,0x59,0x1e,0x53,0x04,0x54,0xd5,0xd1,0x3a,0x6d,0x2e,0x79,0x3e,0xb5,0x70,0xb4,0x9f,0x33,0xff,0x90,0x1d,0xc3,0x54,0x90,0x12,0x96,0x79,0xf4,0xed,0x56,0x75}; +const uint8_t mboot_pack_secretbox_key[] = {0x4a,0xbe,0x9b,0xed,0x55,0x9f,0x74,0xeb,0x1e,0x70,0xde,0xf5,0x19,0x0a,0xeb,0xa5,0x68,0xea,0xb0,0x88,0xe6,0xfe,0x4d,0x10,0x69,0x23,0x06,0x7f,0xdd,0x83,0xf0,0xbf}; + +// The above keys are for demonstration purposes only, do not use them in production! diff --git a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h index 206134941..179369d94 100644 --- a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h +++ b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.h @@ -22,6 +22,8 @@ // UART buses #define MICROPY_HW_UART1_TX (pin_B6) #define MICROPY_HW_UART1_RX (pin_B7) +#define MICROPY_HW_LPUART1_TX (pin_A2) +#define MICROPY_HW_LPUART1_RX (pin_A3) // USART 1 is connected to the virtual com port on the ST-LINK #define MICROPY_HW_UART_REPL PYB_UART_1 #define MICROPY_HW_UART_REPL_BAUD 115200 diff --git a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk index dcec788ed..4cb047c95 100644 --- a/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk +++ b/ports/stm32/boards/NUCLEO_WB55/mpconfigboard.mk @@ -1,3 +1,7 @@ +# By default this board is configured to use mboot with packing (signing and encryption) +# enabled. Mboot must be deployed first. +USE_MBOOT ?= 1 + MCU_SERIES = wb CMSIS_MCU = STM32WB55xx AF_FILE = boards/stm32wb55_af.csv @@ -17,3 +21,6 @@ endif MICROPY_PY_BLUETOOTH = 1 MICROPY_BLUETOOTH_NIMBLE = 1 MICROPY_VFS_LFS2 = 1 + +# Mboot settings +MBOOT_ENABLE_PACKING = 1 diff --git a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h index 45d968f35..dc42751ff 100644 --- a/ports/stm32/boards/PYBD_SF2/mpconfigboard.h +++ b/ports/stm32/boards/PYBD_SF2/mpconfigboard.h @@ -185,6 +185,7 @@ extern struct _spi_bdev_t spi_bdev2; // Bluetooth config #define MICROPY_HW_BLE_UART_ID (PYB_UART_6) #define MICROPY_HW_BLE_UART_BAUDRATE (115200) +#define MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY (3000000) /******************************************************************************/ // Bootloader configuration diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py index a91ed8a2c..a19532331 100755 --- a/ports/stm32/boards/make-pins.py +++ b/ports/stm32/boards/make-pins.py @@ -14,6 +14,7 @@ "I2S": ["CK", "MCK", "SD", "WS", "EXTSD"], "USART": ["RX", "TX", "CTS", "RTS", "CK"], "UART": ["RX", "TX", "CTS", "RTS"], + "LPUART": ["RX", "TX", "CTS", "RTS"], "SPI": ["NSS", "SCK", "MISO", "MOSI"], "SDMMC": ["CK", "CMD", "D0", "D1", "D2", "D3"], "CAN": ["TX", "RX"], @@ -24,6 +25,7 @@ "I2S": "MICROPY_HW_ENABLE_I2S{num}", "SPI": "MICROPY_HW_SPI{num}_SCK", "UART": "MICROPY_HW_UART{num}_TX", + "LPUART": "MICROPY_HW_LPUART{num}_TX", "USART": "MICROPY_HW_UART{num}_TX", "SDMMC": "MICROPY_HW_SDMMC{num}_CK", "CAN": "MICROPY_HW_CAN{num}_TX", diff --git a/ports/stm32/boards/stm32f0xx_hal_conf_base.h b/ports/stm32/boards/stm32f0xx_hal_conf_base.h index 5c6f31d1d..70e6ccf75 100644 --- a/ports/stm32/boards/stm32f0xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32f0xx_hal_conf_base.h @@ -49,6 +49,7 @@ #include "stm32f0xx_hal_wwdg.h" #include "stm32f0xx_ll_adc.h" #include "stm32f0xx_ll_rtc.h" +#include "stm32f0xx_ll_usart.h" // Enable various HAL modules #define HAL_MODULE_ENABLED diff --git a/ports/stm32/boards/stm32f4xx_hal_conf_base.h b/ports/stm32/boards/stm32f4xx_hal_conf_base.h index 91f064835..134a30018 100644 --- a/ports/stm32/boards/stm32f4xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32f4xx_hal_conf_base.h @@ -54,7 +54,9 @@ #include "stm32f4xx_hal_usart.h" #include "stm32f4xx_hal_wwdg.h" #include "stm32f4xx_ll_adc.h" +#include "stm32f4xx_ll_pwr.h" #include "stm32f4xx_ll_rtc.h" +#include "stm32f4xx_ll_usart.h" // Enable various HAL modules #define HAL_ADC_MODULE_ENABLED diff --git a/ports/stm32/boards/stm32f7xx_hal_conf_base.h b/ports/stm32/boards/stm32f7xx_hal_conf_base.h index 1a3fca3ac..efb15d471 100644 --- a/ports/stm32/boards/stm32f7xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32f7xx_hal_conf_base.h @@ -54,7 +54,9 @@ #include "stm32f7xx_hal_usart.h" #include "stm32f7xx_hal_wwdg.h" #include "stm32f7xx_ll_adc.h" +#include "stm32f7xx_ll_pwr.h" #include "stm32f7xx_ll_rtc.h" +#include "stm32f7xx_ll_usart.h" // Enable various HAL modules #define HAL_ADC_MODULE_ENABLED diff --git a/ports/stm32/boards/stm32h7xx_hal_conf_base.h b/ports/stm32/boards/stm32h7xx_hal_conf_base.h index 231f1ac7f..c07ae93e3 100644 --- a/ports/stm32/boards/stm32h7xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32h7xx_hal_conf_base.h @@ -54,7 +54,9 @@ #include "stm32h7xx_hal_usart.h" #include "stm32h7xx_hal_wwdg.h" #include "stm32h7xx_ll_adc.h" +#include "stm32h7xx_ll_pwr.h" #include "stm32h7xx_ll_rtc.h" +#include "stm32h7xx_ll_usart.h" // Enable various HAL modules #define HAL_ADC_MODULE_ENABLED diff --git a/ports/stm32/boards/stm32l0xx_hal_conf_base.h b/ports/stm32/boards/stm32l0xx_hal_conf_base.h index 6b5ece766..cc033666a 100644 --- a/ports/stm32/boards/stm32l0xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32l0xx_hal_conf_base.h @@ -48,6 +48,7 @@ #include "stm32l0xx_hal_wwdg.h" #include "stm32l0xx_ll_adc.h" #include "stm32l0xx_ll_rtc.h" +#include "stm32l0xx_ll_usart.h" // Enable various HAL modules #define HAL_MODULE_ENABLED diff --git a/ports/stm32/boards/stm32l4xx_hal_conf_base.h b/ports/stm32/boards/stm32l4xx_hal_conf_base.h index 215e798b9..8d77a80a7 100644 --- a/ports/stm32/boards/stm32l4xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32l4xx_hal_conf_base.h @@ -52,6 +52,7 @@ #include "stm32l4xx_hal_wwdg.h" #include "stm32l4xx_ll_adc.h" #include "stm32l4xx_ll_rtc.h" +#include "stm32l4xx_ll_usart.h" // Enable various HAL modules #define HAL_MODULE_ENABLED diff --git a/ports/stm32/boards/stm32wbxx_hal_conf_base.h b/ports/stm32/boards/stm32wbxx_hal_conf_base.h index 91309e286..72af0262a 100644 --- a/ports/stm32/boards/stm32wbxx_hal_conf_base.h +++ b/ports/stm32/boards/stm32wbxx_hal_conf_base.h @@ -43,6 +43,7 @@ #include "stm32wbxx_hal_usart.h" #include "stm32wbxx_ll_adc.h" #include "stm32wbxx_ll_rtc.h" +#include "stm32wbxx_ll_usart.h" // Enable various HAL modules #define HAL_MODULE_ENABLED diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 31e6a1789..eceb5b6e7 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -76,7 +76,14 @@ STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { pyb_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!self->is_enabled) { - mp_printf(print, "UART(%u)", self->uart_id); + #ifdef LPUART1 + if (self->uart_id == PYB_LPUART_1) { + mp_printf(print, "UART('LP1')"); + } else + #endif + { + mp_printf(print, "UART(%u)", self->uart_id); + } } else { mp_int_t bits; uint32_t cr1 = self->uartx->CR1; @@ -98,8 +105,16 @@ STATIC void pyb_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_k if (cr1 & USART_CR1_PCE) { bits -= 1; } - mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=", - self->uart_id, uart_get_baudrate(self), bits); + #ifdef LPUART1 + if (self->uart_id == PYB_LPUART_1) { + mp_printf(print, "UART('LP1', baudrate=%u, bits=%u, parity=", + uart_get_baudrate(self), bits); + } else + #endif + { + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=", + self->uart_id, uart_get_baudrate(self), bits); + } if (!(cr1 & USART_CR1_PCE)) { mp_print_str(print, "None"); } else if (!(cr1 & USART_CR1_PS)) { @@ -335,6 +350,14 @@ STATIC mp_obj_t pyb_uart_make_new(const mp_obj_type_t *type, size_t n_args, size } else if (strcmp(port, MICROPY_HW_UART10_NAME) == 0) { uart_id = PYB_UART_10; #endif + #ifdef MICROPY_HW_LPUART1_NAME + } else if (strcmp(port, MICROPY_HW_LPUART1_NAME) == 0) { + uart_id = PYB_LPUART_1; + #endif + #ifdef LPUART1 + } else if (strcmp(port, "LP1") == 0 && uart_exists(PYB_LPUART_1)) { + uart_id = PYB_LPUART_1; + #endif } else { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("UART(%s) doesn't exist"), port); } diff --git a/ports/stm32/main.c b/ports/stm32/main.c index fb10b9650..38710e265 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -153,7 +153,7 @@ STATIC mp_obj_t pyb_main(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a } MP_DEFINE_CONST_FUN_OBJ_KW(pyb_main_obj, 1, pyb_main); -#if MICROPY_HW_ENABLE_STORAGE +#if MICROPY_HW_FLASH_MOUNT_AT_BOOT // avoid inlining to avoid stack usage within main() MP_NOINLINE STATIC bool init_flash_fs(uint reset_mode) { if (reset_mode == 3) { @@ -168,31 +168,35 @@ MP_NOINLINE STATIC bool init_flash_fs(uint reset_mode) { #if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2 - // Try to detect the block device used for the main filesystem, based on the first block - - uint8_t buf[64]; - ret = storage_readblocks_ext(buf, 0, 0, sizeof(buf)); + // Try to detect the block device used for the main filesystem based on the + // contents of the superblock, which can be the first or second block. mp_int_t len = -1; + uint8_t buf[64]; + for (size_t block_num = 0; block_num <= 1; ++block_num) { + ret = storage_readblocks_ext(buf, block_num, 0, sizeof(buf)); + + #if MICROPY_VFS_LFS1 + if (ret == 0 && memcmp(&buf[40], "littlefs", 8) == 0) { + // LFS1 + lfs1_superblock_t *superblock = (void *)&buf[12]; + uint32_t block_size = lfs1_fromle32(superblock->d.block_size); + uint32_t block_count = lfs1_fromle32(superblock->d.block_count); + len = block_count * block_size; + break; + } + #endif - #if MICROPY_VFS_LFS1 - if (ret == 0 && memcmp(&buf[40], "littlefs", 8) == 0) { - // LFS1 - lfs1_superblock_t *superblock = (void *)&buf[12]; - uint32_t block_size = lfs1_fromle32(superblock->d.block_size); - uint32_t block_count = lfs1_fromle32(superblock->d.block_count); - len = block_count * block_size; - } - #endif - - #if MICROPY_VFS_LFS2 - if (ret == 0 && memcmp(&buf[8], "littlefs", 8) == 0) { - // LFS2 - lfs2_superblock_t *superblock = (void *)&buf[20]; - uint32_t block_size = lfs2_fromle32(superblock->block_size); - uint32_t block_count = lfs2_fromle32(superblock->block_count); - len = block_count * block_size; + #if MICROPY_VFS_LFS2 + if (ret == 0 && memcmp(&buf[8], "littlefs", 8) == 0) { + // LFS2 + lfs2_superblock_t *superblock = (void *)&buf[20]; + uint32_t block_size = lfs2_fromle32(superblock->block_size); + uint32_t block_count = lfs2_fromle32(superblock->block_count); + len = block_count * block_size; + break; + } + #endif } - #endif if (len != -1) { // Detected a littlefs filesystem so create correct block device for it @@ -526,7 +530,7 @@ void stm32_main(uint32_t reset_mode) { // Initialise the local flash filesystem. // Create it if needed, mount in on /flash, and set it as current dir. bool mounted_flash = false; - #if MICROPY_HW_ENABLE_STORAGE + #if MICROPY_HW_FLASH_MOUNT_AT_BOOT mounted_flash = init_flash_fs(reset_mode); #endif diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 44372b0e8..0c9244259 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -12,6 +12,12 @@ BOARD_DIR ?= $(abspath ../boards/$(BOARD)) # that can be built with or without mboot. USE_MBOOT ?= 1 +# Set MBOOT_ENABLE_PACKING to 1 to enable DFU packing with encryption and signing. +# Ensure the MBOOT_PACK_xxx values match stm32/Makefile, to build matching application firmware. +MBOOT_ENABLE_PACKING ?= 0 +MBOOT_PACK_CHUNKSIZE ?= 16384 +MBOOT_PACK_KEYS_FILE ?= $(BOARD_DIR)/mboot_keys.h + # Sanity check that the board configuration directory exists ifeq ($(wildcard $(BOARD_DIR)/.),) $(error Invalid BOARD specified: $(BOARD_DIR)) @@ -86,7 +92,7 @@ LDFLAGS += --gc-sections # Debugging/Optimization ifeq ($(DEBUG), 1) CFLAGS += -g -DPENDSV_DEBUG -COPT = -O0 +COPT = -Og else COPT += -Os -DNDEBUG endif @@ -110,6 +116,7 @@ SRC_C = \ elem.c \ fsload.c \ gzstream.c \ + pack.c \ vfs_fat.c \ vfs_lfs.c \ drivers/bus/softspi.c \ @@ -129,6 +136,15 @@ SRC_O = \ $(SYSTEM_FILE) \ ports/stm32/resethandler.o \ +ifeq ($(MBOOT_ENABLE_PACKING), 1) + +SRC_C += lib/libhydrogen/hydrogen.c + +CFLAGS += -DMBOOT_ENABLE_PACKING=1 -DPARTICLE -DPLATFORM_ID=3 +CFLAGS += -DMBOOT_PACK_CHUNKSIZE=$(MBOOT_PACK_CHUNKSIZE) +CFLAGS += -DMBOOT_PACK_KEYS_FILE=\"$(MBOOT_PACK_KEYS_FILE)\" +endif + $(BUILD)/$(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_ll_usb.o: CFLAGS += -Wno-attributes SRC_HAL = $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES)xx_,\ hal_cortex.c \ diff --git a/ports/stm32/mboot/Particle.h b/ports/stm32/mboot/Particle.h new file mode 100644 index 000000000..5e0953739 --- /dev/null +++ b/ports/stm32/mboot/Particle.h @@ -0,0 +1,10 @@ +// Header for libhydrogen use only. Act like a Particle board for the random +// implementation. This code is not actually called when just decrypting and +// verifying a signature, but a correct implementation is provided anyway. + +#include "py/mphal.h" +#include "rng.h" + +static inline uint32_t HAL_RNG_GetRandomNumber(void) { + return rng_get(); +} diff --git a/ports/stm32/mboot/README.md b/ports/stm32/mboot/README.md index d8aa6d456..59213eb61 100644 --- a/ports/stm32/mboot/README.md +++ b/ports/stm32/mboot/README.md @@ -155,6 +155,34 @@ firmware.dfu.gz stored on the default FAT filesystem: The 0x80000000 value is the address understood by Mboot as the location of the external SPI flash, configured via `MBOOT_SPIFLASH_ADDR`. +Signed and encrypted DFU support +-------------------------------- + +Mboot optionally supports signing and encrypting the binary firmware in the DFU file. +In general this is refered to as a packed DFU file. This requires additional settings +in the board config and requires the `pyhy` Python module to be installed for `python3` +to be used when building packed firmware, eg: + + $ pip3 install pyhy + +In addition to the changes made to mpconfigboard.mk earlier, for encrypted +support you also need to add: + + MBOOT_ENABLE_PACKING = 1 + +You will also need to generate signing and encryption keys which will be built into +mboot and used for all subsequent installations of firmware. This can be done via: + + $ python3 ports/stm32/mboot/mboot_pack_dfu.py generate-keys + +This command generates a `mboot_keys.h` file which should be stored in the board +definition folder (next to mpconfigboard.mk). + +Once you build the firmware, the `firmware.pack.dfu` file will contain the encrypted +and signed firmware, and can be deployed via USB DFU, or by copying it to the device's +internal filesystem (if `MBOOT_FSLOAD` is enabled). `firmware.dfu` is still unencrypted +and can be directly flashed with jtag etc. + Example: Mboot on PYBv1.x ------------------------- diff --git a/ports/stm32/mboot/dfu.h b/ports/stm32/mboot/dfu.h index a1d4d10d0..73db3545d 100644 --- a/ports/stm32/mboot/dfu.h +++ b/ports/stm32/mboot/dfu.h @@ -39,6 +39,14 @@ #define MBOOT_ERROR_STR_INVALID_ADDRESS_IDX 0x11 #define MBOOT_ERROR_STR_INVALID_ADDRESS "Address out of range" +#if MBOOT_ENABLE_PACKING +#define MBOOT_ERROR_STR_INVALID_SIG_IDX 0x12 +#define MBOOT_ERROR_STR_INVALID_SIG "Invalid signature in file" + +#define MBOOT_ERROR_STR_INVALID_READ_IDX 0x13 +#define MBOOT_ERROR_STR_INVALID_READ "Read support disabled on encrypted bootloader" +#endif + // DFU class requests enum { DFU_DETACH = 0, @@ -104,6 +112,6 @@ typedef struct _dfu_state_t { uint8_t buf[DFU_XFER_SIZE] __attribute__((aligned(4))); } dfu_context_t; -static dfu_context_t dfu_context SECTION_NOZERO_BSS; +extern dfu_context_t dfu_context; #endif // MICROPY_INCLUDED_STM32_MBOOT_DFU_H diff --git a/ports/stm32/mboot/fsload.c b/ports/stm32/mboot/fsload.c index 1e1ad7a04..9ecc25b0b 100644 --- a/ports/stm32/mboot/fsload.c +++ b/ports/stm32/mboot/fsload.c @@ -28,49 +28,85 @@ #include "py/mphal.h" #include "mboot.h" +#include "pack.h" #include "vfs.h" +// Default block size used for mount operations if none given. +#ifndef MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE +#define MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE (4096) +#endif + #if MBOOT_FSLOAD #if !(MBOOT_VFS_FAT || MBOOT_VFS_LFS1 || MBOOT_VFS_LFS2) #error Must enable at least one VFS component #endif +#if MBOOT_ENABLE_PACKING +// Packed DFU files are gzip'd internally, not on the outside, so reads of the file +// just read the file directly. + +static void *input_stream_data; +static stream_read_t input_stream_read_meth; + +static inline int input_stream_init(void *stream_data, stream_read_t stream_read) { + input_stream_data = stream_data; + input_stream_read_meth = stream_read; + return 0; +} + +static inline int input_stream_read(size_t len, uint8_t *buf) { + return input_stream_read_meth(input_stream_data, buf, len); +} + +#else +// Standard (non-packed) DFU files must be gzip'd externally / on the outside, so +// reads of the file go through gz_stream. + +static inline int input_stream_init(void *stream_data, stream_read_t stream_read) { + return gz_stream_init_from_stream(stream_data, stream_read); +} + +static inline int input_stream_read(size_t len, uint8_t *buf) { + return gz_stream_read(len, buf); +} +#endif + static int fsload_program_file(bool write_to_flash) { // Parse DFU uint8_t buf[512]; size_t file_offset; // Read file header, <5sBIB - int res = gz_stream_read(11, buf); + int res = input_stream_read(11, buf); if (res != 11) { - return -1; + return -MBOOT_ERRNO_DFU_READ_ERROR; } file_offset = 11; // Validate header, version 1 if (memcmp(buf, "DfuSe\x01", 6) != 0) { - return -1; + return -MBOOT_ERRNO_DFU_INVALID_HEADER; } // Must have only 1 target if (buf[10] != 1) { - return -2; + return -MBOOT_ERRNO_DFU_TOO_MANY_TARGETS; } // Get total size uint32_t total_size = get_le32(buf + 6); // Read target header, <6sBi255sII - res = gz_stream_read(274, buf); + res = input_stream_read(274, buf); if (res != 274) { - return -1; + return -MBOOT_ERRNO_DFU_READ_ERROR; } file_offset += 274; // Validate target header, with alt being 0 if (memcmp(buf, "Target\x00", 7) != 0) { - return -1; + return -MBOOT_ERRNO_DFU_INVALID_TARGET; } // Get target size and number of elements @@ -82,9 +118,9 @@ static int fsload_program_file(bool write_to_flash) { // Parse each element for (size_t elem = 0; elem < num_elems; ++elem) { // Read element header, sizeof(buf)) { l = sizeof(buf); } - res = gz_stream_read(l, buf); + res = input_stream_read(l, buf); if (res != l) { - return -1; + return -MBOOT_ERRNO_DFU_READ_ERROR; } if (write_to_flash) { res = do_write(elem_addr, buf, l); if (res != 0) { - return -1; + return res; } elem_addr += l; } @@ -127,17 +165,17 @@ static int fsload_program_file(bool write_to_flash) { } if (target_size != file_offset - file_offset_target) { - return -1; + return -MBOOT_ERRNO_DFU_INVALID_SIZE; } if (total_size != file_offset) { - return -1; + return -MBOOT_ERRNO_DFU_INVALID_SIZE; } // Read trailing info - res = gz_stream_read(16, buf); + res = input_stream_read(16, buf); if (res != 16) { - return -1; + return -MBOOT_ERRNO_DFU_READ_ERROR; } // TODO validate CRC32 @@ -151,7 +189,7 @@ static int fsload_validate_and_program_file(void *stream, const stream_methods_t led_state_all(pass == 0 ? 2 : 4); int res = meth->open(stream, fname); if (res == 0) { - res = gz_stream_init(stream, meth->read); + res = input_stream_init(stream, meth->read); if (res == 0) { res = fsload_program_file(pass == 0 ? false : true); } @@ -167,7 +205,7 @@ static int fsload_validate_and_program_file(void *stream, const stream_methods_t int fsload_process(void) { const uint8_t *elem = elem_search(ELEM_DATA_START, ELEM_TYPE_FSLOAD); if (elem == NULL || elem[-1] < 2) { - return -1; + return -MBOOT_ERRNO_FSLOAD_NO_FSLOAD; } // Get mount point id and create null-terminated filename @@ -180,9 +218,20 @@ int fsload_process(void) { elem = ELEM_DATA_START; for (;;) { elem = elem_search(elem, ELEM_TYPE_MOUNT); - if (elem == NULL || elem[-1] != 10) { - // End of elements, or invalid MOUNT element - return -1; + if (elem == NULL) { + // End of elements. + return -MBOOT_ERRNO_FSLOAD_NO_MOUNT; + } + uint32_t block_size; + if (elem[-1] == 10) { + // No block size given, use default. + block_size = MBOOT_FSLOAD_DEFAULT_BLOCK_SIZE; + } else if (elem[-1] == 14) { + // Block size given, extract it. + block_size = get_le32(&elem[10]); + } else { + // Invalid MOUNT element. + return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT; } if (elem[0] == mount_point) { uint32_t base_addr = get_le32(&elem[2]); @@ -202,25 +251,26 @@ int fsload_process(void) { const stream_methods_t *methods; #if MBOOT_VFS_FAT if (elem[1] == ELEM_MOUNT_FAT) { + (void)block_size; ret = vfs_fat_mount(&ctx.fat, base_addr, byte_len); methods = &vfs_fat_stream_methods; } else #endif #if MBOOT_VFS_LFS1 if (elem[1] == ELEM_MOUNT_LFS1) { - ret = vfs_lfs1_mount(&ctx.lfs1, base_addr, byte_len); + ret = vfs_lfs1_mount(&ctx.lfs1, base_addr, byte_len, block_size); methods = &vfs_lfs1_stream_methods; } else #endif #if MBOOT_VFS_LFS2 if (elem[1] == ELEM_MOUNT_LFS2) { - ret = vfs_lfs2_mount(&ctx.lfs2, base_addr, byte_len); + ret = vfs_lfs2_mount(&ctx.lfs2, base_addr, byte_len, block_size); methods = &vfs_lfs2_stream_methods; } else #endif { // Unknown filesystem type - return -1; + return -MBOOT_ERRNO_FSLOAD_INVALID_MOUNT; } if (ret == 0) { diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py index dab5fa663..d7c8f46db 100644 --- a/ports/stm32/mboot/fwupdate.py +++ b/ports/stm32/mboot/fwupdate.py @@ -1,6 +1,7 @@ # Update Mboot or MicroPython from a .dfu.gz file on the board's filesystem # MIT license; Copyright (c) 2019-2020 Damien P. George +from micropython import const import struct, time import uzlib, machine, stm @@ -9,6 +10,12 @@ VFS_LFS1 = 2 VFS_LFS2 = 3 +# Constants for creating mboot elements. +_ELEM_TYPE_END = const(1) +_ELEM_TYPE_MOUNT = const(2) +_ELEM_TYPE_FSLOAD = const(3) +_ELEM_TYPE_STATUS = const(4) + FLASH_KEY1 = 0x45670123 FLASH_KEY2 = 0xCDEF89AB @@ -156,24 +163,36 @@ def update_mboot(filename): print("Programming finished, can now reset or turn off.") -def update_mpy(filename, fs_base, fs_len, fs_type=VFS_FAT): - # Check firmware is of .dfu.gz type +def _create_element(kind, body): + return bytes([kind, len(body)]) + body + + +def update_mpy(filename, fs_base, fs_len, fs_type=VFS_FAT, fs_blocksize=0, status_addr=None): + # Check firmware is of .dfu or .dfu.gz type try: with open(filename, "rb") as f: hdr = uzlib.DecompIO(f, 16 + 15).read(6) except Exception: - hdr = None + with open(filename, "rb") as f: + hdr = f.read(6) if hdr != b"DfuSe\x01": - print("Firmware must be a .dfu.gz file.") + print("Firmware must be a .dfu(.gz) file.") return - ELEM_TYPE_END = 1 - ELEM_TYPE_MOUNT = 2 - ELEM_TYPE_FSLOAD = 3 + if fs_type in (VFS_LFS1, VFS_LFS2) and not fs_blocksize: + raise Exception("littlefs requires fs_blocksize parameter") + mount_point = 1 - mount = struct.pack(" + +#include "dfu.h" +#include "gzstream.h" +#include "mboot.h" +#include "pack.h" + +#if MBOOT_ENABLE_PACKING + +// Keys provided externally by the board, will be built into mboot flash. +#include MBOOT_PACK_KEYS_FILE + +// Encrypted dfu files using gzip require a decompress buffer. Larger can be faster. +// This setting is independent to the incoming encrypted/signed/compressed DFU file. +#ifndef MBOOT_PACK_GZIP_BUFFER_SIZE +#define MBOOT_PACK_GZIP_BUFFER_SIZE (2048) +#endif + +// State to manage automatic flash erasure. +static uint32_t erased_base_addr; +static uint32_t erased_top_addr; + +// DFU chunk buffer, used to cache incoming blocks of data from USB. +static uint32_t firmware_chunk_base_addr; +static mboot_pack_chunk_buf_t firmware_chunk_buf; + +// Temporary buffer for decrypted data. +static uint8_t decrypted_buf[MBOOT_PACK_DFU_CHUNK_BUF_SIZE] __attribute__((aligned(8))); + +// Temporary buffer for uncompressing. +static uint8_t uncompressed_buf[MBOOT_PACK_GZIP_BUFFER_SIZE] __attribute__((aligned(8))); + +// Buffer to hold the start of the firmware, which is only written once the +// entire firmware is validated. This is 8 bytes due to STM32WB MCUs requiring +// that a double-word write to flash can only be done once (due to ECC). +static uint8_t firmware_head[8]; + +// Flag to indicate that firmware_head contains valid data. +static bool firmware_head_valid; + +void mboot_pack_init(void) { + erased_base_addr = 0; + erased_top_addr = 0; + firmware_chunk_base_addr = 0; + firmware_head_valid = false; +} + +// In encrypted mode the erase is automatically managed. +// Note: this scheme requires blocks be written in sequence, which is the case. +static int mboot_pack_erase(uint32_t addr, size_t len) { + while (!(erased_base_addr <= addr && addr + len <= erased_top_addr)) { + uint32_t erase; + if (erased_base_addr <= addr && addr < erased_top_addr) { + erase = erased_top_addr; + } else { + erase = addr; + erased_base_addr = addr; + } + uint32_t next_addr; + int ret = hw_page_erase(erase, &next_addr); + if (ret != 0) { + return ret; + } + erased_top_addr = next_addr; + } + return 0; +} + +// Commit an unencrypted and uncompressed chunk of firmware to the flash. +static int mboot_pack_commit_chunk(uint32_t addr, uint8_t *data, size_t len) { + // Erase any required sectors before writing. + int ret = mboot_pack_erase(addr, len); + if (ret != 0) { + return ret; + } + + if (addr == APPLICATION_ADDR) { + // Don't write the very start of the firmware, just copy it into a temporary buffer. + // It will be written only if the full firmware passes the checksum/signature. + memcpy(firmware_head, data, sizeof(firmware_head)); + addr += sizeof(firmware_head); + data += sizeof(firmware_head); + len -= sizeof(firmware_head); + firmware_head_valid = true; + } + + // Commit this piece of the firmware. + return hw_write(addr, data, len); +} + +// Handle a chunk with the full firmware signature. +static int mboot_pack_handle_full_sig(void) { + if (firmware_chunk_buf.header.length < hydro_sign_BYTES) { + return -MBOOT_ERRNO_PACK_INVALID_CHUNK; + } + + uint8_t *full_sig = &firmware_chunk_buf.data[firmware_chunk_buf.header.length - hydro_sign_BYTES]; + uint32_t *region_data = (uint32_t *)&firmware_chunk_buf.data[0]; + size_t num_regions = (full_sig - (uint8_t *)region_data) / sizeof(uint32_t) / 2; + + uint8_t *buf = decrypted_buf; + const size_t buf_alloc = sizeof(decrypted_buf); + + // Compute the signature of the full firmware. + hydro_sign_state sign_state; + hydro_sign_init(&sign_state, MBOOT_PACK_HYDRO_CONTEXT); + for (size_t region = 0; region < num_regions; ++region) { + uint32_t addr = region_data[2 * region]; + uint32_t len = region_data[2 * region + 1]; + while (len) { + uint32_t l = len <= buf_alloc ? len : buf_alloc; + hw_read(addr, l, buf); + if (addr == APPLICATION_ADDR && firmware_head_valid) { + // The start of the firmware was not yet written to flash so copy + // it out of the temporary buffer to compute the full signature. + memcpy(buf, firmware_head, sizeof(firmware_head)); + } + int ret = hydro_sign_update(&sign_state, buf, l); + if (ret != 0) { + return -MBOOT_ERRNO_PACK_SIGN_FAILED; + } + addr += l; + len -= l; + } + } + + // Verify the signature of the full firmware. + int ret = hydro_sign_final_verify(&sign_state, full_sig, mboot_pack_sign_public_key); + if (ret != 0) { + dfu_context.status = DFU_STATUS_ERROR_VERIFY; + dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX; + return -MBOOT_ERRNO_PACK_SIGN_FAILED; + } + + // Full firmware passed the signature check. + + if (firmware_head_valid) { + // Write the start of the firmware so it boots. + ret = hw_write(APPLICATION_ADDR, firmware_head, sizeof(firmware_head)); + } + + return ret; +} + +// Handle a chunk with firmware data. +static int mboot_pack_handle_firmware(void) { + const uint8_t *fw_data = &firmware_chunk_buf.data[0]; + const size_t fw_len = firmware_chunk_buf.header.length; + + // Decrypt the chunk. + if (hydro_secretbox_decrypt(decrypted_buf, fw_data, fw_len, 0, MBOOT_PACK_HYDRO_CONTEXT, mboot_pack_secretbox_key) != 0) { + dfu_context.status = DFU_STATUS_ERROR_VERIFY; + dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX; + return -MBOOT_ERRNO_PACK_DECRYPT_FAILED; + } + + // Use the decrypted message contents going formward. + size_t len = fw_len - hydro_secretbox_HEADERBYTES; + uint32_t addr = firmware_chunk_buf.header.address; + + if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FW_GZIP) { + // Decompress chunk data. + gz_stream_init_from_raw_data(decrypted_buf, len); + for (;;) { + int read = gz_stream_read(sizeof(uncompressed_buf), uncompressed_buf); + if (read == 0) { + return 0; // finished decompressing + } else if (read < 0) { + return -MBOOT_ERRNO_GUNZIP_FAILED; // error reading + } + int ret = mboot_pack_commit_chunk(addr, uncompressed_buf, read); + if (ret != 0) { + return ret; + } + addr += read; + } + } else { + // Commit chunk data directly. + return mboot_pack_commit_chunk(addr, decrypted_buf, len); + } +} + +int mboot_pack_write(uint32_t addr, const uint8_t *src8, size_t len) { + if (addr == APPLICATION_ADDR) { + // Base address of main firmware, reset any previous state + firmware_chunk_base_addr = 0; + } + + if (firmware_chunk_base_addr == 0) { + // First piece of data starting a new chunk, so set the base address. + firmware_chunk_base_addr = addr; + } + + if (addr < firmware_chunk_base_addr) { + // Address out of range. + firmware_chunk_base_addr = 0; + return -MBOOT_ERRNO_PACK_INVALID_ADDR; + } + + size_t offset = addr - firmware_chunk_base_addr; + if (offset + len > sizeof(firmware_chunk_buf)) { + // Address/length out of range. + firmware_chunk_base_addr = 0; + return -MBOOT_ERRNO_PACK_INVALID_ADDR; + } + + // Copy in the new data piece into the chunk buffer. + memcpy((uint8_t *)&firmware_chunk_buf + offset, src8, len); + + if (offset + len < sizeof(firmware_chunk_buf.header)) { + // Don't have the header yet. + return 0; + } + + if (firmware_chunk_buf.header.header_vers != MBOOT_PACK_HEADER_VERSION) { + // Chunk header has the wrong version. + dfu_context.status = DFU_STATUS_ERROR_FILE; + dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX; + return -MBOOT_ERRNO_PACK_INVALID_VERSION; + } + + if (firmware_chunk_buf.header.address != firmware_chunk_base_addr) { + // Chunk address doesn't agree with dfu address, abort. + dfu_context.status = DFU_STATUS_ERROR_ADDRESS; + dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX; + return -MBOOT_ERRNO_PACK_INVALID_ADDR; + } + + if (offset + len < sizeof(firmware_chunk_buf.header) + firmware_chunk_buf.header.length + sizeof(firmware_chunk_buf.signature)) { + // Don't have the full chunk yet. + return 0; + } + + // Have the full chunk in firmware_chunk_buf, process it now. + + // Reset the chunk base address for the next chunk that comes in. + firmware_chunk_base_addr = 0; + + // Verify the signature of the chunk. + const size_t fw_len = firmware_chunk_buf.header.length; + const uint8_t *sig = &firmware_chunk_buf.data[0] + fw_len; + if (hydro_sign_verify(sig, &firmware_chunk_buf, sizeof(firmware_chunk_buf.header) + fw_len, + MBOOT_PACK_HYDRO_CONTEXT, mboot_pack_sign_public_key) != 0) { + // Signature failed + dfu_context.status = DFU_STATUS_ERROR_VERIFY; + dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX; + return -MBOOT_ERRNO_PACK_SIGN_FAILED; + } + + // Signature passed, we have valid chunk. + + if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_META) { + // Ignore META chunks. + return 0; + } else if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FULL_SIG) { + return mboot_pack_handle_full_sig(); + } else if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FW_RAW + || firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FW_GZIP) { + return mboot_pack_handle_firmware(); + } else { + // Unsupported contents. + return -MBOOT_ERRNO_PACK_INVALID_CHUNK; + } +} + +#endif // MBOOT_ENABLE_PACKING diff --git a/ports/stm32/mboot/pack.h b/ports/stm32/mboot/pack.h new file mode 100644 index 000000000..195f297ca --- /dev/null +++ b/ports/stm32/mboot/pack.h @@ -0,0 +1,82 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_STM32_MBOOT_PACK_H +#define MICROPY_INCLUDED_STM32_MBOOT_PACK_H + +#include +#include "py/mphal.h" + +#if MBOOT_ENABLE_PACKING + +#include "lib/libhydrogen/hydrogen.h" + +// Encrypted & signed bootloader support + +/******************************************************************************/ +// Interface + +#define MBOOT_PACK_HEADER_VERSION (1) + +// Used by libhydrogen for signing and secretbox context. +#define MBOOT_PACK_HYDRO_CONTEXT "mbootenc" + +// Maximum size of the firmware payload. +#define MBOOT_PACK_DFU_CHUNK_BUF_SIZE (MBOOT_PACK_CHUNKSIZE + hydro_secretbox_HEADERBYTES) + +enum mboot_pack_chunk_format { + MBOOT_PACK_CHUNK_META = 0, + MBOOT_PACK_CHUNK_FULL_SIG = 1, + MBOOT_PACK_CHUNK_FW_RAW = 2, + MBOOT_PACK_CHUNK_FW_GZIP = 3, +}; + +// Each DFU chunk transfered has this header to validate it. + +typedef struct _mboot_pack_chunk_buf_t { + struct { + uint8_t header_vers; + uint8_t format; // enum mboot_pack_chunk_format + uint8_t _pad[2]; + uint32_t address; + uint32_t length; // number of bytes in following "data" payload, excluding "signature" + } header; + uint8_t data[MBOOT_PACK_DFU_CHUNK_BUF_SIZE]; + uint8_t signature[hydro_sign_BYTES]; +} mboot_pack_chunk_buf_t; + +// Signing and encryption keys, stored in mboot flash, provided externally. +extern const uint8_t mboot_pack_sign_public_key[hydro_sign_PUBLICKEYBYTES]; +extern const uint8_t mboot_pack_secretbox_key[hydro_secretbox_KEYBYTES]; + +/******************************************************************************/ +// Implementation + +void mboot_pack_init(void); +int mboot_pack_write(uint32_t addr, const uint8_t *src8, size_t len); + +#endif // MBOOT_ENABLE_PACKING + +#endif // MICROPY_INCLUDED_STM32_MBOOT_PACK_H diff --git a/ports/stm32/mboot/stm32_generic.ld b/ports/stm32/mboot/stm32_generic.ld index 0504d6d6a..ade434967 100644 --- a/ports/stm32/mboot/stm32_generic.ld +++ b/ports/stm32/mboot/stm32_generic.ld @@ -6,11 +6,11 @@ MEMORY { FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 32K /* sector 0 (can be 32K) */ - RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 120K } /* produce a link error if there is not this amount of RAM for these sections */ -_minimum_stack_size = 2K; +_minimum_stack_size = 8K; /* Define tho top end of the stack. The stack is full descending so begins just above last byte of RAM. Note that EABI requires the stack to be 8-byte diff --git a/ports/stm32/mboot/vfs.h b/ports/stm32/mboot/vfs.h index 6cf883a13..22bb98936 100644 --- a/ports/stm32/mboot/vfs.h +++ b/ports/stm32/mboot/vfs.h @@ -65,7 +65,7 @@ typedef struct _vfs_lfs1_context_t { extern const stream_methods_t vfs_lfs1_stream_methods; -int vfs_lfs1_mount(vfs_lfs1_context_t *ctx, uint32_t base_addr, uint32_t byte_len); +int vfs_lfs1_mount(vfs_lfs1_context_t *ctx, uint32_t base_addr, uint32_t byte_len, uint32_t block_size); #endif @@ -89,7 +89,7 @@ typedef struct _vfs_lfs2_context_t { extern const stream_methods_t vfs_lfs2_stream_methods; -int vfs_lfs2_mount(vfs_lfs2_context_t *ctx, uint32_t base_addr, uint32_t byte_len); +int vfs_lfs2_mount(vfs_lfs2_context_t *ctx, uint32_t base_addr, uint32_t byte_len, uint32_t block_size); #endif diff --git a/ports/stm32/mboot/vfs_fat.c b/ports/stm32/mboot/vfs_fat.c index 20de074f0..cfa30fb12 100644 --- a/ports/stm32/mboot/vfs_fat.c +++ b/ports/stm32/mboot/vfs_fat.c @@ -42,7 +42,7 @@ DRESULT disk_read(void *pdrv, BYTE *buf, DWORD sector, UINT count) { vfs_fat_context_t *ctx = pdrv; if (0 <= sector && sector < ctx->bdev_byte_len / 512) { - do_read(ctx->bdev_base_addr + sector * SECSIZE, count * SECSIZE, buf); + hw_read(ctx->bdev_base_addr + sector * SECSIZE, count * SECSIZE, buf); return RES_OK; } @@ -84,7 +84,7 @@ int vfs_fat_mount(vfs_fat_context_t *ctx, uint32_t base_addr, uint32_t byte_len) ctx->fatfs.drv = ctx; FRESULT res = f_mount(&ctx->fatfs); if (res != FR_OK) { - return -1; + return -MBOOT_ERRNO_VFS_FAT_MOUNT_FAILED; } return 0; } @@ -93,7 +93,7 @@ static int vfs_fat_stream_open(void *stream_in, const char *fname) { vfs_fat_context_t *stream = stream_in; FRESULT res = f_open(&stream->fatfs, &stream->fp, fname, FA_READ); if (res != FR_OK) { - return -1; + return -MBOOT_ERRNO_VFS_FAT_OPEN_FAILED; } return 0; } diff --git a/ports/stm32/mboot/vfs_lfs.c b/ports/stm32/mboot/vfs_lfs.c index dec7c015f..e7fd8ce63 100644 --- a/ports/stm32/mboot/vfs_lfs.c +++ b/ports/stm32/mboot/vfs_lfs.c @@ -37,32 +37,30 @@ #error Unsupported #endif +#define MBOOT_ERRNO_VFS_LFS_MOUNT_FAILED MBOOT_ERRNO_VFS_LFS1_MOUNT_FAILED +#define MBOOT_ERRNO_VFS_LFS_OPEN_FAILED MBOOT_ERRNO_VFS_LFS1_OPEN_FAILED + #define LFSx_MACRO(s) LFS1##s #define LFSx_API(x) lfs1_ ## x #define VFS_LFSx_CONTEXT_T vfs_lfs1_context_t #define VFS_LFSx_MOUNT vfs_lfs1_mount #define VFS_LFSx_STREAM_METHODS vfs_lfs1_stream_methods -#define SUPERBLOCK_MAGIC_OFFSET (40) -#define SUPERBLOCK_BLOCK_SIZE_OFFSET (28) -#define SUPERBLOCK_BLOCK_COUNT_OFFSET (32) - static uint8_t lfs_read_buffer[LFS_READ_SIZE]; static uint8_t lfs_prog_buffer[LFS_PROG_SIZE]; static uint8_t lfs_lookahead_buffer[LFS_LOOKAHEAD_SIZE / 8]; #else +#define MBOOT_ERRNO_VFS_LFS_MOUNT_FAILED MBOOT_ERRNO_VFS_LFS2_MOUNT_FAILED +#define MBOOT_ERRNO_VFS_LFS_OPEN_FAILED MBOOT_ERRNO_VFS_LFS2_OPEN_FAILED + #define LFSx_MACRO(s) LFS2##s #define LFSx_API(x) lfs2_ ## x #define VFS_LFSx_CONTEXT_T vfs_lfs2_context_t #define VFS_LFSx_MOUNT vfs_lfs2_mount #define VFS_LFSx_STREAM_METHODS vfs_lfs2_stream_methods -#define SUPERBLOCK_MAGIC_OFFSET (8) -#define SUPERBLOCK_BLOCK_SIZE_OFFSET (24) -#define SUPERBLOCK_BLOCK_COUNT_OFFSET (28) - static uint8_t lfs_read_buffer[LFS_CACHE_SIZE]; static uint8_t lfs_prog_buffer[LFS_CACHE_SIZE]; static uint8_t lfs_lookahead_buffer[LFS_LOOKAHEAD_SIZE]; @@ -72,7 +70,7 @@ static uint8_t lfs_lookahead_buffer[LFS_LOOKAHEAD_SIZE]; static int dev_read(const struct LFSx_API (config) * c, LFSx_API(block_t) block, LFSx_API(off_t) off, void *buffer, LFSx_API(size_t) size) { VFS_LFSx_CONTEXT_T *ctx = c->context; if (0 <= block && block < ctx->config.block_count) { - do_read(ctx->bdev_base_addr + block * ctx->config.block_size + off, size, buffer); + hw_read(ctx->bdev_base_addr + block * ctx->config.block_size + off, size, buffer); return LFSx_MACRO(_ERR_OK); } return LFSx_MACRO(_ERR_IO); @@ -90,23 +88,7 @@ static int dev_sync(const struct LFSx_API (config) * c) { return LFSx_MACRO(_ERR_OK); } -int VFS_LFSx_MOUNT(VFS_LFSx_CONTEXT_T *ctx, uint32_t base_addr, uint32_t byte_len) { - // Read start of superblock. - uint8_t buf[48]; - do_read(base_addr, sizeof(buf), buf); - - // Verify littlefs and detect block size. - if (memcmp(&buf[SUPERBLOCK_MAGIC_OFFSET], "littlefs", 8) != 0) { - return -1; - } - uint32_t block_size = get_le32(&buf[SUPERBLOCK_BLOCK_SIZE_OFFSET]); - uint32_t block_count = get_le32(&buf[SUPERBLOCK_BLOCK_COUNT_OFFSET]); - - // Verify size of volume. - if (block_size * block_count != byte_len) { - return -1; - } - +int VFS_LFSx_MOUNT(VFS_LFSx_CONTEXT_T *ctx, uint32_t base_addr, uint32_t byte_len, uint32_t block_size) { ctx->bdev_base_addr = base_addr; struct LFSx_API (config) *config = &ctx->config; @@ -140,7 +122,7 @@ int VFS_LFSx_MOUNT(VFS_LFSx_CONTEXT_T *ctx, uint32_t base_addr, uint32_t byte_le int ret = LFSx_API(mount)(&ctx->lfs, &ctx->config); if (ret < 0) { - return -1; + return -MBOOT_ERRNO_VFS_LFS_MOUNT_FAILED; } return 0; } @@ -150,7 +132,10 @@ static int vfs_lfs_stream_open(void *stream_in, const char *fname) { memset(&ctx->file, 0, sizeof(ctx->file)); memset(&ctx->filecfg, 0, sizeof(ctx->filecfg)); ctx->filecfg.buffer = &ctx->filebuf[0]; - LFSx_API(file_opencfg)(&ctx->lfs, &ctx->file, fname, LFSx_MACRO(_O_RDONLY), &ctx->filecfg); + int ret = LFSx_API(file_opencfg)(&ctx->lfs, &ctx->file, fname, LFSx_MACRO(_O_RDONLY), &ctx->filecfg); + if (ret < 0) { + return -MBOOT_ERRNO_VFS_LFS_OPEN_FAILED; + } return 0; } diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index c5f49b6ef..f9fd1d9a6 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -406,7 +406,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, + #if MICROPY_PY_MACHINE_PULSE { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, @@ -425,8 +427,10 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { #endif { MP_ROM_QSTR(MP_QSTR_SoftI2C), MP_ROM_PTR(&mp_machine_soft_i2c_type) }, #endif + #if MICROPY_PY_MACHINE_SPI { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&machine_hard_spi_type) }, { MP_ROM_QSTR(MP_QSTR_SoftSPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, + #endif { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&pyb_uart_type) }, { MP_ROM_QSTR(MP_QSTR_WDT), MP_ROM_PTR(&pyb_wdt_type) }, { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, diff --git a/ports/stm32/mpbthciport.c b/ports/stm32/mpbthciport.c index ee9f4e31e..c3cd864e2 100644 --- a/ports/stm32/mpbthciport.c +++ b/ports/stm32/mpbthciport.c @@ -32,8 +32,6 @@ #include "pendsv.h" #include "lib/utils/mpirq.h" -#include "py/obj.h" - #if MICROPY_PY_BLUETOOTH #define DEBUG_printf(...) // printf("mpbthciport.c: " __VA_ARGS__) @@ -197,8 +195,18 @@ int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { mp_bluetooth_hci_uart_obj.timeout_char = 200; MP_STATE_PORT(pyb_uart_obj_all)[mp_bluetooth_hci_uart_obj.uart_id - 1] = &mp_bluetooth_hci_uart_obj; - // This also initialises the UART and adds the RXIDLE IRQ handler. - mp_bluetooth_hci_uart_set_baudrate(baudrate); + // Initialise the UART. + uart_init(&mp_bluetooth_hci_uart_obj, baudrate, UART_WORDLENGTH_8B, UART_PARITY_NONE, UART_STOPBITS_1, UART_HWCONTROL_RTS | UART_HWCONTROL_CTS); + uart_set_rxbuf(&mp_bluetooth_hci_uart_obj, sizeof(hci_uart_rxbuf), hci_uart_rxbuf); + + // Add IRQ handler for IDLE (i.e. packet finished). + uart_irq_config(&mp_bluetooth_hci_uart_obj, false); + mp_irq_init(&mp_bluetooth_hci_uart_irq_obj, &uart_irq_methods, MP_OBJ_FROM_PTR(&mp_bluetooth_hci_uart_obj)); + mp_bluetooth_hci_uart_obj.mp_irq_obj = &mp_bluetooth_hci_uart_irq_obj; + mp_bluetooth_hci_uart_obj.mp_irq_trigger = UART_FLAG_IDLE; + mp_bluetooth_hci_uart_irq_obj.handler = MP_OBJ_FROM_PTR(&mp_uart_interrupt_obj); + mp_bluetooth_hci_uart_irq_obj.ishard = true; + uart_irq_config(&mp_bluetooth_hci_uart_obj, true); return 0; } @@ -213,22 +221,7 @@ int mp_bluetooth_hci_uart_deinit(void) { int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { DEBUG_printf("mp_bluetooth_hci_uart_set_baudrate(%lu) (stm32)\n", baudrate); - if (!baudrate) { - return -1; - } - - uart_init(&mp_bluetooth_hci_uart_obj, baudrate, UART_WORDLENGTH_8B, UART_PARITY_NONE, UART_STOPBITS_1, UART_HWCONTROL_RTS | UART_HWCONTROL_CTS); - uart_set_rxbuf(&mp_bluetooth_hci_uart_obj, sizeof(hci_uart_rxbuf), hci_uart_rxbuf); - - // Add IRQ handler for IDLE (i.e. packet finished). - uart_irq_config(&mp_bluetooth_hci_uart_obj, false); - mp_irq_init(&mp_bluetooth_hci_uart_irq_obj, &uart_irq_methods, MP_OBJ_FROM_PTR(&mp_bluetooth_hci_uart_obj)); - mp_bluetooth_hci_uart_obj.mp_irq_obj = &mp_bluetooth_hci_uart_irq_obj; - mp_bluetooth_hci_uart_obj.mp_irq_trigger = UART_FLAG_IDLE; - mp_bluetooth_hci_uart_irq_obj.handler = MP_OBJ_FROM_PTR(&mp_uart_interrupt_obj); - mp_bluetooth_hci_uart_irq_obj.ishard = true; - uart_irq_config(&mp_bluetooth_hci_uart_obj, true); - + uart_set_baudrate(&mp_bluetooth_hci_uart_obj, baudrate); return 0; } @@ -239,7 +232,7 @@ int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { int errcode; uart_tx_data(&mp_bluetooth_hci_uart_obj, (void *)buf, len, &errcode); if (errcode != 0) { - printf("\nmp_bluetooth_hci_uart_write: failed to write to UART %d\n", errcode); + mp_printf(&mp_plat_print, "\nmp_bluetooth_hci_uart_write: failed to write to UART %d\n", errcode); } return 0; } diff --git a/ports/stm32/mpbtstackport.c b/ports/stm32/mpbtstackport.c index a031a6a15..3dfd8cb3c 100644 --- a/ports/stm32/mpbtstackport.c +++ b/ports/stm32/mpbtstackport.c @@ -58,7 +58,7 @@ uint32_t hal_time_ms(void) { STATIC const hci_transport_config_uart_t hci_transport_config_uart = { HCI_TRANSPORT_CONFIG_UART, MICROPY_HW_BLE_UART_BAUDRATE, - 3000000, + MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY, 0, NULL, }; @@ -90,9 +90,9 @@ void mp_bluetooth_btstack_port_init(void) { const hci_transport_t *transport = hci_transport_h4_instance(&mp_bluetooth_btstack_hci_uart_block); hci_init(transport, &hci_transport_config_uart); - // TODO: Probably not necessary for BCM (we have our own firmware loader in the cyw43 driver), - // but might be worth investigating for other controllers in the future. - // hci_set_chipset(btstack_chipset_bcm_instance()); + #ifdef MICROPY_HW_BLE_BTSTACK_CHIPSET_INSTANCE + hci_set_chipset(MICROPY_HW_BLE_BTSTACK_CHIPSET_INSTANCE); + #endif } void mp_bluetooth_btstack_port_deinit(void) { diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index 60fbc35fc..948897b21 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -37,6 +37,11 @@ #define MICROPY_PY_STM (1) #endif +// Whether to include the pyb module +#ifndef MICROPY_PY_PYB +#define MICROPY_PY_PYB (1) +#endif + // Whether to include legacy functions and classes in the pyb module #ifndef MICROPY_PY_PYB_LEGACY #define MICROPY_PY_PYB_LEGACY (1) @@ -122,6 +127,11 @@ #define MICROPY_HW_HAS_LCD (0) #endif +// Whether to automatically mount (and boot from) the flash filesystem +#ifndef MICROPY_HW_FLASH_MOUNT_AT_BOOT +#define MICROPY_HW_FLASH_MOUNT_AT_BOOT (MICROPY_HW_ENABLE_STORAGE) +#endif + // The volume label used when creating the flash filesystem #ifndef MICROPY_HW_FLASH_FS_LABEL #define MICROPY_HW_FLASH_FS_LABEL "pybflash" @@ -171,6 +181,7 @@ #define MICROPY_HW_MAX_I2C (2) #define MICROPY_HW_MAX_TIMER (17) #define MICROPY_HW_MAX_UART (8) +#define MICROPY_HW_MAX_LPUART (0) // Configuration for STM32F4 series #elif defined(STM32F4) @@ -190,6 +201,7 @@ #else #define MICROPY_HW_MAX_UART (6) #endif +#define MICROPY_HW_MAX_LPUART (0) // Configuration for STM32F7 series #elif defined(STM32F7) @@ -204,6 +216,7 @@ #define MICROPY_HW_MAX_I2C (4) #define MICROPY_HW_MAX_TIMER (17) #define MICROPY_HW_MAX_UART (8) +#define MICROPY_HW_MAX_LPUART (0) // Configuration for STM32H7 series #elif defined(STM32H7) @@ -213,6 +226,7 @@ #define MICROPY_HW_MAX_I2C (4) #define MICROPY_HW_MAX_TIMER (17) #define MICROPY_HW_MAX_UART (8) +#define MICROPY_HW_MAX_LPUART (1) // Configuration for STM32L0 series #elif defined(STM32L0) @@ -222,6 +236,7 @@ #define MICROPY_HW_MAX_I2C (3) #define MICROPY_HW_MAX_TIMER (22) #define MICROPY_HW_MAX_UART (5) +#define MICROPY_HW_MAX_LPUART (1) // Configuration for STM32L4 series #elif defined(STM32L4) @@ -230,7 +245,8 @@ #define PYB_EXTI_NUM_VECTORS (23) #define MICROPY_HW_MAX_I2C (4) #define MICROPY_HW_MAX_TIMER (17) -#define MICROPY_HW_MAX_UART (6) +#define MICROPY_HW_MAX_UART (5) +#define MICROPY_HW_MAX_LPUART (1) // Configuration for STM32WB series #elif defined(STM32WB) @@ -240,11 +256,32 @@ #define MICROPY_HW_MAX_I2C (3) #define MICROPY_HW_MAX_TIMER (17) #define MICROPY_HW_MAX_UART (1) +#define MICROPY_HW_MAX_LPUART (1) #ifndef MICROPY_HW_STM32WB_FLASH_SYNCRONISATION #define MICROPY_HW_STM32WB_FLASH_SYNCRONISATION (1) #endif +// RF core BLE configuration (a board should define +// MICROPY_HW_RFCORE_BLE_NUM_GATT_ATTRIBUTES to override all values) +#ifndef MICROPY_HW_RFCORE_BLE_NUM_GATT_ATTRIBUTES +#define MICROPY_HW_RFCORE_BLE_NUM_GATT_ATTRIBUTES (0) +#define MICROPY_HW_RFCORE_BLE_NUM_GATT_SERVICES (0) +#define MICROPY_HW_RFCORE_BLE_ATT_VALUE_ARRAY_SIZE (0) +#define MICROPY_HW_RFCORE_BLE_NUM_LINK (1) +#define MICROPY_HW_RFCORE_BLE_DATA_LENGTH_EXTENSION (1) +#define MICROPY_HW_RFCORE_BLE_PREPARE_WRITE_LIST_SIZE (0) +#define MICROPY_HW_RFCORE_BLE_MBLOCK_COUNT (0x79) +#define MICROPY_HW_RFCORE_BLE_MAX_ATT_MTU (0) +#define MICROPY_HW_RFCORE_BLE_SLAVE_SCA (0) +#define MICROPY_HW_RFCORE_BLE_MASTER_SCA (0) +#define MICROPY_HW_RFCORE_BLE_LSE_SOURCE (0) // use LSE to clock the rfcore (see errata 2.2.1) +#define MICROPY_HW_RFCORE_BLE_MAX_CONN_EVENT_LENGTH (0xffffffff) +#define MICROPY_HW_RFCORE_BLE_HSE_STARTUP_TIME (0x148) +#define MICROPY_HW_RFCORE_BLE_VITERBI_MODE (1) +#define MICROPY_HW_RFCORE_BLE_LL_ONLY (1) // use LL only, we provide the rest of the BLE stack +#endif + #else #error Unsupported MCU series #endif diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index b6906ef99..174ef22f2 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -165,7 +165,13 @@ #define MICROPY_PY_UCRYPTOLIB (MICROPY_PY_USSL) #ifndef MICROPY_PY_UBINASCII #define MICROPY_PY_UBINASCII (1) +#define MICROPY_PY_UBINASCII_CRC32 (1) #endif +#ifndef MICROPY_PY_UOS +#define MICROPY_PY_UOS (1) +#endif +#define MICROPY_PY_OS_DUPTERM (3) +#define MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM (1) #ifndef MICROPY_PY_URANDOM #define MICROPY_PY_URANDOM (1) #define MICROPY_PY_URANDOM_SEED_INIT_FUNC (rng_get()) @@ -174,13 +180,15 @@ #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) #endif #define MICROPY_PY_USELECT (1) +#ifndef MICROPY_PY_UTIME +#define MICROPY_PY_UTIME (1) +#endif +#define MICROPY_PY_UTIME_MP_HAL (MICROPY_PY_UTIME) #ifndef MICROPY_PY_UTIMEQ #define MICROPY_PY_UTIMEQ (1) #endif -#define MICROPY_PY_UTIME_MP_HAL (1) -#define MICROPY_PY_OS_DUPTERM (3) -#define MICROPY_PY_UOS_DUPTERM_BUILTIN_STREAM (1) #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) +#ifndef MICROPY_PY_MACHINE #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_PULSE (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new @@ -188,6 +196,7 @@ #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SPI_MSB (SPI_FIRSTBIT_MSB) #define MICROPY_PY_MACHINE_SPI_LSB (SPI_FIRSTBIT_LSB) +#endif #define MICROPY_HW_SOFTSPI_MIN_DELAY (0) #define MICROPY_HW_SOFTSPI_MAX_BAUDRATE (HAL_RCC_GetSysClockFreq() / 48) #define MICROPY_PY_UWEBSOCKET (MICROPY_PY_LWIP) @@ -201,6 +210,9 @@ #ifndef MICROPY_PY_NETWORK #define MICROPY_PY_NETWORK (1) #endif +#ifndef MICROPY_PY_ONEWIRE +#define MICROPY_PY_ONEWIRE (1) +#endif // fatfs configuration used in ffconf.h #define MICROPY_FATFS_ENABLE_LFN (1) @@ -246,12 +258,38 @@ extern const struct _mp_obj_module_t mp_module_usocket; extern const struct _mp_obj_module_t mp_module_network; extern const struct _mp_obj_module_t mp_module_onewire; +#if MICROPY_PY_PYB +#define PYB_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_pyb), MP_ROM_PTR(&pyb_module) }, +#else +#define PYB_BUILTIN_MODULE +#endif + #if MICROPY_PY_STM -#define STM_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_stm), MP_ROM_PTR(&stm_module) }, +#define STM_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_stm), MP_ROM_PTR(&stm_module) }, #else #define STM_BUILTIN_MODULE #endif +#if MICROPY_PY_MACHINE +#define MACHINE_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&machine_module) }, +#define MACHINE_BUILTIN_MODULE_CONSTANTS { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&machine_module) }, +#else +#define MACHINE_BUILTIN_MODULE +#define MACHINE_BUILTIN_MODULE_CONSTANTS +#endif + +#if MICROPY_PY_UOS +#define UOS_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, +#else +#define UOS_BUILTIN_MODULE +#endif + +#if MICROPY_PY_UTIME +#define UTIME_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, +#else +#define UTIME_BUILTIN_MODULE +#endif + #if MICROPY_PY_USOCKET && MICROPY_PY_LWIP // usocket implementation provided by lwIP #define SOCKET_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_usocket), MP_ROM_PTR(&mp_module_lwip) }, @@ -269,21 +307,27 @@ extern const struct _mp_obj_module_t mp_module_onewire; #define NETWORK_BUILTIN_MODULE #endif +#if MICROPY_PY_ONEWIRE +#define ONEWIRE_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR__onewire), MP_ROM_PTR(&mp_module_onewire) }, +#else +#define ONEWIRE_BUILTIN_MODULE +#endif + #define MICROPY_PORT_BUILTIN_MODULES \ - { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&machine_module) }, \ - { MP_ROM_QSTR(MP_QSTR_pyb), MP_ROM_PTR(&pyb_module) }, \ + MACHINE_BUILTIN_MODULE \ + PYB_BUILTIN_MODULE \ STM_BUILTIN_MODULE \ - { MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \ - { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \ + UOS_BUILTIN_MODULE \ + UTIME_BUILTIN_MODULE \ SOCKET_BUILTIN_MODULE \ NETWORK_BUILTIN_MODULE \ - { MP_ROM_QSTR(MP_QSTR__onewire), MP_ROM_PTR(&mp_module_onewire) }, \ + ONEWIRE_BUILTIN_MODULE \ // extra constants #define MICROPY_PORT_CONSTANTS \ - { MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&machine_module) }, \ - { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&machine_module) }, \ - { MP_ROM_QSTR(MP_QSTR_pyb), MP_ROM_PTR(&pyb_module) }, \ + MACHINE_BUILTIN_MODULE \ + MACHINE_BUILTIN_MODULE_CONSTANTS \ + PYB_BUILTIN_MODULE \ STM_BUILTIN_MODULE \ #define MP_STATE_PORT MP_STATE_VM @@ -332,7 +376,7 @@ struct _mp_bluetooth_btstack_root_pointers_t; struct _pyb_uart_obj_t *pyb_stdio_uart; \ \ /* pointers to all UART objects (if they have been created) */ \ - struct _pyb_uart_obj_t *pyb_uart_obj_all[MICROPY_HW_MAX_UART]; \ + struct _pyb_uart_obj_t *pyb_uart_obj_all[MICROPY_HW_MAX_UART + MICROPY_HW_MAX_LPUART]; \ \ /* pointers to all CAN objects (if they have been created) */ \ struct _pyb_can_obj_t *pyb_can_obj_all[MICROPY_HW_MAX_CAN]; \ @@ -360,8 +404,6 @@ typedef unsigned int mp_uint_t; // must be pointer size typedef long mp_off_t; -#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) - // We have inlined IRQ functions for efficiency (they are generally // 1 machine instruction). // diff --git a/ports/stm32/mpconfigport.mk b/ports/stm32/mpconfigport.mk index c6b3ddc74..98ae93d20 100644 --- a/ports/stm32/mpconfigport.mk +++ b/ports/stm32/mpconfigport.mk @@ -1,4 +1,4 @@ -# Enable/disable extra modules +# Enable/disable extra modules and features # wiznet5k module for ethernet support; valid values are: # 0 : no Wiznet support @@ -11,3 +11,8 @@ MICROPY_PY_CC3K ?= 0 # VFS FAT FS support MICROPY_VFS_FAT ?= 1 + +# Encrypted/signed bootloader support (ensure the MBOOT_PACK_xxx values match stm32/mboot/Makefile) +MBOOT_ENABLE_PACKING ?= 0 +MBOOT_PACK_CHUNKSIZE ?= 16384 +MBOOT_PACK_KEYS_FILE ?= $(BOARD_DIR)/mboot_keys.h diff --git a/ports/stm32/pin_defs_stm32.h b/ports/stm32/pin_defs_stm32.h index 33a179080..9285c190a 100644 --- a/ports/stm32/pin_defs_stm32.h +++ b/ports/stm32/pin_defs_stm32.h @@ -47,6 +47,7 @@ enum { AF_FN_I2C, AF_FN_USART, AF_FN_UART = AF_FN_USART, + AF_FN_LPUART, AF_FN_SPI, AF_FN_I2S, AF_FN_SDMMC, @@ -77,6 +78,10 @@ enum { AF_PIN_TYPE_UART_RX = AF_PIN_TYPE_USART_RX, AF_PIN_TYPE_UART_CTS = AF_PIN_TYPE_USART_CTS, AF_PIN_TYPE_UART_RTS = AF_PIN_TYPE_USART_RTS, + AF_PIN_TYPE_LPUART_TX = AF_PIN_TYPE_USART_TX, + AF_PIN_TYPE_LPUART_RX = AF_PIN_TYPE_USART_RX, + AF_PIN_TYPE_LPUART_CTS = AF_PIN_TYPE_USART_CTS, + AF_PIN_TYPE_LPUART_RTS = AF_PIN_TYPE_USART_RTS, AF_PIN_TYPE_SPI_MOSI = 0, AF_PIN_TYPE_SPI_MISO, diff --git a/ports/stm32/rfcore.c b/ports/stm32/rfcore.c index 7d0a8520a..55fc229dd 100644 --- a/ports/stm32/rfcore.c +++ b/ports/stm32/rfcore.c @@ -434,10 +434,16 @@ STATIC void tl_process_msg(volatile tl_list_node_t *head, unsigned int ch, parse // Only call this when IRQs are disabled on this channel. STATIC void tl_check_msg(volatile tl_list_node_t *head, unsigned int ch, parse_hci_info_t *parse) { if (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, ch)) { + // Process new data. tl_process_msg(head, ch, parse); - // Clear receive channel. + // Clear receive channel (allows RF core to send more data to us). LL_C1_IPCC_ClearFlag_CHx(IPCC, ch); + + if (ch == IPCC_CH_BLE) { + // Renable IRQs for BLE now that we've cleared the flag. + LL_C1_IPCC_EnableReceiveChannel(IPCC, IPCC_CH_BLE); + } } } @@ -495,17 +501,17 @@ STATIC int tl_ble_wait_resp(void) { } } - // C2 set IPCC flag. + // C2 set IPCC flag -- process the data, clear the flag, and re-enable IRQs. tl_check_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, NULL); return 0; } // Synchronously send a BLE command. STATIC void tl_ble_hci_cmd_resp(uint16_t opcode, const uint8_t *buf, size_t len) { + // Poll for completion rather than wait for IRQ->scheduler. LL_C1_IPCC_DisableReceiveChannel(IPCC, IPCC_CH_BLE); tl_hci_cmd(ipcc_membuf_ble_cmd_buf, IPCC_CH_BLE, HCI_KIND_BT_CMD, opcode, buf, len); tl_ble_wait_resp(); - LL_C1_IPCC_EnableReceiveChannel(IPCC, IPCC_CH_BLE); } /******************************************************************************/ @@ -542,30 +548,30 @@ static const struct { uint16_t AttMtu; uint16_t SlaveSca; uint8_t MasterSca; - uint8_t LsSource; // 0=LSE 1=internal RO + uint8_t LsSource; uint32_t MaxConnEventLength; uint16_t HsStartupTime; uint8_t ViterbiEnable; - uint8_t LlOnly; // 0=LL+Host, 1=LL only + uint8_t LlOnly; uint8_t HwVersion; } ble_init_params = { - 0, - 0, - 0, // NumAttrRecord - 0, // NumAttrServ - 0, // AttrValueArrSize - 1, // NumOfLinks - 1, // ExtendedPacketLengthEnable - 0, // PrWriteListSize - 0x79, // MblockCount - 0, // AttMtu - 0, // SlaveSca - 0, // MasterSca - 1, // LsSource - 0xffffffff, // MaxConnEventLength - 0x148, // HsStartupTime - 0, // ViterbiEnable - 1, // LlOnly + 0, // pBleBufferAddress + 0, // BleBufferSize + MICROPY_HW_RFCORE_BLE_NUM_GATT_ATTRIBUTES, + MICROPY_HW_RFCORE_BLE_NUM_GATT_SERVICES, + MICROPY_HW_RFCORE_BLE_ATT_VALUE_ARRAY_SIZE, + MICROPY_HW_RFCORE_BLE_NUM_LINK, + MICROPY_HW_RFCORE_BLE_DATA_LENGTH_EXTENSION, + MICROPY_HW_RFCORE_BLE_PREPARE_WRITE_LIST_SIZE, + MICROPY_HW_RFCORE_BLE_MBLOCK_COUNT, + MICROPY_HW_RFCORE_BLE_MAX_ATT_MTU, + MICROPY_HW_RFCORE_BLE_SLAVE_SCA, + MICROPY_HW_RFCORE_BLE_MASTER_SCA, + MICROPY_HW_RFCORE_BLE_LSE_SOURCE, + MICROPY_HW_RFCORE_BLE_MAX_CONN_EVENT_LENGTH, + MICROPY_HW_RFCORE_BLE_HSE_STARTUP_TIME, + MICROPY_HW_RFCORE_BLE_VITERBI_MODE, + MICROPY_HW_RFCORE_BLE_LL_ONLY, 0, // HwVersion }; @@ -632,7 +638,7 @@ void rfcore_ble_hci_cmd(size_t len, const uint8_t *src) { void rfcore_ble_check_msg(int (*cb)(void *, const uint8_t *, size_t), void *env) { parse_hci_info_t parse = { cb, env, false }; - tl_process_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, &parse); + tl_check_msg(&ipcc_mem_ble_evt_queue, IPCC_CH_BLE, &parse); // Intercept HCI_Reset events and reconfigure the controller following the reset if (parse.was_hci_reset_evt) { @@ -679,7 +685,8 @@ void IPCC_C1_RX_IRQHandler(void) { DEBUG_printf("IPCC_C1_RX_IRQHandler\n"); if (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, IPCC_CH_BLE)) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, IPCC_CH_BLE); + // Disable this IRQ until the incoming data is processed (in tl_check_msg). + LL_C1_IPCC_DisableReceiveChannel(IPCC, IPCC_CH_BLE); #if MICROPY_PY_BLUETOOTH // Queue up the scheduler to process UART data and run events. diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index bd898d455..02b0f2dbd 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -43,7 +43,7 @@ /// \moduleref pyb /// \class RTC - real time clock /// -/// The RTC is and independent clock that keeps track of the date +/// The RTC is an independent clock that keeps track of the date /// and time. /// /// Example usage: diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index 8e96da177..dc96e2dd6 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -742,11 +742,13 @@ void USART1_IRQHandler(void) { IRQ_EXIT(USART1_IRQn); } +#if defined(USART2) void USART2_IRQHandler(void) { IRQ_ENTER(USART2_IRQn); uart_irq_handler(2); IRQ_EXIT(USART2_IRQn); } +#endif #if defined(STM32F0) @@ -772,29 +774,37 @@ void USART4_5_IRQHandler(void) { #else +#if defined(USART3) void USART3_IRQHandler(void) { IRQ_ENTER(USART3_IRQn); uart_irq_handler(3); IRQ_EXIT(USART3_IRQn); } +#endif +#if defined(UART4) void UART4_IRQHandler(void) { IRQ_ENTER(UART4_IRQn); uart_irq_handler(4); IRQ_EXIT(UART4_IRQn); } +#endif +#if defined(UART5) void UART5_IRQHandler(void) { IRQ_ENTER(UART5_IRQn); uart_irq_handler(5); IRQ_EXIT(UART5_IRQn); } +#endif +#if defined(USART6) void USART6_IRQHandler(void) { IRQ_ENTER(USART6_IRQn); uart_irq_handler(6); IRQ_EXIT(USART6_IRQn); } +#endif #if defined(UART7) void UART7_IRQHandler(void) { @@ -830,6 +840,14 @@ void UART10_IRQHandler(void) { #endif +#if defined(LPUART1) +void LPUART1_IRQHandler(void) { + IRQ_ENTER(LPUART1_IRQn); + uart_irq_handler(PYB_LPUART_1); + IRQ_EXIT(LPUART1_IRQn); +} +#endif + #if MICROPY_PY_PYB_LEGACY #if defined(MICROPY_HW_I2C1_SCL) diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index b14a18c6d..d40a883c5 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -201,6 +201,11 @@ bool uart_exists(int uart_id) { return true; #endif + #if defined(MICROPY_HW_LPUART1_TX) && defined(MICROPY_HW_LPUART1_RX) + case PYB_LPUART_1: + return true; + #endif + default: return false; } @@ -211,6 +216,7 @@ bool uart_init(pyb_uart_obj_t *uart_obj, uint32_t baudrate, uint32_t bits, uint32_t parity, uint32_t stop, uint32_t flow) { USART_TypeDef *UARTx; IRQn_Type irqn; + uint8_t uart_fn = AF_FN_UART; int uart_unit; const pin_obj_t *pins[4] = {0}; @@ -406,6 +412,28 @@ bool uart_init(pyb_uart_obj_t *uart_obj, break; #endif + #if defined(MICROPY_HW_LPUART1_TX) && defined(MICROPY_HW_LPUART1_RX) + case PYB_LPUART_1: + uart_fn = AF_FN_LPUART; + uart_unit = 1; + UARTx = LPUART1; + irqn = LPUART1_IRQn; + pins[0] = MICROPY_HW_LPUART1_TX; + pins[1] = MICROPY_HW_LPUART1_RX; + #if defined(MICROPY_HW_LPUART1_RTS) + if (flow & UART_HWCONTROL_RTS) { + pins[2] = MICROPY_HW_LPUART1_RTS; + } + #endif + #if defined(MICROPY_HW_LPUART1_CTS) + if (flow & UART_HWCONTROL_CTS) { + pins[3] = MICROPY_HW_LPUART1_CTS; + } + #endif + __HAL_RCC_LPUART1_CLK_ENABLE(); + break; + #endif + default: // UART does not exist or is not configured for this board return false; @@ -416,7 +444,7 @@ bool uart_init(pyb_uart_obj_t *uart_obj, for (uint i = 0; i < 4; i++) { if (pins[i] != NULL) { - bool ret = mp_hal_pin_config_alt(pins[i], mode, pull, AF_FN_UART, uart_unit); + bool ret = mp_hal_pin_config_alt(pins[i], mode, pull, uart_fn, uart_unit); if (!ret) { return false; } @@ -596,6 +624,13 @@ void uart_deinit(pyb_uart_obj_t *self) { __HAL_RCC_UART10_RELEASE_RESET(); __HAL_RCC_UART10_CLK_DISABLE(); #endif + #if defined(LPUART1) + } else if (self->uart_id == PYB_LPUART_1) { + HAL_NVIC_DisableIRQ(LPUART1_IRQn); + __HAL_RCC_LPUART1_FORCE_RESET(); + __HAL_RCC_LPUART1_RELEASE_RESET(); + __HAL_RCC_LPUART1_CLK_DISABLE(); + #endif } } @@ -603,7 +638,7 @@ void uart_attach_to_repl(pyb_uart_obj_t *self, bool attached) { self->attached_to_repl = attached; } -uint32_t uart_get_baudrate(pyb_uart_obj_t *self) { +uint32_t uart_get_source_freq(pyb_uart_obj_t *self) { uint32_t uart_clk = 0; #if defined(STM32F0) @@ -672,10 +707,28 @@ uint32_t uart_get_baudrate(pyb_uart_obj_t *self) { } #endif + return uart_clk; +} + +uint32_t uart_get_baudrate(pyb_uart_obj_t *self) { // This formula assumes UART_OVERSAMPLING_16 - uint32_t baudrate = uart_clk / self->uartx->BRR; + uint32_t source_freq = uart_get_source_freq(self); + #if defined(LPUART1) + if (self->uart_id == PYB_LPUART_1) { + return source_freq / (self->uartx->BRR >> 8); + } else + #endif + { + return source_freq / self->uartx->BRR; + } +} - return baudrate; +void uart_set_baudrate(pyb_uart_obj_t *self, uint32_t baudrate) { + LL_USART_SetBaudRate(self->uartx, uart_get_source_freq(self), + #if defined(STM32H7) || defined(STM32WB) + LL_USART_PRESCALER_DIV1, + #endif + LL_USART_OVERSAMPLING_16, baudrate); } mp_uint_t uart_rx_any(pyb_uart_obj_t *self) { diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h index 9a38db593..0490a617f 100644 --- a/ports/stm32/uart.h +++ b/ports/stm32/uart.h @@ -40,6 +40,9 @@ typedef enum { PYB_UART_8 = 8, PYB_UART_9 = 9, PYB_UART_10 = 10, + #ifdef LPUART1 + PYB_LPUART_1 = MICROPY_HW_MAX_UART + 1, + #endif } pyb_uart_t; #define CHAR_WIDTH_8BIT (0) @@ -86,6 +89,8 @@ void uart_irq_handler(mp_uint_t uart_id); void uart_attach_to_repl(pyb_uart_obj_t *self, bool attached); uint32_t uart_get_baudrate(pyb_uart_obj_t *self); +void uart_set_baudrate(pyb_uart_obj_t *self, uint32_t baudrate); + mp_uint_t uart_rx_any(pyb_uart_obj_t *uart_obj); bool uart_rx_wait(pyb_uart_obj_t *self, uint32_t timeout); int uart_rx_char(pyb_uart_obj_t *uart_obj); diff --git a/ports/stm32/usb.h b/ports/stm32/usb.h index f69af86e5..295038ebd 100644 --- a/ports/stm32/usb.h +++ b/ports/stm32/usb.h @@ -30,6 +30,7 @@ #define PYB_USB_FLAG_USB_MODE_CALLED (0x0002) +#ifndef USBD_VID // Windows needs a different PID to distinguish different device configurations #define USBD_VID (0xf055) #define USBD_PID_CDC_MSC (0x9800) @@ -43,6 +44,7 @@ #define USBD_PID_CDC_MSC_HID (0x9808) #define USBD_PID_CDC2_MSC_HID (0x9809) #define USBD_PID_CDC3_MSC_HID (0x980a) +#endif typedef enum { PYB_USB_STORAGE_MEDIUM_NONE = 0, diff --git a/ports/stm32/usbd_cdc_interface.c b/ports/stm32/usbd_cdc_interface.c index a8261a958..a465f608a 100644 --- a/ports/stm32/usbd_cdc_interface.c +++ b/ports/stm32/usbd_cdc_interface.c @@ -382,6 +382,10 @@ void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) { uint32_t start = HAL_GetTick(); while (usbd_cdc_tx_buffer_full(cdc) && HAL_GetTick() - start <= 500) { usbd_cdc_try_tx(cdc); + if (cdc->base.usbd->pdev->dev_state == USBD_STATE_SUSPENDED) { + // The USB is suspended so buffer will never be drained; exit loop + break; + } if (query_irq() == IRQ_STATE_DISABLED) { // IRQs disabled so buffer will never be drained; exit loop break; diff --git a/ports/teensy/mpconfigport.h b/ports/teensy/mpconfigport.h index c00da5ed9..3acedcf02 100644 --- a/ports/teensy/mpconfigport.h +++ b/ports/teensy/mpconfigport.h @@ -62,8 +62,6 @@ typedef int32_t mp_int_t; // must be pointer size typedef unsigned int mp_uint_t; // must be pointer size typedef long mp_off_t; -#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) - // We have inlined IRQ functions for efficiency (they are generally // 1 machine instruction). // diff --git a/ports/unix/main.c b/ports/unix/main.c index 6f85cbf8d..07db8d22c 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -387,7 +387,7 @@ STATIC void pre_process_options(int argc, char **argv) { goto invalid_arg; } if (word_adjust) { - heap_size = heap_size * BYTES_PER_WORD / 4; + heap_size = heap_size * MP_BYTES_PER_OBJ_WORD / 4; } // If requested size too small, we'll crash anyway if (heap_size < 700) { @@ -446,7 +446,7 @@ MP_NOINLINE int main_(int argc, char **argv) { signal(SIGPIPE, SIG_IGN); #endif - mp_stack_set_limit(40000 * (BYTES_PER_WORD / 4)); + mp_stack_set_limit(40000 * (sizeof(void *) / 4)); pre_process_options(argc, argv); @@ -532,8 +532,8 @@ MP_NOINLINE int main_(int argc, char **argv) { { MP_DECLARE_CONST_FUN_OBJ_0(extra_coverage_obj); MP_DECLARE_CONST_FUN_OBJ_0(extra_cpp_coverage_obj); - mp_store_global(QSTR_FROM_STR_STATIC("extra_coverage"), MP_OBJ_FROM_PTR(&extra_coverage_obj)); - mp_store_global(QSTR_FROM_STR_STATIC("extra_cpp_coverage"), MP_OBJ_FROM_PTR(&extra_cpp_coverage_obj)); + mp_store_global(MP_QSTR_extra_coverage, MP_OBJ_FROM_PTR(&extra_coverage_obj)); + mp_store_global(MP_QSTR_extra_cpp_coverage, MP_OBJ_FROM_PTR(&extra_cpp_coverage_obj)); } #endif @@ -546,9 +546,9 @@ MP_NOINLINE int main_(int argc, char **argv) { // test_obj.attr = 42 // // mp_obj_t test_class_type, test_class_instance; - // test_class_type = mp_obj_new_type(QSTR_FROM_STR_STATIC("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0)); - // mp_store_name(QSTR_FROM_STR_STATIC("test_obj"), test_class_instance = mp_call_function_0(test_class_type)); - // mp_store_attr(test_class_instance, QSTR_FROM_STR_STATIC("attr"), mp_obj_new_int(42)); + // test_class_type = mp_obj_new_type(qstr_from_str("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0)); + // mp_store_name(qstr_from_str("test_obj"), test_class_instance = mp_call_function_0(test_class_type)); + // mp_store_attr(test_class_instance, qstr_from_str("attr"), mp_obj_new_int(42)); /* printf("bytes:\n"); diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c index e08409e20..91c0a1941 100644 --- a/ports/unix/modtime.c +++ b/ports/unix/modtime.c @@ -67,7 +67,7 @@ static inline int msec_sleep_tv(struct timeval *tv) { #endif STATIC mp_obj_t mod_time_time(void) { - #if MICROPY_PY_BUILTINS_FLOAT + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE struct timeval tv; gettimeofday(&tv, NULL); mp_float_t val = tv.tv_sec + (mp_float_t)tv.tv_usec / 1000000; @@ -137,7 +137,7 @@ STATIC mp_obj_t mod_time_gm_local_time(size_t n_args, const mp_obj_t *args, stru if (n_args == 0) { t = time(NULL); } else { - #if MICROPY_PY_BUILTINS_FLOAT + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE mp_float_t val = mp_obj_get_float(args[0]); t = (time_t)MICROPY_FLOAT_C_FUN(trunc)(val); #else diff --git a/ports/unix/moduselect.c b/ports/unix/moduselect.c index 6a0ee79aa..a9390a146 100644 --- a/ports/unix/moduselect.c +++ b/ports/unix/moduselect.c @@ -29,6 +29,10 @@ #if MICROPY_PY_USELECT_POSIX +#if MICROPY_PY_USELECT +#error "Can't have both MICROPY_PY_USELECT and MICROPY_PY_USELECT_POSIX." +#endif + #include #include #include diff --git a/ports/unix/mpbtstackport_common.c b/ports/unix/mpbtstackport_common.c index 621e661f9..ec40db65b 100644 --- a/ports/unix/mpbtstackport_common.c +++ b/ports/unix/mpbtstackport_common.c @@ -57,6 +57,11 @@ bool mp_bluetooth_hci_poll(void) { return false; } +bool mp_bluetooth_hci_active(void) { + return mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_OFF + && mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT; +} + // The IRQ functionality in btstack_run_loop_embedded.c is not used, so the // following three functions are empty. diff --git a/ports/unix/mphalport.h b/ports/unix/mphalport.h index 89d23ca5d..03da1e3d8 100644 --- a/ports/unix/mphalport.h +++ b/ports/unix/mphalport.h @@ -30,7 +30,7 @@ #define CHAR_CTRL_C (3) #endif -void mp_hal_set_interrupt_char(char c); +void mp_hal_set_interrupt_char(int c); #define mp_hal_stdio_poll unused // this is not implemented, nor needed void mp_hal_stdio_mode_raw(void); diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index de0f5923b..cbc4901f6 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -206,7 +206,7 @@ void mp_thread_start(void) { void mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) { // default stack size is 8k machine-words if (*stack_size == 0) { - *stack_size = 8192 * BYTES_PER_WORD; + *stack_size = 8192 * sizeof(void *); } // minimum stack size is set by pthreads diff --git a/ports/wapy-unix/main.c b/ports/wapy-unix/main.c index 58bb9a13e..00b974c3c 100644 --- a/ports/wapy-unix/main.c +++ b/ports/wapy-unix/main.c @@ -51,7 +51,7 @@ #include "extmod/vfs_posix.h" #include "genhdr/mpversion.h" #include "input.h" - +mp_state_ctx_t mp_state_ctx; // Command line options, with their defaults STATIC bool compile_only = false; STATIC uint emit_opt = MP_EMIT_OPT_NONE; @@ -426,7 +426,7 @@ STATIC void pre_process_options(int argc, char **argv) { goto invalid_arg; } if (word_adjust) { - heap_size = heap_size * BYTES_PER_WORD / 4; + heap_size = heap_size * (sizeof(void *)) / 4; } // If requested size too small, we'll crash anyway if (heap_size < 700) { @@ -494,7 +494,7 @@ MP_NOINLINE int main_(int argc, char **argv) { signal(SIGPIPE, SIG_IGN); #endif - mp_stack_set_limit(40000 * (BYTES_PER_WORD / 4)); + mp_stack_set_limit(40000 * (sizeof(void *) / 4)); pre_process_options(argc, argv); diff --git a/ports/wapy-unix/mphalport.h b/ports/wapy-unix/mphalport.h index 3c7c3a878..3bc442165 100644 --- a/ports/wapy-unix/mphalport.h +++ b/ports/wapy-unix/mphalport.h @@ -30,7 +30,7 @@ #define CHAR_CTRL_C (3) #endif -void mp_hal_set_interrupt_char(char c); +void mp_hal_set_interrupt_char(int c); #define mp_hal_stdio_poll unused // this is not implemented, nor needed void mp_hal_stdio_mode_raw(void); diff --git a/ports/wapy-unix/mpthreadport.c b/ports/wapy-unix/mpthreadport.c index c24851576..f8369f6a6 100644 --- a/ports/wapy-unix/mpthreadport.c +++ b/ports/wapy-unix/mpthreadport.c @@ -199,7 +199,7 @@ void mp_thread_start(void) { void mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) { // default stack size is 8k machine-words if (*stack_size == 0) { - *stack_size = 8192 * BYTES_PER_WORD; + *stack_size = 8192 * (sizeof(void *)); } // minimum stack size is set by pthreads diff --git a/ports/wapy-unix/unix_mphal.c b/ports/wapy-unix/unix_mphal.c index 86a7374ad..dff3b869a 100644 --- a/ports/wapy-unix/unix_mphal.c +++ b/ports/wapy-unix/unix_mphal.c @@ -68,7 +68,7 @@ STATIC void sighandler(int signum) { } #endif -void mp_hal_set_interrupt_char(char c) { +void mp_hal_set_interrupt_char(int c) { // configure terminal settings to (not) let ctrl-C through if (c == CHAR_CTRL_C) { #ifndef _WIN32 diff --git a/ports/wapy-wasi/Makefile b/ports/wapy-wasi/Makefile index 99efbde70..52c413b57 100644 --- a/ports/wapy-wasi/Makefile +++ b/ports/wapy-wasi/Makefile @@ -28,7 +28,7 @@ include ../../py/mkenv.mk QSTR_DEFS = qstrdefsport.h -CX = --target=wasm32-unknow-wasi -D__WASI__=1 -DNO_NLR=1 +CX = --sysroot=/data/git/wapy-pack/wasi-sdk/share/wasi-sysroot --target=wasm32-unknow-wasi -D__WASI__=1 -DNO_NLR=1 CC ?= clang CC += $(CX) diff --git a/ports/wapy-wasi/main.c b/ports/wapy-wasi/main.c index 52abfd899..63bb1974f 100644 --- a/ports/wapy-wasi/main.c +++ b/ports/wapy-wasi/main.c @@ -58,7 +58,7 @@ copy_argv(int argc, char *argv[]) { } -#include "upython.c" +#include "../wapy/upython.c" #include "vmsl/vmreg.h" @@ -250,6 +250,18 @@ static inline mp_map_elem_t *mp_map_cached_lookup(mp_map_t *map, qstr qst, uint8 int io_encode_hex = 1; //static int loops = 0; + +#ifdef __ARDUINO__ + #ifndef __CPP__ + #include ARDUINO_HAL + #endif +#endif + + + + + + int main(int argc, char *argv[]) { diff --git a/ports/wapy-wasi/mpconfigport.h b/ports/wapy-wasi/mpconfigport.h index a6ebafc8a..88898fe9b 100644 --- a/ports/wapy-wasi/mpconfigport.h +++ b/ports/wapy-wasi/mpconfigport.h @@ -335,7 +335,7 @@ #define UINT_FMT "%u" #define INT_FMT "%d" typedef int mp_int_t; // must be pointer size -typedef unsigned mp_uint_t; // must be pointer size +typedef unsigned int mp_uint_t; // must be pointer size typedef long mp_off_t; diff --git a/ports/wapy-wasm/mod/modtime.c b/ports/wapy-wasm/mod/modtime.c index 3a839fe7f..9f5bd0efc 100644 --- a/ports/wapy-wasm/mod/modtime.c +++ b/ports/wapy-wasm/mod/modtime.c @@ -31,6 +31,7 @@ #include #include #include +#include #if defined(__EMSCRIPTEN__) #include diff --git a/ports/wapy-wasm/mphalport.h b/ports/wapy-wasm/mphalport.h index 95de747d7..e610f29f0 100644 --- a/ports/wapy-wasm/mphalport.h +++ b/ports/wapy-wasm/mphalport.h @@ -11,7 +11,7 @@ #endif -static inline void mp_hal_set_interrupt_char(char c) {} +//static inline void mp_hal_set_interrupt_char(char c) {} // TODO: wasm does not sleep /* diff --git a/ports/wapy/cmod/wasi/embed/modembed.c b/ports/wapy/cmod/wasi/embed/modembed.c index 0311aa1a6..ce58d6973 100644 --- a/ports/wapy/cmod/wasi/embed/modembed.c +++ b/ports/wapy/cmod/wasi/embed/modembed.c @@ -1,6 +1,6 @@ /* http://github.com/pmp-p target pym:/data/cross/wapy/cmod/wasi/embed.pym */ /* - embed.c AUTO-GENERATED by /data/cross/wapy/wapy-lib/modgen/__main__.py + embed.c AUTO-GENERATED by /data/git/wapy-lib/modgen/__main__.py */ // ======= STATIC HEADER ======== @@ -40,7 +40,7 @@ void print(mp_obj_t str) { void null_pointer_exception(void) { - fprintf(stderr, "null pointer exception in function pointer call\n"); + fprintf(stderr, "59: null pointer exception in function pointer call\n"); } mp_obj_t @@ -77,6 +77,9 @@ static int show_os_loop(int state) { extern struct timespec t_timespec; extern struct timeval t_timeval; +#if defined(__WASI__) + extern FILE *fd_logger; +#endif #include "py/emitglue.h" @@ -176,7 +179,7 @@ pycore(const char *fn) { mp_obj_t qst = MP_OBJ_NEW_QSTR(qfn); // ------- method body (try/finally) ----- - fprintf(stderr,"FFYPY[%p->%s]\n", ffpy, fn ); + fprintf(fd_logger, "FFYPY[%p->%s]\n", ffpy, fn ); if (ffpy) { mp_call_function_n_kw((mp_obj_t *)ffpy, 1, 0, &qst); } @@ -275,7 +278,7 @@ embed_run(size_t argc, const mp_obj_t *argv) { if ( ln < cstr_max ) { strcpy(cstr,runstr); } else { - fprintf(stderr, "buffer overrun in embed.run %zu >= %zu", ln, cstr_max); + fprintf(fd_logger, "buffer overrun in embed.run %zu >= %zu", ln, cstr_max); }; return mp_const_none; } @@ -386,7 +389,7 @@ embed_os_stderr(size_t argc, const mp_obj_t *argv) { // ------- method body (try/finally) ----- - fprintf( stderr, "embed.os_stderr(%s)\n", data ); + fprintf(stderr, "embed.os_stderr(%s)\n", data ); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(embed_os_stderr_obj, 0, 1, embed_os_stderr); @@ -405,7 +408,7 @@ embed_log(size_t argc, const mp_obj_t *argv) { // ------- method body (try/finally) ----- - fprintf( stderr, "%s\n", data ); + fprintf(fd_logger, "py: %s\n", data ); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(embed_log_obj, 0, 1, embed_log); @@ -465,7 +468,7 @@ embed_os_compile(size_t argc, const mp_obj_t *argv) { mp_raw_code_t *rc = mp_compile_to_raw_code(&parse_tree, qstr_from_str(source_file), false); - fprintf(stderr,"os_compile: MICROPY_PERSISTENT_CODE_SAVE not enabled\n"); + fprintf(fd_logger, "os_compile: MICROPY_PERSISTENT_CODE_SAVE not enabled\n"); return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(embed_os_compile_obj, 0, 2, embed_os_compile); @@ -497,7 +500,7 @@ embed_os_showloop(size_t argc, const mp_obj_t *argv) { // opt : void return // ------- method body (try/finally) ----- - fprintf(stderr,"will show begin/end for os loop\n"); + fprintf(fd_logger, "will show begin/end for os loop\n"); show_os_loop(1); return mp_const_none; } @@ -511,7 +514,7 @@ embed_os_hideloop(size_t argc, const mp_obj_t *argv) { // opt : void return // ------- method body (try/finally) ----- - fprintf(stderr,"will hide begin/end for os loop\n"); + fprintf(fd_logger, "will hide begin/end for os loop\n"); show_os_loop(0); return mp_const_none; } @@ -643,7 +646,7 @@ embed_callpy(size_t argc, const mp_obj_t *argv) { // ------- method body (try/finally) ----- - fprintf(stderr,"embed.callpy[%s]\n", fn ); + fprintf(fd_logger, "embed.callpy[%s]\n", fn ); pycore(fn); return mp_const_none; } @@ -663,12 +666,12 @@ embed_hash_djb2(size_t argc, const mp_obj_t *argv) { // ------- method body (try/finally) ----- - unsigned long hash = 5381; - int c; - while ((c = *cstr++)) { - hash = ((hash << 5) + hash) + c; - } - { __creturn__ = (unsigned_long)hash % 0xFFFFFFFF; goto lreturn__; }; + unsigned long hash = 5381; + int c; + while ((c = *cstr++)) { + hash = ((hash << 5) + hash) + c; + } + { __creturn__ = (unsigned_long)hash % 0xFFFFFFFF; goto lreturn__; }; lreturn__: return mp_obj_new_int_from_unsigned_long(__creturn__); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(embed_hash_djb2_obj, 0, 1, embed_hash_djb2); @@ -682,7 +685,7 @@ embed_show_trace(size_t argc, const mp_obj_t *argv) { // ------- method body (try/finally) ----- trace_on = 1; - fprintf(stderr,"TRACE[%s:%zu -> %s:%zu]\n", qstr_str(trace_prev_file), trace_prev_line, qstr_str(trace_file), trace_line); + fprintf(fd_logger, "TRACE[%s:%zu -> %s:%zu]\n", qstr_str(trace_prev_file), trace_prev_line, qstr_str(trace_file), trace_line); { __creturn__ = (long)trace_prev_line-1; goto lreturn__; }; lreturn__: return mp_obj_new_int(__creturn__); } @@ -725,7 +728,7 @@ embed_set_ffpy(size_t argc, const mp_obj_t *argv) { // ------- method body (try/finally) ----- - fprintf(stderr,"embed.ffpy[%p]\n", fn ); + fprintf(fd_logger, "embed.ffpy[%p]\n", fn ); ffpy = (mp_obj_t *)fn; return mp_const_none; } @@ -746,7 +749,7 @@ embed_set_ffpy_add(size_t argc, const mp_obj_t *argv) { // ------- method body (try/finally) ----- - fprintf(stderr,"embed.ffpy[%p]\n", fn ); + fprintf(fd_logger, "embed.ffpy[%p]\n", fn ); ffpy_add = (mp_obj_t *)fn; return mp_const_none; } @@ -766,7 +769,7 @@ embed_corepy(size_t argc, const mp_obj_t *argv) { // ------- method body (try/finally) ----- - fprintf(stderr,"embed.ffipy[%p(%s)]\n", ffpy, fn ); + fprintf(fd_logger, "embed.ffipy[%p(%s)]\n", ffpy, fn ); if (ffpy) { mp_call_function_n_kw((mp_obj_t *)ffpy, 1, 0, &argv[0]); } diff --git a/ports/wapy/core/ringbuf_o.c b/ports/wapy/core/ringbuf_o.c index 8e162382d..d87802828 100644 --- a/ports/wapy/core/ringbuf_o.c +++ b/ports/wapy/core/ringbuf_o.c @@ -27,9 +27,9 @@ typedef struct rbo_t { //public: // Returns true if full - bool (*isFull) (rbo_t*); + uint8_t (*isFull) (rbo_t*); // Returns true if empty - bool (*isEmpty) (rbo_t*); + uint8_t (*isEmpty) (rbo_t*); // Returns number of elemnts in buffer unsigned int (*numElements)(rbo_t*); // Add Event, Returns index where added in buffer, -1 on full buffer @@ -191,9 +191,9 @@ rbo_count(rbo_t *self) { } // Returns true if buffer is full -bool +uint8_t rbo_is_full(rbo_t *self) { - bool ret; + uint8_t ret; // Perform all atomic opertaions RB_ATOMIC_START @@ -206,9 +206,9 @@ rbo_is_full(rbo_t *self) { } // Returns true if buffer is empty -bool +uint8_t rbo_is_empty(rbo_t *self) { - bool ret; + uint8_t ret; // Perform all atomic opertaions RB_ATOMIC_START diff --git a/ports/wapy/core/ringbuf_o.h b/ports/wapy/core/ringbuf_o.h index 2f9697d44..07fb7ae58 100644 --- a/ports/wapy/core/ringbuf_o.h +++ b/ports/wapy/core/ringbuf_o.h @@ -5,25 +5,23 @@ January 17, 2015. Released into the public domain. */ + + #ifndef RINGBUF_O_H #define RINGBUF_O_H #if __ARDUINO__ - #include "Arduino.h" - #define RB_ATOMIC_START ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { - #define RB_ATOMIC_END } + #if defined(__WASI__) + #else + #include "Arduino.h" + #define RB_ATOMIC_START ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + #define RB_ATOMIC_END } + #endif #else #define RB_ATOMIC_START #define RB_ATOMIC_END #endif - -#ifndef __cplusplus - #ifndef bool - #define bool uint8_t - #endif -#endif - #if defined(ARDUINO_ARCH_AVR) #include @@ -58,7 +56,6 @@ typedef struct rbo_t rbo_t; - #ifdef __cplusplus extern "C" { #endif @@ -74,8 +71,8 @@ int rbo_incr_start(rbo_t *self); int rbo_append(rbo_t *self, const void *object); void *rbo_peek(rbo_t *self, unsigned int num); void *rbo_pop(rbo_t *self, void *object); -bool rbo_is_full(rbo_t *self); -bool rbo_is_empty(rbo_t *self); +uint8_t rbo_is_full(rbo_t *self); +uint8_t rbo_is_empty(rbo_t *self); unsigned int rbo_count(rbo_t *self); #ifdef __cplusplus @@ -92,8 +89,8 @@ class rbo_tC rbo_tC(int size, int len) { buf = rbo_t_new(size, len); } ~rbo_tC() { rbo_delete(buf); } - bool isFull() { return rbo_is_full(buf); } - bool isEmpty() { return rbo_is_empty(buf); } + uint8_t isFull() { return rbo_is_full(buf); } + uint8_t isEmpty() { return rbo_is_empty(buf); } unsigned int numElements() { return rbo_count(buf); } unsigned int add(const void *object) { return rbo_append(buf, object); } @@ -101,7 +98,7 @@ class rbo_tC void *pull(void *object) { return rbo_pop(buf, object); } // Use this to check if memory allocation failed - bool allocFailed() { return !buf; } + uint8_t allocFailed() { return !buf; } private: rbo_t *buf; diff --git a/ports/wapy/qstrdefsport.h b/ports/wapy/qstrdefsport.h index 8326cb082..010b55d19 100644 --- a/ports/wapy/qstrdefsport.h +++ b/ports/wapy/qstrdefsport.h @@ -27,6 +27,7 @@ Q(heap_lock) Q(heap_unlock) Q(ld) Q(unref) +Q(ffi) Q(ptr) Q(use) Q(srv) diff --git a/ports/wapy/runtime_no_nlr.c b/ports/wapy/runtime_no_nlr.c index 1a65d6ad2..2ee3c4d86 100644 --- a/ports/wapy/runtime_no_nlr.c +++ b/ports/wapy/runtime_no_nlr.c @@ -398,7 +398,9 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { if (rhs_val < 0) { // negative shift not allowed mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); - } else if (rhs_val >= (mp_int_t)BITS_PER_WORD || lhs_val > (MP_SMALL_INT_MAX >> rhs_val) || lhs_val < (MP_SMALL_INT_MIN >> rhs_val)) { + } else if (rhs_val >= (mp_int_t)(sizeof(lhs_val) * MP_BITS_PER_BYTE) + || lhs_val > (MP_SMALL_INT_MAX >> rhs_val) + || lhs_val < (MP_SMALL_INT_MIN >> rhs_val)) { // left-shift will overflow, so use higher precision integer lhs = mp_obj_new_int_from_ll(lhs_val); goto generic_binary_op; @@ -415,10 +417,10 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); } else { // standard precision is enough for right-shift - if (rhs_val >= (mp_int_t)BITS_PER_WORD) { + if (rhs_val >= (mp_int_t)(sizeof(lhs_val) * MP_BITS_PER_BYTE)) { // Shifting to big amounts is underfined behavior // in C and is CPU-dependent; propagate sign bit. - rhs_val = BITS_PER_WORD - 1; + rhs_val = sizeof(lhs_val) * MP_BITS_PER_BYTE - 1; } lhs_val >>= rhs_val; } diff --git a/ports/wapy/runtime_no_nlr.h b/ports/wapy/runtime_no_nlr.h index fadb7e6cc..268d717c0 100644 --- a/ports/wapy/runtime_no_nlr.h +++ b/ports/wapy/runtime_no_nlr.h @@ -163,8 +163,8 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level); mp_obj_t mp_import_from(mp_obj_t module, qstr name); void mp_import_all(mp_obj_t module); -#define mp_raise_type(exc_type) mp_raise_msg(exc_type, NULL) -void mp_raise_msg(const mp_obj_type_t *exc_type, const char *msg); +//#define mp_raise_type(exc_type) mp_raise_msg(exc_type, NULL) +//void mp_raise_msg(const mp_obj_type_t *exc_type, const char *msg); void mp_raise_msg_varg(const mp_obj_type_t *exc_type, const char *fmt, ...); void mp_raise_ValueError(const char *msg); void mp_raise_TypeError(const char *msg); @@ -206,16 +206,16 @@ void mp_warning(const char *category, const char *msg, ...); #define mp_warning(...) #endif -#define mp_raise_or_return(exc) { return mp_raise_o(exc) } +#define mp_raise_or_return(exc) { return mp_raise_o(exc); } #define mp_raise_or_return_value(exc, retval) { mp_raise_o(exc); return retval; } #define mp_raise_OSError(errno) { return mp_raise_OSError_o(errno); } #define mp_raise_OSError_or_return(errno, retval) { mp_raise_OSError_o(errno); return retval; } #define mp_raise_type(extype) { return mp_raise_o(mp_obj_new_exception(extype)); } -#define mp_raise_type_or_return(extype, retval) { mp_raise_o(mp_obj_new_exception(extype)); return retval } +#define mp_raise_type_or_return(extype, retval) { mp_raise_o(mp_obj_new_exception(extype)); return retval; } #define mp_raise_ValueError(v) { return mp_raise_ValueError_o(v); } #define mp_raise_msg(extype, mpt) { return mp_raise_msg_o( mp_obj_new_exception(extype), mpt ); } #define mp_raise_TypeError(mpt) { return mp_raise_TypeError_o(mpt); } -#define mp_raise_TypeError_or_return(mpt, retval) { mp_raise_TypeError_o(mpt); return retval } +#define mp_raise_TypeError_or_return(mpt, retval) { mp_raise_TypeError_o(mpt); return retval; } #define mp_raise_NotImplementedError(mpt) { return mp_raise_NotImplementedError_o(mpt); } diff --git a/ports/wapy/wapy.c b/ports/wapy/wapy.c index 4d18dab3a..ed56a50ee 100644 --- a/ports/wapy/wapy.c +++ b/ports/wapy/wapy.c @@ -1,3 +1,7 @@ +#include "py/mpstate.h" + +mp_state_ctx_t mp_state_ctx; + #if defined(__EMSCRIPTEN__) #define fd_logger stderr #if defined(__WASI__) @@ -137,62 +141,6 @@ has_io() { return check; return 0; } - - - - - - - - -#if 0 // for old esp broken SDK strcmp - size_t - bsd_strlen(const char *str) { - const char *s; - for (s = str; *s; ++s); - return (s - str); - } - - int - bsd_strcmp(const char *s1, const char *s2) { - while (*s1 == *s2++) - if (*s1++ == '\0') - return (0); - return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1)); - } - - int - endswith(const char * str, const char * suffix) { - int str_len = bsd_strlen(str); - int suffix_len = bsd_strlen(suffix); - - return - (str_len >= suffix_len) && (0 == bsd_strcmp(str + (str_len-suffix_len), suffix)); - } - -#endif - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports/windows/README.md b/ports/windows/README.md index 8d907d1b7..1b904f8f5 100644 --- a/ports/windows/README.md +++ b/ports/windows/README.md @@ -90,6 +90,10 @@ Running the tests This is similar for all ports: cd ../../tests + python ./run-tests + +Though when running on Cygwin and using Cygwin's Python installation you'll need: + python3 ./run-tests Depending on the combination of platform and Python version used it might be diff --git a/ports/windows/msvc/genhdr.targets b/ports/windows/msvc/genhdr.targets index 3af0ea263..9ea6ed28c 100644 --- a/ports/windows/msvc/genhdr.targets +++ b/ports/windows/msvc/genhdr.targets @@ -14,6 +14,7 @@ $(PySrcDir)qstrdefs.h $(DestDir)qstrdefscollected.h $(DestDir)qstrdefs.generated.h + $(MICROPY_CPYTHON3) python cl.exe $([System.IO.Path]::Combine(`$(CLToolPath)`, `$(CLToolExe)`)) diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index 50cd031d4..08868d716 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -1,24 +1,118 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright 2020 NXP +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + cmake_minimum_required(VERSION 3.13.1) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) -project(NONE) - -target_sources(app PRIVATE src/zephyr_start.c src/zephyr_getchar.c) - -add_library(libmicropython STATIC IMPORTED) -set_target_properties(libmicropython PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libmicropython.a) -target_link_libraries(app PUBLIC libmicropython) - -zephyr_get_include_directories_for_lang_as_string(C includes) -zephyr_get_system_include_directories_for_lang_as_string(C system_includes) -zephyr_get_compile_definitions_for_lang_as_string(C definitions) -zephyr_get_compile_options_for_lang_as_string(C options) - -add_custom_target( - outputexports - COMMAND echo CC="${CMAKE_C_COMPILER}" - COMMAND echo AR="${CMAKE_AR}" - COMMAND echo Z_CFLAGS=${system_includes} ${includes} ${definitions} ${options} - VERBATIM - USES_TERMINAL +project(micropython) + +set(MICROPY_PORT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(MICROPY_DIR ${MICROPY_PORT_DIR}/../..) +set(MICROPY_TARGET micropython) + +include(${MICROPY_DIR}/py/py.cmake) +include(${MICROPY_DIR}/extmod/extmod.cmake) + +set(MICROPY_SOURCE_PORT + main.c + help.c + machine_i2c.c + machine_pin.c + machine_uart.c + modmachine.c + moduos.c + modusocket.c + modutime.c + modzephyr.c + modzsensor.c + uart_core.c + zephyr_storage.c +) +list(TRANSFORM MICROPY_SOURCE_PORT PREPEND ${MICROPY_PORT_DIR}/) + +set(MICROPY_SOURCE_LIB + timeutils/timeutils.c + utils/mpirq.c + utils/stdout_helpers.c + utils/printf.c + utils/pyexec.c + utils/interrupt_char.c + mp-readline/readline.c + oofatfs/ff.c + oofatfs/ffunicode.c + littlefs/lfs1.c + littlefs/lfs1_util.c + littlefs/lfs2.c + littlefs/lfs2_util.c ) +list(TRANSFORM MICROPY_SOURCE_LIB PREPEND ${MICROPY_DIR}/lib/) + +set(MICROPY_SOURCE_QSTR + ${MICROPY_SOURCE_PY} + ${MICROPY_SOURCE_EXTMOD} + ${MICROPY_SOURCE_LIB} + ${MICROPY_SOURCE_PORT} +) + +zephyr_get_include_directories_for_lang(C includes) +zephyr_get_system_include_directories_for_lang(C system_includes) +zephyr_get_compile_definitions_for_lang(C definitions) +zephyr_get_compile_options_for_lang(C options) + +set(MICROPY_CPP_FLAGS_EXTRA ${includes} ${system_includes} ${definitions} ${options}) + +zephyr_library_named(${MICROPY_TARGET}) + +zephyr_library_include_directories( + ${MICROPY_DIR} + ${MICROPY_PORT_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +zephyr_library_compile_options( + -std=gnu99 -fomit-frame-pointer +) + +zephyr_library_compile_definitions( + NDEBUG + MP_CONFIGFILE=<${CONFIG_MICROPY_CONFIGFILE}> + MICROPY_HEAP_SIZE=${CONFIG_MICROPY_HEAP_SIZE} + FFCONF_H=\"${MICROPY_OOFATFS_DIR}/ffconf.h\" + MICROPY_VFS_FAT=$ + MICROPY_VFS_LFS1=$ + MICROPY_VFS_LFS2=$ +) + +zephyr_library_sources(${MICROPY_SOURCE_QSTR}) + +add_dependencies(${MICROPY_TARGET} zephyr_generated_headers) + +include(${MICROPY_DIR}/py/mkrules.cmake) + +target_sources(app PRIVATE + src/zephyr_start.c + src/zephyr_getchar.c +) + +target_link_libraries(app PRIVATE ${MICROPY_TARGET}) diff --git a/ports/zephyr/Kconfig b/ports/zephyr/Kconfig new file mode 100644 index 000000000..5545cf3b1 --- /dev/null +++ b/ports/zephyr/Kconfig @@ -0,0 +1,46 @@ +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright 2020 NXP +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +menu "MicroPython Options" + +config MICROPY_CONFIGFILE + string "Configuration file" + default "mpconfigport_minimal.h" + +config MICROPY_HEAP_SIZE + int "Heap size" + default 16384 + +config MICROPY_VFS_FAT + bool "FatFS file system" + +config MICROPY_VFS_LFS1 + bool "LittleFs version 1 file system" + +config MICROPY_VFS_LFS2 + bool "LittleFs version 2 file system" + +endmenu # MicroPython Options + +source "Kconfig.zephyr" diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index d69030dc2..cdc3c70f0 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -4,7 +4,7 @@ MicroPython port to Zephyr RTOS This is a work-in-progress port of MicroPython to Zephyr RTOS (http://zephyrproject.org). -This port requires Zephyr version 2.4.0, and may also work on higher +This port requires Zephyr version v2.5.0, and may also work on higher versions. All boards supported by Zephyr (with standard level of features support, like UART console) should work with MicroPython (but not all were tested). @@ -38,59 +38,60 @@ setup is correct. If you already have Zephyr installed but are having issues building the MicroPython port then try installing the correct version of Zephyr via: - $ west init zephyrproject -m https://github.com/zephyrproject-rtos/zephyr --mr v2.4.0 + $ west init zephyrproject -m https://github.com/zephyrproject-rtos/zephyr --mr v2.5.0 Alternatively, you don't have to redo the Zephyr installation to just switch from master to a tagged release, you can instead do: $ cd zephyrproject/zephyr - $ git checkout v2.4.0 + $ git checkout v2.5.0 $ west update With Zephyr installed you may then need to configure your environment, for example by sourcing `zephyrproject/zephyr/zephyr-env.sh`. -Once Zephyr is ready to use you can build the MicroPython port. -In the port subdirectory `ports/zephyr/` run: +Once Zephyr is ready to use you can build the MicroPython port just like any +other Zephyr application. You can do this anywhere in your file system, it does +not have to be in the `ports/zephyr` directory. Assuming you have cloned the +MicroPython repository into your home directory, you can build the Zephyr port +for a frdm_k64f board like this: - $ make BOARD= + $ west build -b frdm_k64f ~/micropython/ports/zephyr -If you don't specify BOARD, the default is `qemu_x86` (x86 target running -in QEMU emulator). Consult the Zephyr documentation above for the list of +To build for QEMU instead: + + $ west build -b qemu_x86 ~/micropython/ports/zephyr + +Consult the Zephyr documentation above for the list of supported boards. Board configuration files appearing in `ports/zephyr/boards/` correspond to boards that have been tested with MicroPython and may have additional options enabled, like filesystem support. - Running ------- -To run the resulting firmware in QEMU (for BOARDs like qemu_x86, -qemu_cortex_m3): +To flash the resulting firmware to your board: - make run + $ west flash -With the default configuration, networking is now enabled, so you need to -follow instructions in https://wiki.zephyrproject.org/view/Networking-with-Qemu -to setup host side of TAP/SLIP networking. If you get error like: +Or, to flash it to your board and start a gdb debug session: - could not connect serial device to character backend 'unix:/tmp/slip.sock' + $ west debug -it's a sign that you didn't followed instructions above. If you would like -to just run it quickly without extra setup, see "minimal" build below. +To run the resulting firmware in QEMU (for BOARDs like qemu_x86, +qemu_cortex_m3): -For deploying/flashing a firmware on a real board, follow Zephyr -documentation for a given board, including known issues for that board -(if any). (Mind again that networking is enabled for the default build, -so you should know if there're any special requirements in that regard, -cf. for example QEMU networking requirements above; real hardware boards -generally should not have any special requirements, unless there're known -issues). + $ west build -t run -For example, to deploy firmware on the FRDM-K64F board run: +Networking is enabled with the default configuration, so you need to follow +instructions in +https://docs.zephyrproject.org/latest/guides/networking/qemu_setup.html#networking-with-qemu +to setup the host side of TAP/SLIP networking. If you get an error like: - $ make BOARD=frdm_k64f flash + could not connect serial device to character backend 'unix:/tmp/slip.sock' +it's a sign that you didn't follow the instructions above. If you would like +to just run it quickly without extra setup, see "minimal" build below. Quick example ------------- @@ -151,9 +152,10 @@ enabled over time. To make a minimal build: - ./make-minimal BOARD= + $ west build -b qemu_x86 ~/micropython/ports/zephyr -- -DCONF_FILE=prj_minimal.conf To run a minimal build in QEMU without requiring TAP networking setup -run the following after you built image with the previous command: +run the following after you built an image with the previous command: + + $ west build -t run - ./make-minimal BOARD= run diff --git a/ports/zephyr/boards/nucleo_h743zi.conf b/ports/zephyr/boards/nucleo_h743zi.conf new file mode 100644 index 000000000..942415b79 --- /dev/null +++ b/ports/zephyr/boards/nucleo_h743zi.conf @@ -0,0 +1,7 @@ +# disable console subsys to get REPL working +CONFIG_CONSOLE_SUBSYS=n + +# flash config +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_FLASH_PAGE_LAYOUT=y diff --git a/ports/zephyr/boards/nucleo_h743zi.overlay b/ports/zephyr/boards/nucleo_h743zi.overlay new file mode 100644 index 000000000..f5e6d4f92 --- /dev/null +++ b/ports/zephyr/boards/nucleo_h743zi.overlay @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 Thomas Popp + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* delete the already defined storage partition and create a new and bigger one */ +/delete-node/ &storage_partition; + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + + /* storage slot: 1024 KB in Flash Memory Bank 2 */ + storage_partition: partition@100000 { + label = "storage"; + reg = <0x00100000 0x100000>; + }; + }; +}; diff --git a/ports/zephyr/machine_uart.c b/ports/zephyr/machine_uart.c new file mode 100644 index 000000000..23d7d5944 --- /dev/null +++ b/ports/zephyr/machine_uart.c @@ -0,0 +1,168 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * Copyright (c) 2020 Yonatan Schachter + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +#include +#include + +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "py/objstr.h" +#include "modmachine.h" + +typedef struct _machine_uart_obj_t { + mp_obj_base_t base; + const struct device *dev; + uint16_t timeout; // timeout waiting for first char (in ms) + uint16_t timeout_char; // timeout waiting between chars (in ms) +} machine_uart_obj_t; + +STATIC const char *_parity_name[] = {"None", "Odd", "Even", "Mark", "Space"}; +STATIC const char *_stop_bits_name[] = {"0.5", "1", "1.5", "2"}; +STATIC const char *_data_bits_name[] = {"5", "6", "7", "8", "9"}; +STATIC const char *_flow_control_name[] = {"None", "RTS/CTS", "DTR/DSR"}; + +STATIC void 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); + struct uart_config config; + uart_config_get(self->dev, &config); + mp_printf(print, "UART(\"%s\", baudrate=%u, data_bits=%s, parity_bits=%s, stop_bits=%s, flow_control=%s, timeout=%u, timeout_char=%u)", + self->dev->name, config.baudrate, _data_bits_name[config.data_bits], + _parity_name[config.parity], _stop_bits_name[config.stop_bits], _flow_control_name[config.flow_ctrl], + self->timeout, self->timeout_char); +} + +STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_timeout, ARG_timeout_char }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + self->timeout = args[ARG_timeout].u_int; + self->timeout_char = args[ARG_timeout_char].u_int; +} + +STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + GET_STR_DATA_LEN(args[0], name, name_len); + + machine_uart_obj_t *self = m_new_obj(machine_uart_obj_t); + self->base.type = &machine_uart_type; + self->dev = device_get_binding(name); + if (!self->dev) { + mp_raise_ValueError(MP_ERROR_TEXT("Bad device name")); + } + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_uart_init_helper(self, n_args - 1, args + 1, &kw_args); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table); + +STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint8_t *buffer = (uint8_t *)buf_in; + uint8_t data; + mp_uint_t bytes_read = 0; + size_t elapsed_ms = 0; + size_t time_to_wait = self->timeout; + + while ((elapsed_ms < time_to_wait) && (bytes_read < size)) { + if (!uart_poll_in(self->dev, &data)) { + buffer[bytes_read++] = data; + elapsed_ms = 0; + time_to_wait = self->timeout_char; + } else { + k_msleep(1); + elapsed_ms++; + } + } + return bytes_read; +} + +STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint8_t *buffer = (uint8_t *)buf_in; + + for (mp_uint_t i = 0; i < size; i++) { + uart_poll_out(self->dev, buffer[i]); + } + + return size; +} + +STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + mp_uint_t ret; + + if (request == MP_STREAM_POLL) { + ret = 0; + // read is always blocking + + if (arg & MP_STREAM_POLL_WR) { + ret |= MP_STREAM_POLL_WR; + } + return ret; + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +STATIC const mp_stream_p_t uart_stream_p = { + .read = machine_uart_read, + .write = machine_uart_write, + .ioctl = machine_uart_ioctl, + .is_text = false, +}; + +const mp_obj_type_t machine_uart_type = { + { &mp_type_type }, + .name = MP_QSTR_UART, + .print = machine_uart_print, + .make_new = machine_uart_make_new, + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &uart_stream_p, + .locals_dict = (mp_obj_dict_t *)&machine_uart_locals_dict, +}; diff --git a/ports/zephyr/modmachine.c b/ports/zephyr/modmachine.c index 968f758b9..107895bea 100644 --- a/ports/zephyr/modmachine.c +++ b/ports/zephyr/modmachine.c @@ -63,6 +63,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { #if MICROPY_PY_MACHINE_I2C { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_hard_i2c_type) }, #endif + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, diff --git a/ports/zephyr/modmachine.h b/ports/zephyr/modmachine.h index b67da5533..957f2dd4f 100644 --- a/ports/zephyr/modmachine.h +++ b/ports/zephyr/modmachine.h @@ -5,6 +5,7 @@ extern const mp_obj_type_t machine_pin_type; extern const mp_obj_type_t machine_hard_i2c_type; +extern const mp_obj_type_t machine_uart_type; MP_DECLARE_CONST_FUN_OBJ_0(machine_info_obj); diff --git a/ports/zephyr/modusocket.c b/ports/zephyr/modusocket.c index 6c900753c..f9fc96a2b 100644 --- a/ports/zephyr/modusocket.c +++ b/ports/zephyr/modusocket.c @@ -79,7 +79,8 @@ STATIC void parse_inet_addr(socket_obj_t *socket, mp_obj_t addr_in, struct socka mp_obj_t *addr_items; mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); - sockaddr_in->sin_family = net_context_get_family((void *)socket->ctx); + void *context = zsock_get_context_object(socket->ctx); + sockaddr_in->sin_family = net_context_get_family(context); RAISE_ERRNO(net_addr_pton(sockaddr_in->sin_family, mp_obj_str_get_str(addr_items[0]), &sockaddr_in->sin_addr)); sockaddr_in->sin_port = htons(mp_obj_get_int(addr_items[1])); } @@ -119,8 +120,8 @@ STATIC void socket_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin if (self->ctx == -1) { mp_printf(print, ""); } else { - struct net_context *ctx = (void *)self->ctx; - mp_printf(print, "", ctx, net_context_get_type(ctx)); + void *context = zsock_get_context_object(self->ctx); + mp_printf(print, "", self->ctx, net_context_get_type(context)); } } diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index 6bfd9ff88..966f7f7e9 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -110,7 +110,7 @@ #define MICROPY_HW_MCU_NAME "unknown-cpu" #endif -#define MICROPY_MODULE_FROZEN_STR (1) +#define MICROPY_MODULE_FROZEN_STR (0) typedef int mp_int_t; // must be pointer size typedef unsigned mp_uint_t; // must be pointer size diff --git a/ports/zephyr/prj.conf b/ports/zephyr/prj.conf index 9824f48e3..c9c2e96da 100644 --- a/ports/zephyr/prj.conf +++ b/ports/zephyr/prj.conf @@ -69,3 +69,8 @@ CONFIG_NET_BUF_POOL_USAGE=y #CONFIG_NET_DEBUG_NET_BUF=y # Change to 4 for "DEBUG" level #CONFIG_SYS_LOG_NET_LEVEL=3 + +# MicroPython options +CONFIG_MICROPY_CONFIGFILE="mpconfigport.h" +CONFIG_MICROPY_VFS_FAT=y +CONFIG_MICROPY_VFS_LFS2=y diff --git a/py/asmthumb.c b/py/asmthumb.c index bb10935fd..f7ac87fa0 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -47,6 +47,7 @@ #define SIGNED_FIT12(x) (((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800) #define SIGNED_FIT23(x) (((x) & 0xffc00000) == 0) || (((x) & 0xffc00000) == 0xffc00000) +#if MICROPY_EMIT_THUMB_ARMV7M // Note: these actually take an imm12 but the high-bit is not encoded here #define OP_ADD_W_RRI_HI(reg_src) (0xf200 | (reg_src)) #define OP_ADD_W_RRI_LO(reg_dest, imm11) ((imm11 << 4 & 0x7000) | reg_dest << 8 | (imm11 & 0xff)) @@ -55,6 +56,7 @@ #define OP_LDR_W_HI(reg_base) (0xf8d0 | (reg_base)) #define OP_LDR_W_LO(reg_dest, imm12) ((reg_dest) << 12 | (imm12)) +#endif static inline byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int n) { return mp_asm_base_get_cur_to_write_bytes(&as->base, n); @@ -122,7 +124,7 @@ void asm_thumb_entry(asm_thumb_t *as, int num_locals) { // If this Thumb machine code is run from ARM state then add a prelude // to switch to Thumb state for the duration of the function. - #if MICROPY_DYNAMIC_COMPILER || MICROPY_EMIT_ARM || (defined(__arm__) && !defined(__thumb2__)) + #if MICROPY_DYNAMIC_COMPILER || MICROPY_EMIT_ARM || (defined(__arm__) && !defined(__thumb2__) && !defined(__thumb__)) #if MICROPY_DYNAMIC_COMPILER if (mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_ARMV6) #endif @@ -171,11 +173,21 @@ void asm_thumb_entry(asm_thumb_t *as, int num_locals) { } asm_thumb_op16(as, OP_PUSH_RLIST_LR(reglist)); if (stack_adjust > 0) { + #if MICROPY_EMIT_THUMB_ARMV7M if (UNSIGNED_FIT7(stack_adjust)) { asm_thumb_op16(as, OP_SUB_SP(stack_adjust)); } else { asm_thumb_op32(as, OP_SUB_W_RRI_HI(ASM_THUMB_REG_SP), OP_SUB_W_RRI_LO(ASM_THUMB_REG_SP, stack_adjust * 4)); } + #else + int adj = stack_adjust; + // we don't expect the stack_adjust to be massive + while (!UNSIGNED_FIT7(adj)) { + asm_thumb_op16(as, OP_SUB_SP(127)); + adj -= 127; + } + asm_thumb_op16(as, OP_SUB_SP(adj)); + #endif } as->push_reglist = reglist; as->stack_adjust = stack_adjust; @@ -183,11 +195,21 @@ void asm_thumb_entry(asm_thumb_t *as, int num_locals) { void asm_thumb_exit(asm_thumb_t *as) { if (as->stack_adjust > 0) { + #if MICROPY_EMIT_THUMB_ARMV7M if (UNSIGNED_FIT7(as->stack_adjust)) { asm_thumb_op16(as, OP_ADD_SP(as->stack_adjust)); } else { asm_thumb_op32(as, OP_ADD_W_RRI_HI(ASM_THUMB_REG_SP), OP_ADD_W_RRI_LO(ASM_THUMB_REG_SP, as->stack_adjust * 4)); } + #else + int adj = as->stack_adjust; + // we don't expect the stack_adjust to be massive + while (!UNSIGNED_FIT7(adj)) { + asm_thumb_op16(as, OP_ADD_SP(127)); + adj -= 127; + } + asm_thumb_op16(as, OP_ADD_SP(adj)); + #endif } asm_thumb_op16(as, OP_POP_RLIST_PC(as->push_reglist)); } @@ -241,6 +263,8 @@ void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src) { asm_thumb_op16(as, 0x4600 | op_lo); } +#if MICROPY_EMIT_THUMB_ARMV7M + // if loading lo half with movw, the i16 value will be zero extended into the r32 register! size_t asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src) { assert(reg_dest < ASM_THUMB_REG_R15); @@ -250,6 +274,16 @@ size_t asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i1 return loc; } +#else + +void asm_thumb_mov_rlo_i16(asm_thumb_t *as, uint rlo_dest, int i16_src) { + asm_thumb_mov_rlo_i8(as, rlo_dest, (i16_src >> 8) & 0xff); + asm_thumb_lsl_rlo_rlo_i5(as, rlo_dest, rlo_dest, 8); + asm_thumb_add_rlo_i8(as, rlo_dest, i16_src & 0xff); +} + +#endif + #define OP_B_N(byte_offset) (0xe000 | (((byte_offset) >> 1) & 0x07ff)) bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) { @@ -274,8 +308,13 @@ bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) { asm_thumb_op16(as, OP_BCC_N(cond, rel)); return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT9(rel); } else { + #if MICROPY_EMIT_THUMB_ARMV7M asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel)); return true; + #else + // this method should not be called for ARMV6M + return false; + #endif } } @@ -296,8 +335,30 @@ size_t asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32) { size_t loc = mp_asm_base_get_code_pos(&as->base); + #if MICROPY_EMIT_THUMB_ARMV7M asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32); asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVT, reg_dest, i32 >> 16); + #else + // should only be called with lo reg for ARMV6M + assert(reg_dest < ASM_THUMB_REG_R8); + + // sanity check that generated code is aligned + assert(!as->base.code_base || !(3u & (uintptr_t)as->base.code_base)); + + // basically: + // (nop) + // ldr reg_dest, _data + // b 1f + // _data: .word i32 + // 1: + if (as->base.code_offset & 2u) { + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + } + asm_thumb_ldr_rlo_pcrel_i8(as, reg_dest, 0); + asm_thumb_op16(as, OP_B_N(2)); + asm_thumb_op16(as, i32 & 0xffff); + asm_thumb_op16(as, i32 >> 16); + #endif return loc; } @@ -305,27 +366,68 @@ size_t asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32) { void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32) { if (reg_dest < 8 && UNSIGNED_FIT8(i32)) { asm_thumb_mov_rlo_i8(as, reg_dest, i32); - } else if (UNSIGNED_FIT16(i32)) { - asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32); } else { - asm_thumb_mov_reg_i32(as, reg_dest, i32); + #if MICROPY_EMIT_THUMB_ARMV7M + if (UNSIGNED_FIT16(i32)) { + asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32); + } else { + asm_thumb_mov_reg_i32(as, reg_dest, i32); + } + #else + uint rlo_dest = reg_dest; + assert(rlo_dest < ASM_THUMB_REG_R8); // should never be called for ARMV6M + + bool negate = i32 < 0 && ((i32 + i32) & 0xffffffffu); // don't negate 0x80000000 + if (negate) { + i32 = -i32; + } + + uint clz = __builtin_clz(i32); + uint ctz = i32 ? __builtin_ctz(i32) : 0; + assert(clz + ctz <= 32); + if (clz + ctz >= 24) { + asm_thumb_mov_rlo_i8(as, rlo_dest, (i32 >> ctz) & 0xff); + asm_thumb_lsl_rlo_rlo_i5(as, rlo_dest, rlo_dest, ctz); + } else if (UNSIGNED_FIT16(i32)) { + asm_thumb_mov_rlo_i16(as, rlo_dest, i32); + } else { + if (negate) { + // no point in negating if we're storing in 32 bit anyway + negate = false; + i32 = -i32; + } + asm_thumb_mov_reg_i32(as, rlo_dest, i32); + } + if (negate) { + asm_thumb_neg_rlo_rlo(as, rlo_dest, rlo_dest); + } + #endif } } #define OP_STR_TO_SP_OFFSET(rlo_dest, word_offset) (0x9000 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff)) #define OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset) (0x9800 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff)) +static void asm_thumb_mov_local_check(asm_thumb_t *as, int word_offset) { + if (as->base.pass >= MP_ASM_PASS_EMIT) { + assert(word_offset >= 0); + if (!UNSIGNED_FIT8(word_offset)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("too many locals for native method")); + } + } +} + void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num, uint rlo_src) { assert(rlo_src < ASM_THUMB_REG_R8); int word_offset = local_num; - assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0); + asm_thumb_mov_local_check(as, word_offset); asm_thumb_op16(as, OP_STR_TO_SP_OFFSET(rlo_src, word_offset)); } void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num) { assert(rlo_dest < ASM_THUMB_REG_R8); int word_offset = local_num; - assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0); + asm_thumb_mov_local_check(as, word_offset); asm_thumb_op16(as, OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset)); } @@ -341,21 +443,63 @@ void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num) void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label) { mp_uint_t dest = get_label_dest(as, label); mp_int_t rel = dest - as->base.code_offset; - rel -= 4 + 4; // adjust for mov_reg_i16 and then PC+4 prefetch of add_reg_reg rel |= 1; // to stay in Thumb state when jumping to this address + #if MICROPY_EMIT_THUMB_ARMV7M + rel -= 4 + 4; // adjust for mov_reg_i16 and then PC+4 prefetch of add_reg_reg asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, rlo_dest, rel); // 4 bytes + #else + rel -= 8 + 4; // adjust for four instructions and then PC+4 prefetch of add_reg_reg + // 6 bytes + asm_thumb_mov_rlo_i16(as, rlo_dest, rel); + // 2 bytes - not always needed, but we want to keep the size the same + asm_thumb_sxth_rlo_rlo(as, rlo_dest, rlo_dest); + #endif asm_thumb_add_reg_reg(as, rlo_dest, ASM_THUMB_REG_R15); // 2 bytes } +#if MICROPY_EMIT_THUMB_ARMV7M static inline void asm_thumb_ldr_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) { asm_thumb_op32(as, OP_LDR_W_HI(reg_base), OP_LDR_W_LO(reg_dest, word_offset * 4)); } +#endif void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) { if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(word_offset)) { asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_base, word_offset); } else { + #if MICROPY_EMIT_THUMB_ARMV7M asm_thumb_ldr_reg_reg_i12(as, reg_dest, reg_base, word_offset); + #else + word_offset -= 31; + if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8) { + if (UNSIGNED_FIT8(word_offset) && (word_offset < 64 || reg_dest != reg_base)) { + if (word_offset < 64) { + if (reg_dest != reg_base) { + asm_thumb_mov_reg_reg(as, reg_dest, reg_base); + } + asm_thumb_add_rlo_i8(as, reg_dest, word_offset * 4); + } else { + asm_thumb_mov_rlo_i8(as, reg_dest, word_offset); + asm_thumb_lsl_rlo_rlo_i5(as, reg_dest, reg_dest, 2); + 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, word_offset * 4); + asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_dest); + } 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, word_offset * 4); + asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_other); + asm_thumb_op16(as, OP_POP_RLIST((1 << reg_other))); + } + } + } else { + assert(0); // should never be called for ARMV6M + } + asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_dest, 31); + #endif } } @@ -378,7 +522,20 @@ void asm_thumb_b_label(asm_thumb_t *as, uint label) { } else { // is a forwards jump, so need to assume it's large large_jump: + #if MICROPY_EMIT_THUMB_ARMV7M asm_thumb_op32(as, OP_BW_HI(rel), OP_BW_LO(rel)); + #else + if (SIGNED_FIT12(rel)) { + // this code path has to be the same number of instructions irrespective of rel + asm_thumb_op16(as, OP_B_N(rel)); + } else { + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + if (dest != (mp_uint_t)-1) { + // we have an actual branch > 12 bits; this is not handled yet + mp_raise_NotImplementedError(MP_ERROR_TEXT("native method too big")); + } + } + #endif } } @@ -397,10 +554,28 @@ void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) { } else { // is a forwards jump, so need to assume it's large large_jump: + #if MICROPY_EMIT_THUMB_ARMV7M asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel)); + #else + // reverse the sense of the branch to jump over a longer branch + asm_thumb_op16(as, OP_BCC_N(cond ^ 1, 0)); + asm_thumb_b_label(as, label); + #endif } } +void asm_thumb_bcc_rel9(asm_thumb_t *as, int cond, int rel) { + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + assert(SIGNED_FIT9(rel)); + asm_thumb_op16(as, OP_BCC_N(cond, rel)); +} + +void asm_thumb_b_rel12(asm_thumb_t *as, int rel) { + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + assert(SIGNED_FIT12(rel)); + asm_thumb_op16(as, OP_B_N(rel)); +} + #define OP_BLX(reg) (0x4780 | ((reg) << 3)) #define OP_SVC(arg) (0xdf00 | (arg)) diff --git a/py/asmthumb.h b/py/asmthumb.h index 17b694a74..17a0cca98 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -157,6 +157,7 @@ static inline void asm_thumb_sub_rlo_rlo_i3(asm_thumb_t *as, uint rlo_dest, uint #define ASM_THUMB_FORMAT_3_CMP (0x2800) #define ASM_THUMB_FORMAT_3_ADD (0x3000) #define ASM_THUMB_FORMAT_3_SUB (0x3800) +#define ASM_THUMB_FORMAT_3_LDR (0x4800) #define ASM_THUMB_FORMAT_3_ENCODE(op, rlo, i8) ((op) | ((rlo) << 8) | (i8)) @@ -177,6 +178,9 @@ static inline void asm_thumb_add_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { static inline void asm_thumb_sub_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_SUB, rlo, i8); } +static inline void asm_thumb_ldr_rlo_pcrel_i8(asm_thumb_t *as, uint rlo, uint i8) { + asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_LDR, rlo, i8); +} // FORMAT 4: ALU operations @@ -202,6 +206,12 @@ void asm_thumb_format_4(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src); static inline void asm_thumb_cmp_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_CMP, rlo_dest, rlo_src); } +static inline void asm_thumb_mvn_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { + asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_MVN, rlo_dest, rlo_src); +} +static inline void asm_thumb_neg_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { + asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_NEG, rlo_dest, rlo_src); +} // FORMAT 5: hi register operations (add, cmp, mov, bx) // For add/cmp/mov, at least one of the args must be a high register @@ -263,6 +273,32 @@ static inline void asm_thumb_ldrb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uin static inline void asm_thumb_ldrh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint byte_offset) { asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_LDRH, rlo_dest, rlo_base, byte_offset); } +static inline void asm_thumb_lsl_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_src, uint shift) { + asm_thumb_format_1(as, ASM_THUMB_FORMAT_1_LSL, rlo_dest, rlo_src, shift); +} +static inline void asm_thumb_asr_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_src, uint shift) { + asm_thumb_format_1(as, ASM_THUMB_FORMAT_1_ASR, rlo_dest, rlo_src, shift); +} + +// FORMAT 11: sign/zero extend + +#define ASM_THUMB_FORMAT_11_ENCODE(op, rlo_dest, rlo_src) \ + ((op) | ((rlo_src) << 3) | (rlo_dest)) + +#define ASM_THUMB_FORMAT_11_SXTH (0xb200) +#define ASM_THUMB_FORMAT_11_SXTB (0xb240) +#define ASM_THUMB_FORMAT_11_UXTH (0xb280) +#define ASM_THUMB_FORMAT_11_UXTB (0xb2c0) + +static inline void asm_thumb_format_11(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src) { + assert(rlo_dest < ASM_THUMB_REG_R8); + assert(rlo_src < ASM_THUMB_REG_R8); + asm_thumb_op16(as, ASM_THUMB_FORMAT_11_ENCODE(op, rlo_dest, rlo_src)); +} + +static inline void asm_thumb_sxth_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { + asm_thumb_format_11(as, ASM_THUMB_FORMAT_11_SXTH, rlo_dest, rlo_src); +} // TODO convert these to above format style @@ -270,7 +306,12 @@ static inline void asm_thumb_ldrh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uin #define ASM_THUMB_OP_MOVT (0xf2c0) void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src); + +#if MICROPY_EMIT_THUMB_ARMV7M size_t asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src); +#else +void asm_thumb_mov_rlo_i16(asm_thumb_t *as, uint rlo_dest, int i16_src); +#endif // these return true if the destination is in range, false otherwise bool asm_thumb_b_n_label(asm_thumb_t *as, uint label); @@ -289,6 +330,8 @@ void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint re void asm_thumb_b_label(asm_thumb_t *as, uint label); // convenience: picks narrow or wide branch void asm_thumb_bcc_label(asm_thumb_t *as, int cc, uint label); // convenience: picks narrow or wide branch void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp); // convenience +void asm_thumb_bcc_rel9(asm_thumb_t *as, int cc, int rel); +void asm_thumb_b_rel12(asm_thumb_t *as, int rel); // Holds a pointer to mp_fun_table #define ASM_THUMB_REG_FUN_TABLE ASM_THUMB_REG_R7 @@ -344,7 +387,11 @@ void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp); // convenien #define ASM_MOV_LOCAL_REG(as, local_num, reg) asm_thumb_mov_local_reg((as), (local_num), (reg)) #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_optimised((as), (reg_dest), (imm)) +#if MICROPY_EMIT_THUMB_ARMV7M #define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_thumb_mov_reg_i16((as), ASM_THUMB_OP_MOVW, (reg_dest), (imm)) +#else +#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_thumb_mov_rlo_i16((as), (reg_dest), (imm)) +#endif #define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_thumb_mov_reg_i32((as), (reg_dest), (imm)) #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_thumb_mov_reg_local((as), (reg_dest), (local_num)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_thumb_mov_reg_reg((as), (reg_dest), (reg_src)) diff --git a/py/binary.c b/py/binary.c index da8cd2552..d2f9de1bf 100644 --- a/py/binary.c +++ b/py/binary.c @@ -325,7 +325,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p double f; } fp_dp; fp_dp.f = mp_obj_get_float_to_d(val_in); - if (BYTES_PER_WORD == 8) { + if (MP_BYTES_PER_OBJ_WORD == 8) { val = fp_dp.i64; } else { int be = struct_type == '>'; @@ -346,7 +346,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p val = mp_obj_get_int(val_in); // zero/sign extend if needed - if (BYTES_PER_WORD < 8 && size > sizeof(val)) { + if (MP_BYTES_PER_OBJ_WORD < 8 && size > sizeof(val)) { int c = (mp_int_t)val < 0 ? 0xff : 0x00; memset(p, c, size); if (struct_type == '>') { diff --git a/py/compile.c b/py/compile.c index 73a8ba5e3..2822db70e 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2006,99 +2006,104 @@ STATIC void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { EMIT(pop_top); // discard last result since this is a statement and leaves nothing on the stack } } - } else if (MP_PARSE_NODE_IS_STRUCT(pn_rhs)) { - mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pn_rhs; - int kind = MP_PARSE_NODE_STRUCT_KIND(pns1); - if (kind == PN_annassign) { - // the annotation is in pns1->nodes[0] and is ignored - if (MP_PARSE_NODE_IS_NULL(pns1->nodes[1])) { - // an annotation of the form "x: y" - // inside a function this declares "x" as a local - if (comp->scope_cur->kind == SCOPE_FUNCTION) { - if (MP_PARSE_NODE_IS_ID(pns->nodes[0])) { - qstr lhs = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); - scope_find_or_add_id(comp->scope_cur, lhs, ID_INFO_KIND_LOCAL); - } + return; + } + if (!MP_PARSE_NODE_IS_STRUCT(pn_rhs)) + goto plain_assign; + + mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pn_rhs; + int kind = MP_PARSE_NODE_STRUCT_KIND(pns1); + if (kind == PN_annassign) { + // the annotation is in pns1->nodes[0] and is ignored + if (MP_PARSE_NODE_IS_NULL(pns1->nodes[1])) { + // an annotation of the form "x: y" + // inside a function this declares "x" as a local + if (comp->scope_cur->kind == SCOPE_FUNCTION) { + if (MP_PARSE_NODE_IS_ID(pns->nodes[0])) { + qstr lhs = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]); + scope_find_or_add_id(comp->scope_cur, lhs, ID_INFO_KIND_LOCAL); } - } else { - // an assigned annotation of the form "x: y = z" - pn_rhs = pns1->nodes[1]; - goto plain_assign; } - } else if (kind == PN_expr_stmt_augassign) { - c_assign(comp, pns->nodes[0], ASSIGN_AUG_LOAD); // lhs load for aug assign - compile_node(comp, pns1->nodes[1]); // rhs - assert(MP_PARSE_NODE_IS_TOKEN(pns1->nodes[0])); - mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]); - mp_binary_op_t op = MP_BINARY_OP_INPLACE_OR + (tok - MP_TOKEN_DEL_PIPE_EQUAL); - EMIT_ARG(binary_op, op); - c_assign(comp, pns->nodes[0], ASSIGN_AUG_STORE); // lhs store for aug assign - } else if (kind == PN_expr_stmt_assign_list) { - int rhs = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1) - 1; - compile_node(comp, pns1->nodes[rhs]); // rhs - // following CPython, we store left-most first - if (rhs > 0) { + } else { + // an assigned annotation of the form "x: y = z" + pn_rhs = pns1->nodes[1]; + goto plain_assign; + } + return; + } else if (kind == PN_expr_stmt_augassign) { + c_assign(comp, pns->nodes[0], ASSIGN_AUG_LOAD); // lhs load for aug assign + compile_node(comp, pns1->nodes[1]); // rhs + assert(MP_PARSE_NODE_IS_TOKEN(pns1->nodes[0])); + mp_token_kind_t tok = (mp_token_kind_t)MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]); + mp_binary_op_t op = (mp_binary_op_t)(MP_BINARY_OP_INPLACE_OR + (tok - MP_TOKEN_DEL_PIPE_EQUAL)); + EMIT_ARG(binary_op, op); + c_assign(comp, pns->nodes[0], ASSIGN_AUG_STORE); // lhs store for aug assign + return; + } else if (kind == PN_expr_stmt_assign_list) { + int rhs = MP_PARSE_NODE_STRUCT_NUM_NODES(pns1) - 1; + compile_node(comp, pns1->nodes[rhs]); // rhs + // following CPython, we store left-most first + if (rhs > 0) { + EMIT(dup_top); + } + c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store + for (int i = 0; i < rhs; i++) { + if (i + 1 < rhs) { EMIT(dup_top); } - c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store - for (int i = 0; i < rhs; i++) { - if (i + 1 < rhs) { - EMIT(dup_top); - } - c_assign(comp, pns1->nodes[i], ASSIGN_STORE); // middle store + c_assign(comp, pns1->nodes[i], ASSIGN_STORE); // middle store + } + return; + } + +plain_assign: + #if MICROPY_COMP_DOUBLE_TUPLE_ASSIGN + if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_rhs, PN_testlist_star_expr) + && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)) { + mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t *)pns->nodes[0]; + pns1 = (mp_parse_node_struct_t *)pn_rhs; + uint32_t n_pns0 = MP_PARSE_NODE_STRUCT_NUM_NODES(pns0); + // Can only optimise a tuple-to-tuple assignment when all of the following hold: + // - equal number of items in LHS and RHS tuples + // - 2 or 3 items in the tuples + // - there are no star expressions in the LHS tuple + if (n_pns0 == MP_PARSE_NODE_STRUCT_NUM_NODES(pns1) + && (n_pns0 == 2 + #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN + || n_pns0 == 3 + #endif + ) + && !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[0], PN_star_expr) + && !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[1], PN_star_expr) + #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN + && (n_pns0 == 2 || !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[2], PN_star_expr)) + #endif + ) { + // Optimisation for a, b = c, d or a, b, c = d, e, f + compile_node(comp, pns1->nodes[0]); // rhs + compile_node(comp, pns1->nodes[1]); // rhs + #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN + if (n_pns0 == 3) { + compile_node(comp, pns1->nodes[2]); // rhs + EMIT(rot_three); } - } else { - plain_assign: - #if MICROPY_COMP_DOUBLE_TUPLE_ASSIGN - if (MP_PARSE_NODE_IS_STRUCT_KIND(pn_rhs, PN_testlist_star_expr) - && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)) { - mp_parse_node_struct_t *pns0 = (mp_parse_node_struct_t *)pns->nodes[0]; - pns1 = (mp_parse_node_struct_t *)pn_rhs; - uint32_t n_pns0 = MP_PARSE_NODE_STRUCT_NUM_NODES(pns0); - // Can only optimise a tuple-to-tuple assignment when all of the following hold: - // - equal number of items in LHS and RHS tuples - // - 2 or 3 items in the tuples - // - there are no star expressions in the LHS tuple - if (n_pns0 == MP_PARSE_NODE_STRUCT_NUM_NODES(pns1) - && (n_pns0 == 2 - #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN - || n_pns0 == 3 - #endif - ) - && !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[0], PN_star_expr) - && !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[1], PN_star_expr) - #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN - && (n_pns0 == 2 || !MP_PARSE_NODE_IS_STRUCT_KIND(pns0->nodes[2], PN_star_expr)) - #endif - ) { - // Optimisation for a, b = c, d or a, b, c = d, e, f - compile_node(comp, pns1->nodes[0]); // rhs - compile_node(comp, pns1->nodes[1]); // rhs - #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN - if (n_pns0 == 3) { - compile_node(comp, pns1->nodes[2]); // rhs - EMIT(rot_three); - } - #endif - EMIT(rot_two); - c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store - c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store - #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN - if (n_pns0 == 3) { - c_assign(comp, pns0->nodes[2], ASSIGN_STORE); // lhs store - } - #endif - return; - } + #endif + EMIT(rot_two); + c_assign(comp, pns0->nodes[0], ASSIGN_STORE); // lhs store + c_assign(comp, pns0->nodes[1], ASSIGN_STORE); // lhs store + #if MICROPY_COMP_TRIPLE_TUPLE_ASSIGN + if (n_pns0 == 3) { + c_assign(comp, pns0->nodes[2], ASSIGN_STORE); // lhs store } #endif - - compile_node(comp, pn_rhs); // rhs - c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store + return; } - } else { - goto plain_assign; } + #endif + + compile_node(comp, pn_rhs); // rhs + c_assign(comp, pns->nodes[0], ASSIGN_STORE); // lhs store + } STATIC void compile_test_if_expr(compiler_t *comp, mp_parse_node_struct_t *pns) { diff --git a/py/emitbc.c b/py/emitbc.c index c74b7fbc4..d7e8e05f0 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -36,7 +36,7 @@ #if MICROPY_ENABLE_COMPILER -#define BYTES_FOR_INT ((BYTES_PER_WORD * 8 + 6) / 7) +#define BYTES_FOR_INT ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7) #define DUMMY_DATA_SIZE (BYTES_FOR_INT) struct _emit_t { diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index cffaa4bb8..1a35e25ad 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -573,7 +573,11 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a goto unknown_op; } int label_num = get_arg_label(emit, op_str, pn_args[0]); - if (!asm_thumb_bcc_nw_label(&emit->as, cc, label_num, op_len == 5 && op_str[4] == 'w')) { + bool wide = op_len == 5 && op_str[4] == 'w'; + if (wide && !ARMV7M) { + goto unknown_op; + } + if (!asm_thumb_bcc_nw_label(&emit->as, cc, label_num, wide)) { goto branch_not_in_range; } } else if (ARMV7M && op_str[0] == 'i' && op_str[1] == 't') { @@ -701,23 +705,24 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a } else if (op == MP_QSTR_sub) { op_code = ASM_THUMB_FORMAT_3_SUB; goto op_format_3; - } else if (ARMV7M && op == MP_QSTR_movw) { + #if ARMV7M + } else if (op == MP_QSTR_movw) { op_code = ASM_THUMB_OP_MOVW; mp_uint_t reg_dest; op_movw_movt: reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff); asm_thumb_mov_reg_i16(&emit->as, op_code, reg_dest, i_src); - } else if (ARMV7M && op == MP_QSTR_movt) { + } else if (op == MP_QSTR_movt) { op_code = ASM_THUMB_OP_MOVT; goto op_movw_movt; - } else if (ARMV7M && op == MP_QSTR_movwt) { + } else if (op == MP_QSTR_movwt) { // this is a convenience instruction mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); uint32_t i_src = get_arg_i(emit, op_str, pn_args[1], 0xffffffff); asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVW, reg_dest, i_src & 0xffff); asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVT, reg_dest, (i_src >> 16) & 0xffff); - } else if (ARMV7M && op == MP_QSTR_ldrex) { + } else if (op == MP_QSTR_ldrex) { mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15); mp_parse_node_t pn_base, pn_offset; if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) { @@ -725,6 +730,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2; asm_thumb_op32(&emit->as, 0xe850 | r_base, 0x0f00 | (r_dest << 12) | i8); } + #endif } else { // search table for ldr/str instructions for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_9_10_op_table); i++) { diff --git a/py/emitnative.c b/py/emitnative.c index a69f73ca1..20de361e2 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -211,6 +211,7 @@ struct _emit_t { int pass; bool do_viper_types; + bool prelude_offset_uses_u16_encoding; mp_uint_t local_vtype_alloc; vtype_kind_t *local_vtype; @@ -339,6 +340,18 @@ STATIC void emit_native_mov_reg_qstr_obj(emit_t *emit, int reg_dest, qstr qst) { emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \ } while (false) +#define emit_native_mov_state_imm_fix_u16_via(emit, local_num, imm, reg_temp) \ + do { \ + ASM_MOV_REG_IMM_FIX_U16((emit)->as, (reg_temp), (imm)); \ + emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \ + } while (false) + +#define emit_native_mov_state_imm_fix_word_via(emit, local_num, imm, reg_temp) \ + do { \ + ASM_MOV_REG_IMM_FIX_WORD((emit)->as, (reg_temp), (imm)); \ + emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \ + } while (false) + STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { DEBUG_printf("start_pass(pass=%u, scope=%p)\n", pass, scope); @@ -549,16 +562,27 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_PARENT_ARG_1); // Set code_state.ip (offset from start of this function to prelude info) + int code_state_ip_local = emit->code_state_start + OFFSETOF_CODE_STATE_IP; #if N_PRELUDE_AS_BYTES_OBJ // Prelude is a bytes object in const_table; store ip = prelude->data - fun_bc->bytecode ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, offsetof(mp_obj_str_t, data) / sizeof(uintptr_t)); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_PARENT_ARG_1, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_BYTECODE); ASM_SUB_REG_REG(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1); - emit_native_mov_state_reg(emit, emit->code_state_start + OFFSETOF_CODE_STATE_IP, REG_LOCAL_3); + emit_native_mov_state_reg(emit, code_state_ip_local, REG_LOCAL_3); #else - // TODO this encoding may change size in the final pass, need to make it fixed - emit_native_mov_state_imm_via(emit, emit->code_state_start + OFFSETOF_CODE_STATE_IP, emit->prelude_offset, REG_PARENT_ARG_1); + if (emit->pass == MP_PASS_CODE_SIZE) { + // Commit to the encoding size based on the value of prelude_offset in this pass. + // By using 32768 as the cut-off it is highly unlikely that prelude_offset will + // grow beyond 65535 by the end of thiss pass, and so require the larger encoding. + emit->prelude_offset_uses_u16_encoding = emit->prelude_offset < 32768; + } + if (emit->prelude_offset_uses_u16_encoding) { + assert(emit->prelude_offset <= 65535); + emit_native_mov_state_imm_fix_u16_via(emit, code_state_ip_local, emit->prelude_offset, REG_PARENT_ARG_1); + } else { + emit_native_mov_state_imm_fix_word_via(emit, code_state_ip_local, emit->prelude_offset, REG_PARENT_ARG_1); + } #endif // Set code_state.n_state (only works on little endian targets due to n_state being uint16_t) @@ -2449,6 +2473,7 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { asm_x86_setcc_r8(emit->as, ops[op_idx], REG_RET); #elif N_THUMB asm_thumb_cmp_rlo_rlo(emit->as, REG_ARG_2, reg_rhs); + #if MICROPY_EMIT_THUMB_ARMV7M static uint16_t ops[6 + 6] = { // unsigned ASM_THUMB_OP_ITE_CC, @@ -2468,6 +2493,28 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { asm_thumb_op16(emit->as, ops[op_idx]); asm_thumb_mov_rlo_i8(emit->as, REG_RET, 1); asm_thumb_mov_rlo_i8(emit->as, REG_RET, 0); + #else + static uint16_t ops[6 + 6] = { + // unsigned + ASM_THUMB_CC_CC, + ASM_THUMB_CC_HI, + ASM_THUMB_CC_EQ, + ASM_THUMB_CC_LS, + ASM_THUMB_CC_CS, + ASM_THUMB_CC_NE, + // signed + ASM_THUMB_CC_LT, + ASM_THUMB_CC_GT, + ASM_THUMB_CC_EQ, + ASM_THUMB_CC_LE, + ASM_THUMB_CC_GE, + ASM_THUMB_CC_NE, + }; + asm_thumb_bcc_rel9(emit->as, ops[op_idx], 6); + asm_thumb_mov_rlo_i8(emit->as, REG_RET, 0); + asm_thumb_b_rel12(emit->as, 4); + asm_thumb_mov_rlo_i8(emit->as, REG_RET, 1); + #endif #elif N_ARM asm_arm_cmp_reg_reg(emit->as, REG_ARG_2, reg_rhs); static uint ccs[6 + 6] = { diff --git a/py/gc.c b/py/gc.c index 767f1b81c..a928fce00 100644 --- a/py/gc.c +++ b/py/gc.c @@ -49,7 +49,7 @@ // detect untraced object still in use #define CLEAR_ON_SWEEP (0) -#define WORDS_PER_BLOCK ((MICROPY_BYTES_PER_GC_BLOCK) / BYTES_PER_WORD) +#define WORDS_PER_BLOCK ((MICROPY_BYTES_PER_GC_BLOCK) / MP_BYTES_PER_OBJ_WORD) #define BYTES_PER_BLOCK (MICROPY_BYTES_PER_GC_BLOCK) // ATB = allocation table byte @@ -118,9 +118,9 @@ void gc_init(void *start, void *end) { // => T = A * (1 + BLOCKS_PER_ATB / BLOCKS_PER_FTB + BLOCKS_PER_ATB * BYTES_PER_BLOCK) size_t total_byte_len = (byte *)end - (byte *)start; #if MICROPY_ENABLE_FINALISER - MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len * BITS_PER_BYTE / (BITS_PER_BYTE + BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK); + MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len * MP_BITS_PER_BYTE / (MP_BITS_PER_BYTE + MP_BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + MP_BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK); #else - MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len / (1 + BITS_PER_BYTE / 2 * BYTES_PER_BLOCK); + MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len / (1 + MP_BITS_PER_BYTE / 2 * BYTES_PER_BLOCK); #endif MP_STATE_MEM(gc_alloc_table_start) = (byte *)start; @@ -132,7 +132,7 @@ void gc_init(void *start, void *end) { size_t gc_pool_block_len = MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; MP_STATE_MEM(gc_pool_start) = (byte *)end - gc_pool_block_len * BYTES_PER_BLOCK; - MP_STATE_MEM(gc_pool_end) = end; + MP_STATE_MEM(gc_pool_end) = (byte *)end; #if MICROPY_ENABLE_FINALISER assert(MP_STATE_MEM(gc_pool_start) >= MP_STATE_MEM(gc_finaliser_table_start) + gc_finaliser_table_byte_len); @@ -294,7 +294,7 @@ STATIC void gc_sweep(void) { } #endif free_tail = 1; - DEBUG_printf("gc_sweep(%p)\n", PTR_FROM_BLOCK(block)); + DEBUG_printf("gc_sweep(%p)\n", (void *)PTR_FROM_BLOCK(block)); #if MICROPY_PY_GC_COLLECT_RETVAL MP_STATE_MEM(gc_collected)++; #endif @@ -375,7 +375,7 @@ void gc_info(gc_info_t *info) { GC_ENTER(); info->total = MP_STATE_MEM(gc_pool_end) - MP_STATE_MEM(gc_pool_start); info->used = 0; - info->free = 0; + info->gc_free = 0; info->max_free = 0; info->num_1block = 0; info->num_2block = 0; @@ -385,7 +385,7 @@ void gc_info(gc_info_t *info) { size_t kind = ATB_GET_KIND(block); switch (kind) { case AT_FREE: - info->free += 1; + info->gc_free += 1; len_free += 1; len = 0; break; @@ -431,7 +431,7 @@ void gc_info(gc_info_t *info) { } info->used *= BYTES_PER_BLOCK; - info->free *= BYTES_PER_BLOCK; + info->gc_free *= BYTES_PER_BLOCK; GC_EXIT(); } @@ -801,7 +801,7 @@ void gc_dump_info(void) { gc_info_t info; gc_info(&info); mp_printf(&mp_plat_print, "GC: total: %u, used: %u, free: %u\n", - (uint)info.total, (uint)info.used, (uint)info.free); + (uint)info.total, (uint)info.used, (uint)info.gc_free); mp_printf(&mp_plat_print, " No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n", (uint)info.num_1block, (uint)info.num_2block, (uint)info.max_block, (uint)info.max_free); } diff --git a/py/gc.h b/py/gc.h index 4690d3937..2d8910121 100644 --- a/py/gc.h +++ b/py/gc.h @@ -26,10 +26,8 @@ #ifndef MICROPY_INCLUDED_PY_GC_H #define MICROPY_INCLUDED_PY_GC_H -#include - -#include "py/mpconfig.h" -#include "py/misc.h" +#include +#include void gc_init(void *start, void *end); @@ -60,7 +58,7 @@ void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move); typedef struct _gc_info_t { size_t total; size_t used; - size_t free; + size_t gc_free; size_t max_free; size_t num_1block; size_t num_2block; diff --git a/py/grammar.h b/py/grammar.h index 285fbded2..0bdfba430 100644 --- a/py/grammar.h +++ b/py/grammar.h @@ -44,12 +44,12 @@ DEF_RULE_NC(generic_equal_test, and_ident(2), tok(DEL_EQUAL), rule(test)) // file_input: (NEWLINE | stmt)* ENDMARKER // eval_input: testlist NEWLINE* ENDMARKER -DEF_RULE_NC(single_input, or(3), tok(NEWLINE), rule(simple_stmt), rule(compound_stmt)) +DEF_RULE_NC(single_input, C_or(3), tok(NEWLINE), rule(simple_stmt), rule(compound_stmt)) DEF_RULE(file_input, c(generic_all_nodes), and_ident(1), opt_rule(file_input_2)) DEF_RULE(file_input_2, c(generic_all_nodes), one_or_more, rule(file_input_3)) -DEF_RULE_NC(file_input_3, or(2), tok(NEWLINE), rule(stmt)) +DEF_RULE_NC(file_input_3, C_or(2), tok(NEWLINE), rule(stmt)) DEF_RULE_NC(eval_input, and_ident(2), rule(testlist), opt_rule(eval_input_2)) -DEF_RULE_NC(eval_input_2, and(1), tok(NEWLINE)) +DEF_RULE_NC(eval_input_2, C_and(1), tok(NEWLINE)) // decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE // decorators: decorator+ @@ -62,35 +62,35 @@ DEF_RULE_NC(eval_input_2, and(1), tok(NEWLINE)) // varargslist: vfpdef ['=' test] (',' vfpdef ['=' test])* [',' ['*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef]] | '*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef // vfpdef: NAME -DEF_RULE_NC(decorator, and(4), tok(OP_AT), rule(dotted_name), opt_rule(trailer_paren), tok(NEWLINE)) +DEF_RULE_NC(decorator, C_and(4), tok(OP_AT), rule(dotted_name), opt_rule(trailer_paren), tok(NEWLINE)) DEF_RULE_NC(decorators, one_or_more, rule(decorator)) DEF_RULE(decorated, c(decorated), and_ident(2), rule(decorators), rule(decorated_body)) #if MICROPY_PY_ASYNC_AWAIT -DEF_RULE_NC(decorated_body, or(3), rule(classdef), rule(funcdef), rule(async_funcdef)) -DEF_RULE_NC(async_funcdef, and(2), tok(KW_ASYNC), rule(funcdef)) +DEF_RULE_NC(decorated_body, C_or(3), rule(classdef), rule(funcdef), rule(async_funcdef)) +DEF_RULE_NC(async_funcdef, C_and(2), tok(KW_ASYNC), rule(funcdef)) #else -DEF_RULE_NC(decorated_body, or(2), rule(classdef), rule(funcdef)) +DEF_RULE_NC(decorated_body, C_or(2), rule(classdef), rule(funcdef)) #endif DEF_RULE(funcdef, c(funcdef), and_blank(8), tok(KW_DEF), tok(NAME), tok(DEL_PAREN_OPEN), opt_rule(typedargslist), tok(DEL_PAREN_CLOSE), opt_rule(funcdefrettype), tok(DEL_COLON), rule(suite)) DEF_RULE_NC(funcdefrettype, and_ident(2), tok(DEL_MINUS_MORE), rule(test)) // note: typedargslist lets through more than is allowed, compiler does further checks DEF_RULE_NC(typedargslist, list_with_end, rule(typedargslist_item), tok(DEL_COMMA)) -DEF_RULE_NC(typedargslist_item, or(3), rule(typedargslist_name), rule(typedargslist_star), rule(typedargslist_dbl_star)) +DEF_RULE_NC(typedargslist_item, C_or(3), rule(typedargslist_name), rule(typedargslist_star), rule(typedargslist_dbl_star)) DEF_RULE_NC(typedargslist_name, and_ident(3), tok(NAME), opt_rule(generic_colon_test), opt_rule(generic_equal_test)) -DEF_RULE_NC(typedargslist_star, and(2), tok(OP_STAR), opt_rule(tfpdef)) -DEF_RULE_NC(typedargslist_dbl_star, and(3), tok(OP_DBL_STAR), tok(NAME), opt_rule(generic_colon_test)) -DEF_RULE_NC(tfpdef, and(2), tok(NAME), opt_rule(generic_colon_test)) +DEF_RULE_NC(typedargslist_star, C_and(2), tok(OP_STAR), opt_rule(tfpdef)) +DEF_RULE_NC(typedargslist_dbl_star, C_and(3), tok(OP_DBL_STAR), tok(NAME), opt_rule(generic_colon_test)) +DEF_RULE_NC(tfpdef, C_and(2), tok(NAME), opt_rule(generic_colon_test)) // note: varargslist lets through more than is allowed, compiler does further checks DEF_RULE_NC(varargslist, list_with_end, rule(varargslist_item), tok(DEL_COMMA)) -DEF_RULE_NC(varargslist_item, or(3), rule(varargslist_name), rule(varargslist_star), rule(varargslist_dbl_star)) +DEF_RULE_NC(varargslist_item, C_or(3), rule(varargslist_name), rule(varargslist_star), rule(varargslist_dbl_star)) DEF_RULE_NC(varargslist_name, and_ident(2), tok(NAME), opt_rule(generic_equal_test)) -DEF_RULE_NC(varargslist_star, and(2), tok(OP_STAR), opt_rule(vfpdef)) -DEF_RULE_NC(varargslist_dbl_star, and(2), tok(OP_DBL_STAR), tok(NAME)) +DEF_RULE_NC(varargslist_star, C_and(2), tok(OP_STAR), opt_rule(vfpdef)) +DEF_RULE_NC(varargslist_dbl_star, C_and(2), tok(OP_DBL_STAR), tok(NAME)) DEF_RULE_NC(vfpdef, and_ident(1), tok(NAME)) // stmt: compound_stmt | simple_stmt -DEF_RULE_NC(stmt, or(2), rule(compound_stmt), rule(simple_stmt)) +DEF_RULE_NC(stmt, C_or(2), rule(compound_stmt), rule(simple_stmt)) // simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE @@ -104,17 +104,17 @@ DEF_RULE(simple_stmt_2, c(generic_all_nodes), list_with_end, rule(small_stmt), t // augassign: '+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//=' // # For normal and annotated assignments, additional restrictions enforced by the interpreter -DEF_RULE_NC(small_stmt, or(8), rule(del_stmt), rule(pass_stmt), rule(flow_stmt), rule(import_stmt), rule(global_stmt), rule(nonlocal_stmt), rule(assert_stmt), rule(expr_stmt)) -DEF_RULE(expr_stmt, c(expr_stmt), and(2), rule(testlist_star_expr), opt_rule(expr_stmt_2)) -DEF_RULE_NC(expr_stmt_2, or(3), rule(annassign), rule(expr_stmt_augassign), rule(expr_stmt_assign_list)) +DEF_RULE_NC(small_stmt, C_or(8), rule(del_stmt), rule(pass_stmt), rule(flow_stmt), rule(import_stmt), rule(global_stmt), rule(nonlocal_stmt), rule(assert_stmt), rule(expr_stmt)) +DEF_RULE(expr_stmt, c(expr_stmt), C_and(2), rule(testlist_star_expr), opt_rule(expr_stmt_2)) +DEF_RULE_NC(expr_stmt_2, C_or(3), rule(annassign), rule(expr_stmt_augassign), rule(expr_stmt_assign_list)) DEF_RULE_NC(expr_stmt_augassign, and_ident(2), rule(augassign), rule(expr_stmt_6)) DEF_RULE_NC(expr_stmt_assign_list, one_or_more, rule(expr_stmt_assign)) DEF_RULE_NC(expr_stmt_assign, and_ident(2), tok(DEL_EQUAL), rule(expr_stmt_6)) -DEF_RULE_NC(expr_stmt_6, or(2), rule(yield_expr), rule(testlist_star_expr)) +DEF_RULE_NC(expr_stmt_6, C_or(2), rule(yield_expr), rule(testlist_star_expr)) DEF_RULE(testlist_star_expr, c(generic_tuple), list_with_end, rule(testlist_star_expr_2), tok(DEL_COMMA)) -DEF_RULE_NC(testlist_star_expr_2, or(2), rule(star_expr), rule(test)) -DEF_RULE_NC(annassign, and(3), tok(DEL_COLON), rule(test), opt_rule(expr_stmt_assign)) -DEF_RULE_NC(augassign, or(13), tok(DEL_PLUS_EQUAL), tok(DEL_MINUS_EQUAL), tok(DEL_STAR_EQUAL), tok(DEL_AT_EQUAL), tok(DEL_SLASH_EQUAL), tok(DEL_PERCENT_EQUAL), tok(DEL_AMPERSAND_EQUAL), tok(DEL_PIPE_EQUAL), tok(DEL_CARET_EQUAL), tok(DEL_DBL_LESS_EQUAL), tok(DEL_DBL_MORE_EQUAL), tok(DEL_DBL_STAR_EQUAL), tok(DEL_DBL_SLASH_EQUAL)) +DEF_RULE_NC(testlist_star_expr_2, C_or(2), rule(star_expr), rule(test)) +DEF_RULE_NC(annassign, C_and(3), tok(DEL_COLON), rule(test), opt_rule(expr_stmt_assign)) +DEF_RULE_NC(augassign, C_or(13), tok(DEL_PLUS_EQUAL), tok(DEL_MINUS_EQUAL), tok(DEL_STAR_EQUAL), tok(DEL_AT_EQUAL), tok(DEL_SLASH_EQUAL), tok(DEL_PERCENT_EQUAL), tok(DEL_AMPERSAND_EQUAL), tok(DEL_PIPE_EQUAL), tok(DEL_CARET_EQUAL), tok(DEL_DBL_LESS_EQUAL), tok(DEL_DBL_MORE_EQUAL), tok(DEL_DBL_STAR_EQUAL), tok(DEL_DBL_SLASH_EQUAL)) // del_stmt: 'del' exprlist // pass_stmt: 'pass' @@ -125,14 +125,14 @@ DEF_RULE_NC(augassign, or(13), tok(DEL_PLUS_EQUAL), tok(DEL_MINUS_EQUAL), tok(DE // yield_stmt: yield_expr // raise_stmt: 'raise' [test ['from' test]] -DEF_RULE(del_stmt, c(del_stmt), and(2), tok(KW_DEL), rule(exprlist)) -DEF_RULE(pass_stmt, c(generic_all_nodes), and(1), tok(KW_PASS)) -DEF_RULE_NC(flow_stmt, or(5), rule(break_stmt), rule(continue_stmt), rule(return_stmt), rule(raise_stmt), rule(yield_stmt)) -DEF_RULE(break_stmt, c(break_cont_stmt), and(1), tok(KW_BREAK)) -DEF_RULE(continue_stmt, c(break_cont_stmt), and(1), tok(KW_CONTINUE)) -DEF_RULE(return_stmt, c(return_stmt), and(2), tok(KW_RETURN), opt_rule(testlist)) -DEF_RULE(yield_stmt, c(yield_stmt), and(1), rule(yield_expr)) -DEF_RULE(raise_stmt, c(raise_stmt), and(2), tok(KW_RAISE), opt_rule(raise_stmt_arg)) +DEF_RULE(del_stmt, c(del_stmt), C_and(2), tok(KW_DEL), rule(exprlist)) +DEF_RULE(pass_stmt, c(generic_all_nodes), C_and(1), tok(KW_PASS)) +DEF_RULE_NC(flow_stmt, C_or(5), rule(break_stmt), rule(continue_stmt), rule(return_stmt), rule(raise_stmt), rule(yield_stmt)) +DEF_RULE(break_stmt, c(break_cont_stmt), C_and(1), tok(KW_BREAK)) +DEF_RULE(continue_stmt, c(break_cont_stmt), C_and(1), tok(KW_CONTINUE)) +DEF_RULE(return_stmt, c(return_stmt), C_and(2), tok(KW_RETURN), opt_rule(testlist)) +DEF_RULE(yield_stmt, c(yield_stmt), C_and(1), rule(yield_expr)) +DEF_RULE(raise_stmt, c(raise_stmt), C_and(2), tok(KW_RAISE), opt_rule(raise_stmt_arg)) DEF_RULE_NC(raise_stmt_arg, and_ident(2), rule(test), opt_rule(raise_stmt_from)) DEF_RULE_NC(raise_stmt_from, and_ident(2), tok(KW_FROM), rule(test)) @@ -148,25 +148,25 @@ DEF_RULE_NC(raise_stmt_from, and_ident(2), tok(KW_FROM), rule(test)) // nonlocal_stmt: 'nonlocal' NAME (',' NAME)* // assert_stmt: 'assert' test [',' test] -DEF_RULE_NC(import_stmt, or(2), rule(import_name), rule(import_from)) -DEF_RULE(import_name, c(import_name), and(2), tok(KW_IMPORT), rule(dotted_as_names)) -DEF_RULE(import_from, c(import_from), and(4), tok(KW_FROM), rule(import_from_2), tok(KW_IMPORT), rule(import_from_3)) -DEF_RULE_NC(import_from_2, or(2), rule(dotted_name), rule(import_from_2b)) +DEF_RULE_NC(import_stmt, C_or(2), rule(import_name), rule(import_from)) +DEF_RULE(import_name, c(import_name), C_and(2), tok(KW_IMPORT), rule(dotted_as_names)) +DEF_RULE(import_from, c(import_from), C_and(4), tok(KW_FROM), rule(import_from_2), tok(KW_IMPORT), rule(import_from_3)) +DEF_RULE_NC(import_from_2, C_or(2), rule(dotted_name), rule(import_from_2b)) DEF_RULE_NC(import_from_2b, and_ident(2), rule(one_or_more_period_or_ellipsis), opt_rule(dotted_name)) -DEF_RULE_NC(import_from_3, or(3), tok(OP_STAR), rule(import_as_names_paren), rule(import_as_names)) +DEF_RULE_NC(import_from_3, C_or(3), tok(OP_STAR), rule(import_as_names_paren), rule(import_as_names)) DEF_RULE_NC(import_as_names_paren, and_ident(3), tok(DEL_PAREN_OPEN), rule(import_as_names), tok(DEL_PAREN_CLOSE)) DEF_RULE_NC(one_or_more_period_or_ellipsis, one_or_more, rule(period_or_ellipsis)) -DEF_RULE_NC(period_or_ellipsis, or(2), tok(DEL_PERIOD), tok(ELLIPSIS)) -DEF_RULE_NC(import_as_name, and(2), tok(NAME), opt_rule(as_name)) +DEF_RULE_NC(period_or_ellipsis, C_or(2), tok(DEL_PERIOD), tok(ELLIPSIS)) +DEF_RULE_NC(import_as_name, C_and(2), tok(NAME), opt_rule(as_name)) DEF_RULE_NC(dotted_as_name, and_ident(2), rule(dotted_name), opt_rule(as_name)) DEF_RULE_NC(as_name, and_ident(2), tok(KW_AS), tok(NAME)) DEF_RULE_NC(import_as_names, list_with_end, rule(import_as_name), tok(DEL_COMMA)) DEF_RULE_NC(dotted_as_names, list, rule(dotted_as_name), tok(DEL_COMMA)) DEF_RULE_NC(dotted_name, list, tok(NAME), tok(DEL_PERIOD)) -DEF_RULE(global_stmt, c(global_nonlocal_stmt), and(2), tok(KW_GLOBAL), rule(name_list)) -DEF_RULE(nonlocal_stmt, c(global_nonlocal_stmt), and(2), tok(KW_NONLOCAL), rule(name_list)) +DEF_RULE(global_stmt, c(global_nonlocal_stmt), C_and(2), tok(KW_GLOBAL), rule(name_list)) +DEF_RULE(nonlocal_stmt, c(global_nonlocal_stmt), C_and(2), tok(KW_NONLOCAL), rule(name_list)) DEF_RULE_NC(name_list, list, tok(NAME), tok(DEL_COMMA)) -DEF_RULE(assert_stmt, c(assert_stmt), and(3), tok(KW_ASSERT), rule(test), opt_rule(assert_stmt_extra)) +DEF_RULE(assert_stmt, c(assert_stmt), C_and(3), tok(KW_ASSERT), rule(test), opt_rule(assert_stmt_extra)) DEF_RULE_NC(assert_stmt_extra, and_ident(2), tok(DEL_COMMA), rule(test)) // compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt @@ -182,30 +182,30 @@ DEF_RULE_NC(assert_stmt_extra, and_ident(2), tok(DEL_COMMA), rule(test)) // async_stmt: 'async' (funcdef | with_stmt | for_stmt) #if MICROPY_PY_ASYNC_AWAIT -DEF_RULE_NC(compound_stmt, or(9), rule(if_stmt), rule(while_stmt), rule(for_stmt), rule(try_stmt), rule(with_stmt), rule(funcdef), rule(classdef), rule(decorated), rule(async_stmt)) -DEF_RULE(async_stmt, c(async_stmt), and(2), tok(KW_ASYNC), rule(async_stmt_2)) -DEF_RULE_NC(async_stmt_2, or(3), rule(funcdef), rule(with_stmt), rule(for_stmt)) +DEF_RULE_NC(compound_stmt, C_or(9), rule(if_stmt), rule(while_stmt), rule(for_stmt), rule(try_stmt), rule(with_stmt), rule(funcdef), rule(classdef), rule(decorated), rule(async_stmt)) +DEF_RULE(async_stmt, c(async_stmt), C_and(2), tok(KW_ASYNC), rule(async_stmt_2)) +DEF_RULE_NC(async_stmt_2, C_or(3), rule(funcdef), rule(with_stmt), rule(for_stmt)) #else -DEF_RULE_NC(compound_stmt, or(8), rule(if_stmt), rule(while_stmt), rule(for_stmt), rule(try_stmt), rule(with_stmt), rule(funcdef), rule(classdef), rule(decorated)) +DEF_RULE_NC(compound_stmt, C_or(8), rule(if_stmt), rule(while_stmt), rule(for_stmt), rule(try_stmt), rule(with_stmt), rule(funcdef), rule(classdef), rule(decorated)) #endif -DEF_RULE(if_stmt, c(if_stmt), and(6), tok(KW_IF), rule(namedexpr_test), tok(DEL_COLON), rule(suite), opt_rule(if_stmt_elif_list), opt_rule(else_stmt)) +DEF_RULE(if_stmt, c(if_stmt), C_and(6), tok(KW_IF), rule(namedexpr_test), tok(DEL_COLON), rule(suite), opt_rule(if_stmt_elif_list), opt_rule(else_stmt)) DEF_RULE_NC(if_stmt_elif_list, one_or_more, rule(if_stmt_elif)) -DEF_RULE_NC(if_stmt_elif, and(4), tok(KW_ELIF), rule(namedexpr_test), tok(DEL_COLON), rule(suite)) -DEF_RULE(while_stmt, c(while_stmt), and(5), tok(KW_WHILE), rule(namedexpr_test), tok(DEL_COLON), rule(suite), opt_rule(else_stmt)) -DEF_RULE(for_stmt, c(for_stmt), and(7), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(testlist), tok(DEL_COLON), rule(suite), opt_rule(else_stmt)) -DEF_RULE(try_stmt, c(try_stmt), and(4), tok(KW_TRY), tok(DEL_COLON), rule(suite), rule(try_stmt_2)) -DEF_RULE_NC(try_stmt_2, or(2), rule(try_stmt_except_and_more), rule(try_stmt_finally)) +DEF_RULE_NC(if_stmt_elif, C_and(4), tok(KW_ELIF), rule(namedexpr_test), tok(DEL_COLON), rule(suite)) +DEF_RULE(while_stmt, c(while_stmt), C_and(5), tok(KW_WHILE), rule(namedexpr_test), tok(DEL_COLON), rule(suite), opt_rule(else_stmt)) +DEF_RULE(for_stmt, c(for_stmt), C_and(7), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(testlist), tok(DEL_COLON), rule(suite), opt_rule(else_stmt)) +DEF_RULE(try_stmt, c(try_stmt), C_and(4), tok(KW_TRY), tok(DEL_COLON), rule(suite), rule(try_stmt_2)) +DEF_RULE_NC(try_stmt_2, C_or(2), rule(try_stmt_except_and_more), rule(try_stmt_finally)) DEF_RULE_NC(try_stmt_except_and_more, and_ident(3), rule(try_stmt_except_list), opt_rule(else_stmt), opt_rule(try_stmt_finally)) -DEF_RULE_NC(try_stmt_except, and(4), tok(KW_EXCEPT), opt_rule(try_stmt_as_name), tok(DEL_COLON), rule(suite)) +DEF_RULE_NC(try_stmt_except, C_and(4), tok(KW_EXCEPT), opt_rule(try_stmt_as_name), tok(DEL_COLON), rule(suite)) DEF_RULE_NC(try_stmt_as_name, and_ident(2), rule(test), opt_rule(as_name)) DEF_RULE_NC(try_stmt_except_list, one_or_more, rule(try_stmt_except)) -DEF_RULE_NC(try_stmt_finally, and(3), tok(KW_FINALLY), tok(DEL_COLON), rule(suite)) +DEF_RULE_NC(try_stmt_finally, C_and(3), tok(KW_FINALLY), tok(DEL_COLON), rule(suite)) DEF_RULE_NC(else_stmt, and_ident(3), tok(KW_ELSE), tok(DEL_COLON), rule(suite)) -DEF_RULE(with_stmt, c(with_stmt), and(4), tok(KW_WITH), rule(with_stmt_list), tok(DEL_COLON), rule(suite)) +DEF_RULE(with_stmt, c(with_stmt), C_and(4), tok(KW_WITH), rule(with_stmt_list), tok(DEL_COLON), rule(suite)) DEF_RULE_NC(with_stmt_list, list, rule(with_item), tok(DEL_COMMA)) DEF_RULE_NC(with_item, and_ident(2), rule(test), opt_rule(with_item_as)) DEF_RULE_NC(with_item_as, and_ident(2), tok(KW_AS), rule(expr)) -DEF_RULE_NC(suite, or(2), rule(suite_block), rule(simple_stmt)) +DEF_RULE_NC(suite, C_or(2), rule(suite_block), rule(simple_stmt)) DEF_RULE_NC(suite_block, and_ident(4), tok(NEWLINE), tok(INDENT), rule(suite_block_stmts), tok(DEDENT)) DEF_RULE(suite_block_stmts, c(generic_all_nodes), one_or_more, rule(stmt)) @@ -218,12 +218,12 @@ DEF_RULE(suite_block_stmts, c(generic_all_nodes), one_or_more, rule(stmt)) DEF_RULE(namedexpr_test, c(namedexpr), and_ident(2), rule(test), opt_rule(namedexpr_test_2)) DEF_RULE_NC(namedexpr_test_2, and_ident(2), tok(OP_ASSIGN), rule(test)) #else -DEF_RULE_NC(namedexpr_test, or(1), rule(test)) +DEF_RULE_NC(namedexpr_test, C_or(1), rule(test)) #endif -DEF_RULE_NC(test, or(2), rule(lambdef), rule(test_if_expr)) +DEF_RULE_NC(test, C_or(2), rule(lambdef), rule(test_if_expr)) DEF_RULE(test_if_expr, c(test_if_expr), and_ident(2), rule(or_test), opt_rule(test_if_else)) -DEF_RULE_NC(test_if_else, and(4), tok(KW_IF), rule(or_test), tok(KW_ELSE), rule(test)) -DEF_RULE_NC(test_nocond, or(2), rule(lambdef_nocond), rule(or_test)) +DEF_RULE_NC(test_if_else, C_and(4), tok(KW_IF), rule(or_test), tok(KW_ELSE), rule(test)) +DEF_RULE_NC(test_nocond, C_or(2), rule(lambdef_nocond), rule(or_test)) DEF_RULE(lambdef, c(lambdef), and_blank(4), tok(KW_LAMBDA), opt_rule(varargslist), tok(DEL_COLON), rule(test)) DEF_RULE(lambdef_nocond, c(lambdef), and_blank(4), tok(KW_LAMBDA), opt_rule(varargslist), tok(DEL_COLON), rule(test_nocond)) @@ -245,32 +245,32 @@ DEF_RULE(lambdef_nocond, c(lambdef), and_blank(4), tok(KW_LAMBDA), opt_rule(vara DEF_RULE(or_test, c(or_and_test), list, rule(and_test), tok(KW_OR)) DEF_RULE(and_test, c(or_and_test), list, rule(not_test), tok(KW_AND)) -DEF_RULE_NC(not_test, or(2), rule(not_test_2), rule(comparison)) -DEF_RULE(not_test_2, c(not_test_2), and(2), tok(KW_NOT), rule(not_test)) +DEF_RULE_NC(not_test, C_or(2), rule(not_test_2), rule(comparison)) +DEF_RULE(not_test_2, c(not_test_2), C_and(2), tok(KW_NOT), rule(not_test)) DEF_RULE(comparison, c(comparison), list, rule(expr), rule(comp_op)) -DEF_RULE_NC(comp_op, or(9), tok(OP_LESS), tok(OP_MORE), tok(OP_DBL_EQUAL), tok(OP_LESS_EQUAL), tok(OP_MORE_EQUAL), tok(OP_NOT_EQUAL), tok(KW_IN), rule(comp_op_not_in), rule(comp_op_is)) -DEF_RULE_NC(comp_op_not_in, and(2), tok(KW_NOT), tok(KW_IN)) -DEF_RULE_NC(comp_op_is, and(2), tok(KW_IS), opt_rule(comp_op_is_not)) -DEF_RULE_NC(comp_op_is_not, and(1), tok(KW_NOT)) -DEF_RULE(star_expr, c(star_expr), and(2), tok(OP_STAR), rule(expr)) +DEF_RULE_NC(comp_op, C_or(9), tok(OP_LESS), tok(OP_MORE), tok(OP_DBL_EQUAL), tok(OP_LESS_EQUAL), tok(OP_MORE_EQUAL), tok(OP_NOT_EQUAL), tok(KW_IN), rule(comp_op_not_in), rule(comp_op_is)) +DEF_RULE_NC(comp_op_not_in, C_and(2), tok(KW_NOT), tok(KW_IN)) +DEF_RULE_NC(comp_op_is, C_and(2), tok(KW_IS), opt_rule(comp_op_is_not)) +DEF_RULE_NC(comp_op_is_not, C_and(1), tok(KW_NOT)) +DEF_RULE(star_expr, c(star_expr), C_and(2), tok(OP_STAR), rule(expr)) DEF_RULE(expr, c(binary_op), list, rule(xor_expr), tok(OP_PIPE)) DEF_RULE(xor_expr, c(binary_op), list, rule(and_expr), tok(OP_CARET)) DEF_RULE(and_expr, c(binary_op), list, rule(shift_expr), tok(OP_AMPERSAND)) DEF_RULE(shift_expr, c(term), list, rule(arith_expr), rule(shift_op)) -DEF_RULE_NC(shift_op, or(2), tok(OP_DBL_LESS), tok(OP_DBL_MORE)) +DEF_RULE_NC(shift_op, C_or(2), tok(OP_DBL_LESS), tok(OP_DBL_MORE)) DEF_RULE(arith_expr, c(term), list, rule(term), rule(arith_op)) -DEF_RULE_NC(arith_op, or(2), tok(OP_PLUS), tok(OP_MINUS)) +DEF_RULE_NC(arith_op, C_or(2), tok(OP_PLUS), tok(OP_MINUS)) DEF_RULE(term, c(term), list, rule(factor), rule(term_op)) -DEF_RULE_NC(term_op, or(5), tok(OP_STAR), tok(OP_AT), tok(OP_SLASH), tok(OP_PERCENT), tok(OP_DBL_SLASH)) -DEF_RULE_NC(factor, or(2), rule(factor_2), rule(power)) +DEF_RULE_NC(term_op, C_or(5), tok(OP_STAR), tok(OP_AT), tok(OP_SLASH), tok(OP_PERCENT), tok(OP_DBL_SLASH)) +DEF_RULE_NC(factor, C_or(2), rule(factor_2), rule(power)) DEF_RULE(factor_2, c(factor_2), and_ident(2), rule(factor_op), rule(factor)) -DEF_RULE_NC(factor_op, or(3), tok(OP_PLUS), tok(OP_MINUS), tok(OP_TILDE)) +DEF_RULE_NC(factor_op, C_or(3), tok(OP_PLUS), tok(OP_MINUS), tok(OP_TILDE)) DEF_RULE(power, c(power), and_ident(2), rule(atom_expr), opt_rule(power_dbl_star)) #if MICROPY_PY_ASYNC_AWAIT -DEF_RULE_NC(atom_expr, or(2), rule(atom_expr_await), rule(atom_expr_normal)) -DEF_RULE(atom_expr_await, c(atom_expr_await), and(3), tok(KW_AWAIT), rule(atom), opt_rule(atom_expr_trailers)) +DEF_RULE_NC(atom_expr, C_or(2), rule(atom_expr_await), rule(atom_expr_normal)) +DEF_RULE(atom_expr_await, c(atom_expr_await), C_and(3), tok(KW_AWAIT), rule(atom), opt_rule(atom_expr_trailers)) #else -DEF_RULE_NC(atom_expr, or(1), rule(atom_expr_normal)) +DEF_RULE_NC(atom_expr, C_or(1), rule(atom_expr_normal)) #endif DEF_RULE(atom_expr_normal, c(atom_expr_normal), and_ident(2), rule(atom), opt_rule(atom_expr_trailers)) DEF_RULE_NC(atom_expr_trailers, one_or_more, rule(trailer)) @@ -280,20 +280,20 @@ DEF_RULE_NC(power_dbl_star, and_ident(2), tok(OP_DBL_STAR), rule(factor)) // testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] ) // trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME -DEF_RULE_NC(atom, or(12), tok(NAME), tok(INTEGER), tok(FLOAT_OR_IMAG), tok(STRING), tok(BYTES), tok(ELLIPSIS), tok(KW_NONE), tok(KW_TRUE), tok(KW_FALSE), rule(atom_paren), rule(atom_bracket), rule(atom_brace)) -DEF_RULE(atom_paren, c(atom_paren), and(3), tok(DEL_PAREN_OPEN), opt_rule(atom_2b), tok(DEL_PAREN_CLOSE)) -DEF_RULE_NC(atom_2b, or(2), rule(yield_expr), rule(testlist_comp)) -DEF_RULE(atom_bracket, c(atom_bracket), and(3), tok(DEL_BRACKET_OPEN), opt_rule(testlist_comp), tok(DEL_BRACKET_CLOSE)) -DEF_RULE(atom_brace, c(atom_brace), and(3), tok(DEL_BRACE_OPEN), opt_rule(dictorsetmaker), tok(DEL_BRACE_CLOSE)) +DEF_RULE_NC(atom, C_or(12), tok(NAME), tok(INTEGER), tok(FLOAT_OR_IMAG), tok(STRING), tok(BYTES), tok(ELLIPSIS), tok(KW_NONE), tok(KW_TRUE), tok(KW_FALSE), rule(atom_paren), rule(atom_bracket), rule(atom_brace)) +DEF_RULE(atom_paren, c(atom_paren), C_and(3), tok(DEL_PAREN_OPEN), opt_rule(atom_2b), tok(DEL_PAREN_CLOSE)) +DEF_RULE_NC(atom_2b, C_or(2), rule(yield_expr), rule(testlist_comp)) +DEF_RULE(atom_bracket, c(atom_bracket), C_and(3), tok(DEL_BRACKET_OPEN), opt_rule(testlist_comp), tok(DEL_BRACKET_CLOSE)) +DEF_RULE(atom_brace, c(atom_brace), C_and(3), tok(DEL_BRACE_OPEN), opt_rule(dictorsetmaker), tok(DEL_BRACE_CLOSE)) DEF_RULE_NC(testlist_comp, and_ident(2), rule(testlist_comp_2), opt_rule(testlist_comp_3)) -DEF_RULE_NC(testlist_comp_2, or(2), rule(star_expr), rule(namedexpr_test)) -DEF_RULE_NC(testlist_comp_3, or(2), rule(comp_for), rule(testlist_comp_3b)) +DEF_RULE_NC(testlist_comp_2, C_or(2), rule(star_expr), rule(namedexpr_test)) +DEF_RULE_NC(testlist_comp_3, C_or(2), rule(comp_for), rule(testlist_comp_3b)) DEF_RULE_NC(testlist_comp_3b, and_ident(2), tok(DEL_COMMA), opt_rule(testlist_comp_3c)) DEF_RULE_NC(testlist_comp_3c, list_with_end, rule(testlist_comp_2), tok(DEL_COMMA)) -DEF_RULE_NC(trailer, or(3), rule(trailer_paren), rule(trailer_bracket), rule(trailer_period)) -DEF_RULE(trailer_paren, c(trailer_paren), and(3), tok(DEL_PAREN_OPEN), opt_rule(arglist), tok(DEL_PAREN_CLOSE)) -DEF_RULE(trailer_bracket, c(trailer_bracket), and(3), tok(DEL_BRACKET_OPEN), rule(subscriptlist), tok(DEL_BRACKET_CLOSE)) -DEF_RULE(trailer_period, c(trailer_period), and(2), tok(DEL_PERIOD), tok(NAME)) +DEF_RULE_NC(trailer, C_or(3), rule(trailer_paren), rule(trailer_bracket), rule(trailer_period)) +DEF_RULE(trailer_paren, c(trailer_paren), C_and(3), tok(DEL_PAREN_OPEN), opt_rule(arglist), tok(DEL_PAREN_CLOSE)) +DEF_RULE(trailer_bracket, c(trailer_bracket), C_and(3), tok(DEL_BRACKET_OPEN), rule(subscriptlist), tok(DEL_BRACKET_CLOSE)) +DEF_RULE(trailer_period, c(trailer_period), C_and(2), tok(DEL_PERIOD), tok(NAME)) // subscriptlist: subscript (',' subscript)* [','] // subscript: test | [test] ':' [test] [sliceop] @@ -301,13 +301,13 @@ DEF_RULE(trailer_period, c(trailer_period), and(2), tok(DEL_PERIOD), tok(NAME)) #if MICROPY_PY_BUILTINS_SLICE DEF_RULE(subscriptlist, c(generic_tuple), list_with_end, rule(subscript), tok(DEL_COMMA)) -DEF_RULE_NC(subscript, or(2), rule(subscript_3), rule(subscript_2)) +DEF_RULE_NC(subscript, C_or(2), rule(subscript_3), rule(subscript_2)) DEF_RULE(subscript_2, c(subscript), and_ident(2), rule(test), opt_rule(subscript_3)) -DEF_RULE(subscript_3, c(subscript), and(2), tok(DEL_COLON), opt_rule(subscript_3b)) -DEF_RULE_NC(subscript_3b, or(2), rule(subscript_3c), rule(subscript_3d)) -DEF_RULE_NC(subscript_3c, and(2), tok(DEL_COLON), opt_rule(test)) +DEF_RULE(subscript_3, c(subscript), C_and(2), tok(DEL_COLON), opt_rule(subscript_3b)) +DEF_RULE_NC(subscript_3b, C_or(2), rule(subscript_3c), rule(subscript_3d)) +DEF_RULE_NC(subscript_3c, C_and(2), tok(DEL_COLON), opt_rule(test)) DEF_RULE_NC(subscript_3d, and_ident(2), rule(test), opt_rule(sliceop)) -DEF_RULE_NC(sliceop, and(2), tok(DEL_COLON), opt_rule(test)) +DEF_RULE_NC(sliceop, C_and(2), tok(DEL_COLON), opt_rule(test)) #else DEF_RULE(subscriptlist, c(generic_tuple), list_with_end, rule(test), tok(DEL_COMMA)) #endif @@ -317,17 +317,17 @@ DEF_RULE(subscriptlist, c(generic_tuple), list_with_end, rule(test), tok(DEL_COM // dictorsetmaker: (test ':' test (comp_for | (',' test ':' test)* [','])) | (test (comp_for | (',' test)* [','])) DEF_RULE_NC(exprlist, list_with_end, rule(exprlist_2), tok(DEL_COMMA)) -DEF_RULE_NC(exprlist_2, or(2), rule(star_expr), rule(expr)) +DEF_RULE_NC(exprlist_2, C_or(2), rule(star_expr), rule(expr)) DEF_RULE(testlist, c(generic_tuple), list_with_end, rule(test), tok(DEL_COMMA)) // TODO dictorsetmaker lets through more than is allowed DEF_RULE_NC(dictorsetmaker, and_ident(2), rule(dictorsetmaker_item), opt_rule(dictorsetmaker_tail)) #if MICROPY_PY_BUILTINS_SET DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and_ident(2), rule(test), opt_rule(generic_colon_test)) #else -DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and(3), rule(test), tok(DEL_COLON), rule(test)) +DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), C_and(3), rule(test), tok(DEL_COLON), rule(test)) #endif -DEF_RULE_NC(dictorsetmaker_tail, or(2), rule(comp_for), rule(dictorsetmaker_list)) -DEF_RULE_NC(dictorsetmaker_list, and(2), tok(DEL_COMMA), opt_rule(dictorsetmaker_list2)) +DEF_RULE_NC(dictorsetmaker_tail, C_or(2), rule(comp_for), rule(dictorsetmaker_list)) +DEF_RULE_NC(dictorsetmaker_list, C_and(2), tok(DEL_COMMA), opt_rule(dictorsetmaker_list2)) DEF_RULE_NC(dictorsetmaker_list2, list_with_end, rule(dictorsetmaker_item), tok(DEL_COMMA)) // classdef: 'class' NAME ['(' [arglist] ')'] ':' suite @@ -339,9 +339,9 @@ DEF_RULE_NC(classdef_2, and_ident(3), tok(DEL_PAREN_OPEN), opt_rule(arglist), to // TODO arglist lets through more than is allowed, compiler needs to do further verification DEF_RULE_NC(arglist, list_with_end, rule(arglist_2), tok(DEL_COMMA)) -DEF_RULE_NC(arglist_2, or(3), rule(arglist_star), rule(arglist_dbl_star), rule(argument)) -DEF_RULE_NC(arglist_star, and(2), tok(OP_STAR), rule(test)) -DEF_RULE_NC(arglist_dbl_star, and(2), tok(OP_DBL_STAR), rule(test)) +DEF_RULE_NC(arglist_2, C_or(3), rule(arglist_star), rule(arglist_dbl_star), rule(argument)) +DEF_RULE_NC(arglist_star, C_and(2), tok(OP_STAR), rule(test)) +DEF_RULE_NC(arglist_dbl_star, C_and(2), tok(OP_DBL_STAR), rule(test)) // # The reason that keywords are test nodes instead of NAME is that using NAME // # results in an ambiguity. ast.c makes sure it's a NAME. @@ -352,14 +352,14 @@ DEF_RULE_NC(arglist_dbl_star, and(2), tok(OP_DBL_STAR), rule(test)) DEF_RULE_NC(argument, and_ident(2), rule(test), opt_rule(argument_2)) #if MICROPY_PY_ASSIGN_EXPR -DEF_RULE_NC(argument_2, or(3), rule(comp_for), rule(generic_equal_test), rule(argument_3)) -DEF_RULE_NC(argument_3, and(2), tok(OP_ASSIGN), rule(test)) +DEF_RULE_NC(argument_2, C_or(3), rule(comp_for), rule(generic_equal_test), rule(argument_3)) +DEF_RULE_NC(argument_3, C_and(2), tok(OP_ASSIGN), rule(test)) #else -DEF_RULE_NC(argument_2, or(2), rule(comp_for), rule(generic_equal_test)) +DEF_RULE_NC(argument_2, C_or(2), rule(comp_for), rule(generic_equal_test)) #endif -DEF_RULE_NC(comp_iter, or(2), rule(comp_for), rule(comp_if)) +DEF_RULE_NC(comp_iter, C_or(2), rule(comp_for), rule(comp_if)) DEF_RULE_NC(comp_for, and_blank(5), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(or_test), opt_rule(comp_iter)) -DEF_RULE_NC(comp_if, and(3), tok(KW_IF), rule(test_nocond), opt_rule(comp_iter)) +DEF_RULE_NC(comp_if, C_and(3), tok(KW_IF), rule(test_nocond), opt_rule(comp_iter)) // # not used in grammar, but may appear in "node" passed from Parser to Compiler // encoding_decl: NAME @@ -367,6 +367,6 @@ DEF_RULE_NC(comp_if, and(3), tok(KW_IF), rule(test_nocond), opt_rule(comp_iter)) // yield_expr: 'yield' [yield_arg] // yield_arg: 'from' test | testlist -DEF_RULE(yield_expr, c(yield_expr), and(2), tok(KW_YIELD), opt_rule(yield_arg)) -DEF_RULE_NC(yield_arg, or(2), rule(yield_arg_from), rule(testlist)) -DEF_RULE_NC(yield_arg_from, and(2), tok(KW_FROM), rule(test)) +DEF_RULE(yield_expr, c(yield_expr), C_and(2), tok(KW_YIELD), opt_rule(yield_arg)) +DEF_RULE_NC(yield_arg, C_or(2), rule(yield_arg_from), rule(testlist)) +DEF_RULE_NC(yield_arg_from, C_and(2), tok(KW_FROM), rule(test)) diff --git a/py/lexer.c b/py/lexer.c index 3b8dd695c..98f36cced 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -576,7 +576,7 @@ void mp_lexer_to_next(mp_lexer_t *lex) { for (size_t i = 0; i < MP_ARRAY_SIZE(tok_kw); i++) { int cmp = strcmp(s, tok_kw[i]); if (cmp == 0) { - lex->tok_kind = MP_TOKEN_KW_FALSE + i; + lex->tok_kind = (mp_token_kind_t)(MP_TOKEN_KW_FALSE + i); if (lex->tok_kind == MP_TOKEN_KW___DEBUG__) { lex->tok_kind = (MP_STATE_VM(mp_optimise_value) == 0 ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE); } @@ -683,7 +683,7 @@ void mp_lexer_to_next(mp_lexer_t *lex) { } // set token kind - lex->tok_kind = tok_enc_kind[tok_enc_index]; + lex->tok_kind = (mp_token_kind_t)tok_enc_kind[tok_enc_index]; // compute bracket level for implicit line joining if (lex->tok_kind == MP_TOKEN_DEL_PAREN_OPEN || lex->tok_kind == MP_TOKEN_DEL_BRACKET_OPEN || lex->tok_kind == MP_TOKEN_DEL_BRACE_OPEN) { diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 970a86e6c..2f4bc9182 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -80,6 +80,12 @@ def make_version_header(filename): git_tag, git_hash = info + build_date = datetime.date.today() + if "SOURCE_DATE_EPOCH" in os.environ: + build_date = datetime.datetime.utcfromtimestamp( + int(os.environ["SOURCE_DATE_EPOCH"]) + ).date() + # Generate the file with the git and version info file_data = """\ // This file was generated by py/makeversionhdr.py @@ -89,7 +95,7 @@ def make_version_header(filename): """ % ( git_tag, git_hash, - datetime.date.today().strftime("%Y-%m-%d"), + build_date.strftime("%Y-%m-%d"), ) # Check if the file contents changed from last time diff --git a/py/misc.h b/py/misc.h index 44ba02b26..a1d7b3ab8 100644 --- a/py/misc.h +++ b/py/misc.h @@ -37,6 +37,8 @@ typedef unsigned char byte; typedef unsigned int uint; +//#include // for mpuint_t + /** generic ops *************************************************/ #ifndef MIN diff --git a/py/mkrules.cmake b/py/mkrules.cmake new file mode 100644 index 000000000..9c4c4afab --- /dev/null +++ b/py/mkrules.cmake @@ -0,0 +1,135 @@ +# CMake fragment for MicroPython rules + +set(MICROPY_GENHDR_DIR "${CMAKE_BINARY_DIR}/genhdr") +set(MICROPY_MPVERSION "${MICROPY_GENHDR_DIR}/mpversion.h") +set(MICROPY_MODULEDEFS "${MICROPY_GENHDR_DIR}/moduledefs.h") +set(MICROPY_QSTRDEFS_PY "${MICROPY_PY_DIR}/qstrdefs.h") +set(MICROPY_QSTRDEFS_LAST "${MICROPY_GENHDR_DIR}/qstr.i.last") +set(MICROPY_QSTRDEFS_SPLIT "${MICROPY_GENHDR_DIR}/qstr.split") +set(MICROPY_QSTRDEFS_COLLECTED "${MICROPY_GENHDR_DIR}/qstrdefs.collected.h") +set(MICROPY_QSTRDEFS_PREPROCESSED "${MICROPY_GENHDR_DIR}/qstrdefs.preprocessed.h") +set(MICROPY_QSTRDEFS_GENERATED "${MICROPY_GENHDR_DIR}/qstrdefs.generated.h") + +# Provide defaults for preprocessor flags if not already defined +if(NOT MICROPY_CPP_FLAGS) + get_target_property(MICROPY_CPP_INC ${MICROPY_TARGET} INCLUDE_DIRECTORIES) + get_target_property(MICROPY_CPP_DEF ${MICROPY_TARGET} COMPILE_DEFINITIONS) +endif() + +# Compute MICROPY_CPP_FLAGS for preprocessor +list(APPEND MICROPY_CPP_INC ${MICROPY_CPP_INC_EXTRA}) +list(APPEND MICROPY_CPP_DEF ${MICROPY_CPP_DEF_EXTRA}) +set(_prefix "-I") +foreach(_arg ${MICROPY_CPP_INC}) + list(APPEND MICROPY_CPP_FLAGS ${_prefix}${_arg}) +endforeach() +set(_prefix "-D") +foreach(_arg ${MICROPY_CPP_DEF}) + list(APPEND MICROPY_CPP_FLAGS ${_prefix}${_arg}) +endforeach() +list(APPEND MICROPY_CPP_FLAGS ${MICROPY_CPP_FLAGS_EXTRA}) + +find_package(Python3 REQUIRED COMPONENTS Interpreter) + +target_sources(${MICROPY_TARGET} PRIVATE + ${MICROPY_MPVERSION} + ${MICROPY_QSTRDEFS_GENERATED} +) + +# Command to force the build of another command + +add_custom_command( + OUTPUT MICROPY_FORCE_BUILD + COMMENT "" + COMMAND echo -n +) + +# Generate mpversion.h + +add_custom_command( + OUTPUT ${MICROPY_MPVERSION} + COMMAND ${CMAKE_COMMAND} -E make_directory ${MICROPY_GENHDR_DIR} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/py/makeversionhdr.py ${MICROPY_MPVERSION} + DEPENDS MICROPY_FORCE_BUILD +) + +# Generate moduledefs.h + +add_custom_command( + OUTPUT ${MICROPY_MODULEDEFS} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makemoduledefs.py --vpath="/" ${MICROPY_SOURCE_QSTR} > ${MICROPY_MODULEDEFS} + DEPENDS ${MICROPY_MPVERSION} + ${MICROPY_SOURCE_QSTR} +) + +# Generate qstrs + +# If any of the dependencies in this rule change then the C-preprocessor step must be run. +# It only needs to be passed the list of MICROPY_SOURCE_QSTR files that have changed since +# it was last run, but it looks like it's not possible to specify that with cmake. +add_custom_command( + OUTPUT ${MICROPY_QSTRDEFS_LAST} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py pp ${CMAKE_C_COMPILER} -E output ${MICROPY_GENHDR_DIR}/qstr.i.last cflags ${MICROPY_CPP_FLAGS} -DNO_QSTR sources ${MICROPY_SOURCE_QSTR} + DEPENDS ${MICROPY_MODULEDEFS} + ${MICROPY_SOURCE_QSTR} + VERBATIM + COMMAND_EXPAND_LISTS +) + +add_custom_command( + OUTPUT ${MICROPY_QSTRDEFS_SPLIT} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py split qstr ${MICROPY_GENHDR_DIR}/qstr.i.last ${MICROPY_GENHDR_DIR}/qstr _ + COMMAND touch ${MICROPY_QSTRDEFS_SPLIT} + DEPENDS ${MICROPY_QSTRDEFS_LAST} + VERBATIM + COMMAND_EXPAND_LISTS +) + +add_custom_command( + OUTPUT ${MICROPY_QSTRDEFS_COLLECTED} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py cat qstr _ ${MICROPY_GENHDR_DIR}/qstr ${MICROPY_QSTRDEFS_COLLECTED} + DEPENDS ${MICROPY_QSTRDEFS_SPLIT} + VERBATIM + COMMAND_EXPAND_LISTS +) + +add_custom_command( + OUTPUT ${MICROPY_QSTRDEFS_PREPROCESSED} + COMMAND cat ${MICROPY_QSTRDEFS_PY} ${MICROPY_QSTRDEFS_PORT} ${MICROPY_QSTRDEFS_COLLECTED} | sed "s/^Q(.*)/\"&\"/" | ${CMAKE_C_COMPILER} -E ${MICROPY_CPP_FLAGS} - | sed "s/^\\\"\\(Q(.*)\\)\\\"/\\1/" > ${MICROPY_QSTRDEFS_PREPROCESSED} + DEPENDS ${MICROPY_QSTRDEFS_PY} + ${MICROPY_QSTRDEFS_PORT} + ${MICROPY_QSTRDEFS_COLLECTED} + VERBATIM + COMMAND_EXPAND_LISTS +) + +add_custom_command( + OUTPUT ${MICROPY_QSTRDEFS_GENERATED} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdata.py ${MICROPY_QSTRDEFS_PREPROCESSED} > ${MICROPY_QSTRDEFS_GENERATED} + DEPENDS ${MICROPY_QSTRDEFS_PREPROCESSED} + VERBATIM + COMMAND_EXPAND_LISTS +) + +# Build frozen code if enabled + +if(MICROPY_FROZEN_MANIFEST) + set(MICROPY_FROZEN_CONTENT "${CMAKE_BINARY_DIR}/frozen_content.c") + + target_sources(${MICROPY_TARGET} PRIVATE + ${MICROPY_FROZEN_CONTENT} + ) + + target_compile_definitions(${MICROPY_TARGET} PUBLIC + MICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool + MICROPY_MODULE_FROZEN_MPY=\(1\) + ) + + add_custom_command( + OUTPUT ${MICROPY_FROZEN_CONTENT} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/tools/makemanifest.py -o ${MICROPY_FROZEN_CONTENT} -v "MPY_DIR=${MICROPY_DIR}" -v "PORT_DIR=${MICROPY_PORT_DIR}" -b "${CMAKE_BINARY_DIR}" -f${MICROPY_CROSS_FLAGS} ${MICROPY_FROZEN_MANIFEST} + DEPENDS MICROPY_FORCE_BUILD + ${MICROPY_QSTRDEFS_GENERATED} + VERBATIM + ) +endif() diff --git a/py/modgc.c b/py/modgc.c index 534a711c1..a0d4ec545 100644 --- a/py/modgc.c +++ b/py/modgc.c @@ -64,7 +64,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(gc_isenabled_obj, gc_isenabled); STATIC mp_obj_t gc_mem_free(void) { gc_info_t info; gc_info(&info); - return MP_OBJ_NEW_SMALL_INT(info.free); + return MP_OBJ_NEW_SMALL_INT(info.gc_free); } MP_DEFINE_CONST_FUN_OBJ_0(gc_mem_free_obj, gc_mem_free); diff --git a/py/modio.c b/py/modio.c index 1825d1aee..07c5a5b33 100644 --- a/py/modio.c +++ b/py/modio.c @@ -247,7 +247,12 @@ STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) { vstr_add_strn(&path_buf, path, len); len = path_buf.len; +#if __ARDUINO__ + #pragma message "cannot link mp_find_frozen_str" + const char *data = NULL; +#else const char *data = mp_find_frozen_str(path_buf.buf, &len); +#endif if (data != NULL) { mp_obj_stringio_t *o = m_new_obj(mp_obj_stringio_t); o->base.type = &mp_type_bytesio; diff --git a/py/modmicropython.c b/py/modmicropython.c index f7eadf79b..e62256d39 100644 --- a/py/modmicropython.c +++ b/py/modmicropython.c @@ -32,6 +32,9 @@ #include "py/gc.h" #include "py/mphal.h" +// #include "lib/utils/interrupt_char.h" // for mp_hal_set_interrupt_char +extern void mp_hal_set_interrupt_char(int c); + // Various builtins specific to MicroPython runtime, // living in micropython module diff --git a/py/mpconfig.h b/py/mpconfig.h index cec43272e..a9404d562 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -28,7 +28,7 @@ // Current version of MicroPython #define MICROPY_VERSION_MAJOR 1 -#define MICROPY_VERSION_MINOR 13 +#define MICROPY_VERSION_MINOR 14 #define MICROPY_VERSION_MICRO 0 // Combined version as a 32-bit number for convenience @@ -126,7 +126,7 @@ // Number of bytes in memory allocation/GC block. Any size allocated will be // rounded up to be multiples of this. #ifndef MICROPY_BYTES_PER_GC_BLOCK -#define MICROPY_BYTES_PER_GC_BLOCK (4 * BYTES_PER_WORD) +#define MICROPY_BYTES_PER_GC_BLOCK (4 * MP_BYTES_PER_OBJ_WORD) #endif // Number of words allocated (in BSS) to the GC stack (minimum is 1) @@ -283,6 +283,11 @@ #define MICROPY_PERSISTENT_CODE_SAVE (0) #endif +// Whether to support saving persistent code to a file via mp_raw_code_save_file +#ifndef MICROPY_PERSISTENT_CODE_SAVE_FILE +#define MICROPY_PERSISTENT_CODE_SAVE_FILE (0) +#endif + // Whether generated code can persist independently of the VM/runtime instance // This is enabled automatically when needed by other features #ifndef MICROPY_PERSISTENT_CODE @@ -304,6 +309,11 @@ #define MICROPY_EMIT_THUMB (0) #endif +// Whether to emit ARMv7-M instruction support in thumb native code +#ifndef MICROPY_EMIT_THUMB_ARMV7M +#define MICROPY_EMIT_THUMB_ARMV7M (1) +#endif + // Whether to enable the thumb inline assembler #ifndef MICROPY_EMIT_INLINE_THUMB #define MICROPY_EMIT_INLINE_THUMB (0) @@ -1527,17 +1537,17 @@ typedef double mp_float_t; #define STATIC static #endif -// Number of bytes in a word -#ifndef BYTES_PER_WORD -#define BYTES_PER_WORD (sizeof(mp_uint_t)) +// Number of bytes in an object word: mp_obj_t, mp_uint_t, mp_uint_t +#ifndef MP_BYTES_PER_OBJ_WORD +#define MP_BYTES_PER_OBJ_WORD (sizeof(mp_uint_t)) #endif -#ifndef BITS_PER_BYTE -#define BITS_PER_BYTE (8) +// Number of bits in a byte +#ifndef MP_BITS_PER_BYTE +#define MP_BITS_PER_BYTE (8) #endif -#define BITS_PER_WORD (BITS_PER_BYTE * BYTES_PER_WORD) // mp_int_t value with most significant bit set -#define WORD_MSBIT_HIGH (((mp_uint_t)1) << (BYTES_PER_WORD * 8 - 1)) +#define MP_OBJ_WORD_MSBIT_HIGH (((mp_uint_t)1) << (MP_BYTES_PER_OBJ_WORD * MP_BITS_PER_BYTE - 1)) // Make sure both MP_ENDIANNESS_LITTLE and MP_ENDIANNESS_BIG are // defined and that they are the opposite of each other. diff --git a/py/mpstate.c b/py/mpstate.c index 6ce64adfd..c18325e44 100644 --- a/py/mpstate.c +++ b/py/mpstate.c @@ -30,4 +30,4 @@ mp_dynamic_compiler_t mp_dynamic_compiler = {0}; #endif -mp_state_ctx_t mp_state_ctx; +extern mp_state_ctx_t mp_state_ctx; diff --git a/py/mpz.c b/py/mpz.c index 751593053..e0d249c21 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -531,60 +531,37 @@ STATIC void mpn_div(mpz_dig_t *num_dig, size_t *num_len, const mpz_dig_t *den_di quo /= lead_den_digit; // Multiply quo by den and subtract from num to get remainder. - // We have different code here to handle different compile-time - // configurations of mpz: - // - // 1. DIG_SIZE is stricly less than half the number of bits - // available in mpz_dbl_dig_t. In this case we can use a - // slightly more optimal (in time and space) routine that - // uses the extra bits in mpz_dbl_dig_signed_t to store a - // sign bit. - // - // 2. DIG_SIZE is exactly half the number of bits available in - // mpz_dbl_dig_t. In this (common) case we need to be careful - // not to overflow the borrow variable. And the shifting of - // borrow needs some special logic (it's a shift right with - // round up). - // + // Must be careful with overflow of the borrow variable. Both + // borrow and low_digs are signed values and need signed right-shift, + // but x is unsigned and may take a full-range value. const mpz_dig_t *d = den_dig; mpz_dbl_dig_t d_norm = 0; - mpz_dbl_dig_t borrow = 0; + mpz_dbl_dig_signed_t borrow = 0; for (mpz_dig_t *n = num_dig - den_len; n < num_dig; ++n, ++d) { + // Get the next digit in (den). d_norm = ((mpz_dbl_dig_t)*d << norm_shift) | (d_norm >> DIG_SIZE); + // Multiply the next digit in (quo * den). mpz_dbl_dig_t x = (mpz_dbl_dig_t)quo * (d_norm & DIG_MASK); - #if DIG_SIZE < MPZ_DBL_DIG_SIZE / 2 - borrow += (mpz_dbl_dig_t)*n - x; // will overflow if DIG_SIZE >= MPZ_DBL_DIG_SIZE/2 - *n = borrow & DIG_MASK; - borrow = (mpz_dbl_dig_signed_t)borrow >> DIG_SIZE; - #else // DIG_SIZE == MPZ_DBL_DIG_SIZE / 2 - if (x >= *n || *n - x <= borrow) { - borrow += x - (mpz_dbl_dig_t)*n; - *n = (-borrow) & DIG_MASK; - borrow = (borrow >> DIG_SIZE) + ((borrow & DIG_MASK) == 0 ? 0 : 1); // shift-right with round-up - } else { - *n = ((mpz_dbl_dig_t)*n - x - borrow) & DIG_MASK; - borrow = 0; - } - #endif + // Compute the low DIG_MASK bits of the next digit in (num - quo * den) + mpz_dbl_dig_signed_t low_digs = (borrow & DIG_MASK) + *n - (x & DIG_MASK); + // Store the digit result for (num). + *n = low_digs & DIG_MASK; + // Compute the borrow, shifted right before summing to avoid overflow. + borrow = (borrow >> DIG_SIZE) - (x >> DIG_SIZE) + (low_digs >> DIG_SIZE); } - #if DIG_SIZE < MPZ_DBL_DIG_SIZE / 2 - // Borrow was negative in the above for-loop, make it positive for next if-block. - borrow = -borrow; - #endif - // At this point we have either: // // 1. quo was the correct value and the most-sig-digit of num is exactly - // cancelled by borrow (borrow == *num_dig). In this case there is + // cancelled by borrow (borrow + *num_dig == 0). In this case there is // nothing more to do. // // 2. quo was too large, we subtracted too many den from num, and the - // most-sig-digit of num is 1 less than borrow (borrow == *num_dig + 1). + // most-sig-digit of num is less than needed (borrow + *num_dig < 0). // In this case we must reduce quo and add back den to num until the // carry from this operation cancels out the borrow. // - borrow -= *num_dig; + borrow += *num_dig; for (; borrow != 0; --quo) { d = den_dig; d_norm = 0; @@ -595,7 +572,7 @@ STATIC void mpn_div(mpz_dig_t *num_dig, size_t *num_len, const mpz_dig_t *den_di *n = carry & DIG_MASK; carry >>= DIG_SIZE; } - borrow -= carry; + borrow += carry; } // store this digit of the quotient @@ -1573,7 +1550,7 @@ bool mpz_as_int_checked(const mpz_t *i, mp_int_t *value) { mpz_dig_t *d = i->dig + i->len; while (d-- > i->dig) { - if (val > (~(WORD_MSBIT_HIGH) >> DIG_SIZE)) { + if (val > (~(MP_OBJ_WORD_MSBIT_HIGH) >> DIG_SIZE)) { // will overflow return false; } @@ -1598,7 +1575,7 @@ bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) { mpz_dig_t *d = i->dig + i->len; while (d-- > i->dig) { - if (val > (~(WORD_MSBIT_HIGH) >> (DIG_SIZE - 1))) { + if (val > (~(MP_OBJ_WORD_MSBIT_HIGH) >> (DIG_SIZE - 1))) { // will overflow return false; } diff --git a/py/objexcept.c b/py/objexcept.c index 885032c3e..0baebb9f6 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -439,7 +439,7 @@ STATIC void exc_add_strn(void *data, const char *str, size_t len) { memcpy(pr->buf + pr->len, str, len); pr->len += len; } - +mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list args); mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { va_list args; va_start(args, fmt); diff --git a/py/objfloat.c b/py/objfloat.c index 64c3324ff..d00ba07d7 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -82,7 +82,7 @@ mp_int_t mp_float_hash(mp_float_t src) { // number may have a fraction; xor the integer part with the fractional part val = (frc >> (MP_FLOAT_FRAC_BITS - adj_exp)) ^ (frc & (((mp_float_uint_t)1 << (MP_FLOAT_FRAC_BITS - adj_exp)) - 1)); - } else if ((unsigned int)adj_exp < BITS_PER_BYTE * sizeof(mp_int_t) - 1) { + } else if ((unsigned int)adj_exp < MP_BITS_PER_BYTE * sizeof(mp_int_t) - 1) { // the number is a (big) whole integer and will fit in val's signed-width val = (mp_int_t)frc << (adj_exp - MP_FLOAT_FRAC_BITS); } else { diff --git a/py/objfun.c b/py/objfun.c index b360255b3..a03f2fc6d 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -357,6 +357,10 @@ void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (attr == MP_QSTR___name__) { dest[0] = MP_OBJ_NEW_QSTR(mp_obj_fun_get_name(self_in)); } + if (attr == MP_QSTR___globals__) { + mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); + dest[0] = MP_OBJ_FROM_PTR(self->globals); + } } #endif diff --git a/py/objint.c b/py/objint.c index 74a38308e..67d0f8911 100644 --- a/py/objint.c +++ b/py/objint.c @@ -131,7 +131,7 @@ STATIC mp_fp_as_int_class_t mp_classify_fp_as_int(mp_float_t val) { return MP_FP_CLASS_FIT_SMALLINT; } #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG - if (e <= (((sizeof(long long) * BITS_PER_BYTE) + MP_FLOAT_EXP_BIAS - 2) << MP_FLOAT_EXP_SHIFT_I32)) { + if (e <= (((sizeof(long long) * MP_BITS_PER_BYTE) + MP_FLOAT_EXP_BIAS - 2) << MP_FLOAT_EXP_SHIFT_I32)) { return MP_FP_CLASS_FIT_LONGINT; } #endif diff --git a/py/parse.c b/py/parse.c index 7923ba5ed..4b06f2360 100644 --- a/py/parse.c +++ b/py/parse.c @@ -76,8 +76,8 @@ enum { // Define an array of actions corresponding to each rule STATIC const uint8_t rule_act_table[] = { -#define or(n) (RULE_ACT_OR | n) -#define and(n) (RULE_ACT_AND | n) +#define C_or(n) (RULE_ACT_OR | n) +#define C_and(n) (RULE_ACT_AND | n) #define and_ident(n) (RULE_ACT_AND | n | RULE_ACT_ALLOW_IDENT) #define and_blank(n) (RULE_ACT_AND | n | RULE_ACT_ADD_BLANK) #define one_or_more (RULE_ACT_LIST | 2) @@ -98,8 +98,8 @@ STATIC const uint8_t rule_act_table[] = { #undef DEF_RULE #undef DEF_RULE_NC -#undef or -#undef and +#undef C_or +#undef C_and #undef and_ident #undef and_blank #undef one_or_more @@ -454,7 +454,7 @@ STATIC void push_result_node(parser_t *parser, mp_parse_node_t pn) { } STATIC mp_parse_node_t make_node_const_object(parser_t *parser, size_t src_line, mp_obj_t obj) { - mp_parse_node_struct_t *pn = parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_obj_t)); + mp_parse_node_struct_t *pn = (mp_parse_node_struct_t *)parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_obj_t)); #if NO_NLR if (pn == NULL) { return MP_PARSE_NODE_NULL; @@ -835,7 +835,7 @@ STATIC void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, } #endif - mp_parse_node_struct_t *pn = parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t) * num_args); + mp_parse_node_struct_t *pn = (mp_parse_node_struct_t *)parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t) * num_args); #if NO_NLR if (pn == NULL) { return; @@ -969,7 +969,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { for (; i < n; ++i) { if ((rule_arg[i] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { // need to match a token - mp_token_kind_t tok_kind = rule_arg[i] & RULE_ARG_ARG_MASK; + mp_token_kind_t tok_kind = (mp_token_kind_t)(rule_arg[i] & RULE_ARG_ARG_MASK); if (lex->tok_kind == tok_kind) { // matched token if (tok_kind == MP_TOKEN_NAME) { @@ -1021,7 +1021,7 @@ mp_parse_tree_t mp_parse(mp_lexer_t *lex, mp_parse_input_kind_t input_kind) { for (size_t x = n; x > 0;) { --x; if ((rule_arg[x] & RULE_ARG_KIND_MASK) == RULE_ARG_TOK) { - mp_token_kind_t tok_kind = rule_arg[x] & RULE_ARG_ARG_MASK; + mp_token_kind_t tok_kind = (mp_token_kind_t)(rule_arg[x] & RULE_ARG_ARG_MASK); if (tok_kind == MP_TOKEN_NAME) { // only tokens which were names are pushed to stack i += 1; diff --git a/py/persistentcode.c b/py/persistentcode.c index 21693d985..8447d649f 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -605,7 +605,7 @@ STATIC void mp_print_bytes(mp_print_t *print, const byte *data, size_t len) { print->print_strn(print->data, (const char *)data, len); } -#define BYTES_FOR_INT ((BYTES_PER_WORD * 8 + 6) / 7) +#define BYTES_FOR_INT ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7) STATIC void mp_print_uint(mp_print_t *print, size_t n) { byte buf[BYTES_FOR_INT]; byte *p = buf + sizeof(buf); @@ -835,10 +835,7 @@ void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) { save_raw_code(print, rc, &qw); } -// here we define mp_raw_code_save_file depending on the port -// TODO abstract this away properly - -#if defined(__i386__) || defined(__x86_64__) || defined(_WIN32) || defined(__unix__) +#if MICROPY_PERSISTENT_CODE_SAVE_FILE #include #include @@ -863,12 +860,6 @@ void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename) { MP_THREAD_GIL_ENTER(); } -#else - #if defined(__EMSCRIPTEN__) || defined(__WASI__) - #pragma message "#error mp_raw_code_save_file not implemented for this platform" - #else - #error mp_raw_code_save_file not implemented for this platform - #endif -#endif +#endif // MICROPY_PERSISTENT_CODE_SAVE_FILE #endif // MICROPY_PERSISTENT_CODE_SAVE diff --git a/py/py.cmake b/py/py.cmake new file mode 100644 index 000000000..52baaa8ef --- /dev/null +++ b/py/py.cmake @@ -0,0 +1,124 @@ +# CMake fragment for MicroPython core py component + +set(MICROPY_PY_DIR "${MICROPY_DIR}/py") + +# All py/ source files +set(MICROPY_SOURCE_PY + ${MICROPY_PY_DIR}/argcheck.c + ${MICROPY_PY_DIR}/asmarm.c + ${MICROPY_PY_DIR}/asmbase.c + ${MICROPY_PY_DIR}/asmthumb.c + ${MICROPY_PY_DIR}/asmx64.c + ${MICROPY_PY_DIR}/asmx86.c + ${MICROPY_PY_DIR}/asmxtensa.c + ${MICROPY_PY_DIR}/bc.c + ${MICROPY_PY_DIR}/binary.c + ${MICROPY_PY_DIR}/builtinevex.c + ${MICROPY_PY_DIR}/builtinhelp.c + ${MICROPY_PY_DIR}/builtinimport.c + ${MICROPY_PY_DIR}/compile.c + ${MICROPY_PY_DIR}/emitbc.c + ${MICROPY_PY_DIR}/emitcommon.c + ${MICROPY_PY_DIR}/emitglue.c + ${MICROPY_PY_DIR}/emitinlinethumb.c + ${MICROPY_PY_DIR}/emitinlinextensa.c + ${MICROPY_PY_DIR}/emitnarm.c + ${MICROPY_PY_DIR}/emitnthumb.c + ${MICROPY_PY_DIR}/emitnx64.c + ${MICROPY_PY_DIR}/emitnx86.c + ${MICROPY_PY_DIR}/emitnxtensa.c + ${MICROPY_PY_DIR}/emitnxtensawin.c + ${MICROPY_PY_DIR}/formatfloat.c + ${MICROPY_PY_DIR}/frozenmod.c + ${MICROPY_PY_DIR}/gc.c + ${MICROPY_PY_DIR}/lexer.c + ${MICROPY_PY_DIR}/malloc.c + ${MICROPY_PY_DIR}/map.c + ${MICROPY_PY_DIR}/modarray.c + ${MICROPY_PY_DIR}/modbuiltins.c + ${MICROPY_PY_DIR}/modcmath.c + ${MICROPY_PY_DIR}/modcollections.c + ${MICROPY_PY_DIR}/modgc.c + ${MICROPY_PY_DIR}/modio.c + ${MICROPY_PY_DIR}/modmath.c + ${MICROPY_PY_DIR}/modmicropython.c + ${MICROPY_PY_DIR}/modstruct.c + ${MICROPY_PY_DIR}/modsys.c + ${MICROPY_PY_DIR}/modthread.c + ${MICROPY_PY_DIR}/moduerrno.c + ${MICROPY_PY_DIR}/mpprint.c + ${MICROPY_PY_DIR}/mpstate.c + ${MICROPY_PY_DIR}/mpz.c + ${MICROPY_PY_DIR}/nativeglue.c + ${MICROPY_PY_DIR}/nlr.c + ${MICROPY_PY_DIR}/nlrpowerpc.c + ${MICROPY_PY_DIR}/nlrsetjmp.c + ${MICROPY_PY_DIR}/nlrthumb.c + ${MICROPY_PY_DIR}/nlrx64.c + ${MICROPY_PY_DIR}/nlrx86.c + ${MICROPY_PY_DIR}/nlrxtensa.c + ${MICROPY_PY_DIR}/obj.c + ${MICROPY_PY_DIR}/objarray.c + ${MICROPY_PY_DIR}/objattrtuple.c + ${MICROPY_PY_DIR}/objbool.c + ${MICROPY_PY_DIR}/objboundmeth.c + ${MICROPY_PY_DIR}/objcell.c + ${MICROPY_PY_DIR}/objclosure.c + ${MICROPY_PY_DIR}/objcomplex.c + ${MICROPY_PY_DIR}/objdeque.c + ${MICROPY_PY_DIR}/objdict.c + ${MICROPY_PY_DIR}/objenumerate.c + ${MICROPY_PY_DIR}/objexcept.c + ${MICROPY_PY_DIR}/objfilter.c + ${MICROPY_PY_DIR}/objfloat.c + ${MICROPY_PY_DIR}/objfun.c + ${MICROPY_PY_DIR}/objgenerator.c + ${MICROPY_PY_DIR}/objgetitemiter.c + ${MICROPY_PY_DIR}/objint.c + ${MICROPY_PY_DIR}/objint_longlong.c + ${MICROPY_PY_DIR}/objint_mpz.c + ${MICROPY_PY_DIR}/objlist.c + ${MICROPY_PY_DIR}/objmap.c + ${MICROPY_PY_DIR}/objmodule.c + ${MICROPY_PY_DIR}/objnamedtuple.c + ${MICROPY_PY_DIR}/objnone.c + ${MICROPY_PY_DIR}/objobject.c + ${MICROPY_PY_DIR}/objpolyiter.c + ${MICROPY_PY_DIR}/objproperty.c + ${MICROPY_PY_DIR}/objrange.c + ${MICROPY_PY_DIR}/objreversed.c + ${MICROPY_PY_DIR}/objset.c + ${MICROPY_PY_DIR}/objsingleton.c + ${MICROPY_PY_DIR}/objslice.c + ${MICROPY_PY_DIR}/objstr.c + ${MICROPY_PY_DIR}/objstringio.c + ${MICROPY_PY_DIR}/objstrunicode.c + ${MICROPY_PY_DIR}/objtuple.c + ${MICROPY_PY_DIR}/objtype.c + ${MICROPY_PY_DIR}/objzip.c + ${MICROPY_PY_DIR}/opmethods.c + ${MICROPY_PY_DIR}/pairheap.c + ${MICROPY_PY_DIR}/parse.c + ${MICROPY_PY_DIR}/parsenum.c + ${MICROPY_PY_DIR}/parsenumbase.c + ${MICROPY_PY_DIR}/persistentcode.c + ${MICROPY_PY_DIR}/profile.c + ${MICROPY_PY_DIR}/pystack.c + ${MICROPY_PY_DIR}/qstr.c + ${MICROPY_PY_DIR}/reader.c + ${MICROPY_PY_DIR}/repl.c + ${MICROPY_PY_DIR}/ringbuf.c + ${MICROPY_PY_DIR}/runtime.c + ${MICROPY_PY_DIR}/runtime_utils.c + ${MICROPY_PY_DIR}/scheduler.c + ${MICROPY_PY_DIR}/scope.c + ${MICROPY_PY_DIR}/sequence.c + ${MICROPY_PY_DIR}/showbc.c + ${MICROPY_PY_DIR}/smallint.c + ${MICROPY_PY_DIR}/stackctrl.c + ${MICROPY_PY_DIR}/stream.c + ${MICROPY_PY_DIR}/unicode.c + ${MICROPY_PY_DIR}/vm.c + ${MICROPY_PY_DIR}/vstr.c + ${MICROPY_PY_DIR}/warning.c +) diff --git a/py/pystack.c b/py/pystack.c index c761061ac..cc4b5e2e4 100644 --- a/py/pystack.c +++ b/py/pystack.c @@ -31,9 +31,9 @@ #if MICROPY_ENABLE_PYSTACK void mp_pystack_init(void *start, void *end) { - MP_STATE_THREAD(pystack_start) = start; - MP_STATE_THREAD(pystack_end) = end; - MP_STATE_THREAD(pystack_cur) = start; + MP_STATE_THREAD(pystack_start) = (uint8_t *)start; + MP_STATE_THREAD(pystack_end) = (uint8_t *)end; + MP_STATE_THREAD(pystack_cur) = (uint8_t *)start; } void *mp_pystack_alloc(size_t n_bytes) { diff --git a/py/qstr.c b/py/qstr.c index aff77e93e..14b11f628 100644 --- a/py/qstr.c +++ b/py/qstr.c @@ -99,7 +99,7 @@ mp_uint_t qstr_compute_hash(const byte *data, size_t len) { } return hash; } - +#pragma message "error: initialization of flexible array member is not allowed" const qstr_pool_t mp_qstr_const_pool = { NULL, // no previous pool 0, // no previous pool diff --git a/py/qstr.h b/py/qstr.h index df87217ff..0b6fb12b0 100644 --- a/py/qstr.h +++ b/py/qstr.h @@ -32,7 +32,7 @@ // See qstrdefs.h for a list of qstr's that are available as constants. // Reference them as MP_QSTR_xxxx. // -// Note: it would be possible to define MP_QSTR_xxx as qstr_from_str_static("xxx") +// Note: it would be possible to define MP_QSTR_xxx as qstr_from_str("xxx") // for qstrs that are referenced this way, but you don't want to have them in ROM. // first entry in enum will be MP_QSTRnull=0, which indicates invalid/no qstr @@ -55,7 +55,6 @@ typedef struct _qstr_pool_t { const byte *qstrs[]; } qstr_pool_t; -#define QSTR_FROM_STR_STATIC(s) (qstr_from_strn((s), strlen(s))) #define QSTR_TOTAL() (MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len) void qstr_init(void); diff --git a/py/runtime.c b/py/runtime.c index 4e9eebda1..f0d2a8930 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -390,7 +390,9 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { if (rhs_val < 0) { // negative shift not allowed mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); - } else if (rhs_val >= (mp_int_t)BITS_PER_WORD || lhs_val > (MP_SMALL_INT_MAX >> rhs_val) || lhs_val < (MP_SMALL_INT_MIN >> rhs_val)) { + } else if (rhs_val >= (mp_int_t)(sizeof(lhs_val) * MP_BITS_PER_BYTE) + || lhs_val > (MP_SMALL_INT_MAX >> rhs_val) + || lhs_val < (MP_SMALL_INT_MIN >> rhs_val)) { // left-shift will overflow, so use higher precision integer lhs = mp_obj_new_int_from_ll(lhs_val); goto generic_binary_op; @@ -407,10 +409,10 @@ mp_obj_t mp_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); } else { // standard precision is enough for right-shift - if (rhs_val >= (mp_int_t)BITS_PER_WORD) { + if (rhs_val >= (mp_int_t)(sizeof(lhs_val) * MP_BITS_PER_BYTE)) { // Shifting to big amounts is underfined behavior // in C and is CPU-dependent; propagate sign bit. - rhs_val = BITS_PER_WORD - 1; + rhs_val = sizeof(lhs_val) * MP_BITS_PER_BYTE - 1; } lhs_val >>= rhs_val; } diff --git a/py/runtime.h b/py/runtime.h index f1b4a95ff..84043ce90 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -346,7 +346,7 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level); mp_obj_t mp_import_from(mp_obj_t module, qstr name); void mp_import_all(mp_obj_t module); -#define mp_raise_type(exc_type) mp_raise_msg(exc_type, NULL) +//#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); diff --git a/py/smallint.h b/py/smallint.h index 6a3c75236..58d843e8a 100644 --- a/py/smallint.h +++ b/py/smallint.h @@ -36,17 +36,17 @@ // In SMALL_INT, next-to-highest bits is used as sign, so both must match for value in range #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C -#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)WORD_MSBIT_HIGH) >> 1)) -#define MP_SMALL_INT_FITS(n) ((((n) ^ ((n) << 1)) & WORD_MSBIT_HIGH) == 0) +#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)MP_OBJ_WORD_MSBIT_HIGH) >> 1)) +#define MP_SMALL_INT_FITS(n) ((((n) ^ ((n) << 1)) & MP_OBJ_WORD_MSBIT_HIGH) == 0) // Mask to truncate mp_int_t to positive value -#define MP_SMALL_INT_POSITIVE_MASK ~(WORD_MSBIT_HIGH | (WORD_MSBIT_HIGH >> 1)) +#define MP_SMALL_INT_POSITIVE_MASK ~(MP_OBJ_WORD_MSBIT_HIGH | (MP_OBJ_WORD_MSBIT_HIGH >> 1)) #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B -#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)WORD_MSBIT_HIGH) >> 2)) +#define MP_SMALL_INT_MIN ((mp_int_t)(((mp_int_t)MP_OBJ_WORD_MSBIT_HIGH) >> 2)) #define MP_SMALL_INT_FITS(n) ((((n) & MP_SMALL_INT_MIN) == 0) || (((n) & MP_SMALL_INT_MIN) == MP_SMALL_INT_MIN)) // Mask to truncate mp_int_t to positive value -#define MP_SMALL_INT_POSITIVE_MASK ~(WORD_MSBIT_HIGH | (WORD_MSBIT_HIGH >> 1) | (WORD_MSBIT_HIGH >> 2)) +#define MP_SMALL_INT_POSITIVE_MASK ~(MP_OBJ_WORD_MSBIT_HIGH | (MP_OBJ_WORD_MSBIT_HIGH >> 1) | (MP_OBJ_WORD_MSBIT_HIGH >> 2)) #elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D diff --git a/py/vstr.c b/py/vstr.c index 3084ef9ee..1893c6369 100644 --- a/py/vstr.c +++ b/py/vstr.c @@ -34,6 +34,9 @@ #include "py/runtime.h" #include "py/mpprint.h" +#pragma message "missing fwd decl of mp_vprintf" +extern int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args); + // returned value is always at least 1 greater than argument #define ROUND_ALLOC(a) (((a) & ((~0U) - 7)) + 8) diff --git a/tests/basics/fun_globals.py b/tests/basics/fun_globals.py new file mode 100644 index 000000000..3f32e8bdb --- /dev/null +++ b/tests/basics/fun_globals.py @@ -0,0 +1,21 @@ +# test the __globals__ attribute of a function + + +def foo(): + pass + + +if not hasattr(foo, "__globals__"): + print("SKIP") + raise SystemExit + +print(type(foo.__globals__)) +print(foo.__globals__ is globals()) + +foo.__globals__["bar"] = 123 +print(bar) + +try: + foo.__globals__ = None +except AttributeError: + print("AttributeError") diff --git a/tests/basics/int_big_div.py b/tests/basics/int_big_div.py index 642f051d4..29fd40597 100644 --- a/tests/basics/int_big_div.py +++ b/tests/basics/int_big_div.py @@ -8,3 +8,7 @@ print((x + 1) // x) x = 0x86c60128feff5330 print((x + 1) // x) + +# these check edge cases where borrow overflows +print((2 ** 48 - 1) ** 2 // (2 ** 48 - 1)) +print((2 ** 256 - 2 ** 32) ** 2 // (2 ** 256 - 2 ** 32)) diff --git a/tests/esp32/esp32_nvs.py b/tests/esp32/esp32_nvs.py new file mode 100644 index 000000000..fd8b152ca --- /dev/null +++ b/tests/esp32/esp32_nvs.py @@ -0,0 +1,67 @@ +# Test the esp32 NVS class - access to esp-idf's Non-Volatile-Storage + +from esp32 import NVS + +nvs = NVS("mp-test") + +# test setting and gettin an integer kv +nvs.set_i32("key1", 1234) +print(nvs.get_i32("key1")) +nvs.set_i32("key2", -503) +print(nvs.get_i32("key2")) +print(nvs.get_i32("key1")) + +# test setting and getting a blob kv using a bytearray +blob1 = "testing a string as a blob" +nvs.set_blob("blob1", blob1) +buf1 = bytearray(len(blob1)) +len1 = nvs.get_blob("blob1", buf1) +print(buf1) +print(len(blob1), len1) + +# test setting and getting a blob kv using a string +blob2 = b"testing a bytearray" +nvs.set_blob("blob2", blob2) +buf2 = bytearray(len(blob2)) +len2 = nvs.get_blob("blob2", buf2) +print(buf2) +print(len(blob2), len2) + +# test raising of error exceptions +nvs.erase_key("key1") +try: + nvs.erase_key("key1") # not found +except OSError as e: + print(e) +try: + nvs.get_i32("key1") # not found +except OSError as e: + print(e) +try: + nvs.get_i32("blob1") # not found (blob1 exists but diff type) +except OSError as e: + print(e) +try: + buf3 = bytearray(10) + nvs.get_blob("blob1", buf3) # invalid length (too short) +except OSError as e: + print(e) + +nvs.commit() # we're not verifying that this does anything, just doesn't error + +# test using a second namespace and that it doesn't interfere with first +nvs2 = NVS("mp-test2") +try: + print(nvs2.get_i32("key2")) +except OSError as e: + print(e) +nvs2.set_i32("key2", 7654) +print(nvs.get_i32("key2")) +print(nvs2.get_i32("key2")) + +# clean-up (the namespaces will remain) +nvs.erase_key("key2") +nvs.erase_key("blob1") +nvs.erase_key("blob2") +nvs2.erase_key("key2") +nvs.commit() diff --git a/tests/esp32/esp32_nvs.py.exp b/tests/esp32/esp32_nvs.py.exp new file mode 100644 index 000000000..33cdfd6df --- /dev/null +++ b/tests/esp32/esp32_nvs.py.exp @@ -0,0 +1,14 @@ +1234 +-503 +1234 +bytearray(b'testing a string as a blob') +26 26 +bytearray(b'testing a bytearray') +19 19 +(-4354, 'ESP_ERR_NVS_NOT_FOUND') +(-4354, 'ESP_ERR_NVS_NOT_FOUND') +(-4354, 'ESP_ERR_NVS_NOT_FOUND') +(-4364, 'ESP_ERR_NVS_INVALID_LENGTH') +(-4354, 'ESP_ERR_NVS_NOT_FOUND') +-503 +7654 diff --git a/tests/extmod/uasyncio_current_task.py b/tests/extmod/uasyncio_current_task.py new file mode 100644 index 000000000..3165a2cf1 --- /dev/null +++ b/tests/extmod/uasyncio_current_task.py @@ -0,0 +1,25 @@ +# Test current_task() function + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(result): + result[0] = asyncio.current_task() + + +async def main(): + result = [None] + t = asyncio.create_task(task(result)) + await asyncio.sleep(0) + await asyncio.sleep(0) + print(t is result[0]) + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_current_task.py.exp b/tests/extmod/uasyncio_current_task.py.exp new file mode 100644 index 000000000..0ca95142b --- /dev/null +++ b/tests/extmod/uasyncio_current_task.py.exp @@ -0,0 +1 @@ +True diff --git a/tests/extmod/uasyncio_threadsafeflag.py b/tests/extmod/uasyncio_threadsafeflag.py new file mode 100644 index 000000000..4e002a3d2 --- /dev/null +++ b/tests/extmod/uasyncio_threadsafeflag.py @@ -0,0 +1,79 @@ +# Test Event class + +try: + import uasyncio as asyncio +except ImportError: + print("SKIP") + raise SystemExit + + +import micropython + +try: + micropython.schedule +except AttributeError: + print("SKIP") + raise SystemExit + + +try: + # Unix port can't select/poll on user-defined types. + import uselect as select + + poller = select.poll() + poller.register(asyncio.ThreadSafeFlag()) +except TypeError: + print("SKIP") + raise SystemExit + + +async def task(id, flag): + print("task", id) + await flag.wait() + print("task", id, "done") + + +def set_from_schedule(flag): + print("schedule") + flag.set() + print("schedule done") + + +async def main(): + flag = asyncio.ThreadSafeFlag() + + # Set the flag from within the loop. + t = asyncio.create_task(task(1, flag)) + print("yield") + await asyncio.sleep(0) + print("set event") + flag.set() + print("yield") + await asyncio.sleep(0) + print("wait task") + await t + + # Set the flag from scheduler context. + print("----") + t = asyncio.create_task(task(2, flag)) + print("yield") + await asyncio.sleep(0) + print("set event") + micropython.schedule(set_from_schedule, flag) + print("yield") + await asyncio.sleep(0) + print("wait task") + await t + + # Flag already set. + print("----") + print("set event") + flag.set() + t = asyncio.create_task(task(3, flag)) + print("yield") + await asyncio.sleep(0) + print("wait task") + await t + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_threadsafeflag.py.exp b/tests/extmod/uasyncio_threadsafeflag.py.exp new file mode 100644 index 000000000..aef4e479b --- /dev/null +++ b/tests/extmod/uasyncio_threadsafeflag.py.exp @@ -0,0 +1,21 @@ +yield +task 1 +set event +yield +wait task +task 1 done +---- +yield +task 2 +set event +yield +schedule +schedule done +wait task +task 2 done +---- +set event +yield +task 3 +task 3 done +wait task diff --git a/tests/extmod/utime_res.py b/tests/extmod/utime_res.py new file mode 100644 index 000000000..4b6243348 --- /dev/null +++ b/tests/extmod/utime_res.py @@ -0,0 +1,65 @@ +# test utime resolutions + +try: + import utime +except ImportError: + print("SKIP") + raise SystemExit + + +def gmtime_time(): + return utime.gmtime(utime.time()) + + +def localtime_time(): + return utime.localtime(utime.time()) + + +def test(): + TEST_TIME = 2500 + EXPECTED_MAP = ( + # (function name, min. number of results in 2.5 sec) + ("time", 3), + ("gmtime", 3), + ("localtime", 3), + ("gmtime_time", 3), + ("localtime_time", 3), + ("ticks_ms", 15), + ("ticks_us", 15), + ("ticks_ns", 15), + ("ticks_cpu", 15), + ) + + # call time functions + results_map = {} + end_time = utime.ticks_ms() + TEST_TIME + while utime.ticks_diff(end_time, utime.ticks_ms()) > 0: + utime.sleep_ms(100) + for func_name, _ in EXPECTED_MAP: + try: + time_func = getattr(utime, func_name, None) or globals()[func_name] + now = time_func() # may raise AttributeError + except (KeyError, AttributeError): + continue + try: + results_map[func_name].add(now) + except KeyError: + results_map[func_name] = {now} + + # check results + for func_name, min_len in EXPECTED_MAP: + print("Testing %s" % func_name) + results = results_map.get(func_name) + if results is None: + pass + elif func_name == "ticks_cpu" and results == {0}: + # ticks_cpu() returns 0 on some ports (e.g. unix) + pass + elif len(results) < min_len: + print( + "%s() returns %s result%s in %s ms, expecting >= %s" + % (func_name, len(results), "s"[: len(results) != 1], TEST_TIME, min_len) + ) + + +test() diff --git a/tests/extmod/utime_res.py.exp b/tests/extmod/utime_res.py.exp new file mode 100644 index 000000000..08c2d8295 --- /dev/null +++ b/tests/extmod/utime_res.py.exp @@ -0,0 +1,9 @@ +Testing time +Testing gmtime +Testing localtime +Testing gmtime_time +Testing localtime_time +Testing ticks_ms +Testing ticks_us +Testing ticks_ns +Testing ticks_cpu diff --git a/tests/extmod/utime_time_ns.py b/tests/extmod/utime_time_ns.py index 8f3890f1c..0d13f839d 100644 --- a/tests/extmod/utime_time_ns.py +++ b/tests/extmod/utime_time_ns.py @@ -11,14 +11,14 @@ t0 = utime.time_ns() -utime.sleep_us(1000) +utime.sleep_us(5000) t1 = utime.time_ns() # Check that time_ns increases. print(t0 < t1) -# Check that time_ns counts correctly, but be very lenient with the upper bound (50ms). -if 950000 < t1 - t0 < 50000000: +# Check that time_ns counts correctly, but be very lenient with the bounds (2ms to 50ms). +if 2000000 < t1 - t0 < 50000000: print(True) else: print(t0, t1, t1 - t0) diff --git a/tests/extmod/vfs_lfs_superblock.py b/tests/extmod/vfs_lfs_superblock.py new file mode 100644 index 000000000..1ac567555 --- /dev/null +++ b/tests/extmod/vfs_lfs_superblock.py @@ -0,0 +1,47 @@ +# Test for VfsLfs using a RAM device, when the first superblock does not exist + +try: + import uos + + uos.VfsLfs2 +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +class RAMBlockDevice: + def __init__(self, block_size, data): + self.block_size = block_size + self.data = data + + def readblocks(self, block, buf, off): + addr = block * self.block_size + off + for i in range(len(buf)): + buf[i] = self.data[addr + i] + + def ioctl(self, op, arg): + if op == 4: # block count + return len(self.data) // self.block_size + if op == 5: # block size + return self.block_size + if op == 6: # erase block + return 0 + + +# This is a valid littlefs2 filesystem with a block size of 64 bytes. +# The first block (where the first superblock is stored) is fully erased. +lfs2_data = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x02\x00\x00\x00\xf0\x0f\xff\xf7littlefs/\xe0\x00\x10\x00\x00\x02\x00@\x00\x00\x00\x04\x00\x00\x00\xff\x00\x00\x00\xff\xff\xff\x7f\xfe\x03\x00\x00p\x1f\xfc\x08\x1b\xb4\x14\xa7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x00\x00\xff\xef\xff\xf7test.txt \x00\x00\x08p\x1f\xfc\x08\x83\xf1u\xba\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + +# Create the block device from the static data (it will be read-only). +bdev = RAMBlockDevice(64, lfs2_data) + +# Create the VFS explicitly, no auto-detection is needed for this. +vfs = uos.VfsLfs2(bdev) +print(list(vfs.ilistdir())) + +# Mount the block device directly; this relies on auto-detection. +uos.mount(bdev, "/userfs") +print(uos.listdir("/userfs")) + +# Clean up. +uos.umount("/userfs") diff --git a/tests/extmod/vfs_lfs_superblock.py.exp b/tests/extmod/vfs_lfs_superblock.py.exp new file mode 100644 index 000000000..c71bf50e8 --- /dev/null +++ b/tests/extmod/vfs_lfs_superblock.py.exp @@ -0,0 +1,2 @@ +[] +[] diff --git a/tests/extmod/vfs_posix.py b/tests/extmod/vfs_posix.py index 3bea99365..f8c4aae40 100644 --- a/tests/extmod/vfs_posix.py +++ b/tests/extmod/vfs_posix.py @@ -8,6 +8,15 @@ print("SKIP") raise SystemExit +# We need a directory for testing that doesn't already exist. +# Skip the test if it does exist. +temp_dir = "micropy_test_dir" +try: + uos.stat(temp_dir) + print("SKIP") + raise SystemExit +except OSError: + pass # getcwd and chdir curdir = uos.getcwd() @@ -21,3 +30,59 @@ # listdir and ilistdir print(type(uos.listdir("/"))) + +# mkdir +uos.mkdir(temp_dir) + +# file create +f = open(temp_dir + "/test", "w") +f.write("hello") +f.close() + +# close on a closed file should succeed +f.close() + +# construct a file object using the type constructor, with a raw fileno +f = type(f)(2) +print(f) + +# file read +f = open(temp_dir + "/test", "r") +print(f.read()) +f.close() + +# rename +uos.rename(temp_dir + "/test", temp_dir + "/test2") +print(uos.listdir(temp_dir)) + +# construct new VfsPosix with path argument +vfs = uos.VfsPosix(temp_dir) +print(list(i[0] for i in vfs.ilistdir("."))) + +# stat, statvfs +print(type(vfs.stat("."))) +print(type(vfs.statvfs("."))) + +# check types of ilistdir with str/bytes arguments +print(type(list(vfs.ilistdir("."))[0][0])) +print(type(list(vfs.ilistdir(b"."))[0][0])) + +# remove +uos.remove(temp_dir + "/test2") +print(uos.listdir(temp_dir)) + +# remove with error +try: + uos.remove(temp_dir + "/test2") +except OSError: + print("remove OSError") + +# rmdir +uos.rmdir(temp_dir) +print(temp_dir in uos.listdir()) + +# rmdir with error +try: + uos.rmdir(temp_dir) +except OSError: + print("rmdir OSError") diff --git a/tests/extmod/vfs_posix.py.exp b/tests/extmod/vfs_posix.py.exp index 1b1f59b43..e7d68f38e 100644 --- a/tests/extmod/vfs_posix.py.exp +++ b/tests/extmod/vfs_posix.py.exp @@ -2,3 +2,15 @@ True + +hello +['test2'] +['test2'] + + + + +[] +remove OSError +False +rmdir OSError diff --git a/tests/io/file1.py b/tests/io/file1.py index 2a46c9c63..de30045d3 100644 --- a/tests/io/file1.py +++ b/tests/io/file1.py @@ -44,3 +44,6 @@ except OSError: print("OSError") f.close() + +# close() on a closed file +f.close() diff --git a/tests/micropython/native_for.py b/tests/micropython/native_for.py new file mode 100644 index 000000000..c640a8d08 --- /dev/null +++ b/tests/micropython/native_for.py @@ -0,0 +1,19 @@ +# test for native for loops + + +@micropython.native +def f1(n): + for i in range(n): + print(i) + + +f1(4) + + +@micropython.native +def f2(r): + for i in r: + print(i) + + +f2(range(4)) diff --git a/tests/micropython/native_for.py.exp b/tests/micropython/native_for.py.exp new file mode 100644 index 000000000..d4dc73eff --- /dev/null +++ b/tests/micropython/native_for.py.exp @@ -0,0 +1,8 @@ +0 +1 +2 +3 +0 +1 +2 +3 diff --git a/tests/multi_bluetooth/perf_gatt_notify.py b/tests/multi_bluetooth/perf_gatt_notify.py new file mode 100644 index 000000000..88986dda3 --- /dev/null +++ b/tests/multi_bluetooth/perf_gatt_notify.py @@ -0,0 +1,133 @@ +# Ping-pong GATT notifications between two devices. + +from micropython import const +import time, machine, bluetooth + +TIMEOUT_MS = 2000 + +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) +_IRQ_GATTC_CHARACTERISTIC_DONE = const(12) +_IRQ_GATTC_NOTIFY = const(18) + +# How long to run the test for. +_NUM_NOTIFICATIONS = const(50) + +SERVICE_UUID = bluetooth.UUID("A5A5A5A5-FFFF-9999-1111-5A5A5A5A5A5A") +CHAR_UUID = bluetooth.UUID("00000000-1111-2222-3333-444444444444") +CHAR = ( + CHAR_UUID, + bluetooth.FLAG_NOTIFY, +) +SERVICE = ( + SERVICE_UUID, + (CHAR,), +) +SERVICES = (SERVICE,) + +is_central = False + +waiting_events = {} + + +def irq(event, data): + if event == _IRQ_CENTRAL_CONNECT: + waiting_events[event] = data[0] + elif event == _IRQ_PERIPHERAL_CONNECT: + waiting_events[event] = data[0] + elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT: + # conn_handle, def_handle, value_handle, properties, uuid = data + if data[-1] == CHAR_UUID: + waiting_events[event] = data[2] + else: + return + elif event == _IRQ_GATTC_NOTIFY: + if is_central: + conn_handle, value_handle, notify_data = data + ble.gatts_notify(conn_handle, value_handle, b"central" + notify_data) + + if event not in waiting_events: + waiting_events[event] = None + + +def wait_for_event(event, timeout_ms): + t0 = time.ticks_ms() + while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms: + if event in waiting_events: + return waiting_events.pop(event) + machine.idle() + raise ValueError("Timeout waiting for {}".format(event)) + + +# Acting in peripheral role. +def instance0(): + multitest.globals(BDADDR=ble.config("mac")) + ((char_handle,),) = ble.gatts_register_services(SERVICES) + print("gap_advertise") + ble.gap_advertise(20_000, b"\x02\x01\x06\x04\xffMPY") + multitest.next() + try: + # Wait for central to connect to us. + conn_handle = wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS) + + # Discover characteristics. + ble.gattc_discover_characteristics(conn_handle, 1, 65535) + value_handle = wait_for_event(_IRQ_GATTC_CHARACTERISTIC_RESULT, TIMEOUT_MS) + wait_for_event(_IRQ_GATTC_CHARACTERISTIC_DONE, TIMEOUT_MS) + + # Give the central enough time to discover chars. + time.sleep_ms(500) + + ticks_start = time.ticks_ms() + + for i in range(_NUM_NOTIFICATIONS): + # Send a notification and wait for a response. + ble.gatts_notify(conn_handle, value_handle, "peripheral" + str(i)) + wait_for_event(_IRQ_GATTC_NOTIFY, TIMEOUT_MS) + + ticks_end = time.ticks_ms() + ticks_total = time.ticks_diff(ticks_end, ticks_start) + print( + "Acknowledged {} notifications in {} ms. {} ms/notification.".format( + _NUM_NOTIFICATIONS, ticks_total, ticks_total // _NUM_NOTIFICATIONS + ) + ) + + # Disconnect the central. + print("gap_disconnect:", ble.gap_disconnect(conn_handle)) + wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS) + finally: + ble.active(0) + + +# Acting in central role. +def instance1(): + global is_central + is_central = True + ((char_handle,),) = ble.gatts_register_services(SERVICES) + multitest.next() + try: + # Connect to peripheral and then disconnect. + print("gap_connect") + ble.gap_connect(*BDADDR) + conn_handle = wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS) + + # Discover characteristics. + ble.gattc_discover_characteristics(conn_handle, 1, 65535) + value_handle = wait_for_event(_IRQ_GATTC_CHARACTERISTIC_RESULT, TIMEOUT_MS) + wait_for_event(_IRQ_GATTC_CHARACTERISTIC_DONE, TIMEOUT_MS) + + # The IRQ handler will respond to each notification. + + # Wait for the peripheral to disconnect us. + wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, 20000) + finally: + ble.active(0) + + +ble = bluetooth.BLE() +ble.active(1) +ble.irq(irq) diff --git a/tests/multi_bluetooth/perf_gatt_notify.py.exp b/tests/multi_bluetooth/perf_gatt_notify.py.exp new file mode 100644 index 000000000..e69de29bb diff --git a/tests/multi_bluetooth/perf_l2cap.py b/tests/multi_bluetooth/perf_l2cap.py new file mode 100644 index 000000000..9b07bb1dc --- /dev/null +++ b/tests/multi_bluetooth/perf_l2cap.py @@ -0,0 +1,148 @@ +# Send L2CAP data as fast as possible and time it. + +from micropython import const +import time, machine, bluetooth, random + +TIMEOUT_MS = 1000 + +_IRQ_CENTRAL_CONNECT = const(1) +_IRQ_CENTRAL_DISCONNECT = const(2) +_IRQ_PERIPHERAL_CONNECT = const(7) +_IRQ_PERIPHERAL_DISCONNECT = const(8) +_IRQ_L2CAP_ACCEPT = const(22) +_IRQ_L2CAP_CONNECT = const(23) +_IRQ_L2CAP_DISCONNECT = const(24) +_IRQ_L2CAP_RECV = const(25) +_IRQ_L2CAP_SEND_READY = const(26) + +_L2CAP_PSM = const(22) +_L2CAP_MTU = const(512) + +_PAYLOAD_LEN = const(_L2CAP_MTU) +_NUM_PAYLOADS = const(20) + +_RANDOM_SEED = 22 + + +waiting_events = {} + + +def irq(event, data): + if event == _IRQ_CENTRAL_CONNECT: + conn_handle, addr_type, addr = data + waiting_events[event] = conn_handle + elif event == _IRQ_PERIPHERAL_CONNECT: + conn_handle, addr_type, addr = data + waiting_events[event] = conn_handle + elif event == _IRQ_L2CAP_ACCEPT: + conn_handle, cid, psm, our_mtu, peer_mtu = data + waiting_events[event] = (conn_handle, cid, psm) + elif event == _IRQ_L2CAP_CONNECT: + conn_handle, cid, psm, our_mtu, peer_mtu = data + waiting_events[event] = (conn_handle, cid, psm, our_mtu, peer_mtu) + + if event not in waiting_events: + waiting_events[event] = None + + +def wait_for_event(event, timeout_ms): + t0 = time.ticks_ms() + while time.ticks_diff(time.ticks_ms(), t0) < timeout_ms: + if event in waiting_events: + return waiting_events.pop(event) + machine.idle() + raise ValueError("Timeout waiting for {}".format(event)) + + +def send_data(ble, conn_handle, cid): + buf = bytearray(_PAYLOAD_LEN) + for i in range(_NUM_PAYLOADS): + for j in range(_PAYLOAD_LEN): + buf[j] = random.randint(0, 255) + if not ble.l2cap_send(conn_handle, cid, buf): + wait_for_event(_IRQ_L2CAP_SEND_READY, TIMEOUT_MS) + + +def recv_data(ble, conn_handle, cid): + buf = bytearray(_PAYLOAD_LEN) + recv_bytes = 0 + recv_correct = 0 + expected_bytes = _PAYLOAD_LEN * _NUM_PAYLOADS + ticks_first_byte = 0 + while recv_bytes < expected_bytes: + wait_for_event(_IRQ_L2CAP_RECV, TIMEOUT_MS) + if not ticks_first_byte: + ticks_first_byte = time.ticks_ms() + while True: + n = ble.l2cap_recvinto(conn_handle, cid, buf) + if n == 0: + break + recv_bytes += n + for i in range(n): + if buf[i] == random.randint(0, 255): + recv_correct += 1 + ticks_end = time.ticks_ms() + return recv_bytes, recv_correct, time.ticks_diff(ticks_end, ticks_first_byte) + + +# Acting in peripheral role. +def instance0(): + multitest.globals(BDADDR=ble.config("mac")) + ble.gap_advertise(20_000, b"\x02\x01\x06\x04\xffMPY") + multitest.next() + try: + # Wait for central to connect to us. + conn_handle = wait_for_event(_IRQ_CENTRAL_CONNECT, TIMEOUT_MS) + + ble.l2cap_listen(_L2CAP_PSM, _L2CAP_MTU) + + conn_handle, cid, psm = wait_for_event(_IRQ_L2CAP_ACCEPT, TIMEOUT_MS) + conn_handle, cid, psm, our_mtu, peer_mtu = wait_for_event(_IRQ_L2CAP_CONNECT, TIMEOUT_MS) + + random.seed(_RANDOM_SEED) + + send_data(ble, conn_handle, cid) + + wait_for_event(_IRQ_L2CAP_DISCONNECT, TIMEOUT_MS) + + # Wait for the central to disconnect. + wait_for_event(_IRQ_CENTRAL_DISCONNECT, TIMEOUT_MS) + finally: + ble.active(0) + + +# Acting in central role. +def instance1(): + multitest.next() + try: + # Connect to peripheral and then disconnect. + ble.gap_connect(*BDADDR) + conn_handle = wait_for_event(_IRQ_PERIPHERAL_CONNECT, TIMEOUT_MS) + + ble.l2cap_connect(conn_handle, _L2CAP_PSM, _L2CAP_MTU) + conn_handle, cid, psm, our_mtu, peer_mtu = wait_for_event(_IRQ_L2CAP_CONNECT, TIMEOUT_MS) + + random.seed(_RANDOM_SEED) + + recv_bytes, recv_correct, total_ticks = recv_data(ble, conn_handle, cid) + + # Disconnect channel. + ble.l2cap_disconnect(conn_handle, cid) + wait_for_event(_IRQ_L2CAP_DISCONNECT, TIMEOUT_MS) + + print( + "Received {}/{} bytes in {} ms. {} B/s".format( + recv_bytes, recv_correct, total_ticks, recv_bytes * 1000 // total_ticks + ) + ) + + # Disconnect from peripheral. + ble.gap_disconnect(conn_handle) + wait_for_event(_IRQ_PERIPHERAL_DISCONNECT, TIMEOUT_MS) + finally: + ble.active(0) + + +ble = bluetooth.BLE() +ble.active(1) +ble.irq(irq) diff --git a/tests/multi_bluetooth/perf_l2cap.py.exp b/tests/multi_bluetooth/perf_l2cap.py.exp new file mode 100644 index 000000000..e69de29bb diff --git a/tests/net_hosted/accept_timeout.py b/tests/net_hosted/accept_timeout.py index ff989110a..5f528d557 100644 --- a/tests/net_hosted/accept_timeout.py +++ b/tests/net_hosted/accept_timeout.py @@ -1,9 +1,9 @@ # test that socket.accept() on a socket with timeout raises ETIMEDOUT try: - import usocket as socket + import uerrno as errno, usocket as socket except: - import socket + import errno, socket try: socket.socket.settimeout @@ -18,5 +18,5 @@ try: s.accept() except OSError as er: - print(er.args[0] in (110, "timed out")) # 110 is ETIMEDOUT; CPython uses a string + print(er.args[0] in (errno.ETIMEDOUT, "timed out")) # CPython uses a string instead of errno s.close() diff --git a/tests/net_hosted/connect_nonblock.py b/tests/net_hosted/connect_nonblock.py index 3a3eaa2ba..c024b65a0 100644 --- a/tests/net_hosted/connect_nonblock.py +++ b/tests/net_hosted/connect_nonblock.py @@ -2,8 +2,9 @@ try: import usocket as socket + import uerrno as errno except: - import socket + import socket, errno def test(peer_addr): @@ -12,7 +13,7 @@ def test(peer_addr): try: s.connect(peer_addr) except OSError as er: - print(er.args[0] == 115) # 115 is EINPROGRESS + print(er.args[0] == errno.EINPROGRESS) s.close() diff --git a/tests/net_hosted/connect_nonblock_xfer.py b/tests/net_hosted/connect_nonblock_xfer.py new file mode 100644 index 000000000..feb648ea0 --- /dev/null +++ b/tests/net_hosted/connect_nonblock_xfer.py @@ -0,0 +1,147 @@ +# test that socket.connect() on a non-blocking socket raises EINPROGRESS +# and that an immediate write/send/read/recv does the right thing + +try: + import sys, time + import uerrno as errno, usocket as socket, ussl as ssl +except: + import socket, errno, ssl +isMP = sys.implementation.name == "micropython" + + +def dp(e): + # uncomment next line for development and testing, to print the actual exceptions + # print(repr(e)) + pass + + +# do_connect establishes the socket and wraps it if tls is True. +# If handshake is true, the initial connect (and TLS handshake) is +# allowed to be performed before returning. +def do_connect(peer_addr, tls, handshake): + s = socket.socket() + s.setblocking(False) + try: + # print("Connecting to", peer_addr) + s.connect(peer_addr) + except OSError as er: + print("connect:", er.args[0] == errno.EINPROGRESS) + if er.args[0] != errno.EINPROGRESS: + print(" got", er.args[0]) + # wrap with ssl/tls if desired + if tls: + try: + if sys.implementation.name == "micropython": + s = ssl.wrap_socket(s, do_handshake=handshake) + else: + s = ssl.wrap_socket(s, do_handshake_on_connect=handshake) + print("wrap: True") + except Exception as e: + dp(e) + print("wrap:", e) + elif handshake: + # just sleep a little bit, this allows any connect() errors to happen + time.sleep(0.2) + return s + + +# test runs the test against a specific peer address. +def test(peer_addr, tls=False, handshake=False): + # MicroPython plain sockets have read/write, but CPython's don't + # MicroPython TLS sockets and CPython's have read/write + # hasRW captures this wonderful state of affairs + hasRW = isMP or tls + + # MicroPython plain sockets and CPython's have send/recv + # MicroPython TLS sockets don't have send/recv, but CPython's do + # hasSR captures this wonderful state of affairs + hasSR = not (isMP and tls) + + # connect + send + if hasSR: + s = do_connect(peer_addr, tls, handshake) + # send -> 4 or EAGAIN + try: + ret = s.send(b"1234") + print("send:", handshake and ret == 4) + except OSError as er: + # + dp(er) + print("send:", er.args[0] in (errno.EAGAIN, errno.EINPROGRESS)) + s.close() + else: # fake it... + print("connect:", True) + if tls: + print("wrap:", True) + print("send:", True) + + # connect + write + if hasRW: + s = do_connect(peer_addr, tls, handshake) + # write -> None + try: + ret = s.write(b"1234") + print("write:", ret in (4, None)) # SSL may accept 4 into buffer + except OSError as er: + dp(er) + print("write:", False) # should not raise + except ValueError as er: # CPython + dp(er) + print("write:", er.args[0] == "Write on closed or unwrapped SSL socket.") + s.close() + else: # fake it... + print("connect:", True) + if tls: + print("wrap:", True) + print("write:", True) + + if hasSR: + # connect + recv + s = do_connect(peer_addr, tls, handshake) + # recv -> EAGAIN + try: + print("recv:", s.recv(10)) + except OSError as er: + dp(er) + print("recv:", er.args[0] == errno.EAGAIN) + s.close() + else: # fake it... + print("connect:", True) + if tls: + print("wrap:", True) + print("recv:", True) + + # connect + read + if hasRW: + s = do_connect(peer_addr, tls, handshake) + # read -> None + try: + ret = s.read(10) + print("read:", ret is None) + except OSError as er: + dp(er) + print("read:", False) # should not raise + except ValueError as er: # CPython + dp(er) + print("read:", er.args[0] == "Read on closed or unwrapped SSL socket.") + s.close() + else: # fake it... + print("connect:", True) + if tls: + print("wrap:", True) + print("read:", True) + + +if __name__ == "__main__": + # these tests use a non-existent test IP address, this way the connect takes forever and + # we can see EAGAIN/None (https://tools.ietf.org/html/rfc5737) + print("--- Plain sockets to nowhere ---") + test(socket.getaddrinfo("192.0.2.1", 80)[0][-1], False, False) + print("--- SSL sockets to nowhere ---") + # this test fails with AXTLS because do_handshake=False blocks on first read/write and + # there it times out until the connect is aborted + test(socket.getaddrinfo("192.0.2.1", 443)[0][-1], True, False) + print("--- Plain sockets ---") + test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, True) + print("--- SSL sockets ---") + test(socket.getaddrinfo("micropython.org", 443)[0][-1], True, True) diff --git a/tests/net_inet/ssl_errors.py b/tests/net_inet/ssl_errors.py new file mode 100644 index 000000000..fd281b1c4 --- /dev/null +++ b/tests/net_inet/ssl_errors.py @@ -0,0 +1,51 @@ +# test that socket.connect() on a non-blocking socket raises EINPROGRESS +# and that an immediate write/send/read/recv does the right thing + +import sys + +try: + import uerrno as errno, usocket as socket, ussl as ssl +except: + import errno, socket, ssl + + +def test(addr, hostname, block=True): + print("---", hostname or addr) + s = socket.socket() + s.setblocking(block) + try: + s.connect(addr) + print("connected") + except OSError as e: + if e.args[0] != errno.EINPROGRESS: + raise + print("EINPROGRESS") + + try: + if sys.implementation.name == "micropython": + s = ssl.wrap_socket(s, do_handshake=block) + else: + s = ssl.wrap_socket(s, do_handshake_on_connect=block) + print("wrap: True") + except OSError: + print("wrap: error") + + if not block: + try: + while s.write(b"0") is None: + pass + except (ValueError, OSError): # CPython raises ValueError, MicroPython raises OSError + print("write: error") + s.close() + + +if __name__ == "__main__": + # connect to plain HTTP port, oops! + addr = socket.getaddrinfo("micropython.org", 80)[0][-1] + test(addr, None) + # connect to plain HTTP port, oops! + addr = socket.getaddrinfo("micropython.org", 80)[0][-1] + test(addr, None, False) + # connect to server with self-signed cert, oops! + addr = socket.getaddrinfo("test.mosquitto.org", 8883)[0][-1] + test(addr, "test.mosquitto.org") diff --git a/tests/net_inet/test_tls_nonblock.py b/tests/net_inet/test_tls_nonblock.py new file mode 100644 index 000000000..c27ead3d5 --- /dev/null +++ b/tests/net_inet/test_tls_nonblock.py @@ -0,0 +1,116 @@ +try: + import usocket as socket, ussl as ssl, uerrno as errno, sys +except: + import socket, ssl, errno, sys, time, select + + +def test_one(site, opts): + ai = socket.getaddrinfo(site, 443) + addr = ai[0][-1] + print(addr) + + # Connect the raw socket + s = socket.socket() + s.setblocking(False) + try: + s.connect(addr) + raise OSError(-1, "connect blocks") + except OSError as e: + if e.args[0] != errno.EINPROGRESS: + raise + + if sys.implementation.name != "micropython": + # in CPython we have to wait, otherwise wrap_socket is not happy + select.select([], [s], []) + + try: + # Wrap with SSL + try: + if sys.implementation.name == "micropython": + s = ssl.wrap_socket(s, do_handshake=False) + else: + s = ssl.wrap_socket(s, do_handshake_on_connect=False) + except OSError as e: + if e.args[0] != errno.EINPROGRESS: + raise + print("wrapped") + + # CPython needs to be told to do the handshake + if sys.implementation.name != "micropython": + while True: + try: + s.do_handshake() + break + except ssl.SSLError as err: + if err.args[0] == ssl.SSL_ERROR_WANT_READ: + select.select([s], [], []) + elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: + select.select([], [s], []) + else: + raise + time.sleep(0.1) + # print("shook hands") + + # Write HTTP request + out = b"GET / HTTP/1.0\r\nHost: %s\r\n\r\n" % bytes(site, "latin") + while len(out) > 0: + n = s.write(out) + if n is None: + continue + if n > 0: + out = out[n:] + elif n == 0: + raise OSError(-1, "unexpected EOF in write") + print("wrote") + + # Read response + resp = b"" + while True: + try: + b = s.read(128) + except OSError as err: + if err.args[0] == 2: # 2=ssl.SSL_ERROR_WANT_READ: + continue + raise + if b is None: + continue + if len(b) > 0: + if len(resp) < 1024: + resp += b + elif len(b) == 0: + break + print("read") + + if resp[:7] != b"HTTP/1.": + raise ValueError("response doesn't start with HTTP/1.") + # print(resp) + + finally: + s.close() + + +SITES = [ + "google.com", + {"host": "www.google.com"}, + "micropython.org", + "pypi.org", + "api.telegram.org", + {"host": "api.pushbullet.com", "sni": True}, +] + + +def main(): + for site in SITES: + opts = {} + if isinstance(site, dict): + opts = site + site = opts["host"] + try: + test_one(site, opts) + print(site, "ok") + except Exception as e: + print(site, "error") + print("DONE") + + +main() diff --git a/tests/net_inet/test_tls_sites.py b/tests/net_inet/test_tls_sites.py index d2cb928c8..3f945efb8 100644 --- a/tests/net_inet/test_tls_sites.py +++ b/tests/net_inet/test_tls_sites.py @@ -27,6 +27,8 @@ def test_one(site, opts): s.write(b"GET / HTTP/1.0\r\nHost: %s\r\n\r\n" % bytes(site, "latin")) resp = s.read(4096) + if resp[:7] != b"HTTP/1.": + raise ValueError("response doesn't start with HTTP/1.") # print(resp) finally: @@ -36,10 +38,10 @@ def test_one(site, opts): SITES = [ "google.com", "www.google.com", + "micropython.org", + "pypi.org", "api.telegram.org", {"host": "api.pushbullet.com", "sni": True}, - # "w9rybpfril.execute-api.ap-southeast-2.amazonaws.com", - {"host": "w9rybpfril.execute-api.ap-southeast-2.amazonaws.com", "sni": True}, ] diff --git a/tests/net_inet/test_tls_sites.py.exp b/tests/net_inet/test_tls_sites.py.exp index 2f3c113d2..bc4a8dbd1 100644 --- a/tests/net_inet/test_tls_sites.py.exp +++ b/tests/net_inet/test_tls_sites.py.exp @@ -1,5 +1,6 @@ google.com ok www.google.com ok +micropython.org ok +pypi.org ok api.telegram.org ok api.pushbullet.com ok -w9rybpfril.execute-api.ap-southeast-2.amazonaws.com ok diff --git a/tests/run-tests b/tests/run-tests index cb5b5cd0a..b733d8a9f 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -20,7 +20,7 @@ def base_path(*p): # is of lower version, you can point MICROPY_CPYTHON3 environment var # to the correct executable. if os.name == 'nt': - CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3.exe') + CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python') MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', base_path('../ports/windows/micropython.exe')) else: CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3') diff --git a/tools/ci.sh b/tools/ci.sh index 1a413ec99..a815e9483 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -29,10 +29,21 @@ function ci_code_formatting_run { tools/codeformat.py -v } +######################################################################################## +# commit formatting + +function ci_commit_formatting_run { + git remote add upstream https://github.com/micropython/micropython.git + git fetch --depth=100 upstream master + # For a PR, upstream/master..HEAD ends with a merge commit into master, exlude that one. + tools/verifygitlog.py -v upstream/master..HEAD --no-merges +} + ######################################################################################## # code size function ci_code_size_setup { + sudo apt-get update sudo apt-get install gcc-multilib gcc --version ci_gcc_arm_setup @@ -42,7 +53,7 @@ function ci_code_size_build { # starts off at either the ref/pull/N/merge FETCH_HEAD, or the current branch HEAD git checkout -b pull_request # save the current location git remote add upstream https://github.com/micropython/micropython.git - git fetch --depth=100 upstream + git fetch --depth=100 upstream master # build reference, save to size0 # ignore any errors with this build, in case master is failing git checkout `git merge-base --fork-point upstream/master pull_request` @@ -71,38 +82,22 @@ function ci_cc3200_build { ######################################################################################## # ports/esp32 -function ci_esp32_idf3_setup { - sudo pip3 install pyserial 'pyparsing<2.4' - curl -L https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz | tar zxf - - git clone https://github.com/espressif/esp-idf.git -} - -function ci_esp32_idf3_path { - echo $(pwd)/xtensa-esp32-elf/bin -} - -function ci_esp32_idf3_build { - make ${MAKEOPTS} -C mpy-cross - git -C esp-idf checkout $(grep "ESPIDF_SUPHASH_V3 :=" ports/esp32/Makefile | cut -d " " -f 3) - git -C esp-idf submodule update --init components/json/cJSON components/esp32/lib components/esptool_py/esptool components/expat/expat components/lwip/lwip components/mbedtls/mbedtls components/micro-ecc/micro-ecc components/nghttp/nghttp2 components/nimble components/bt - make ${MAKEOPTS} -C ports/esp32 submodules - make ${MAKEOPTS} -C ports/esp32 -} - -function ci_esp32_idf4_setup { - sudo pip3 install pyserial 'pyparsing<2.4' - curl -L https://dl.espressif.com/dl/xtensa-esp32-elf-gcc8_2_0-esp-2019r2-linux-amd64.tar.gz | tar zxf - +function ci_esp32_setup { git clone https://github.com/espressif/esp-idf.git -} - -function ci_esp32_idf4_path { - echo $(pwd)/xtensa-esp32-elf/bin -} - -function ci_esp32_idf4_build { + git -C esp-idf checkout v4.0.2 + git -C esp-idf submodule update --init \ + components/bt/controller/lib \ + components/bt/host/nimble/nimble \ + components/esp_wifi/lib_esp32 \ + components/esptool_py/esptool \ + components/lwip/lwip \ + components/mbedtls/mbedtls + ./esp-idf/install.sh +} + +function ci_esp32_build { + source esp-idf/export.sh make ${MAKEOPTS} -C mpy-cross - git -C esp-idf checkout $(grep "ESPIDF_SUPHASH_V4 :=" ports/esp32/Makefile | cut -d " " -f 3) - git -C esp-idf submodule update --init components/bt/controller/lib components/bt/host/nimble/nimble components/esp_wifi/lib_esp32 components/esptool_py/esptool components/lwip/lwip components/mbedtls/mbedtls make ${MAKEOPTS} -C ports/esp32 submodules make ${MAKEOPTS} -C ports/esp32 } @@ -175,6 +170,19 @@ function ci_qemu_arm_build { make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test test } +######################################################################################## +# ports/rp2 + +function ci_rp2_setup { + ci_gcc_arm_setup +} + +function ci_rp2_build { + make ${MAKEOPTS} -C mpy-cross + git submodule update --init lib/pico-sdk lib/tinyusb + make ${MAKEOPTS} -C ports/rp2 +} + ######################################################################################## # ports/samd @@ -192,6 +200,7 @@ function ci_samd_build { function ci_stm32_setup { ci_gcc_arm_setup + pip3 install pyhy } function ci_stm32_pyb_build { @@ -214,6 +223,11 @@ function ci_stm32_nucleo_build { make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L476RG DEBUG=1 make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_WB55 make ${MAKEOPTS} -C ports/stm32/mboot BOARD=NUCLEO_WB55 + # Test mboot_pack_dfu.py created a valid file, and that its unpack-dfu command works. + BOARD_WB55=ports/stm32/boards/NUCLEO_WB55 + BUILD_WB55=ports/stm32/build-NUCLEO_WB55 + python3 ports/stm32/mboot/mboot_pack_dfu.py -k $BOARD_WB55/mboot_keys.h unpack-dfu $BUILD_WB55/firmware.pack.dfu $BUILD_WB55/firmware.unpack.dfu + diff $BUILD_WB55/firmware.unpack.dfu $BUILD_WB55/firmware.dfu } ######################################################################################## @@ -446,27 +460,28 @@ function ci_windows_build { # ports/zephyr function ci_zephyr_setup { - docker pull zephyrprojectrtos/ci:v0.11.8 + docker pull zephyrprojectrtos/ci:v0.11.13 docker run --name zephyr-ci -d -it \ -v "$(pwd)":/micropython \ - -e ZEPHYR_SDK_INSTALL_DIR=/opt/sdk/zephyr-sdk-0.11.3 \ + -e ZEPHYR_SDK_INSTALL_DIR=/opt/sdk/zephyr-sdk-0.12.2 \ -e ZEPHYR_TOOLCHAIN_VARIANT=zephyr \ + -e ZEPHYR_BASE=/zephyrproject/zephyr \ -w /micropython/ports/zephyr \ - zephyrprojectrtos/ci:v0.11.8 + zephyrprojectrtos/ci:v0.11.13 docker ps -a } function ci_zephyr_install { - docker exec zephyr-ci west init --mr v2.4.0 /zephyrproject + docker exec zephyr-ci west init --mr v2.5.0 /zephyrproject docker exec -w /zephyrproject zephyr-ci west update docker exec -w /zephyrproject zephyr-ci west zephyr-export } function ci_zephyr_build { - docker exec zephyr-ci bash -c "make clean; ./make-minimal ${MAKEOPTS}" - docker exec zephyr-ci bash -c "make clean; ./make-minimal ${MAKEOPTS} BOARD=frdm_k64f" - docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS}" - docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS} BOARD=frdm_k64f" - docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS} BOARD=mimxrt1050_evk" - docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS} BOARD=reel_board" + docker exec zephyr-ci west build -p auto -b qemu_x86 -- -DCONF_FILE=prj_minimal.conf + docker exec zephyr-ci west build -p auto -b frdm_k64f -- -DCONF_FILE=prj_minimal.conf + docker exec zephyr-ci west build -p auto -b qemu_x86 + docker exec zephyr-ci west build -p auto -b frdm_k64f + docker exec zephyr-ci west build -p auto -b mimxrt1050_evk + docker exec zephyr-ci west build -p auto -b reel_board } diff --git a/tools/makemanifest.py b/tools/makemanifest.py index 9ef036826..117d1536e 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -34,13 +34,27 @@ # Public functions to be used in the manifest -def include(manifest): +def include(manifest, **kwargs): """Include another manifest. The manifest argument can be a string (filename) or an iterable of strings. Relative paths are resolved with respect to the current manifest file. + + Optional kwargs can be provided which will be available to the + included script via the `options` variable. + + e.g. include("path.py", extra_features=True) + + in path.py: + options.defaults(standard_features=True) + + # freeze minimal modules. + if options.standard_features: + # freeze standard modules. + if options.extra_features: + # freeze extra modules. """ if not isinstance(manifest, str): @@ -53,7 +67,7 @@ def include(manifest): # Applies to includes and input files. prev_cwd = os.getcwd() os.chdir(os.path.dirname(manifest)) - exec(f.read()) + exec(f.read(), globals(), {"options": IncludeOptions(**kwargs)}) os.chdir(prev_cwd) @@ -125,6 +139,18 @@ def freeze_mpy(path, script=None, opt=0): manifest_list = [] +class IncludeOptions: + def __init__(self, **kwargs): + self._kwargs = kwargs + self._defaults = {} + + def defaults(self, **kwargs): + self._defaults = kwargs + + def __getattr__(self, name): + return self._kwargs.get(name, self._defaults.get(name, None)) + + class FreezeError(Exception): pass @@ -172,6 +198,8 @@ def mkdir(filename): def freeze_internal(kind, path, script, opt): path = convert_path(path) + if not os.path.isdir(path): + raise FreezeError("freeze path must be a directory") if script is None and kind == KIND_AS_STR: if any(f[0] == KIND_AS_STR for f in manifest_list): raise FreezeError("can only freeze one str directory") diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index de7cfe5d6..ea756d3ee 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -916,6 +916,17 @@ def freeze_mpy(base_qstrs, raw_codes): print(" &raw_code_%s," % rc.escaped_name) print("};") + # If a port defines MICROPY_FROZEN_LIST_ITEM then list all modules wrapped in that macro. + print("#ifdef MICROPY_FROZEN_LIST_ITEM") + for rc in raw_codes: + module_name = rc.source_file.str + if module_name.endswith("/__init__.py"): + short_name = module_name[: -len("/__init__.py")] + else: + short_name = module_name[: -len(".py")] + print('MICROPY_FROZEN_LIST_ITEM("%s", "%s")' % (short_name, module_name)) + print("#endif") + def merge_mpy(raw_codes, output_file): assert len(raw_codes) <= 31 # so var-uints all fit in 1 byte diff --git a/tools/pyboard.py b/tools/pyboard.py index 3ecdf03f1..069f7490d 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -651,7 +651,11 @@ def main(): help="Do not follow the output after running the scripts.", ) cmd_parser.add_argument( - "-f", "--filesystem", action="store_true", help="perform a filesystem action" + "-f", + "--filesystem", + action="store_true", + help="perform a filesystem action: " + "cp local :device | cp :device local | cat path | ls [path] | rm path | mkdir path | rmdir path", ) cmd_parser.add_argument("files", nargs="*", help="input files") args = cmd_parser.parse_args() diff --git a/tools/pydfu.py b/tools/pydfu.py index 030f56bf8..42b4fa2da 100755 --- a/tools/pydfu.py +++ b/tools/pydfu.py @@ -521,7 +521,7 @@ def write_elements(elements, mass_erase_used, progress=None): data = elem["data"] elem_size = size elem_addr = addr - if progress: + if progress and elem_size: progress(elem_addr, 0, elem_size) while size > 0: write_size = size diff --git a/tools/verifygitlog.py b/tools/verifygitlog.py new file mode 100755 index 000000000..cc4b80f4a --- /dev/null +++ b/tools/verifygitlog.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 + +import re +import subprocess +import sys + +verbosity = 0 # Show what's going on, 0 1 or 2. +suggestions = 1 # Set to 0 to not include lengthy suggestions in error messages. + + +def verbose(*args): + if verbosity: + print(*args) + + +def very_verbose(*args): + if verbosity > 1: + print(*args) + + +def git_log(pretty_format, *args): + # Delete pretty argument from user args so it doesn't interfere with what we do. + args = ["git", "log"] + [arg for arg in args if "--pretty" not in args] + args.append("--pretty=format:" + pretty_format) + very_verbose("git_log", *args) + # Generator yielding each output line. + for line in subprocess.Popen(args, stdout=subprocess.PIPE).stdout: + yield line.decode().rstrip("\r\n") + + +def verify(sha): + verbose("verify", sha) + errors = [] + warnings = [] + + def error_text(err): + return "commit " + sha + ": " + err + + def error(err): + errors.append(error_text(err)) + + def warning(err): + warnings.append(error_text(err)) + + # Author and committer email. + for line in git_log("%ae%n%ce", sha, "-n1"): + very_verbose("email", line) + if "noreply" in line: + error("Unwanted email address: " + line) + + # Message body. + raw_body = list(git_log("%B", sha, "-n1")) + if not raw_body: + error("Message is empty") + return errors, warnings + + # Subject line. + subject_line = raw_body[0] + very_verbose("subject_line", subject_line) + subject_line_format = r"^[^!]+: [A-Z]+.+ .+\.$" + if not re.match(subject_line_format, subject_line): + error("Subject line should match " + repr(subject_line_format) + ": " + subject_line) + if len(subject_line) >= 73: + error("Subject line should be 72 or less characters: " + subject_line) + + # Second one divides subject and body. + if len(raw_body) > 1 and raw_body[1]: + error("Second message line should be empty: " + raw_body[1]) + + # Message body lines. + for line in raw_body[2:]: + if len(line) >= 76: + 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]: + warning("Message should be signed-off") + + return errors, warnings + + +def run(args): + verbose("run", *args) + has_errors = False + has_warnings = False + for sha in git_log("%h", *args): + errors, warnings = verify(sha) + has_errors |= any(errors) + has_warnings |= any(warnings) + for err in errors: + print("error:", err) + for err in warnings: + print("warning:", err) + if has_errors or has_warnings: + if suggestions: + print("See https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md") + else: + print("ok") + if has_errors: + sys.exit(1) + + +def show_help(): + print("usage: verifygitlog.py [-v -n -h] ...") + print("-v : increase verbosity, can be speficied multiple times") + print("-n : do not print multi-line suggestions") + print("-h : print this help message and exit") + print("... : arguments passed to git log to retrieve commits to verify") + print(" see https://www.git-scm.com/docs/git-log") + print(" passing no arguments at all will verify all commits") + print("examples:") + print("verifygitlog.py -n10 # Check last 10 commits") + print("verifygitlog.py -v master..HEAD # Check commits since master") + + +if __name__ == "__main__": + args = sys.argv[1:] + verbosity = args.count("-v") + suggestions = args.count("-n") == 0 + if "-h" in args: + show_help() + else: + args = [arg for arg in args if arg not in ["-v", "-n", "-h"]] + run(args)