diff --git a/.github/workflows/code_size.yml b/.github/workflows/code_size.yml index 163d4455cffec..67261933798cb 100644 --- a/.github/workflows/code_size.yml +++ b/.github/workflows/code_size.yml @@ -1,7 +1,6 @@ name: Check code size on: - push: pull_request: paths: - '.github/workflows/*.yml' diff --git a/.github/workflows/commit_formatting.yml b/.github/workflows/commit_formatting.yml index 3fdcabc4ca73f..fcbcaa7092ee2 100644 --- a/.github/workflows/commit_formatting.yml +++ b/.github/workflows/commit_formatting.yml @@ -1,6 +1,6 @@ name: Check commit message formatting -on: [push, pull_request] +on: [pull_request] concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - fetch-depth: '100' + fetch-depth: 100 - uses: actions/setup-python@v5 - name: Check commit message formatting run: source tools/ci.sh && ci_commit_formatting_run diff --git a/docs/conf.py b/docs/conf.py index 32cc2be10e343..e5c6ba98090af 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,6 +36,9 @@ "is_release": micropy_version != "latest", } +# Authors used in various parts of the documentation. +micropy_authors = "MicroPython authors and contributors" + # -- General configuration ------------------------------------------------ @@ -68,7 +71,7 @@ # General information about the project. project = "MicroPython" -copyright = "- The MicroPython Documentation is Copyright © 2014-2024, Damien P. George, Paul Sokolovsky, and contributors" +copyright = "- The MicroPython Documentation is Copyright © 2014-2024, " + micropy_authors # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -244,7 +247,7 @@ master_doc, "MicroPython.tex", "MicroPython Documentation", - "Damien P. George, Paul Sokolovsky, and contributors", + micropy_authors, "manual", ), ] @@ -281,7 +284,7 @@ "index", "micropython", "MicroPython Documentation", - ["Damien P. George, Paul Sokolovsky, and contributors"], + [micropy_authors], 1, ), ] @@ -300,7 +303,7 @@ master_doc, "MicroPython", "MicroPython Documentation", - "Damien P. George, Paul Sokolovsky, and contributors", + micropy_authors, "MicroPython", "One line description of project.", "Miscellaneous", diff --git a/docs/develop/gettingstarted.rst b/docs/develop/gettingstarted.rst index c1fd338c54cc0..fed632ea1ac15 100644 --- a/docs/develop/gettingstarted.rst +++ b/docs/develop/gettingstarted.rst @@ -278,7 +278,7 @@ To run a selection of tests on a board/device connected over USB use: .. code-block:: bash $ cd tests - $ ./run-tests.py --target minimal --device /dev/ttyACM0 + $ ./run-tests.py -t /dev/ttyACM0 See also :ref:`writingtests`. diff --git a/docs/develop/writingtests.rst b/docs/develop/writingtests.rst index 9bb5178f55f22..a7d033f17e9b9 100644 --- a/docs/develop/writingtests.rst +++ b/docs/develop/writingtests.rst @@ -60,7 +60,7 @@ Then to run on a board: .. code-block:: bash - $ ./run-tests.py --target minimal --device /dev/ttyACM0 + $ ./run-tests.py -t /dev/ttyACM0 And to run only a certain set of tests (eg a directory): diff --git a/docs/esp32/quickref.rst b/docs/esp32/quickref.rst index 3ab4e8f5ecd96..b9ca0f8225f8f 100644 --- a/docs/esp32/quickref.rst +++ b/docs/esp32/quickref.rst @@ -79,11 +79,11 @@ Networking WLAN ^^^^ -The :mod:`network` module:: +The :class:`network.WLAN` class in the :mod:`network` module:: import network - wlan = network.WLAN(network.STA_IF) # create station interface + wlan = network.WLAN(network.WLAN.IF_STA) # create station interface wlan.active(True) # activate the interface wlan.scan() # scan for access points wlan.isconnected() # check if the station is connected to an AP @@ -91,7 +91,7 @@ The :mod:`network` module:: wlan.config('mac') # get the interface's MAC address wlan.ipconfig('addr4') # get the interface's IPv4 addresses - ap = network.WLAN(network.AP_IF) # create access-point interface + ap = network.WLAN(network.WLAN.IF_AP) # create access-point interface ap.config(ssid='ESP-AP') # set the SSID of the access point ap.config(max_clients=10) # set how many clients can connect to the network ap.active(True) # activate the interface @@ -100,7 +100,7 @@ A useful function for connecting to your local WiFi network is:: def do_connect(): import network - wlan = network.WLAN(network.STA_IF) + wlan = network.WLAN(network.WLAN.IF_STA) wlan.active(True) if not wlan.isconnected(): print('connecting to network...') @@ -124,7 +124,8 @@ to reconnect forever). LAN ^^^ -To use the wired interfaces one has to specify the pins and mode :: +To use the wired interfaces via :class:`network.LAN` one has to specify the pins +and mode :: import network diff --git a/docs/esp32/tutorial/index.rst b/docs/esp32/tutorial/index.rst index c6242d731f180..2435f2ecd2f59 100644 --- a/docs/esp32/tutorial/index.rst +++ b/docs/esp32/tutorial/index.rst @@ -21,3 +21,4 @@ to ``__. intro.rst pwm.rst peripheral_access.rst + reset.rst diff --git a/docs/esp32/tutorial/intro.rst b/docs/esp32/tutorial/intro.rst index be09599871ce6..cf4d0bcbd2f55 100644 --- a/docs/esp32/tutorial/intro.rst +++ b/docs/esp32/tutorial/intro.rst @@ -50,6 +50,8 @@ features, there are daily builds. If your board has SPIRAM support you can use either the standard firmware or the firmware with SPIRAM support, and in the latter case you will have access to more RAM for Python objects. +.. _esp32_flashing: + Deploying the firmware ---------------------- diff --git a/docs/esp32/tutorial/reset.rst b/docs/esp32/tutorial/reset.rst new file mode 100644 index 0000000000000..b3fc6a85bd436 --- /dev/null +++ b/docs/esp32/tutorial/reset.rst @@ -0,0 +1,25 @@ +Factory reset +============= + +If something unexpected happens and your ESP32-based board no longer boots +MicroPython, then you may have to factory reset it. For more details, see +:ref:`soft_bricking`. + +Factory resetting the MicroPython esp32 port involves fully erasing the flash +and resetting the flash memory, so you will need to re-flash the MicroPython +firmware afterwards and copy any Python files to the filesystem again. + +1. You will need the Espressif `esptool`_ installed on your system. This is the + same tool that you may have used to initially install MicroPython on your + board (see :ref:`installation instructions `). +2. Find the serial port name of your board, and then use esptool to erase the + entire flash contents:: + + esptool.py -p PORTNAME erase_flash + +3. Use esptool to flash the MicroPython file to your board again. If needed, + this file and flashing instructions can be found on the `MicroPython + downloads page`_. + +.. _esptool: https://github.com/espressif/esptool +.. _MicroPython downloads page: https://micropython.org/download/?port=esp32 diff --git a/docs/esp8266/general.rst b/docs/esp8266/general.rst index be0437e752eed..d7211aa5ab745 100644 --- a/docs/esp8266/general.rst +++ b/docs/esp8266/general.rst @@ -74,40 +74,7 @@ as possible after use. Boot process ------------ -On boot, MicroPython EPS8266 port executes ``_boot.py`` script from internal -frozen modules. It mounts filesystem in FlashROM, or if it's not available, -performs first-time setup of the module and creates the filesystem. This -part of the boot process is considered fixed, and not available for customization -for end users (even if you build from source, please refrain from changes to -it; customization of early boot process is available only to advanced users -and developers, who can diagnose themselves any issues arising from -modifying the standard process). - -Once the filesystem is mounted, ``boot.py`` is executed from it. The standard -version of this file is created during first-time module set up and has -commands to start a WebREPL daemon (disabled by default, configurable -with ``webrepl_setup`` module), etc. This -file is customizable by end users (for example, you may want to set some -parameters or add other services which should be run on -a module start-up). But keep in mind that incorrect modifications to boot.py -may still lead to boot loops or lock ups, requiring to reflash a module -from scratch. (In particular, it's recommended that you use either -``webrepl_setup`` module or manual editing to configure WebREPL, but not -both). - -As a final step of boot procedure, ``main.py`` is executed from filesystem, -if exists. This file is a hook to start up a user application each time -on boot (instead of going to REPL). For small test applications, you may -name them directly as ``main.py``, and upload to module, but instead it's -recommended to keep your application(s) in separate files, and have just -the following in ``main.py``:: - - import my_app - my_app.main() - -This will allow to keep the structure of your application clear, as well as -allow to install multiple applications on a board, and switch among them. - +See :doc:`/reference/reset_boot`. Known Issues ------------ diff --git a/docs/esp8266/quickref.rst b/docs/esp8266/quickref.rst index b130ce65db2b7..635f1f834bb60 100644 --- a/docs/esp8266/quickref.rst +++ b/docs/esp8266/quickref.rst @@ -49,11 +49,11 @@ The :mod:`esp` module:: Networking ---------- -The :mod:`network` module:: +The :class:`network.WLAN` class in the :mod:`network` module:: import network - wlan = network.WLAN(network.STA_IF) # create station interface + wlan = network.WLAN(network.WLAN.IF_STA) # create station interface wlan.active(True) # activate the interface wlan.scan() # scan for access points wlan.isconnected() # check if the station is connected to an AP @@ -61,7 +61,7 @@ The :mod:`network` module:: wlan.config('mac') # get the interface's MAC address wlan.ipconfig('addr4') # get the interface's IPv4 addresses - ap = network.WLAN(network.AP_IF) # create access-point interface + ap = network.WLAN(network.WLAN.IF_AP) # create access-point interface ap.active(True) # activate the interface ap.config(ssid='ESP-AP') # set the SSID of the access point @@ -69,7 +69,7 @@ A useful function for connecting to your local WiFi network is:: def do_connect(): import network - wlan = network.WLAN(network.STA_IF) + wlan = network.WLAN(network.WLAN.IF_STA) wlan.active(True) if not wlan.isconnected(): print('connecting to network...') @@ -163,10 +163,10 @@ sys.stdin.read() if it's needed to read characters from the UART(0) while it's also used for the REPL (or detach, read, then reattach). When detached the UART(0) can be used for other purposes. -If there are no objects in any of the dupterm slots when the REPL is -started (on hard or soft reset) then UART(0) is automatically attached. -Without this, the only way to recover a board without a REPL would be to -completely erase and reflash (which would install the default boot.py which +If there are no objects in any of the dupterm slots when the REPL is started (on +:doc:`hard or soft reset `) then UART(0) is automatically +attached. Without this, the only way to recover a board without a REPL would be +to completely erase and reflash (which would install the default boot.py which attaches the REPL). To detach the REPL from UART0, use:: diff --git a/docs/esp8266/tutorial/network_basics.rst b/docs/esp8266/tutorial/network_basics.rst index 9d74a6283ac47..e383c00c6ceed 100644 --- a/docs/esp8266/tutorial/network_basics.rst +++ b/docs/esp8266/tutorial/network_basics.rst @@ -1,14 +1,15 @@ Network basics ============== -The network module is used to configure the WiFi connection. There are two WiFi -interfaces, one for the station (when the ESP8266 connects to a router) and one -for the access point (for other devices to connect to the ESP8266). Create +The :class:`network.WLAN` class in the :mod:`network` module is used to +configure the WiFi connection. There are two WiFi interfaces, one for +the station (when the ESP8266 connects to a router) and one for the +access point (for other devices to connect to the ESP8266). Create instances of these objects using:: >>> import network - >>> sta_if = network.WLAN(network.STA_IF) - >>> ap_if = network.WLAN(network.AP_IF) + >>> sta_if = network.WLAN(network.WLAN.IF_STA) + >>> ap_if = network.WLAN(network.WLAN.IF_AP) You can check if the interfaces are active by:: @@ -57,7 +58,7 @@ connect to your WiFi network:: def do_connect(): import network - sta_if = network.WLAN(network.STA_IF) + sta_if = network.WLAN(network.WLAN.IF_STA) if not sta_if.isconnected(): print('connecting to network...') sta_if.active(True) diff --git a/docs/library/builtins.rst b/docs/library/builtins.rst index e489375b1f917..5956aea7ab20f 100644 --- a/docs/library/builtins.rst +++ b/docs/library/builtins.rst @@ -170,6 +170,10 @@ Exceptions .. exception:: KeyboardInterrupt + |see_cpython| `python:KeyboardInterrupt`. + + See also in the context of :ref:`soft_bricking`. + .. exception:: KeyError .. exception:: MemoryError @@ -190,6 +194,12 @@ Exceptions |see_cpython| `python:SystemExit`. + On non-embedded ports (i.e. Windows and Unix), an unhandled ``SystemExit`` + exits the MicroPython process in a similar way to CPython. + + On embedded ports, an unhandled ``SystemExit`` currently causes a + :ref:`soft_reset` of MicroPython. + .. exception:: TypeError |see_cpython| `python:TypeError`. diff --git a/docs/library/espnow.rst b/docs/library/espnow.rst index f0b592dffc8ab..1bcc9d7623d14 100644 --- a/docs/library/espnow.rst +++ b/docs/library/espnow.rst @@ -56,7 +56,7 @@ A simple example would be: import espnow # A WLAN interface must be active to send()/recv() - sta = network.WLAN(network.STA_IF) # Or network.AP_IF + sta = network.WLAN(network.WLAN.IF_STA) # Or network.WLAN.IF_AP sta.active(True) sta.disconnect() # For ESP8266 @@ -76,7 +76,7 @@ A simple example would be: import espnow # A WLAN interface must be active to send()/recv() - sta = network.WLAN(network.STA_IF) + sta = network.WLAN(network.WLAN.IF_STA) sta.active(True) sta.disconnect() # Because ESP8266 auto-connects to last Access Point @@ -182,14 +182,14 @@ Configuration Sending and Receiving Data -------------------------- -A wifi interface (``network.STA_IF`` or ``network.AP_IF``) must be +A wifi interface (``network.WLAN.IF_STA`` or ``network.WLAN.IF_AP``) must be `active()` before messages can be sent or received, but it is not necessary to connect or configure the WLAN interface. For example:: import network - sta = network.WLAN(network.STA_IF) + sta = network.WLAN(network.WLAN.IF_STA) sta.active(True) sta.disconnect() # For ESP8266 @@ -445,8 +445,8 @@ must first register the sender and use the same encryption keys as the sender - *ifidx*: (ESP32 only) Index of the wifi interface which will be used to send data to this peer. Must be an integer set to - ``network.STA_IF`` (=0) or ``network.AP_IF`` (=1). - (default=0/``network.STA_IF``). See `ESPNow and Wifi Operation`_ + ``network.WLAN.IF_STA`` (=0) or ``network.WLAN.IF_AP`` (=1). + (default=0/``network.WLAN.IF_STA``). See `ESPNow and Wifi Operation`_ below for more information. - *encrypt*: (ESP32 only) If set to ``True`` data exchanged with @@ -588,7 +588,7 @@ api-reference/network/esp_now.html#api-reference>`_. For example:: elif err.args[1] == 'ESP_ERR_ESPNOW_NOT_FOUND': e.add_peer(peer) elif err.args[1] == 'ESP_ERR_ESPNOW_IF': - network.WLAN(network.STA_IF).active(True) + network.WLAN(network.WLAN.IF_STA).active(True) else: raise err @@ -645,7 +645,7 @@ A small async server example:: import asyncio # A WLAN interface must be active to send()/recv() - network.WLAN(network.STA_IF).active(True) + network.WLAN(network.WLAN.IF_STA).active(True) e = aioespnow.AIOESPNow() # Returns AIOESPNow enhanced with async support e.active(True) @@ -747,8 +747,8 @@ ESPNow and Wifi Operation ------------------------- ESPNow messages may be sent and received on any `active()` -`WLAN` interface (``network.STA_IF`` or ``network.AP_IF``), even -if that interface is also connected to a wifi network or configured as an access +`WLAN` interface (``network.WLAN.IF_STA`` or ``network.WLAN.IF_AP``), +even if that interface is also connected to a wifi network or configured as an access point. When an ESP32 or ESP8266 device connects to a Wifi Access Point (see `ESP32 Quickref <../esp32/quickref.html#networking>`__) the following things happen which affect ESPNow communications: @@ -832,8 +832,8 @@ Other issues to take care with when using ESPNow with wifi are: import network, time def wifi_reset(): # Reset wifi to AP_IF off, STA_IF on and disconnected - sta = network.WLAN(network.STA_IF); sta.active(False) - ap = network.WLAN(network.AP_IF); ap.active(False) + sta = network.WLAN(network.WLAN.IF_STA); sta.active(False) + ap = network.WLAN(network.WLAN.IF_AP); ap.active(False) sta.active(True) while not sta.active(): time.sleep(0.1) diff --git a/docs/library/machine.RTC.rst b/docs/library/machine.RTC.rst index fcd78f1c39863..ad0b0759efe5e 100644 --- a/docs/library/machine.RTC.rst +++ b/docs/library/machine.RTC.rst @@ -83,7 +83,7 @@ Methods a `bytes` object. Data written to RTC user memory is persistent across restarts, including - `machine.soft_reset()` and `machine.deepsleep()`. + :ref:`soft_reset` and `machine.deepsleep()`. The maximum length of RTC user memory is 2048 bytes by default on esp32, and 492 bytes on esp8266. diff --git a/docs/library/machine.USBDevice.rst b/docs/library/machine.USBDevice.rst index a47fda2a28d35..5c18c49a75bb5 100644 --- a/docs/library/machine.USBDevice.rst +++ b/docs/library/machine.USBDevice.rst @@ -32,10 +32,10 @@ Managing a runtime USB interface can be tricky, especially if you are communicat with MicroPython over a built-in USB-CDC serial port that's part of the same USB device. -- A MicroPython soft reset will always clear all runtime USB interfaces, which - results in the entire USB device disconnecting from the host. If MicroPython - is also providing a built-in USB-CDC serial port then this will re-appear - after the soft reset. +- A MicroPython :ref:`soft reset ` will always clear all runtime USB + interfaces, which results in the entire USB device disconnecting from the + host. If MicroPython is also providing a built-in USB-CDC serial port then + this will re-appear after the soft reset. This means some functions (like ``mpremote run``) that target the USB-CDC serial port will immediately fail if a runtime USB interface is active, @@ -44,9 +44,9 @@ device. no more runtime USB interface. - To configure a runtime USB device on every boot, it's recommended to place the - configuration code in the ``boot.py`` file on the :ref:`device VFS + configuration code in the :ref:`boot.py` file on the :ref:`device VFS `. On each reset this file is executed before the USB subsystem is - initialised (and before ``main.py``), so it allows the board to come up with the runtime + initialised (and before :ref:`main.py`), so it allows the board to come up with the runtime USB device immediately. - For development or debugging, it may be convenient to connect a hardware diff --git a/docs/library/machine.rst b/docs/library/machine.rst index 7d2eb26a7ea34..76d111f11ef3d 100644 --- a/docs/library/machine.rst +++ b/docs/library/machine.rst @@ -62,14 +62,13 @@ Reset related functions .. function:: reset() - Resets the device in a manner similar to pushing the external RESET - button. + :ref:`Hard resets ` the device in a manner similar to pushing the + external RESET button. .. function:: soft_reset() - Performs a soft reset of the interpreter, deleting all Python objects and - resetting the Python heap. It tries to retain the method by which the user - is connected to the MicroPython REPL (eg serial, USB, Wifi). + Performs a :ref:`soft reset ` of the interpreter, deleting all + Python objects and resetting the Python heap. .. function:: reset_cause() diff --git a/docs/library/network.PPP.rst b/docs/library/network.PPP.rst index 85f580ce540ed..17a8d1c1cd89d 100644 --- a/docs/library/network.PPP.rst +++ b/docs/library/network.PPP.rst @@ -70,8 +70,11 @@ Methods .. method:: PPP.config(config_parameters) - Sets or gets parameters of the PPP interface. There are currently no parameter that - can be set or retrieved. + Sets or gets parameters of the PPP interface. The only parameter that can be + retrieved and set is the underlying stream, using:: + + stream = PPP.config("stream") + PPP.config(stream=stream) .. method:: PPP.ipconfig('param') PPP.ipconfig(param=value, ...) diff --git a/docs/library/network.WLAN.rst b/docs/library/network.WLAN.rst index c1eb520961fcb..3c401acb420bf 100644 --- a/docs/library/network.WLAN.rst +++ b/docs/library/network.WLAN.rst @@ -8,7 +8,7 @@ This class provides a driver for WiFi network processors. Example usage:: import network # enable station interface and connect to WiFi access point - nic = network.WLAN(network.STA_IF) + nic = network.WLAN(network.WLAN.IF_STA) nic.active(True) nic.connect('your-ssid', 'your-key') # now use sockets as usual @@ -18,8 +18,8 @@ Constructors .. class:: WLAN(interface_id) Create a WLAN network interface object. Supported interfaces are -``network.STA_IF`` (station aka client, connects to upstream WiFi access -points) and ``network.AP_IF`` (access point, allows other WiFi clients to +``network.WLAN.IF_STA`` (station aka client, connects to upstream WiFi access +points) and ``network.WLAN.IF_AP`` (access point, allows other WiFi clients to connect). Availability of the methods below depends on interface type. For example, only STA interface may `WLAN.connect()` to an access point. @@ -75,7 +75,7 @@ Methods Return the current status of the wireless connection. When called with no argument the return value describes the network link status. - The possible statuses are defined as constants: + The possible statuses are defined as constants in the :mod:`network` module: * ``STAT_IDLE`` -- no connection and no activity, * ``STAT_CONNECTING`` -- connecting in progress, diff --git a/docs/library/pyb.rst b/docs/library/pyb.rst index f169a77f3a6ca..5eb75c22b608d 100644 --- a/docs/library/pyb.rst +++ b/docs/library/pyb.rst @@ -147,10 +147,10 @@ Power related functions (internal oscillator) directly. The higher frequencies use the HSE to drive the PLL (phase locked loop), and then use the output of the PLL. - Note that if you change the frequency while the USB is enabled then - the USB may become unreliable. It is best to change the frequency - in boot.py, before the USB peripheral is started. Also note that sysclk - frequencies below 36MHz do not allow the USB to function correctly. + Note that if you change the frequency while the USB is enabled then the USB + may become unreliable. It is best to change the frequency in :ref:`boot.py`, + before the USB peripheral is started. Also note that sysclk frequencies below + 36MHz do not allow the USB to function correctly. .. function:: wfi() @@ -205,8 +205,9 @@ Miscellaneous functions .. function:: main(filename) - Set the filename of the main script to run after boot.py is finished. If - this function is not called then the default file main.py will be executed. + Set the filename of the main script to run after :ref:`boot.py` is finished. + If this function is not called then the default file :ref:`main.py` will be + executed. It only makes sense to call this function from within boot.py. diff --git a/docs/library/sys.rst b/docs/library/sys.rst index 7b34a0e31c6bf..c72214c13108e 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -12,9 +12,12 @@ Functions .. function:: exit(retval=0, /) Terminate current program with a given exit code. Underlyingly, this - function raise as `SystemExit` exception. If an argument is given, its + function raises a `SystemExit` exception. If an argument is given, its value given as an argument to `SystemExit`. + On embedded ports (i.e. all ports but Windows and Unix), an unhandled + `SystemExit` currently causes a :ref:`soft_reset` of MicroPython. + .. function:: atexit(func) Register *func* to be called upon termination. *func* must be a callable diff --git a/docs/mimxrt/quickref.rst b/docs/mimxrt/quickref.rst index cfd0605054a7b..34e0aa79f139f 100644 --- a/docs/mimxrt/quickref.rst +++ b/docs/mimxrt/quickref.rst @@ -193,7 +193,7 @@ PWM Constructor - *freq* should be an integer which sets the frequency in Hz for the PWM cycle. The valid frequency range is 15 Hz resp. 18Hz resp. 24Hz up to > 1 MHz. - - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65536``. + - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65535``. The duty cycle of a X channel can only be changed, if the A and B channel of the respective submodule is not used. Otherwise the duty_16 value of the X channel is 32768 (50%). @@ -231,7 +231,7 @@ is created by dividing the pwm_clk signal by an integral factor, according to th f = pwm_clk / (2**n * m) -with n being in the range of 0..7, and m in the range of 2..65536. pmw_clk is 125Mhz +with n being in the range of 0..7, and m in the range of 2..65535. pmw_clk is 125Mhz for MIMXRT1010/1015/1020, 150 MHz for MIMXRT1050/1060/1064 and 160MHz for MIMXRT1170. The lowest frequency is pwm_clk/2**23 (15, 18, 20Hz). The highest frequency with U16 resolution is pwm_clk/2**16 (1907, 2288, 2441 Hz), the highest frequency @@ -255,7 +255,7 @@ Use the :ref:`machine.ADC ` class:: from machine import ADC adc = ADC(Pin('A2')) # create ADC object on ADC pin - adc.read_u16() # read value, 0-65536 across voltage range 0.0v - 3.3v + adc.read_u16() # read value, 0-65535 across voltage range 0.0v - 3.3v The resolution of the ADC is 12 bit with 10 to 11 bit accuracy, irrespective of the value returned by read_u16(). If you need a higher resolution or better accuracy, use diff --git a/docs/pyboard/tutorial/reset.rst b/docs/pyboard/tutorial/reset.rst index 59a3cd82ae1a7..ad56995c60dd7 100644 --- a/docs/pyboard/tutorial/reset.rst +++ b/docs/pyboard/tutorial/reset.rst @@ -10,6 +10,8 @@ execution of ``boot.py`` and ``main.py`` and gives default USB settings. If you have problems with the filesystem you can do a factory reset, which restores the filesystem to its original state. +For more information, see :doc:`/reference/reset_boot`. + Safe mode --------- diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 98f6b65737699..1558c0fdfa9b6 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -21,6 +21,7 @@ implementation and the best practices to use them. glossary.rst repl.rst + reset_boot.rst mpremote.rst mpyfiles.rst isr_rules.rst diff --git a/docs/reference/mpremote.rst b/docs/reference/mpremote.rst index 4d86f0080b117..d4c6983595fec 100644 --- a/docs/reference/mpremote.rst +++ b/docs/reference/mpremote.rst @@ -477,7 +477,7 @@ An example ``config.py`` might look like: """,], # Print out nearby WiFi networks. "wl_ipconfig": [ "exec", - "import network; sta_if = network.WLAN(network.STA_IF); print(sta_if.ipconfig('addr4'))", + "import network; sta_if = network.WLAN(network.WLAN.IF_STA); print(sta_if.ipconfig('addr4'))", """,], # Print ip address of station interface. "test": ["mount", ".", "exec", "import test"], # Mount current directory and run test.py. "demo": ["run", "path/to/demo.py"], # Execute demo.py on the device. @@ -547,9 +547,9 @@ device at ``/dev/ttyACM1``, printing each result. mpremote resume exec "print_state_info()" soft-reset -Connect to the device without triggering a soft reset and execute the -``print_state_info()`` function (e.g. to find out information about the current -program state), then trigger a soft reset. +Connect to the device without triggering a :ref:`soft reset ` and +execute the ``print_state_info()`` function (e.g. to find out information about +the current program state), then trigger a soft reset. .. code-block:: bash diff --git a/docs/reference/packages.rst b/docs/reference/packages.rst index e47c226b24187..86b8efaa1953d 100644 --- a/docs/reference/packages.rst +++ b/docs/reference/packages.rst @@ -69,9 +69,9 @@ On the Unix port, ``mip`` can be used at the REPL as above, and also by using `` $ ./micropython -m mip install pkgname-or-url $ ./micropython -m mip install pkgname-or-url@version -The ``--target=path``, ``--no-mpy``, and ``--index`` arguments can be set:: +The ``--target path``, ``--no-mpy``, and ``--index`` arguments can be set:: - $ ./micropython -m mip install --target=third-party pkgname + $ ./micropython -m mip install --target third-party pkgname $ ./micropython -m mip install --no-mpy pkgname $ ./micropython -m mip install --index https://host/pi pkgname diff --git a/docs/reference/repl.rst b/docs/reference/repl.rst index 55f76ee1a03b9..be02ddebde8e2 100644 --- a/docs/reference/repl.rst +++ b/docs/reference/repl.rst @@ -143,10 +143,12 @@ the auto-indent feature, and changes the prompt from ``>>>`` to ``===``. For exa Paste Mode allows blank lines to be pasted. The pasted text is compiled as if it were a file. Pressing Ctrl-D exits paste mode and initiates the compilation. +.. _repl_soft_reset: + Soft reset ---------- -A soft reset will reset the python interpreter, but tries not to reset the +A :ref:`soft_reset` will reset the python interpreter, but tries not to reset the method by which you're connected to the MicroPython board (USB-serial, or Wifi). You can perform a soft reset from the REPL by pressing Ctrl-D, or from your python @@ -182,6 +184,9 @@ variables no longer exist: ['__name__', 'pyb'] >>> +For more information about reset types and the startup process, see +:doc:`/reference/reset_boot`. + The special variable _ (underscore) ----------------------------------- @@ -196,6 +201,8 @@ So you can use the underscore to save the result in a variable. For example: 15 >>> +.. _raw_repl: + Raw mode and raw-paste mode --------------------------- diff --git a/docs/reference/reset_boot.rst b/docs/reference/reset_boot.rst new file mode 100644 index 0000000000000..4772d02dd1e32 --- /dev/null +++ b/docs/reference/reset_boot.rst @@ -0,0 +1,262 @@ +Reset and Boot Sequence +======================= + +A device running MicroPython follows a particular boot sequence to start up and +initialise itself after a reset. + +.. _hard_reset: + +Hard reset +---------- + +Booting from hard reset is what happens when a board is first powered up, a cold +boot. This is a complete reset of the MCU hardware. + +The MicroPython port code initialises all essential hardware (including embedded +clocks and power regulators, internal serial UART, etc), and then starts the +MicroPython environment. Existing :doc:`RTC ` +configuration may be retained after a hard reset, but all other hardware state +is cleared. + +The same hard reset boot sequence can be triggered by a number of events such as: + +- Python code executing :func:`machine.reset()`. +- User presses a physical Reset button on the board (where applicable). +- Waking from deep sleep (on most ports). +- MCU hardware watchdog reset. +- MCU hardware brown out detector. + +The details of hardware-specific reset triggers depend on the port and +associated hardware. The :func:`machine.reset_cause()` function can be used to +further determine the cause of a reset. + +.. _soft_reset: + +Soft Reset +---------- + +When MicroPython is already running, it's possible to trigger a soft reset by +:ref:`typing Ctrl-D in the REPL ` or executing +:func:`machine.soft_reset()`. + +A soft reset clears the Python interpreter, frees all Python memory, and starts +the MicroPython environment again. + +State which is cleared by a soft reset includes: + +- All Python variables, objects, imported modules, etc. +- Most peripherals configured using the :doc:`machine module + `. There are very limited exceptions, for example + :doc:`machine.Pin ` modes (i.e. if a pin is input or + output, high or low) are not reset on most ports. More advanced configuration + such as :func:`Pin.irq()` is always reset. +- Bluetooth. +- Network sockets. Open TCP sockets are closed cleanly with respect to the other party. +- Open files. The filesystem is left in a valid state. + +Some system state remains the same after a soft reset, including: + +- Any existing network connections (Ethernet, Wi-Fi, etc) remain active at the + IP Network layer. Querying the :doc:`network interface from code + ` may indicate the network interface is still active with a + configured IP address, etc. +- An active :doc:`REPL ` appears continuous before and after soft reset, + except in some unusual cases: + + * If the :ref:`machine.USBDevice ` class has been used to + create a custom USB interface then any built-in USB serial device will + appear to disconnect and reconnect as the custom USB interface must be + cleared during reset. + * A serial UART REPL will restore its default hardware configuration (baud + rate, etc). + +- CPU clock speed is usually not changed by a soft reset. +- :doc:`RTC ` configuration (i.e. setting of the current + time) is not changed by soft reset. + +.. _boot_sequence: + +Boot Sequence +------------- + +When MicroPython boots following either a hard or soft reset, it follows this +boot sequence in order: + +_boot.py +^^^^^^^^ + +This is an internal script :doc:`frozen into the MicroPython firmware +`. It is provided by MicroPython on many ports to do essential +initialisation. + +For example, on most ports ``_boot.py`` will detect the first boot of a new +device and format the :doc:`internal flash filesystem ` ready for +use. + +Unless you're creating a custom MicroPython build or adding a new port then you +probably don't need to worry about ``_boot.py``. It's best not to change the +contents unless you really know what you're doing. + +.. _boot.py: + +boot.py +^^^^^^^ + +A file named ``boot.py`` can be copied to the board's internal :ref:`filesystem +` using :doc:`mpremote `. + +If ``boot.py`` is found then it is executed. You can add code in ``boot.py`` to +perform custom one-off initialisation (for example, to configure the board's +hardware). + +A common practice is to configure a board's network connection in ``boot.py`` so +that it's always available after reset for use with the :doc:`REPL `, +:doc:`mpremote `, etc. + +.. warning:: boot.py should always exit and not run indefinitely. + + Depending on the port, some hardware initialisation is delayed until after + ``boot.py`` exits. This includes initialising USB on the stm32 port and all + ports which support :ref:`machine.USBDevice `. On these + ports, output printed from ``boot.py`` may not be visible on the built-in USB + serial port until after ``boot.py`` finishes running. + + The purpose of this late initialisation is so that it's possible to + pre-configure particular hardware in ``boot.py``, and then have it start with + the correct configuration. + +.. note:: It is sometimes simpler to not have a ``boot.py`` file and place any + initialisation code at the top of ``main.py`` instead. + +.. _main.py: + +main.py +^^^^^^^ + +Similar to ``boot.py``, a file named ``main.py`` can be copied to the board's +internal :ref:`filesystem `. If found then it is executed next in the +startup process. + +``main.py`` is for any Python code that you want to run each time your device +starts. + +Some tips for ``main.py`` usage: + +- ``main.py`` doesn't have to exit, feel free to put an infinite ``while + True`` loop in there. +- For complex Python applications then you don't need to put all your + code in ``main.py``. ``main.py`` can be a simple entry point that + imports your application and starts execution:: + + import my_app + my_app.main() + + This can help keep the structure of your application clear. It also makes + it easy to install multiple applications on a board and switch among them. +- It's good practice when writing robust apps to wrap code in ``main.py`` with an + exception handler to take appropriate action if the code crashes. For example:: + + import machine, sys + import my_app + try: + my_app.main() + except Exception as e: + print("Fatal error in main:") + sys.print_exception(e) + + # Following a normal Exception or main() exiting, reset the board. + # Following a non-Exception error such as KeyboardInterrupt (Ctrl-C), + # this code will drop to a REPL. Place machine.reset() in a finally + # block to always reset, instead. + machine.reset() + + Otherwise MicroPython will drop to the REPL following any crash or if main + exits (see below). + +- Any global variables that were set in ``boot.py`` will still be set in the + global context of ``main.py``. + +- To fully optimise flash usage and memory consumption, you can copy + :doc:`pre-compiled ` ``main.mpy`` and/or ``boot.mpy`` files to the + filesystem, or even :doc:`freeze ` them into the firmware build + instead. +- ``main.py`` execution is skipped when a soft reset is initiated from :ref:`raw + REPL mode ` (for example, when :doc:`mpremote ` or another + program is interacting directly with MicroPython). + +Interactive Interpreter (REPL) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If ``main.py`` is not found, or if ``main.py`` exits, then :doc:`repl` +will start immediately. + +.. note:: Even if ``main.py`` contains an infinite loop, typing Ctrl-C on the + REPL serial port will inject a `KeyboardInterrupt`. If no exception + handler catches it then ``main.py`` will exit and the REPL will start. + +Any global variables that were set in ``boot.py`` and ``main.py`` will still be +set in the global context of the REPL. + +The REPL continues executing until Python code triggers a hard or soft reset. + +.. _soft_bricking: + +Soft Bricking (failure to boot) +--------------------------------- + +It is rare but possible for MicroPython to become unresponsive during startup, a +state sometimes called "soft bricked". For example: + +- If ``boot.py`` execution gets stuck and the native USB serial port + never initialises. +- If Python code reconfigures the REPL interface, making it inaccessible. + +Rest assured, recovery is possible! + +KeyboardInterrupt +^^^^^^^^^^^^^^^^^ + +In many cases, opening the REPL serial port and typing ``Ctrl-C`` will inject +`KeyboardInterrupt` and may cause the running script to exit and a REPL to +start. From the REPL, you can use :func:`os.remove()` to remove the misbehaving +Python file:: + + import os + os.remove('main.py') + +To confirm which files are still present in the internal filesystem:: + + import os + os.listdir() + +Safe Mode and Factory Reset +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you're unable to easily access the REPL then you may need to perform one of +two processes: + +1. "Safe mode" boot, which skips ``boot.py`` and ``main.py`` and immediately + starts a REPL, allowing you to clean up. This is only supported on some ports. +2. Factory Reset to erase the entire contents of the flash filesystem. This may + also be necessary if the internal flash filesystem has become corrupted + somehow. + +The specific process(es) are different on each port: + +- :doc:`pyboard and stm32 port instructions ` +- :doc:`esp32 port instructions ` +- :doc:`renesas-ra port instructions ` +- :doc:`rp2 port instructions ` +- :doc:`wipy port instructions ` + +For ports without specific instructions linked above, the factory reset process +involves erasing the board's entire flash and then flashing MicroPython again +from scratch. Usually this will involve the same tool(s) that were originally +used to install MicroPython. Consult the installation docs for your board, or +ask on the `GitHub Discussions`_ if you're not sure. + +.. warning:: Re-flashing the MicroPython firmware without erasing the entire + flash first will usually not recover from soft bricking, as a + firmware update usually preserves the contents of the filesystem. + +.. _GitHub Discussions: https://github.com/orgs/micropython/discussions diff --git a/docs/renesas-ra/tutorial/program_in_flash.rst b/docs/renesas-ra/tutorial/program_in_flash.rst index 99a7a3839dd46..985ce6c7fe661 100644 --- a/docs/renesas-ra/tutorial/program_in_flash.rst +++ b/docs/renesas-ra/tutorial/program_in_flash.rst @@ -28,6 +28,8 @@ As the factory setting, following 2 files are created in the file system: * boot.py : executed first when the system starts * main.py : executed after boot.py completes +See :doc:`/reference/reset_boot` for more information. + Write a program in the internal file system ------------------------------------------- diff --git a/docs/renesas-ra/tutorial/reset.rst b/docs/renesas-ra/tutorial/reset.rst index de5f4db91b3e0..8799796479560 100644 --- a/docs/renesas-ra/tutorial/reset.rst +++ b/docs/renesas-ra/tutorial/reset.rst @@ -20,6 +20,8 @@ If that isn't working you can perform a hard reset (turn-it-off-and-on-again) by pressing the RESET button. This will end your session, disconnecting whatever program (PuTTY, screen, etc) that you used to connect to the board. +For more details, see :doc:`/reference/reset_boot`. + boot mode --------- @@ -29,7 +31,9 @@ There are 3 boot modes: * safe boot mode * factory filesystem boot mode -boot.py and main.py are executed on "normal boot mode". +boot.py and main.py are executed on "normal boot mode". See :ref:`boot_sequence`. + +The other modes can be used to recover from :ref:`soft_bricking`: boot.py and main.py are *NOT* executed on "safe boot mode". @@ -46,16 +50,4 @@ on the board: You have created the main.py which executes LED1 blinking in the previous part. If you change the boot mode to safe boot mode, the MicroPython starts without -the execution of main.py. Then you can remove the main.py by following -command or change the boot mode to factory file system boot mode.:: - - import os - os.remove('main.py') - -or change the boot mode to factory file system boot mode. - -You can confirm that the initialized file system that there are only boot.py and main.py files.:: - - import os - os.listdir() - +the execution of main.py. diff --git a/docs/rp2/tutorial/intro.rst b/docs/rp2/tutorial/intro.rst index 69c3e6b0a54ae..2d2990105dba0 100644 --- a/docs/rp2/tutorial/intro.rst +++ b/docs/rp2/tutorial/intro.rst @@ -8,4 +8,5 @@ Let's get started! .. toctree:: :maxdepth: 1 + reset.rst pio.rst diff --git a/docs/rp2/tutorial/reset.rst b/docs/rp2/tutorial/reset.rst new file mode 100644 index 0000000000000..3c296ec46ee18 --- /dev/null +++ b/docs/rp2/tutorial/reset.rst @@ -0,0 +1,18 @@ +Factory reset +============= + +If something unexpected happens and your RP2xxx-based board no longer boots +MicroPython, then you may have to factory reset it. For more details, see +:ref:`soft_bricking`. + +Factory resetting the MicroPython rp2 port involves fully erasing the flash and +resetting the flash memory, so you will need to re-flash the MicroPython +firmware afterwards and copy any Python files to the filesystem again. + +1. Follow the instructions on the Raspberry Pi website for `resetting flash + memory`_. +2. Copy the MicroPython .uf2 firmware file to your board. If needed, this file + can be found on the `MicroPython downloads page`_. + +.. _resetting flash memory: https://www.raspberrypi.com/documentation/microcontrollers/pico-series.html#resetting-flash-memory +.. _MicroPython downloads page: https://micropython.org/download/?port=rp2 diff --git a/docs/samd/quickref.rst b/docs/samd/quickref.rst index 25b5a8fc8ab2b..d57dc67908398 100644 --- a/docs/samd/quickref.rst +++ b/docs/samd/quickref.rst @@ -215,7 +215,7 @@ PWM Constructor - *freq* should be an integer which sets the frequency in Hz for the PWM cycle. The valid frequency range is 1 Hz to 24 MHz. - - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65536``. + - *duty_u16* sets the duty cycle as a ratio ``duty_u16 / 65535``. - *duty_ns* sets the pulse width in nanoseconds. The limitation for X channels apply as well. - *invert*\=True|False. Setting a bit inverts the respective output. @@ -246,7 +246,7 @@ Use the :ref:`machine.ADC ` class:: from machine import ADC adc0 = ADC(Pin('A0')) # create ADC object on ADC pin, average=16 - adc0.read_u16() # read value, 0-65536 across voltage range 0.0v - 3.3v + adc0.read_u16() # read value, 0-65535 across voltage range 0.0v - 3.3v adc1 = ADC(Pin('A1'), average=1) # create ADC object on ADC pin, average=1 The resolution of the ADC is 12 bit with 12 bit accuracy, irrespective of the diff --git a/docs/wipy/tutorial/reset.rst b/docs/wipy/tutorial/reset.rst index 1715d3e29788d..fc56429b81a3f 100644 --- a/docs/wipy/tutorial/reset.rst +++ b/docs/wipy/tutorial/reset.rst @@ -16,6 +16,8 @@ There are soft resets and hard resets. import machine machine.reset() +For more information, see :doc:`/reference/reset_boot`. + Safe boot --------- diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 21c6f7264befa..ce70627c31c60 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -701,7 +701,12 @@ static mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui MICROPY_PY_LWIP_ENTER - u16_t available = tcp_sndbuf(socket->pcb.tcp); + // If the socket is still connecting then don't let data be written to it. + // Otherwise, get the number of available bytes in the output buffer. + u16_t available = 0; + if (socket->state != STATE_CONNECTING) { + available = tcp_sndbuf(socket->pcb.tcp); + } if (available == 0) { // Non-blocking socket @@ -718,7 +723,8 @@ static mp_uint_t lwip_tcp_send(lwip_socket_obj_t *socket, const byte *buf, mp_ui // If peer fully closed socket, we would have socket->state set to ERR_RST (connection // reset) by error callback. // Avoid sending too small packets, so wait until at least 16 bytes available - while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) { + while (socket->state == STATE_CONNECTING + || (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16)) { MICROPY_PY_LWIP_EXIT if (socket->timeout != -1 && mp_hal_ticks_ms() - start > socket->timeout) { *_errno = MP_ETIMEDOUT; @@ -1432,7 +1438,7 @@ static mp_obj_t lwip_socket_setsockopt(size_t n_args, const mp_obj_t *args) { case IP_DROP_MEMBERSHIP: { mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); - if (bufinfo.len != sizeof(ip_addr_t) * 2) { + if (bufinfo.len != sizeof(MP_IGMP_IP_ADDR_TYPE) * 2) { mp_raise_ValueError(NULL); } @@ -1548,7 +1554,7 @@ static mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ // raw socket is writable ret |= MP_STREAM_POLL_WR; #endif - } else if (socket->pcb.tcp != NULL && tcp_sndbuf(socket->pcb.tcp) > 0) { + } else if (socket->state != STATE_CONNECTING && socket->pcb.tcp != NULL && tcp_sndbuf(socket->pcb.tcp) > 0) { // TCP socket is writable // Note: pcb.tcp==NULL if state<0, and in this case we can't call tcp_sndbuf ret |= MP_STREAM_POLL_WR; diff --git a/extmod/network_cyw43.c b/extmod/network_cyw43.c index 3066cac75d3b7..02b022cb81327 100644 --- a/extmod/network_cyw43.c +++ b/extmod/network_cyw43.c @@ -60,6 +60,10 @@ typedef struct _network_cyw43_obj_t { static const network_cyw43_obj_t network_cyw43_wl_sta = { { &mp_network_cyw43_type }, &cyw43_state, CYW43_ITF_STA }; static const network_cyw43_obj_t network_cyw43_wl_ap = { { &mp_network_cyw43_type }, &cyw43_state, CYW43_ITF_AP }; +// Avoid race conditions with callbacks by tracking the last up or down request +// we have made for each interface. +static bool if_active[2]; + static void network_cyw43_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(self_in); struct netif *netif = &self->cyw->netif[self->itf]; @@ -122,6 +126,10 @@ static MP_DEFINE_CONST_FUN_OBJ_3(network_cyw43_ioctl_obj, network_cyw43_ioctl); /*******************************************************************************/ // network API +static uint32_t get_country_code(void) { + return CYW43_COUNTRY(mod_network_country_code[0], mod_network_country_code[1], 0); +} + static mp_obj_t network_cyw43_deinit(mp_obj_t self_in) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(self_in); cyw43_deinit(self->cyw); @@ -132,10 +140,11 @@ static MP_DEFINE_CONST_FUN_OBJ_1(network_cyw43_deinit_obj, network_cyw43_deinit) static mp_obj_t network_cyw43_active(size_t n_args, const mp_obj_t *args) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (n_args == 1) { - return mp_obj_new_bool(cyw43_tcpip_link_status(self->cyw, self->itf)); + return mp_obj_new_bool(if_active[self->itf]); } else { - uint32_t country = CYW43_COUNTRY(mod_network_country_code[0], mod_network_country_code[1], 0); - cyw43_wifi_set_up(self->cyw, self->itf, mp_obj_is_true(args[1]), country); + bool value = mp_obj_is_true(args[1]); + cyw43_wifi_set_up(self->cyw, self->itf, value, get_country_code()); + if_active[self->itf] = value; return mp_const_none; } } @@ -311,7 +320,17 @@ static MP_DEFINE_CONST_FUN_OBJ_1(network_cyw43_disconnect_obj, network_cyw43_dis static mp_obj_t network_cyw43_isconnected(mp_obj_t self_in) { network_cyw43_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_bool(cyw43_tcpip_link_status(self->cyw, self->itf) == 3); + bool result = (cyw43_tcpip_link_status(self->cyw, self->itf) == CYW43_LINK_UP); + + if (result && self->itf == CYW43_ITF_AP) { + // For AP we need to not only know if the link is up, but also if any stations + // have associated. + uint8_t mac_buf[6]; + int num_stas = 1; + cyw43_wifi_ap_get_stas(self->cyw, &num_stas, mac_buf); + result = num_stas > 0; + } + return mp_obj_new_bool(result); } static MP_DEFINE_CONST_FUN_OBJ_1(network_cyw43_isconnected_obj, network_cyw43_isconnected); @@ -351,13 +370,15 @@ static mp_obj_t network_cyw43_status(size_t n_args, const mp_obj_t *args) { if (self->itf != CYW43_ITF_AP) { mp_raise_ValueError(MP_ERROR_TEXT("AP required")); } - int num_stas; - uint8_t macs[32 * 6]; + static const unsigned mac_len = 6; + static const unsigned max_stas = 32; + int num_stas = max_stas; + uint8_t macs[max_stas * mac_len]; cyw43_wifi_ap_get_stas(self->cyw, &num_stas, macs); mp_obj_t list = mp_obj_new_list(num_stas, NULL); for (int i = 0; i < num_stas; ++i) { mp_obj_t tuple[1] = { - mp_obj_new_bytes(&macs[i * 6], 6), + mp_obj_new_bytes(&macs[i * mac_len], mac_len), }; ((mp_obj_list_t *)MP_OBJ_TO_PTR(list))->items[i] = mp_obj_new_tuple(1, tuple); } @@ -445,6 +466,10 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map mp_raise_TypeError(MP_ERROR_TEXT("can't specify pos and kw args")); } + // A number of these options only update buffers in memory, and + // won't do anything until the interface is cycled down and back up + bool cycle_active = false; + for (size_t i = 0; i < kwargs->alloc; ++i) { if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { mp_map_elem_t *e = &kwargs->table[i]; @@ -457,6 +482,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map } case MP_QSTR_channel: { cyw43_wifi_ap_set_channel(self->cyw, mp_obj_get_int(e->value)); + cycle_active = true; break; } case MP_QSTR_ssid: @@ -464,6 +490,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map size_t len; const char *str = mp_obj_str_get_data(e->value, &len); cyw43_wifi_ap_set_ssid(self->cyw, len, (const uint8_t *)str); + cycle_active = true; break; } case MP_QSTR_monitor: { @@ -483,6 +510,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map } case MP_QSTR_security: { cyw43_wifi_ap_set_auth(self->cyw, mp_obj_get_int(e->value)); + cycle_active = true; break; } case MP_QSTR_key: @@ -490,6 +518,7 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map size_t len; const char *str = mp_obj_str_get_data(e->value, &len); cyw43_wifi_ap_set_password(self->cyw, len, (const uint8_t *)str); + cycle_active = true; break; } case MP_QSTR_pm: { @@ -519,6 +548,13 @@ static mp_obj_t network_cyw43_config(size_t n_args, const mp_obj_t *args, mp_map } } + // If the interface is already active, cycle it down and up + if (cycle_active && if_active[self->itf]) { + uint32_t country = get_country_code(); + cyw43_wifi_set_up(self->cyw, self->itf, false, country); + cyw43_wifi_set_up(self->cyw, self->itf, true, country); + } + return mp_const_none; } } diff --git a/extmod/network_ppp_lwip.c b/extmod/network_ppp_lwip.c index 2b77662a24f26..8eb90ea4a6529 100644 --- a/extmod/network_ppp_lwip.c +++ b/extmod/network_ppp_lwip.c @@ -60,6 +60,18 @@ const mp_obj_type_t mp_network_ppp_lwip_type; static mp_obj_t network_ppp___del__(mp_obj_t self_in); +static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } + + // Disable UART IRQ. + mp_obj_t dest[3]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_const_none; + mp_call_method_n_kw(1, 0, dest); +} + static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { network_ppp_obj_t *self = ctx; switch (err_code) { @@ -68,12 +80,9 @@ static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { break; case PPPERR_USER: if (self->state >= STATE_ERROR) { - // Disable UART IRQ. - mp_obj_t dest[3]; - mp_load_method(self->stream, MP_QSTR_irq, dest); - dest[2] = mp_const_none; - mp_call_method_n_kw(1, 0, dest); - // Indicate that the IRQ is disabled. + network_ppp_stream_uart_irq_disable(self); + // Indicate that we are no longer connected and thus + // only need to free the PPP PCB, not close it. self->state = STATE_ACTIVE; } // Clean up the PPP PCB. @@ -91,7 +100,9 @@ static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, s mp_obj_t stream = all_args[0]; - mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + if (stream != mp_const_none) { + mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } network_ppp_obj_t *self = mp_obj_malloc_with_finaliser(network_ppp_obj_t, type); self->state = STATE_INACTIVE; @@ -105,7 +116,7 @@ static mp_obj_t network_ppp___del__(mp_obj_t self_in) { network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); if (self->state >= STATE_ACTIVE) { if (self->state >= STATE_ERROR) { - // Still connected over the UART stream. + // Still connected over the stream. // Force the connection to close, with nocarrier=1. self->state = STATE_INACTIVE; ppp_close(self->pcb, 1); @@ -127,10 +138,11 @@ static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { } mp_int_t total_len = 0; - for (;;) { + mp_obj_t stream = self->stream; + while (stream != mp_const_none) { uint8_t buf[256]; int err; - mp_uint_t len = mp_stream_rw(self->stream, buf, sizeof(buf), &err, 0); + mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); if (len == 0) { break; } @@ -149,16 +161,42 @@ static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll); +static void network_ppp_stream_uart_irq_enable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } + + // Enable UART IRQ to call PPP.poll() when incoming data is ready. + mp_obj_t dest[4]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self)); + dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE); + mp_call_method_n_kw(2, 0, dest); +} + static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { if (n_args != 1 && kwargs->used != 0) { mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed")); } - // network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (kwargs->used != 0) { for (size_t i = 0; i < kwargs->alloc; i++) { if (mp_map_slot_is_filled(kwargs, i)) { switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { + case MP_QSTR_stream: { + if (kwargs->table[i].value != mp_const_none) { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_disable(self); + } + self->stream = kwargs->table[i].value; + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_enable(self); + } + break; + } default: break; } @@ -174,6 +212,10 @@ static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t mp_obj_t val = mp_const_none; switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_stream: { + val = self->stream; + break; + } default: mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); } @@ -201,10 +243,14 @@ static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t l } mp_printf(&mp_plat_print, ")\n"); #endif + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + return 0; + } int err; // The return value from this output callback is the number of bytes written out. // If it's less than the requested number of bytes then lwIP will propagate out an error. - return mp_stream_rw(self->stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); + return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); } static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { @@ -227,12 +273,7 @@ static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_ } self->state = STATE_ACTIVE; - // Enable UART IRQ to call PPP.poll() when incoming data is ready. - mp_obj_t dest[4]; - mp_load_method(self->stream, MP_QSTR_irq, dest); - dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self)); - dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE); - mp_call_method_n_kw(2, 0, dest); + network_ppp_stream_uart_irq_enable(self); } if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 8d555f1124f9a..6ca0c172677c7 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -1911,24 +1911,21 @@ static int ble_secret_store_read(int obj_type, const union ble_store_key *key, u // (single) // Find the entry for this specific peer. assert(key->sec.idx == 0); - assert(!key->sec.ediv_rand_present); key_data = (const uint8_t *)&key->sec.peer_addr; key_data_len = sizeof(ble_addr_t); } else { // (with index) // Iterate all known peers. - assert(!key->sec.ediv_rand_present); key_data = NULL; key_data_len = 0; } break; } case BLE_STORE_OBJ_TYPE_OUR_SEC: { - // - // Find our secret for this remote device, matching this ediv/rand key. + // + // Find our secret for this remote device. assert(ble_addr_cmp(&key->sec.peer_addr, BLE_ADDR_ANY)); // Must have address. assert(key->sec.idx == 0); - assert(key->sec.ediv_rand_present); key_data = (const uint8_t *)&key->sec.peer_addr; key_data_len = sizeof(ble_addr_t); break; @@ -1958,10 +1955,6 @@ static int ble_secret_store_read(int obj_type, const union ble_store_key *key, u DEBUG_printf("ble_secret_store_read: found secret\n"); - if (obj_type == BLE_STORE_OBJ_TYPE_OUR_SEC) { - // TODO: Verify ediv_rand matches. - } - return 0; } @@ -1970,14 +1963,13 @@ static int ble_secret_store_write(int obj_type, const union ble_store_value *val switch (obj_type) { case BLE_STORE_OBJ_TYPE_PEER_SEC: case BLE_STORE_OBJ_TYPE_OUR_SEC: { - // + // struct ble_store_key_sec key_sec; const struct ble_store_value_sec *value_sec = &val->sec; ble_store_key_from_value_sec(&key_sec, value_sec); assert(ble_addr_cmp(&key_sec.peer_addr, BLE_ADDR_ANY)); // Must have address. - assert(key_sec.ediv_rand_present); if (!mp_bluetooth_gap_on_set_secret(obj_type, (const uint8_t *)&key_sec.peer_addr, sizeof(ble_addr_t), (const uint8_t *)value_sec, sizeof(struct ble_store_value_sec))) { DEBUG_printf("Failed to write key: type=%d\n", obj_type); @@ -2005,9 +1997,7 @@ static int ble_secret_store_delete(int obj_type, const union ble_store_key *key) case BLE_STORE_OBJ_TYPE_PEER_SEC: case BLE_STORE_OBJ_TYPE_OUR_SEC: { // - assert(ble_addr_cmp(&key->sec.peer_addr, BLE_ADDR_ANY)); // Must have address. - // ediv_rand is optional (will not be present for delete). if (!mp_bluetooth_gap_on_set_secret(obj_type, (const uint8_t *)&key->sec.peer_addr, sizeof(ble_addr_t), NULL, 0)) { DEBUG_printf("Failed to delete key: type=%d\n", obj_type); diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index a7c14b76ee368..d43c96b08f326 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -58,6 +58,13 @@ static int mp_vfs_blockdev_call_rw(mp_obj_t *args, size_t block_num, size_t bloc if (ret == mp_const_none) { return 0; } else { + // Some block devices return a bool indicating success, so + // convert those to an errno integer code. + if (ret == mp_const_true) { + return 0; + } else if (ret == mp_const_false) { + return -MP_EIO; + } // Block device functions are expected to return 0 on success // and negative integer on errors. Check for positive integer // results as some callers (i.e. littlefs) will produce corrupt diff --git a/ports/cc3200/tools/smoke.py b/ports/cc3200/tools/smoke.py index 463a485c4d4c8..f7105411b4d7e 100644 --- a/ports/cc3200/tools/smoke.py +++ b/ports/cc3200/tools/smoke.py @@ -6,7 +6,7 @@ """ Execute it like this: -python3 run-tests.py --target wipy --device 192.168.1.1 ../cc3200/tools/smoke.py +python3 run-tests.py -t 192.168.1.1 ../cc3200/tools/smoke.py """ pin_map = [23, 24, 11, 12, 13, 14, 15, 16, 17, 22, 28, 10, 9, 8, 7, 6, 30, 31, 3, 0, 4, 5] diff --git a/ports/esp32/CMakeLists.txt b/ports/esp32/CMakeLists.txt index 83fca88b6972b..d695170fa2b5d 100644 --- a/ports/esp32/CMakeLists.txt +++ b/ports/esp32/CMakeLists.txt @@ -1,5 +1,8 @@ # Top-level cmake file for building MicroPython on ESP32. - +# +# Note for maintainers: Where possible, functionality should be put into +# esp32_common.cmake not this file. This is because this CMakeLists.txt file +# needs to be duplicated for out-of-tree builds, and can easily get out of date. cmake_minimum_required(VERSION 3.12) # Retrieve IDF version @@ -71,12 +74,5 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake) # Set the location of the main component for the project (one per target). list(APPEND EXTRA_COMPONENT_DIRS main_${IDF_TARGET}) -# Enable the panic handler wrapper -idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) - -# Patch LWIP memory pool allocators (see lwip_patch.c) -idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_malloc" APPEND) -idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_free" APPEND) - # Define the project. project(micropython) diff --git a/ports/esp32/README.md b/ports/esp32/README.md index a04ad0c6eea1b..4eb791389380c 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -195,7 +195,7 @@ quickly call `wlan_connect()` and it just works): ```python def wlan_connect(ssid='MYSSID', password='MYPASS'): import network - wlan = network.WLAN(network.STA_IF) + wlan = network.WLAN(network.WLAN.IF_STA) if not wlan.active() or not wlan.isconnected(): wlan.active(True) print('connecting to:', ssid) diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py index bfe02c35765b8..98ea0adcbf86c 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/modules/lilygo_oled.py @@ -30,7 +30,7 @@ def display_wifi(self): self.text("Scan...", 0, 0, 1) self.show() - sta_if = network.WLAN(network.STA_IF) + sta_if = network.WLAN(network.WLAN.IF_STA) sta_if.active(True) _wifi = sta_if.scan() diff --git a/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py b/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py index 37dc5a340fe46..58120d44adf7e 100644 --- a/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py +++ b/ports/esp32/boards/LOLIN_S2_PICO/modules/s2pico_oled.py @@ -37,7 +37,7 @@ def display_wifi(self): self.text("Scan...", 0, 0, 1) self.show() - sta_if = network.WLAN(network.STA_IF) + sta_if = network.WLAN(network.WLAN.IF_STA) sta_if.active(True) _wifi = sta_if.scan() diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index 565f6feec2324..9d51a03aa856e 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -1,3 +1,9 @@ +# This is the common ESP-IDF "main component" CMakeLists.txt contents for MicroPython. +# +# This file is included directly from a main_${IDF_TARGET}/CMakeLists.txt file +# (or included from an out-of-tree main component CMakeLists.txt for out-of-tree +# builds.) + # Set location of base MicroPython directory. if(NOT MICROPY_DIR) get_filename_component(MICROPY_DIR ${CMAKE_CURRENT_LIST_DIR}/../.. ABSOLUTE) @@ -236,6 +242,13 @@ target_include_directories(${MICROPY_TARGET} PUBLIC target_link_libraries(${MICROPY_TARGET} micropy_extmod_btree) target_link_libraries(${MICROPY_TARGET} usermod) +# Enable the panic handler wrapper +idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND) + +# Patch LWIP memory pool allocators (see lwip_patch.c) +idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_malloc" APPEND) +idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=memp_free" APPEND) + # Collect all of the include directories and compile definitions for the IDF components, # including those added by the IDF Component Manager via idf_components.yaml. foreach(comp ${__COMPONENT_NAMES_RESOLVED}) diff --git a/ports/esp32/help.c b/ports/esp32/help.c index 2351d7dc73943..62639a0b408ce 100644 --- a/ports/esp32/help.c +++ b/ports/esp32/help.c @@ -48,7 +48,7 @@ const char esp32_help_text[] = "Basic WiFi configuration:\n" "\n" "import network\n" - "sta_if = network.WLAN(network.STA_IF); sta_if.active(True)\n" + "sta_if = network.WLAN(network.WLAN.IF_STA); sta_if.active(True)\n" "sta_if.scan() # Scan for available access points\n" "sta_if.connect(\"\", \"\") # Connect to an AP\n" "sta_if.isconnected() # Check for successful connection\n" diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index 4b3d392cbdae2..6eb83fc09c37a 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -196,6 +196,10 @@ static void machine_hw_spi_init_internal(machine_hw_spi_obj_t *self, mp_arg_val_ changed = true; } + if (args[ARG_bits].u_int != -1 && args[ARG_bits].u_int <= 0) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); + } + if (args[ARG_bits].u_int != -1 && args[ARG_bits].u_int != self->bits) { self->bits = args[ARG_bits].u_int; changed = true; diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 71402e1c49e44..0134fa2cc8c89 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -36,6 +36,10 @@ #include "esp_err.h" #include "soc/gpio_sig_map.h" +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) +#include "esp_clk_tree.h" +#endif + #define PWM_DBG(...) // #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n"); @@ -207,53 +211,79 @@ static void configure_channel(machine_pwm_obj_t *self) { } } -static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) { - if (freq != timer->freq_hz) { - // Find the highest bit resolution for the requested frequency - unsigned int i = APB_CLK_FREQ; // 80 MHz - #if SOC_LEDC_SUPPORT_REF_TICK - if (freq < EMPIRIC_FREQ) { - i = REF_CLK_FREQ; // 1 MHz - } - #endif +// Temporary workaround for ledc_find_suitable_duty_resolution function only being added in IDF V5.2 +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0) +static uint32_t ledc_find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) { + // This implementation is based on the one used in Micropython v1.23 - int divider = (i + freq / 2) / freq; // rounded - if (divider == 0) { - divider = 1; - } - float f = (float)i / divider; // actual frequency - if (f <= 1.0) { - f = 1.0; - } - i = (unsigned int)roundf((float)i / f); + // Find the highest bit resolution for the requested frequency + unsigned int freq = src_clk_freq; - unsigned int res = 0; - for (; i > 1; i >>= 1) { - ++res; - } - if (res == 0) { - res = 1; - } else if (res > HIGHEST_PWM_RES) { - // Limit resolution to HIGHEST_PWM_RES to match units of our duty - res = HIGHEST_PWM_RES; - } + int divider = (freq + timer_freq / 2) / timer_freq; // rounded + if (divider == 0) { + divider = 1; + } + float f = (float)freq / divider; // actual frequency + if (f <= 1.0) { + f = 1.0; + } + freq = (unsigned int)roundf((float)freq / f); - // Configure the new resolution and frequency - timer->duty_resolution = res; + unsigned int res = 0; + for (; freq > 1; freq >>= 1) { + ++res; + } + if (res == 0) { + res = 1; + } else if (res > HIGHEST_PWM_RES) { + // Limit resolution to HIGHEST_PWM_RES to match units of our duty + res = HIGHEST_PWM_RES; + } + + return res; +} +#endif + +static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) { + esp_err_t err; + if (freq != timer->freq_hz) { + // Configure the new frequency and resolution timer->freq_hz = freq; - #if SOC_LEDC_SUPPORT_XTAL_CLOCK + + #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK + timer->clk_cfg = LEDC_USE_PLL_DIV_CLK; + #elif SOC_LEDC_SUPPORT_APB_CLOCK + timer->clk_cfg = LEDC_USE_APB_CLK; + #elif SOC_LEDC_SUPPORT_XTAL_CLOCK timer->clk_cfg = LEDC_USE_XTAL_CLK; #else - timer->clk_cfg = LEDC_USE_APB_CLK; + #error No supported PWM / LEDC clocks. #endif #if SOC_LEDC_SUPPORT_REF_TICK if (freq < EMPIRIC_FREQ) { timer->clk_cfg = LEDC_USE_REF_TICK; } #endif + uint32_t src_clk_freq = 0; + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) + err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq); + if (err != ESP_OK) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg); + } + #else + // Simplified fallback logic for IDF V5.0.x, for targets with APB only. + src_clk_freq = APB_CLK_FREQ; // 80 MHz + #if SOC_LEDC_SUPPORT_REF_TICK + if (timer->clk_cfg == LEDC_USE_REF_TICK) { + src_clk_freq = REF_CLK_FREQ; // 1 MHz + } + #endif // SOC_LEDC_SUPPORT_REF_TICK + #endif // ESP_IDF_VERSION + + timer->duty_resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer->freq_hz); // Set frequency - esp_err_t err = ledc_timer_config(timer); + err = ledc_timer_config(timer); if (err != ESP_OK) { if (err == ESP_FAIL) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unreachable frequency %d"), freq); diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 03dc0807a025c..18ef9d7354798 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -39,6 +39,7 @@ #include "esp_task.h" #include "esp_event.h" #include "esp_log.h" +#include "esp_memory_utils.h" #include "esp_psram.h" #include "py/cstack.h" @@ -237,6 +238,13 @@ void *esp_native_code_commit(void *buf, size_t len, void *reloc) { len = (len + 3) & ~3; size_t len_node = sizeof(native_code_node_t) + len; native_code_node_t *node = heap_caps_malloc(len_node, MALLOC_CAP_EXEC); + #if CONFIG_IDF_TARGET_ESP32S2 + // Workaround for ESP-IDF bug https://github.com/espressif/esp-idf/issues/14835 + if (node != NULL && !esp_ptr_executable(node)) { + free(node); + node = NULL; + } + #endif // CONFIG_IDF_TARGET_ESP32S2 if (node == NULL) { m_malloc_fail(len_node); } diff --git a/ports/esp32/modnetwork_globals.h b/ports/esp32/modnetwork_globals.h index c0cf229421daf..c6092bd142178 100644 --- a/ports/esp32/modnetwork_globals.h +++ b/ports/esp32/modnetwork_globals.h @@ -13,7 +13,8 @@ { MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_network_ipconfig_obj) }, -#if MICROPY_PY_NETWORK_WLAN +// These WLAN constants are now in the WLAN class and remain here only for backwards compatibility. +#if !MICROPY_PREVIEW_VERSION_2 && MICROPY_PY_NETWORK_WLAN { MP_ROM_QSTR(MP_QSTR_STA_IF), MP_ROM_INT(WIFI_IF_STA)}, { MP_ROM_QSTR(MP_QSTR_AP_IF), MP_ROM_INT(WIFI_IF_AP)}, diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 916eb79bd9265..7ea5e855d325e 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -213,7 +213,7 @@ static int mdns_getaddrinfo(const char *host_str, const char *port_str, #endif // MICROPY_HW_ENABLE_MDNS_QUERIES static void _getaddrinfo_inner(const mp_obj_t host, const mp_obj_t portx, - const struct addrinfo *hints, struct addrinfo **res) { + struct addrinfo *hints, struct addrinfo **res) { int retval = 0; *res = NULL; @@ -235,6 +235,9 @@ static void _getaddrinfo_inner(const mp_obj_t host, const mp_obj_t portx, MP_THREAD_GIL_EXIT(); + // The ai_canonname field is used below, so set the hint. + hints->ai_flags |= AI_CANONNAME; + #if MICROPY_HW_ENABLE_MDNS_QUERIES retval = mdns_getaddrinfo(host_str, port_str, hints, res); #endif @@ -264,7 +267,8 @@ static void _getaddrinfo_inner(const mp_obj_t host, const mp_obj_t portx, static void _socket_getaddrinfo(const mp_obj_t addrtuple, struct addrinfo **resp) { mp_obj_t *elem; mp_obj_get_array_fixed_n(addrtuple, 2, &elem); - _getaddrinfo_inner(elem[0], elem[1], NULL, resp); + struct addrinfo hints = { 0 }; + _getaddrinfo_inner(elem[0], elem[1], &hints, resp); } static mp_obj_t socket_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { diff --git a/ports/esp32/network_common.c b/ports/esp32/network_common.c index d6bba32146c5c..fce8a0304c8be 100644 --- a/ports/esp32/network_common.c +++ b/ports/esp32/network_common.c @@ -337,11 +337,3 @@ static mp_obj_t esp_phy_mode(size_t n_args, const mp_obj_t *args) { return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj, 0, 1, esp_phy_mode); - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) -_Static_assert(WIFI_AUTH_MAX == 13, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); -#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 5) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) -_Static_assert(WIFI_AUTH_MAX == 11, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); -#else -_Static_assert(WIFI_AUTH_MAX == 10, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); -#endif diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index f2813f430d482..5c41cf948c1ed 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -85,7 +85,9 @@ static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { } static mp_obj_t ppp_make_new(mp_obj_t stream) { - mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + if (stream != mp_const_none) { + mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } ppp_if_obj_t *self = mp_obj_malloc_with_finaliser(ppp_if_obj_t, &ppp_if_type); self->stream = stream; @@ -100,8 +102,14 @@ MP_DEFINE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj, ppp_make_new); static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) { ppp_if_obj_t *self = ctx; + + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + return 0; + } + int err; - return mp_stream_rw(self->stream, data, len, &err, MP_STREAM_RW_WRITE); + return mp_stream_rw(stream, data, len, &err, MP_STREAM_RW_WRITE); } static void pppos_client_task(void *self_in) { @@ -110,10 +118,15 @@ static void pppos_client_task(void *self_in) { int len = 0; while (ulTaskNotifyTake(pdTRUE, len <= 0) == 0) { - int err; - len = mp_stream_rw(self->stream, buf, sizeof(buf), &err, 0); - if (len > 0) { - pppos_input_tcpip(self->pcb, (u8_t *)buf, len); + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + len = 0; + } else { + int err; + len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); + if (len > 0) { + pppos_input_tcpip(self->pcb, (u8_t *)buf, len); + } } } @@ -323,6 +336,13 @@ static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs for (size_t i = 0; i < kwargs->alloc; i++) { if (mp_map_slot_is_filled(kwargs, i)) { switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { + case MP_QSTR_stream: { + if (kwargs->table[i].value != mp_const_none) { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } + self->stream = kwargs->table[i].value; + break; + } default: break; } @@ -338,6 +358,10 @@ static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs mp_obj_t val = mp_const_none; switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_stream: { + val = self->stream; + break; + } case MP_QSTR_ifname: { if (self->pcb != NULL) { struct netif *pppif = ppp_netif(self->pcb); diff --git a/ports/esp32/network_wlan.c b/ports/esp32/network_wlan.c index 7e802166c116c..d415c56d32175 100644 --- a/ports/esp32/network_wlan.c +++ b/ports/esp32/network_wlan.c @@ -744,6 +744,13 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_SEC_WPA2_WPA3), MP_ROM_INT(WIFI_AUTH_WPA2_WPA3_PSK) }, { MP_ROM_QSTR(MP_QSTR_SEC_WAPI), MP_ROM_INT(WIFI_AUTH_WAPI_PSK) }, { MP_ROM_QSTR(MP_QSTR_SEC_OWE), MP_ROM_INT(WIFI_AUTH_OWE) }, + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 5) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) + { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_ENT_192), MP_ROM_INT(WIFI_AUTH_WPA3_ENT_192) }, + #endif + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) + { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_EXT_PSK), MP_ROM_INT(WIFI_AUTH_WPA3_EXT_PSK) }, + { MP_ROM_QSTR(MP_QSTR_SEC_WPA3_EXT_PSK_MIXED_MODE), MP_ROM_INT(WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE) }, + #endif { MP_ROM_QSTR(MP_QSTR_PM_NONE), MP_ROM_INT(WIFI_PS_NONE) }, { MP_ROM_QSTR(MP_QSTR_PM_PERFORMANCE), MP_ROM_INT(WIFI_PS_MIN_MODEM) }, @@ -751,6 +758,14 @@ static const mp_rom_map_elem_t wlan_if_locals_dict_table[] = { }; static MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table); +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 0) +_Static_assert(WIFI_AUTH_MAX == 13, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); +#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 5) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0) || ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) +_Static_assert(WIFI_AUTH_MAX == 11, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); +#else +_Static_assert(WIFI_AUTH_MAX == 10, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h"); +#endif + MP_DEFINE_CONST_OBJ_TYPE( esp_network_wlan_type, MP_QSTR_WLAN, diff --git a/ports/esp8266/help.c b/ports/esp8266/help.c index a9cb27bad51c8..694cd90b951d2 100644 --- a/ports/esp8266/help.c +++ b/ports/esp8266/help.c @@ -36,12 +36,12 @@ const char esp_help_text[] = "Basic WiFi configuration:\n" "\n" "import network\n" - "sta_if = network.WLAN(network.STA_IF); sta_if.active(True)\n" + "sta_if = network.WLAN(network.WLAN.IF_STA); sta_if.active(True)\n" "sta_if.scan() # Scan for available access points\n" "sta_if.connect(\"\", \"\") # Connect to an AP\n" "sta_if.isconnected() # Check for successful connection\n" "# Change name/password of ESP8266's AP:\n" - "ap_if = network.WLAN(network.AP_IF)\n" + "ap_if = network.WLAN(network.WLAN.IF_AP)\n" "ap_if.config(ssid=\"\", security=network.AUTH_WPA_WPA2_PSK, key=\"\")\n" "\n" "Control commands:\n" diff --git a/ports/esp8266/machine_pwm.c b/ports/esp8266/machine_pwm.c index 25a2d6898f7c7..0d5ed595428e3 100644 --- a/ports/esp8266/machine_pwm.c +++ b/ports/esp8266/machine_pwm.c @@ -80,7 +80,7 @@ static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, c pwm_set_duty(args[ARG_duty].u_int, self->channel); } if (args[ARG_duty_u16].u_int != -1) { - pwm_set_duty(args[ARG_duty_u16].u_int * 1000 / 65536, self->channel); + pwm_set_duty(args[ARG_duty_u16].u_int * 1000 / 65535, self->channel); } if (args[ARG_duty_ns].u_int != -1) { uint32_t freq = pwm_get_freq(0); @@ -164,13 +164,13 @@ static void mp_machine_pwm_duty_set(machine_pwm_obj_t *self, mp_int_t duty) { static mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { set_active(self, true); - return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel) * 65536 / 1024); + return MP_OBJ_NEW_SMALL_INT(pwm_get_duty(self->channel) * 65535 / 1024); } static void mp_machine_pwm_duty_set_u16(machine_pwm_obj_t *self, mp_int_t duty) { set_active(self, false); self->duty_ns = -1; - pwm_set_duty(duty * 1024 / 65536, self->channel); + pwm_set_duty(duty * 1024 / 65535, self->channel); pwm_start(); } diff --git a/ports/esp8266/modules/inisetup.py b/ports/esp8266/modules/inisetup.py index 20bb28e80b064..f97c238520026 100644 --- a/ports/esp8266/modules/inisetup.py +++ b/ports/esp8266/modules/inisetup.py @@ -6,7 +6,7 @@ def wifi(): import binascii - ap_if = network.WLAN(network.AP_IF) + ap_if = network.WLAN(network.WLAN.IF_AP) ssid = b"MicroPython-%s" % binascii.hexlify(ap_if.config("mac")[-3:]) ap_if.config(ssid=ssid, security=network.AUTH_WPA_WPA2_PSK, key=b"micropythoN") diff --git a/ports/esp8266/modules/port_diag.py b/ports/esp8266/modules/port_diag.py index 4eea6a6d90d36..bbd6225a616f8 100644 --- a/ports/esp8266/modules/port_diag.py +++ b/ports/esp8266/modules/port_diag.py @@ -23,8 +23,8 @@ def main(): print(esp.check_fw()) print("\nNetworking:") - print("STA ifconfig:", network.WLAN(network.STA_IF).ifconfig()) - print("AP ifconfig:", network.WLAN(network.AP_IF).ifconfig()) + print("STA ifconfig:", network.WLAN(network.WLAN.IF_STA).ifconfig()) + print("AP ifconfig:", network.WLAN(network.WLAN.IF_AP).ifconfig()) print("Free WiFi driver buffers of type:") for i, comm in enumerate( ("1,2 TX", "4 Mngmt TX(len: 0x41-0x100)", "5 Mngmt TX (len: 0-0x40)", "7", "8 RX") diff --git a/ports/mimxrt/hal/pwm_backport.c b/ports/mimxrt/hal/pwm_backport.c index 7732e0e8167c8..7815fd1dff57d 100644 --- a/ports/mimxrt/hal/pwm_backport.c +++ b/ports/mimxrt/hal/pwm_backport.c @@ -36,7 +36,7 @@ void PWM_UpdatePwmDutycycle_u16( // Setup the PWM dutycycle of channel A or B if (pwmSignal == kPWM_PwmA) { - if (dutyCycle >= 65536) { + if (dutyCycle >= PWM_FULL_SCALE) { base->SM[subModule].VAL2 = 0; base->SM[subModule].VAL3 = pulseCnt; } else { @@ -44,7 +44,7 @@ void PWM_UpdatePwmDutycycle_u16( base->SM[subModule].VAL3 = base->SM[subModule].VAL2 + pwmHighPulse; } } else { - if (dutyCycle >= 65536) { + if (dutyCycle >= PWM_FULL_SCALE) { base->SM[subModule].VAL4 = 0; base->SM[subModule].VAL5 = pulseCnt; } else { @@ -110,12 +110,8 @@ void PWM_SetupPwmx_u16(PWM_Type *base, pwm_submodule_t subModule, base->SM[subModule].OCTRL = (base->SM[subModule].OCTRL & ~PWM_OCTRL_POLX_MASK) | PWM_OCTRL_POLX(!invert); - // Switch the output on or off. - if (duty_cycle == 0) { - base->OUTEN &= ~(1U << subModule); - } else { - base->OUTEN |= (1U << subModule); - } + // Enable PWM output + base->OUTEN |= (1U << subModule); } #ifdef FSL_FEATURE_SOC_TMR_COUNT @@ -160,7 +156,7 @@ status_t QTMR_SetupPwm_u16(TMR_Type *base, qtmr_channel_selection_t channel, uin if (dutyCycleU16 == 0) { // Clear the output at the next compare reg |= (TMR_CTRL_LENGTH_MASK | TMR_CTRL_OUTMODE(kQTMR_ClearOnCompare)); - } else if (dutyCycleU16 >= 65536) { + } else if (dutyCycleU16 >= PWM_FULL_SCALE) { // Set the output at the next compare reg |= (TMR_CTRL_LENGTH_MASK | TMR_CTRL_OUTMODE(kQTMR_SetOnCompare)); } else { diff --git a/ports/mimxrt/hal/pwm_backport.h b/ports/mimxrt/hal/pwm_backport.h index 04899173e20b3..9c9f6811add28 100644 --- a/ports/mimxrt/hal/pwm_backport.h +++ b/ports/mimxrt/hal/pwm_backport.h @@ -24,7 +24,7 @@ typedef struct _pwm_signal_param_u16 uint16_t deadtimeValue; // The deadtime value; only used if channel pair is operating in complementary mode } pwm_signal_param_u16_t; -#define PWM_FULL_SCALE (65536UL) +#define PWM_FULL_SCALE (65535UL) void PWM_UpdatePwmDutycycle_u16(PWM_Type *base, pwm_submodule_t subModule, pwm_channels_t pwmSignal, uint32_t dutyCycle, uint16_t center); diff --git a/ports/mimxrt/machine_pwm.c b/ports/mimxrt/machine_pwm.c index f9ae5e22edcc3..b68521281ae6c 100644 --- a/ports/mimxrt/machine_pwm.c +++ b/ports/mimxrt/machine_pwm.c @@ -45,6 +45,8 @@ typedef struct _machine_pwm_obj_t { mp_obj_base_t base; PWM_Type *instance; + const machine_pin_obj_t *pwm_pin; + const machine_pin_af_obj_t *pwm_pin_af_obj; bool is_flexpwm; uint8_t complementary; uint8_t module; @@ -255,6 +257,8 @@ static void configure_flexpwm(machine_pwm_obj_t *self) { PWM_SetupFaultDisableMap(self->instance, self->submodule, self->channel2, kPWM_faultchannel_1, 0); } + // clear the load okay bit for the submodules in case there is a pending load + PWM_SetPwmLdok(self->instance, 1 << self->submodule, false); if (self->channel1 != kPWM_PwmX) { // Only for A/B channels // Initialize the channel parameters pwmSignal.pwmChannel = self->channel1; @@ -283,6 +287,17 @@ static void configure_flexpwm(machine_pwm_obj_t *self) { self->instance->SM[self->submodule].CTRL &= ~(PWM_CTRL_DBLEN_MASK | PWM_CTRL_SPLIT_MASK); } } else { + if (self->duty_u16 == 0) { + // For duty_u16 == 0 just set the output to GPIO mode + if (self->invert) { + mp_hal_pin_high(self->pwm_pin); + } else { + mp_hal_pin_low(self->pwm_pin); + } + IOMUXC_SetPinMux(self->pwm_pin->muxRegister, PIN_AF_MODE_ALT5, 0, 0, 0, 0U); + } else { + IOMUXC_SetPinMux(self->pwm_pin->muxRegister, self->pwm_pin_af_obj->af_mode, 0, 0, 0, 0U); + } PWM_SetupPwmx_u16(self->instance, self->submodule, self->freq, self->duty_u16, self->invert, pwmSourceClockInHz); if (self->xor) { @@ -408,12 +423,16 @@ static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, } self->center = center; } else { // Use alignment setting shortcut - if (args[ARG_align].u_int >= 0) { + uint32_t duty = self->duty_u16; + if (duty == VALUE_NOT_SET && self->duty_ns != VALUE_NOT_SET) { + duty = duty_ns_to_duty_u16(self->freq, self->duty_ns); + } + if (args[ARG_align].u_int >= 0 && duty != VALUE_NOT_SET && self->freq != VALUE_NOT_SET) { uint8_t align = args[ARG_align].u_int & 3; // limit to 0..3 if (align == PWM_BEGIN) { - self->center = self->duty_u16 / 2; + self->center = duty / 2; } else if (align == PWM_END) { - self->center = PWM_FULL_SCALE - self->duty_u16 / 2; + self->center = PWM_FULL_SCALE - duty / 2; } else { self->center = 32768; // Default value: mid. } @@ -515,6 +534,8 @@ static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args // Create and populate the PWM object. machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); + self->pwm_pin = pin1; + self->pwm_pin_af_obj = af_obj1; self->is_flexpwm = is_flexpwm; self->instance = af_obj1->instance; self->module = module; @@ -534,6 +555,8 @@ static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args // Initialize the Pin(s). CLOCK_EnableClock(kCLOCK_Iomuxc); // just in case it was not set yet + // Configure PWMX channels to pin output mode to be prepared for duty_u16 == 0. + mp_hal_pin_output(pin1); IOMUXC_SetPinMux(pin1->muxRegister, af_obj1->af_mode, af_obj1->input_register, af_obj1->input_daisy, pin1->configRegister, 0U); IOMUXC_SetPinConfig(pin1->muxRegister, af_obj1->af_mode, af_obj1->input_register, af_obj1->input_daisy, diff --git a/ports/nrf/modules/machine/pwm.c b/ports/nrf/modules/machine/pwm.c index 8145509c76284..13d824e86674f 100644 --- a/ports/nrf/modules/machine/pwm.c +++ b/ports/nrf/modules/machine/pwm.c @@ -285,7 +285,7 @@ static mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { if (self->p_config->duty_mode[self->channel] == DUTY_PERCENT) { return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel]); } else if (self->p_config->duty_mode[self->channel] == DUTY_U16) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 100 / 65536); + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 100 / 65535); } else { return MP_OBJ_NEW_SMALL_INT(-1); } @@ -301,7 +301,7 @@ static mp_obj_t mp_machine_pwm_duty_get_u16(machine_pwm_obj_t *self) { if (self->p_config->duty_mode[self->channel] == DUTY_U16) { return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel]); } else if (self->p_config->duty_mode[self->channel] == DUTY_PERCENT) { - return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 65536 / 100); + return MP_OBJ_NEW_SMALL_INT(self->p_config->duty[self->channel] * 65535 / 100); } else { return MP_OBJ_NEW_SMALL_INT(-1); } @@ -365,7 +365,7 @@ static void machine_hard_pwm_start(const machine_pwm_obj_t *self) { if (self->p_config->duty_mode[i] == DUTY_PERCENT) { pulse_width = ((period * self->p_config->duty[i]) / 100); } else if (self->p_config->duty_mode[i] == DUTY_U16) { - pulse_width = ((period * self->p_config->duty[i]) / 65536); + pulse_width = ((period * self->p_config->duty[i]) / 65535); } else if (self->p_config->duty_mode[i] == DUTY_NS) { pulse_width = (uint64_t)self->p_config->duty[i] * tick_freq / 1000000000ULL; } diff --git a/ports/pic16bit/Makefile b/ports/pic16bit/Makefile index 6d061514f9583..dda48fa3ce5f1 100644 --- a/ports/pic16bit/Makefile +++ b/ports/pic16bit/Makefile @@ -7,7 +7,7 @@ QSTR_DEFS = qstrdefsport.h include $(TOP)/py/py.mk include $(TOP)/extmod/extmod.mk -XCVERSION ?= 1.35 +XCVERSION ?= 2.10 XC16 ?= /opt/microchip/xc16/v$(XCVERSION) CROSS_COMPILE ?= $(XC16)/bin/xc16- @@ -31,7 +31,7 @@ CFLAGS += -O1 -DNDEBUG endif LDFLAGS += --heap=0 -nostdlib -T $(XC16)/support/$(PARTFAMILY)/gld/p$(PART).gld -Map=$@.map --cref -p$(PART) -LIBS += -L$(XC16)/lib -L$(XC16)/lib/$(PARTFAMILY) -lc -lm -lpic30 +LIBS += -L$(XC16)/lib -L$(XC16)/lib/$(PARTFAMILY) -lc99-elf -lm-elf -lc99-pic30-elf SRC_C = \ main.c \ diff --git a/ports/pic16bit/mpconfigport.h b/ports/pic16bit/mpconfigport.h index a2d607fb29325..d80f7edb9e546 100644 --- a/ports/pic16bit/mpconfigport.h +++ b/ports/pic16bit/mpconfigport.h @@ -93,7 +93,3 @@ typedef int mp_off_t; #define MICROPY_MPHALPORT_H "pic16bit_mphal.h" #define MICROPY_HW_BOARD_NAME "dsPICSK" #define MICROPY_HW_MCU_NAME "dsPIC33" - -// XC16 toolchain doesn't seem to define these -typedef int intptr_t; -typedef unsigned int uintptr_t; diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index 4d73c025d7dc9..62601324d6a92 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -166,7 +166,7 @@ run: $(BUILD)/firmware.elf .PHONY: test test: $(BUILD)/firmware.elf $(eval DIRNAME=ports/$(notdir $(CURDIR))) - cd $(TOP)/tests && ./run-tests.py --target qemu --device execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA) + cd $(TOP)/tests && ./run-tests.py -t execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA) $(BUILD)/firmware.elf: $(LDSCRIPT) $(OBJ) $(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) diff --git a/ports/qemu/README.md b/ports/qemu/README.md index d9108311bef92..984faf8704794 100644 --- a/ports/qemu/README.md +++ b/ports/qemu/README.md @@ -95,7 +95,7 @@ Or manually by first starting the emulation with `make run` and then running the tests against the serial device, for example: $ cd ../../tests - $ ./run-tests.py --target qemu --device /dev/pts/1 + $ ./run-tests.py -t /dev/pts/1 Extra make options ------------------ diff --git a/ports/rp2/README.md b/ports/rp2/README.md index c2f3771cd313f..911d797fe01a2 100644 --- a/ports/rp2/README.md +++ b/ports/rp2/README.md @@ -69,7 +69,6 @@ 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) diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json b/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json index c59ebc1b7b7be..899e56e905a68 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/board.json @@ -1,6 +1,6 @@ { "deploy": [ - "../deploy.md" + "deploy_xplained_pro.md" ], "docs": "", "features": [ diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/deploy_xplained_pro.md b/ports/samd/boards/SAMD21_XPLAINED_PRO/deploy_xplained_pro.md new file mode 100644 index 0000000000000..d95ab5061a30f --- /dev/null +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/deploy_xplained_pro.md @@ -0,0 +1,24 @@ +The SAMD21 Xplained Pro board is shipped without a bootloader. For use +with MicroPyhton a suitable bootloader has be be installed first. The +following procedure has been found to work and be simple: + +1. Get the bootloader from https://micropython.org/resources/firmware/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex. +2. Connect your board to the debug port. A drive with the name XPLAINED +shall appear. +3. Copy the Intel hex file of the bootloader to that drive. +4. Connect your board to the target port. A drive with the name SAMD21XPL should +appear. If not, push reset twice. Then it should appear. If that does not +happen, the bootloader was not properly installed or is not compatible. +5. Copy the MicroPython firmware, a .uf2 file, to the SAMD21 drive. When the SAMD21 +drive disappears, MicroPython is installed. + +From now on only steps 4 and 5 are needed to update MicroPython. You can use the +usual methods to invoke the bootloader, namely: + +- Push Reset twice. +- Call machine.bootloader(). +- Use the touch 1200 procedure by switching the USB baud rate to 1200 baud and back. + +At the above link above there are as well .uf2 versions of the bootloader +which one can install using steps 5. and 6. above once a .uf2 capable +bootloader is installed. diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h index 064d1ecc0d030..09bad81bb79aa 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.h @@ -2,3 +2,7 @@ #define MICROPY_HW_MCU_NAME "SAMD21J18A" #define MICROPY_HW_XOSC32K (1) + +#define MICROPY_HW_SPIFLASH (1) +#define MICROPY_HW_SPIFLASH_ID (5) +#define MICROPY_HW_SPIFLASH_BAUDRATE (12000000) diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk index f95c6549381ba..cc43c22cea588 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/mpconfigboard.mk @@ -2,3 +2,5 @@ MCU_SERIES = SAMD21 CMSIS_MCU = SAMD21J18A LD_FILES = boards/samd21x18a.ld sections.ld TEXT0 = 0x2000 + +MICROPY_HW_CODESIZE ?= 248K diff --git a/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv b/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv index 69d0c136d08ef..e5327e7916bf0 100644 --- a/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv +++ b/ports/samd/boards/SAMD21_XPLAINED_PRO/pins.csv @@ -53,3 +53,7 @@ USB_DP,PA25 SWCLK,PA30 SWDIO,PA31 +FLASH_MOSI,PB22 +FLASH_MISO,PB16 +FLASH_SCK,PB23 +FLASH_CS,PA13 diff --git a/ports/samd/machine_pwm.c b/ports/samd/machine_pwm.c index b2a383c21cba1..468e34a1653dd 100644 --- a/ports/samd/machine_pwm.c +++ b/ports/samd/machine_pwm.c @@ -54,7 +54,7 @@ typedef struct _machine_pwm_obj_t { #define PWM_CLK_READY (1) #define PWM_TCC_ENABLED (2) #define PWM_MASTER_CLK (get_peripheral_freq()) -#define PWM_FULL_SCALE (65536) +#define PWM_FULL_SCALE (65535) #define PWM_UPDATE_TIMEOUT (2000) #define VALUE_NOT_SET (-1) diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index b0dc4c5768d13..6e9a5538647bc 100644 --- a/ports/samd/machine_uart.c +++ b/ports/samd/machine_uart.c @@ -107,10 +107,17 @@ static const char *_parity_name[] = {"None", "", "0", "1"}; // Is defined as 0, // take all bytes from the fifo and store them in the buffer static void uart_drain_rx_fifo(machine_uart_obj_t *self, Sercom *uart) { + uint8_t bits = self->bits; while (uart->USART.INTFLAG.bit.RXC != 0) { - if (ringbuf_free(&self->read_buffer) > 0) { - // get a byte from uart and put into the buffer - ringbuf_put(&(self->read_buffer), uart->USART.DATA.bit.DATA); + if (ringbuf_free(&self->read_buffer) >= (bits <= 8 ? 1 : 2)) { + // get a word from uart and put into the buffer + if (bits <= 8) { + ringbuf_put(&(self->read_buffer), uart->USART.DATA.bit.DATA); + } else { + uint16_t data = uart->USART.DATA.bit.DATA; + ringbuf_put(&(self->read_buffer), data); + ringbuf_put(&(self->read_buffer), data >> 8); + } } else { // if the buffer is full, disable the RX interrupt // allowing RTS to come up. It will be re-enabled by the next read @@ -150,7 +157,12 @@ void common_uart_irq_handler(int uart_id) { #if MICROPY_HW_UART_TXBUF // handle the outgoing data if (ringbuf_avail(&self->write_buffer) > 0) { - uart->USART.DATA.bit.DATA = ringbuf_get(&self->write_buffer); + if (self->bits <= 8) { + uart->USART.DATA.bit.DATA = ringbuf_get(&self->write_buffer); + } else { + uart->USART.DATA.bit.DATA = + ringbuf_get(&self->write_buffer) | (ringbuf_get(&self->write_buffer) << 8); + } } else { #if MICROPY_PY_MACHINE_UART_IRQ // Set the TXIDLE flag @@ -274,6 +286,17 @@ void machine_uart_set_baudrate(mp_obj_t self_in, uint32_t baudrate) { static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t rxbuf_len = self->read_buffer.size - 1; + #if MICROPY_HW_UART_TXBUF + size_t txbuf_len = self->write_buffer.size - 1; + #endif + if (self->bits > 8) { + rxbuf_len /= 2; + #if MICROPY_HW_UART_TXBUF + txbuf_len /= 2; + #endif + } + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, " "timeout=%u, timeout_char=%u, rxbuf=%d" #if MICROPY_HW_UART_TXBUF @@ -287,9 +310,9 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ #endif ")", self->id, self->baudrate, self->bits, _parity_name[self->parity], - self->stop + 1, self->timeout, self->timeout_char, self->read_buffer.size - 1 + self->stop + 1, self->timeout, self->timeout_char, rxbuf_len #if MICROPY_HW_UART_TXBUF - , self->write_buffer.size - 1 + , txbuf_len #endif #if MICROPY_HW_UART_RTSCTS , self->rts != 0xff ? pin_find_by_id(self->rts)->name : MP_QSTR_None @@ -411,6 +434,14 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, } } #endif + + // Double the buffer lengths for 9 bit transfer + if (self->bits > 8) { + rxbuf_len *= 2; + #if MICROPY_HW_UART_TXBUF + txbuf_len *= 2; + #endif + } // Initialise the UART peripheral if any arguments given, or it was not initialised previously. if (n_args > 0 || kw_args->used > 0 || self->new) { self->new = false; @@ -619,7 +650,12 @@ static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t uint64_t timeout_char = self->timeout_char; uint8_t *dest = buf_in; - for (size_t i = 0; i < size; i++) { + // Check that size is even for 9 bit transfers. + if ((self->bits >= 9) && (size & 1)) { + *errcode = MP_EIO; + return MP_STREAM_ERROR; + } + for (size_t i = 0; i < size;) { // Wait for the first/next character while (ringbuf_avail(&self->read_buffer) == 0) { if (mp_hal_ticks_ms_64() > t) { // timed out @@ -633,6 +669,11 @@ static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t MICROPY_EVENT_POLL_HOOK } *dest++ = ringbuf_get(&(self->read_buffer)); + i++; + if (self->bits >= 9 && i < size) { + *dest++ = ringbuf_get(&(self->read_buffer)); + i++; + } t = mp_hal_ticks_ms_64() + timeout_char; // (Re-)Enable RXC interrupt if ((uart->USART.INTENSET.reg & SERCOM_USART_INTENSET_RXC) == 0) { @@ -647,12 +688,19 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ size_t i = 0; const uint8_t *src = buf_in; Sercom *uart = sercom_instance[self->id]; - uint64_t t = mp_hal_ticks_ms_64() + self->timeout; + uint8_t bits = self->bits; + // Check that size is even for 9 bit transfers. + if ((bits >= 9) && (size & 1)) { + *errcode = MP_EIO; + return MP_STREAM_ERROR; + } + #if MICROPY_HW_UART_TXBUF #if MICROPY_PY_MACHINE_UART_IRQ // Prefill the FIFO to get rid of the initial IRQ_TXIDLE event + // Do not care for 9 Bit transfer here since the UART is not yet started. while (i < size && ringbuf_free(&(self->write_buffer)) > 0) { ringbuf_put(&(self->write_buffer), *src++); i++; @@ -672,8 +720,16 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ } MICROPY_EVENT_POLL_HOOK } - ringbuf_put(&(self->write_buffer), *src++); - i++; + if (bits >= 9) { + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + ringbuf_put(&(self->write_buffer), *src++); + ringbuf_put(&(self->write_buffer), *src++); + i += 2; + MICROPY_END_ATOMIC_SECTION(atomic_state); + } else { + ringbuf_put(&(self->write_buffer), *src++); + i += 1; + } uart->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; // kick off the IRQ } @@ -691,7 +747,13 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ } MICROPY_EVENT_POLL_HOOK } - uart->USART.DATA.bit.DATA = *src++; + if (self->bits > 8 && i < (size - 1)) { + uart->USART.DATA.bit.DATA = *(uint16_t *)src; + i++; + src += 2; + } else { + uart->USART.DATA.bit.DATA = *src++; + } i++; } #endif diff --git a/ports/samd/mboot/LICENSE b/ports/samd/mboot/LICENSE new file mode 100644 index 0000000000000..7fffbab21e0e6 --- /dev/null +++ b/ports/samd/mboot/LICENSE @@ -0,0 +1,60 @@ +The MIT License (MIT) + +Copyright (c) Microsoft + +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. + +Third Party Programs: The software may include third party programs that +Microsoft, not the third party, licenses to you under this agreement. +Notices, if any, for the third party programs are included for your +information only. + + + +Otherwise, where noted: + +/* ---------------------------------------------------------------------------- + * SAM Software Package License + * ---------------------------------------------------------------------------- + * Copyright (c) 2011-2014, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following condition is met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + diff --git a/ports/samd/mboot/README.md b/ports/samd/mboot/README.md new file mode 100644 index 0000000000000..1695f0a891126 --- /dev/null +++ b/ports/samd/mboot/README.md @@ -0,0 +1,5 @@ +The SAMD Xplained Pro bootloader is built using the files from the +repository https://github.com/adafruit/uf2-samdx1. The repository +includes the LICENSE file https://github.com/adafruit/uf2-samdx1/LICENSE +and a README https://github.com/adafruit/uf2-samdx1/README.md with +build instructions. diff --git a/ports/samd/mboot/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex b/ports/samd/mboot/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex new file mode 100644 index 0000000000000..df8d5afd19c2b --- /dev/null +++ b/ports/samd/mboot/bootloader-xplained-pro-v3.16.0-15-gaa52b22.hex @@ -0,0 +1,513 @@ +:10000000E02D002069020000650200006702000088 +:1000100000000000000000000000000000000000E0 +:100020000000000000000000000000006502000069 +:100030000000000000000000650200005D020000FA +:100040006502000065020000650200006502000014 +:100050006502000065020000650200006502000004 +:1000600065020000650200006502000065020000F4 +:1000700065020000650200006502000065020000E4 +:1000800065020000650200006502000065020000D4 +:1000900065020000650200006502000065020000C4 +:1000A00065020000650200006502000065020000B4 +:1000B00000000000074B1A7DD207FCD52022FF323A +:1000C0001A83054A4008D8611A801A7DD207FCD5E8 +:1000D0007047C0460040004102A5FFFF70B5802573 +:1000E0000400ED02AC4200D370BD20000134FFF7E4 +:1000F000E1FFFF34F6E7002330B59A4200D130BD6E +:100100009C000D5901330551F7E70000F0B580253B +:10011000104C114E6368114FAB436360002A00D14D +:10012000F0BD1300102A00D91023D21A2680257D95 +:10013000ED07FCD59B009C460023CD58C5500433E9 +:100140006345FAD1C018C9182780237DDB07FCD589 +:10015000E4E7C0460040004144A5FFFF04A5FFFFBF +:10016000802270B5002304000D005200E958E058C9 +:10017000814203D104339342F8D170BD2000FFF7D0 +:1001800099FF402229002000FFF7C0FFF5E700009B +:1001900010230249CA681A42FCD070470008004088 +:1001A0001E2270B52149224C4B6893431C3A1343DD +:1001B0004B60A284FFF7ECFF1E4B1B689B0E3F2B8E +:1001C00000D1203B00251C4A9B0213431B4AE262DC +:1001D000A362A584FFF7DCFF194BA384FFF7D8FFC8 +:1001E0000223A28C1343A384FFF7D2FF154B9D601B +:1001F0005A7852B2002AFBDB134A5A605A7852B23C +:10020000002AFBDBC021114A114B12485360036ADC +:1002100009061B021B0A0B4303620023936007338A +:10022000136030220C4B1A6070BDC0460040004184 +:100230000008004024608000FF01000080BB0A1C11 +:1002400024050000000C00400007030010E000E05F +:10025000E703000000ED00E00000002010B500F012 +:1002600007F910BDFEE7FEE72449254870B5814235 +:100270000AD0244BC41E0022A34203D303331A1A0C +:100280009208920001F058FC1F48204BC11E00222A +:10029000994203D803331A1A92089200002101F000 +:1002A00054FCFF221A4B032193431A4A0C259360F6 +:1002B000022208243026184B18485A62C3788B4310 +:1002C0001343C370C378AB432343C370144B987B71 +:1002D000B0430600202030439873987BA843044322 +:1002E0009C73987B884302439A7380230D4A51681C +:1002F0000B43536000F07AFEFEE7C0466C1D000021 +:1003000000000020D0010020D0010020DC0D0020E2 +:100310000000000000ED00E0FC70004100500041D2 +:100320000048004100400041F7B5134D01906B8833 +:10033000002B18D1082738001A0292B214B2A44632 +:1003400066465400A2B2002E02DA0C4A5440A2B211 +:10035000013880B20028F1D158002A52802201339E +:1003600052009342E7D1019A0B0A53405800285A91 +:100370000902484080B2FEBDD001002021100000DB +:10038000074B1B685843074B1A789523002A00D166 +:100390001233012243439A4200D370470132FAE7F5 +:1003A00000000020D003002070B505001C240020B0 +:1003B0000F260B00E3403340002A09D1002B07D160 +:1003C000002C0BD0002809D1043C231DF1D170BDB5 +:1003D000092B03DD37332B540130F5E73033FAE7CF +:1003E000054B064A1A60BFF34F8F054B054ADA608A +:1003F000BFF34F8FFEE7C046FC7F0020EF6926F079 +:1004000000ED00E00400FA0510B50B4A1368002B5C +:100410000ED10A4911600A490A6801320A60094985 +:100420000868002806D0824204D30B60FFF7D8FF8B +:10043000013B136010BDC046E4030020DC05000052 +:10044000E0030020D8030020054B064A1A60BFF3E2 +:100450004F8F054B054ADA60BFF34F8FFEE7C0466A +:10046000FC7F0020EF6916F000ED00E00400FA05C3 +:10047000012210B5194B1A491A701A4A1368013330 +:1004800013600A68002A0CD017481018834203D161 +:1004900080241648E4050460934201D100230B60D8 +:1004A00010BD1348DBB20278002B10D180210F4B16 +:1004B000C905196011000A39C9B20E4BF02902D9D9 +:1004C0001978494219701B78D2180270E8E79342F4 +:1004D000E6D18022084BD2051A60E1E7D003002064 +:1004E000DC030020D403002018FCFFFF98440041E7 +:1004F000050000200400002094440041074B084AF6 +:100500001B681168994207D2FA21C9005B18136071 +:100510008022044BD2051A607047C046D4030020E5 +:10052000DC03002094440041044B80221900D205D2 +:10053000883198330A601A607047C0460044004111 +:1005400070470000202370B5354A0F20D1690B4356 +:10055000D361012233490B7813430B70324B197866 +:100560008143197006211C78214319702F490C789A +:1005700022430A701A7802401A70602219780A43DE +:100580001A702B4A2B4B53805378DB09FCD1012284 +:10059000294B19780A431A709A78D207FCD41F2085 +:1005A00026490A68520B0240824200D105221C8D66 +:1005B000234D024092012C4022431A850A68920C76 +:1005C00002401F2A00D1023A1F24188D2240A04366 +:1005D000024307201A850A68D20D0240824200D1E8 +:1005E0000322198D02401748120301400A437F215C +:1005F0001A851A7814480A401A70042219780A4396 +:100600000C211A7058621A898A431A811A890B3987 +:100610008A431A8180220021520001F096FA70BDAF +:1006200000040040584400413C440041594400410A +:10063000000C004006400000005000412460800093 +:100640003FF8FFFFFF8FFFFF74060020F7B51C0087 +:10065000314B06005B6A0F0015000193002C05D199 +:10066000443454432D4BE418FFF7CEFE63782278D0 +:10067000934210D2D51ABD4200D93D00002E08D0B9 +:10068000211DC9182A00300001F056FA63785B1961 +:1006900063702800FEBD019BA1786A01D318002970 +:1006A00011D1211D196059681D48890B89035960B2 +:1006B0005968014059601B49551840212879014368 +:1006C00029710121A1701449002551180191164981 +:1006D0005218127AD207DCD55D68A278EDB2257087 +:1006E000022A04D12A001968201D01F025FABD4212 +:1006F00000D93D00002E0CD02A0030006570211D6D +:1007000001F01AFA0122019BFF331A720023A37031 +:10071000BFE76670F6E7C046005000413004002095 +:10072000FF3F00F000510041FF50004110B5002391 +:10073000FFF78CFF10BDF8B505000C0016001F0078 +:10074000002C00D1F8BD210028003B003200FFF74B +:100750007DFF241A2D18F3E7F8B50D001F49560147 +:100760004C6A3419002B1BD063695B005B08636122 +:1007700020616369AA049B0B920C9B031343636182 +:100780006369174A174813406361321802238021B6 +:10079000160013729171327A1A42FCD02800F8BD0B +:1007A000012163695B005B0F03339940A94209D8BB +:1007B000043A531E9A416369D1075B005A080A4301 +:1007C0006261D5E744275743074A0437BF18010041 +:1007D0002A00380001F0B0F93800C9E700500041A4 +:1007E000FF3F00F0FF5000413004002010B500230F +:1007F000FFF7B2FF10BD000010B5054A0B001188CD +:10080000994200D919000022FFF7F0FF10BDC04641 +:100810007407002007B500216B460A00D81D01703F +:10082000FFF7E4FF07BD0000F0B5B94C9BB0FFF740 +:10083000EBFDA38B08211A000A400392B54A0B4234 +:100840002ED080234020A183A372C024093151708F +:1008500090715371B04BB14D5968A40529402143A3 +:100860005960596929400C43AD495C6104311960F4 +:10087000AC49AD4C196159680C4080218902214373 +:1008800059605968890B8903596050710022A74B40 +:100890001A70A64B1878431E9841C0B21BB0F0BD29 +:1008A0001020137A0342F4D09D4D10726D899C483C +:1008B000AC46664680799E4D02909948984BC78811 +:1008C000C0792E80402501909B889548994E0089DB +:1008D0005571B34200D1B0E136DC81246400A342FB +:1008E00000D1BFE115DC822B00D190E106DC803B1A +:1008F000012B00D886E120239371CAE780214900AB +:100900008B42F8D00221FF318B42F4D1FFF782FFF6 +:10091000BFE7B02189008B4200D16DE10EDC0139C7 +:10092000FF398B42E7D0A23B8349FF3B0B42E2D128 +:10093000002308210893099308A8C4E0C021890076 +:100940008B42D8D07D49DFE77D488342DED000DD91 +:1009500096E07C498B4212DC01398B4200DBACE033 +:1009600079498B42E4D06031FF318B42C3D1FFF72C +:1009700051FF80235B423B43DBB2A37289E78821AE +:100980006A4809018B4200D195E080318B42B2D197 +:100990000770FFF73FFFA02303225B00E254C02251 +:1009A0005D4B5E49586C92050840104358644620E0 +:1009B000FF302554654830271864902040002754A4 +:1009C000586B8026084010435863922040002654FC +:1009D0005F481863B02040002554586F084058679E +:1009E000B220400026543C307D3E2654FC388446DC +:1009F0009C44604606680E4016430660A626FF36F5 +:100A0000A5551E005348A0363060C0267600A75575 +:100A10001F0094373E6880200E4016433E60C22679 +:100A20007600A0551E004C4F90363760E0264427D4 +:100A30007600A7550436A555A055C01984469C4498 +:100A4000604606680E4016430660D42084469C44E7 +:100A5000604606680E4016430660F0268020760049 +:100A6000A7550436A555A0551C00E4342068F4337E +:100A7000084010432060186801400A431A6008E7E4 +:100A80003648834200D122E10ADC35498B4200D14D +:100A90003CE7344934480B40834200D136E72AE72B +:100AA00032498B4200D131E731498B4200D022E7F5 +:100AB000002308A80380012105E080235B009F42FA +:100AC00004D112212B48FFF797FEE2E680239B001A +:100AD0009F4202D199212848F5E7019B032B78D149 +:100AE000029B032B00D906E708AC48220021200016 +:100AF00001F02BF8019B6370029B002B3ED1092271 +:100B000004332370A270E37020002178DBE7C04635 +:100B100000500041FF50004174060020FFFFFF8F8E +:100B2000E803002034040020FF3F00F02C040020E4 +:100B30007407002002030000FFFE00000103000014 +:100B40002109000081060000A1030000BC04002070 +:100B5000780400208805002044050020A121000021 +:100B600021200000FFFEFFFF210A000021220000DB +:100B7000A1FE0000981B00004400002004AE3200DB +:100B8000544B23CB23C200252F001B681360AB00FE +:100B9000514AF358D01919680122FFF705FC0135B5 +:100BA0003F18042DF3D100234B4AD3554B4B4C4AED +:100BB0009B799B009D58280000F0CFFF2300013057 +:100BC000400020702A7802330135002A9CD01A7028 +:100BD000F8E7019B0F2B02D13921424873E7019BB3 +:100BE000212B02D1092140486DE7019B222B00D027 +:100BF00081E621213D4866E7072800D07BE6AA214F +:100C00003B4860E7039B08A8022103805BE708429A +:100C100000D070E60F235022034008335B011042DE +:100C200006D0344A9B189B799B06DB0F08A8EBE79C +:100C3000304A9B189B79DB06F7E70F24030023401B +:100C4000204200D157E60140394300D053E6294AFB +:100C500008335B019B18020602D520225A7155E623 +:100C60001022FBE70F2403002340204200D142E67C +:100C70000140394300D03EE65B01020613D51E4910 +:100C80001C4A9A185B182021D879084200D13DE609 +:100C900059711B7A2B4200D138E613004022FF33F2 +:100CA0001A723E3ADAE710201349124A9A185B1872 +:100CB000D979014200D129E658711B7A01180B42FB +:100CC00000D123E61300FF3301221972C6E70B4857 +:100CD000F9E6C046881B000050060020E80300200B +:100CE000D01B000008000020E0000020AC1B00002A +:100CF000F400002000500041FF500041EC000020B3 +:100D000010B5FFF71FFC0022034B1A700223034AA1 +:100D100011780B43137010BD2C04002000500041CB +:100D200010B50C000122FFF761FD200010BD10B5C9 +:100D30000C000122FFF75AFD200010BD70B5040021 +:100D40000D00FFF771FD03000020834204D0022252 +:100D500029002000FFF7EAFC70BD70B505000C000B +:100D6000FFF762FD002807D000230222210028009F +:100D7000FFF7E1FC200070BD0400FBE730B5002365 +:100D80002025934200DB30BD0C78002C03D00131CC +:100D9000C4540133F5E72C00FAE70000F8B58022CF +:100DA0000C00050000212000920000F0CEFE002D76 +:100DB0000CD13E2220004A4900F0BEFEFF235522FE +:100DC0005B00E254474B9218E254F8BD7E2D23D8C5 +:100DD0006E1E3E2E01D9403D2E00002E12D08025E1 +:100DE0003302581C404EFF30ED001A1F591CAA4216 +:100DF00003D2B3420ED08BB223800B000234814267 +:100E0000F3D1E2E7F0230922FF212370601C00F0F8 +:100E10009CFEE4E7354BEFE7822D31D87F2DD4D10E +:100E20002F490B2220002B31FFF7A8FF8027282312 +:100E30002F4EE3727D3D3F03F0683B0020340028D5 +:100E400002D000F08AFE03001A0A237762771A0C98 +:100E50001B0EE3772B0A3100A277A5760B22E376EF +:100E60002000FFF78BFF67224D2301355242ADB2C0 +:100E700022746374227663761036052DDCD1A4E7E4 +:100E80002B00833B012B0CD8194A1B01D318DD68BA +:100E9000280000F062FE29000200200000F04CFE55 +:100EA00093E78023853D2902DB02994200D38CE73A +:100EB000104B114A2360114B20006360FE235B003E +:100EC000E2508023DB00A361802380229B01A3608A +:100ED0000B4B52006561E1602261E3612030DDE788 +:100EE000E01B0000FF01000003040000FFFF000002 +:100EF000881C00005546320A306FB10A57515D9E7A +:100F0000882BED6870B516001D000A682F4B0C0089 +:100F10009A4252D12E4B4A689A424ED1FE2252003A +:100F20002C4B8A589A4248D18B689A0403D52A4A96 +:100F3000C969914241D1DB0712D4802322695B0049 +:100F40009A420DD1E068C3B2002B09D1F822234B9D +:100F50009202C318934203D221002031FFF700F917 +:100F6000002D2BD0A369002B28D029681C4A8B4266 +:100F700006D0934201D8002901D001235B422B60A7 +:100F80006369934219D8072201211A409140DB0876 +:100F9000EB181A7AC8B2114204D1696802430131D0 +:100FA0001A7269606A682B689A4206D3002E04D1CF +:100FB0000C4B1B681E330C4A136070BD002EFCD115 +:100FC000084B1B682D33FF33F5E7C0465546320A00 +:100FD00057515D9E306FB10A882BED6800E0FFFF2E +:100FE00063040000E0030020D8030020F8B5624D40 +:100FF000AB68002B00D0FEE70221604B5A6B8A439E +:101000005A63DA681205FCD55D4A5A630222596BAD +:101010000A435A63DA689205FCD5DA685205FCD4B3 +:1010200002215A6B8A435A63DA681205FCD50822FA +:10103000596B0A435A630222596B0A4351495A6356 +:101040000B68013321D120224F4BFF32188B024312 +:101050001A8358684D4A02435A604D4ADA614D4A34 +:101060001A801A7DD207FCD54B4A1A801A7DD20706 +:10107000FCD54A4A0A604A4A4A4911604A4A1A80DB +:101080001A7DD207FCD5FFF7DFF90023474A13701A +:10109000D379DB09FCD1FFF747FA454B1E68454B76 +:1010A000F218F8239B029A4213D8434B434A19681B +:1010B000434C444B914232D11978434AC90702D478 +:1010C000216891422BD0414B22601B68404A323349 +:1010D0001360FFF765F8BFF35F8F62B6FFF710FE8E +:1010E0004020FFF72DFA80270A230126394DFF0102 +:1010F0002B70FFF799FB384C00280AD02378DAB21E +:10110000002B05D1324B38001A60FFF719FA2E7008 +:1011100026702378002B25D000F0D6FAFCE71B7848 +:10112000DB07DA0F002B0DD00023802223602B4B2E +:10113000D2051A6080239B011A6882F30888AB608D +:101140003047C6E72168264B994201D12260C0E7AB +:1011500021681D4A9142E7D0FA2023604000FFF742 +:101160000FF9E1E72378002BC3D1FF33C046013BE1 +:10117000002BFBD1BDE7C04600ED00E000080040B9 +:10118000040027000040800000400041800004006F +:101190000020400005A5FFFF44A5FFFFFAC7E0D8E7 +:1011A000044080005DFCFFFF06A5FFFF001000402B +:1011B0000420000000E0FFFFB42000007CB0EE87B8 +:1011C000FC7F002038040040EF6926F0E003002097 +:1011D000D80300200400002076070020944400413A +:1011E000EF6916F010B5054C12220021200000F026 +:1011F000ACFCF0232370E63BE37110BDB309002083 +:1012000010B5FFF7EFFF0022014B1A7310BDC04667 +:101210009E010020F7B580270400160000250191EB +:10122000BF00A368AB4200D8F7BD3900A278019B8C +:10123000E068FFF780FA80235B01E81801223300A1 +:10124000E168FFF75FFE0135EBE770B513000600BC +:101250000C00150000200A000121FFF7F7F9002318 +:10126000984206D02B0022001F213000FFF7EEF934 +:101270000123180070BD000010B504220D210248A2 +:10128000FFF7B4FA10BDC0469E01002010B50B4C0C +:101290000B4861792379A27909021943E379120491 +:1012A00011431B060B431A0A037142711A0C1B0EE1 +:1012B0008271C371FFF7E0FF10BDC04694090020A2 +:1012C0009E01002070B504220D00FFF78FFA154C27 +:1012D00085420FD0FFF786FF012304222373124BB0 +:1012E0009A700022DA701A715A719A711A735A73CD +:1012F000FFF7CCFFFFF784FF637A217A1B020B43D1 +:10130000A17AE27A0904120619431143491B0B0A18 +:10131000217263720B0C090EA372E172FFF7B6FF24 +:1013200070BDC0469E010020B309002070B5184C66 +:10133000142205000021200000F007FC154A002DB2 +:101340001FD021000823D07D08313F26527C324037 +:101350001C2A01D0B24205D10F4A0C330A80052263 +:10136000DBB2CA70191C834200D9011CC9B2002D1E +:101370000BD0023B9BB21B0223802000FFF7A2FF91 +:1013800070BD0423D07C211DDFE7013B2370F4E70F +:1013900078090020940900201C0A0000F0B5040020 +:1013A00000252A4B85B0597C03AAD170997C917095 +:1013B000D97C5170197D1170997D02AAD170DB7DA5 +:1013C000937053880193019BAB4205D8FFF718FF38 +:1013D000FFF75CFF05B0F0BDFFF726FA0028F9D053 +:1013E000039B1B4FEE18002C1FD039003000FFF775 +:1013F000D5FC8021042238008900FFF7F7F9154A4F +:101400000135507A137A917A00021843D37A09048D +:1014100008431B06104903435B18190A13725172E3 +:10142000190C1B0E9172D372CDE780212300380076 +:1014300005228900FFF77FF9220039003000074BB1 +:10144000FFF760FDFFF75AF8D9E7C046940900207E +:10145000780700209E01002000FEFFFFC809002041 +:101460007FB500F02FFB444E002205213000FFF72E +:10147000ECFE002849D0737A327AB57A1B021A43FF +:10148000F37A2D041B0615433C4C1D432B0A637253 +:101490002B0CA3722B0E2572E372F07B2F2827D81A +:1014A000192815D8032827D012282FD0002840D07B +:1014B000FFF798FE012305222373314B9A70002217 +:1014C000DA701A715A719A711A7320325A7332E0B3 +:1014D0001A381528ECD800F01BFB282CEBEB2CEB72 +:1014E000EBEBEB3FEB31EBEB39EB3DEBEBEBEB2CD1 +:1014F0005A28DDD1012019E0F37C191C122B00D9E8 +:1015000012211F48C9B2FFF7DDFE7FBD1D4C1E49E9 +:10151000200018220830FFF731FCF37C191C242B23 +:1015200000D924212000C9B2EDE70020FFF7FEFE1C +:10153000EBE7FFF765FEFFF7A9FEE6E78023134818 +:101540009B024360124B08210360DCE70120FFF798 +:1015500025FFDAE70020FAE70C260E49320001A841 +:1015600000F0EAFAB54204D9002326726372A3722E +:10157000E3720C2101A8C6E7940900209E01002017 +:10158000B3090020AC010020721B00008C09002070 +:1015900000003E7F231D000072B6BFF35F8F044B37 +:1015A000044A9A829A830022034B9A607047C0468D +:1015B00000500041FF03000000ED00E0F0B5C5B0B1 +:1015C0000DAF0400FFF7E8FF44220021380000F0CF +:1015D000BCFA982200211EA800F0B7FA02230F21BE +:1015E000BB70A37862780B40A370264B0A4005932A +:1015F000636805AD06930023E16807932248627093 +:101600002B7301221EABFFF77DFC390020001EAAC0 +:10161000FFF700FE280001230D216278FFF79CF8F8 +:10162000002505AE3A003000A178FFF70EFE002835 +:101630001ED0144B01AD0193069B6B60079BAB6002 +:1016400000232B73F37B002BE4D02A2B17D16B469E +:10165000B27D3900DA70F27D20009A705E881EAA91 +:10166000A660FFF7D7FDAB6876029E1BAE60D1E7A0 +:10167000064B9D4201D9FEF7B3FE0135D1E7FEF7D7 +:10168000E3FEC7E753425355FF0F0000F824010063 +:1016900030B5EFF30883054C2360036883F30888B3 +:1016A0004568A847236883F3088830BDC00A002036 +:1016B00007B5010001226846FEF776FE082168465C +:1016C000FFF72EFB07BD0000F8B500237A227F4C00 +:1016D00023607F4B1A70FFF7C3FE7E4D4021280028 +:1016E000FFF72CFB7C4B186000232B54984201D051 +:1016F000FEF704FF0022794B1D60794B1A60784891 +:10170000754B02681F68BA42E5D2744E31680B7897 +:10171000FF2B37D0734D232B00D0B4E06C4B1B78DC +:10172000532B34D12B680132013102603160BA1A77 +:101730009A4200D91A006C4D20682A6000F0FCF92A +:10174000674829680368654ACB18013B0360106845 +:101750004B1EC3181360FF23644D29701940614B61 +:101760001B688B4203D92068591AFFF7F6FAC04666 +:101770007A22574B1A7000225A4B1A60574A136844 +:1017800001331360564A136801331360B7E7522BD5 +:1017900004D129682068FFF7CAFAE9E74F2B03D183 +:1017A0002B6822681370E3E7482B03D12B6822686B +:1017B0001380DDE7572B0AD14D4B22689A4202D1A4 +:1017C0000020FEF7BDFE23682A681A60D0E76F2B61 +:1017D00004D101212068FFF7A3FAC9E7682B05D1DE +:1017E000022123681B882B602800F4E7772B04D1A3 +:1017F000236804211B682B60F6E7472B09D1286872 +:10180000FFF746FF3B4B1B78002BB1D001213A4834 +:10181000E1E7542BACD04E2BAAD0562B02D12A2173 +:101820003648D8E7582B05D12868FEF757FC032126 +:101830003348D0E7592B0DD12A682068314B002A54 +:1018400003D1186003213048C5E719689208FEF7F4 +:101850005DFCF7E75A2B8BD12F6800252668F71916 +:10186000B74209D101212948FFF75AFA2800FFF7AA +:101870001FFF03212648AEE729003078FEF754FD0C +:1018800001360500ECE71A00303AD1B2092904D834 +:101890002B681B0113432B6070E71A00413A052A9D +:1018A00003D82A68373B1201F4E71A00613A052A87 +:1018B00003D82A68573B1201ECE72C2B03D12B6885 +:1018C00023600023E7E7024A1370FAE7BC0A00200E +:1018D000610A0020680A0020B40A0020B80A00202B +:1018E000AC0A0020640A0020C80A0020B00A0020C8 +:1018F0000CED00E0600A00202F1D00003F1D0000DD +:10190000311D0000C40A0020351D0000391D0000F3 +:101910003B1D00000300F7B5473304001A7840214F +:1019200003000020FEF792FE002801D10020FEBD3A +:1019300000232370237901930423E356002BF5DB66 +:101940003F262000019FA51DEB8F37404830C0186F +:101950003A00611D00F0F0F8E88F019BC01980B2D9 +:10196000B34301D1E887E1E70023EB87DFE7F0B578 +:1019700006000C001F0093B001923F25AC4203DC2F +:101980002500002F00D140373B0002AA2B431370E3 +:1019900002AB31002A00581C00F0CEF86B46402103 +:1019A0001A7902A80123FEF7D7FE7619641BE4D149 +:1019B00013B0F0BD030010B547331A78043100238B +:1019C0004830FFF7D4FF10BDF7B50400FFF7A2FFC2 +:1019D000002842D0230048339A88A06C1A80002245 +:1019E00001385A80082865D800F09CF8150523278F +:1019F000252A483F3800314E300000F0AEF805008F +:101A0000200031002A004C3000F096F82900200018 +:101A1000FFF7D0FF21E00123E364FF332365802338 +:101A2000DB006365A0235B00A365254B1421E36500 +:101A3000EDE7FEF7D5FC0021E9E7FEF705FDFAE743 +:101A400020000021FFF7B6FF8023206D9B01984204 +:101A500003D321005431FEF783FBF7BD2100626DF3 +:101A6000206D5831FEF747FBE5E72000656D216DDD +:101A70002A004C30FEF73FFBA900C8E70026636D43 +:101A8000276D0193019BB34201DC5900BFE73D0084 +:101A900000212B001878FEF747FC7B1C0135FF3333 +:101AA00001009D42F5D1230072004C332F00985263 +:101AB0000136E7E701225A80BDE7C046B81C0000A6 +:101AC000882BED6807480622030010B547331A70CB +:101AD000FFF77AFF04480722030047331A70FFF725 +:101AE00073FF10BDCC0A0020540C002010B5E2B0EA +:101AF0000400FFF751FDC42200216846520000F0A7 +:101B000024F847236B441C706846FFF75DFFFBE732 +:101B100002B4714649084900095649008E4402BC86 +:101B20007047C04602B4714649084900095C490043 +:101B30008E4402BC7047C046002310B59A4200D1C3 +:101B400010BDCC5CC4540133F8E703008218934203 +:101B500000D1704719700133F9E70023C25C0133EB +:101B6000002AFBD1581E70474D6963726F63686924 +:101B7000700053414D4432312058706C61696E657C +:101B8000642050726F0000000CA0800040A0800014 +:101B900044A0800048A0800012011002EF02014022 +:101BA000D804022401420102030100000697FF0944 +:101BB00001A101150026FF00750895400901810269 +:101BC00095400901910295010901B102C000000090 +:101BD00000000000681B0000721B0000500600207F +:101BE000EB3C905546322055463220000201010060 +:101BF0000240007E3EF83F000100010000000000AE +:101C0000000000008000294200420053414D443250 +:101C10003158504C000046415431362020203C21A0 +:101C2000646F63747970652068746D6C3E0A3C68FB +:101C3000746D6C3E3C626F64793E3C736372697094 +:101C4000743E0A6C6F636174696F6E2E7265706C9E +:101C5000616365282268747470733A2F2F777777E1 +:101C60002E7078742E696F2F22293B0A3C2F7363E4 +:101C7000726970743E3C2F626F64793E3C2F6874C9 +:101C80006D6C3E0A00000000494E464F5F554632DB +:101C900054585400B81C0000494E44455820202098 +:101CA00048544D001E1C000043555252454E5420CE +:101CB000554632000000000055463220426F6F74D6 +:101CC0006C6F616465722076332E31362E302D3183 +:101CD000352D6761613532623232205346485752A2 +:101CE0004F0D0A4D6F64656C3A2053414D443231BB +:101CF0002058706C61696E65642050726F0D0A42E5 +:101D00006F6172642D49443A2053414D4432314A47 +:101D10003138412D58706C61696E65642D50726F59 +:101D20000D0A000000000800003E800200020006CC +:101D300000580A0D00590A0D005A00230A0D0076BA +:101D4000312E31205B41726475696E6F3A58595A71 +:101D50005D205365702032382032303234203135E6 +:101D60003A35353A34340A0D000000000100000015 +:101D700001C80000050F3900021810050038B60828 +:101D800034A909A0478BFDA0768815B6650001012E +:101D9000001C100500DF60DDD88945C74C9CD2656A +:101DA0009D9E648A9F00000306AA000200000000B6 +:101DB000090299000501008032080B0002020201AD +:101DC00000090400000102020100052400100104C2 +:101DD00024020605240600010524010301070583EA +:101DE000030800FF09040100020A00000007058142 +:101DF0000240000007050202400000090402000240 +:101E0000080650000705840240000007050502404F +:101E10000000090403000203000000092100010082 +:101E20000122210007058603400001070506034043 +:101E300000010904040002FF2A010007058703408E +:101E4000000107050703400001000000090403002A +:101E5000020300000300000000C2010000000800AF +:101E60000A00000000000306AA00080002000400A7 +:101E7000A0001400030057494E55534200000000D3 +:101E80000000000000008400040007002A00440055 +:101E90006500760069006300650049006E0074000B +:101EA0006500720066006100630065004700550030 +:101EB000490044007300000050007B0039003200EC +:101EC0004300450036003400360032002D00390052 +:101ED0004300370037002D0034003600460045002F +:101EE0002D0039003300330042002D003300310053 +:101EF00043004200390043003500420042003300F5 +:101F0000420039007D00000000005342535500009C +:101F1000000000000000000000800202200000001D +:101F200000000000000000000000000000000000B1 +:101F30000000000000000000312E303000000000E2 +:101F40000000000000000000000000000000000091 +:101F50000000000000000000000000000000000081 +:101F60000000000000000000000000000000000071 +:101F70000000000000000000000000000000000061 +:101F80000000000000000000000000000000000051 +:101F90000000000000000000000000000000000041 +:101FA0000000000000000000000000000000000031 +:101FB0000000000000000000000000000000000021 +:101FC0000000000000000000000000000000000011 +:101FD0000000000000000000000000000000000001 +:101FE00000000000000000000000000000000000F1 +:101FF00000000000ED1A0000BD150000B81C000034 +:00000001FF diff --git a/ports/samd/samd_flash.c b/ports/samd/samd_flash.c index ef0de7b6fb585..f68bdf140f490 100644 --- a/ports/samd/samd_flash.c +++ b/ports/samd/samd_flash.c @@ -103,7 +103,8 @@ static mp_obj_t samd_flash_version(void) { static MP_DEFINE_CONST_FUN_OBJ_0(samd_flash_version_obj, samd_flash_version); static mp_obj_t samd_flash_readblocks(size_t n_args, const mp_obj_t *args) { - uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + samd_flash_obj.flash_base; + samd_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + self->flash_base; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); if (n_args == 4) { @@ -118,7 +119,8 @@ static mp_obj_t samd_flash_readblocks(size_t n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(samd_flash_readblocks_obj, 3, 4, samd_flash_readblocks); static mp_obj_t samd_flash_writeblocks(size_t n_args, const mp_obj_t *args) { - uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + samd_flash_obj.flash_base; + samd_flash_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t offset = (mp_obj_get_int(args[1]) * BLOCK_SIZE) + self->flash_base; mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); if (n_args == 3) { diff --git a/ports/samd/samd_spiflash.c b/ports/samd/samd_spiflash.c index cd4f10640dbf1..8ada7e96092c2 100644 --- a/ports/samd/samd_spiflash.c +++ b/ports/samd/samd_spiflash.c @@ -45,6 +45,7 @@ const uint8_t _COMMANDS_32BIT[] = {0x13, 0x12, 0x21}; // READ, PROGRAM_PAGE, ER #define COMMAND_JEDEC_ID (0x9F) #define COMMAND_READ_STATUS (0x05) +#define COMMAND_WRITE_SR1 (0x01) #define COMMAND_WRITE_ENABLE (0x06) #define COMMAND_READ_SFDP (0x5A) #define PAGE_SIZE (256) @@ -88,7 +89,7 @@ static void wait(spiflash_obj_t *self) { mp_hal_pin_write(self->cs, 0); spi_transfer((mp_obj_base_t *)self->spi, 2, msg, msg); mp_hal_pin_write(self->cs, 1); - } while (msg[1] != 0 && timeout-- > 0); + } while ((msg[1] & 1) != 0 && timeout-- > 0); } static void get_id(spiflash_obj_t *self, uint8_t id[3]) { @@ -123,6 +124,17 @@ static void write_enable(spiflash_obj_t *self) { mp_hal_pin_write(self->cs, 1); } +// Write status register 1 +static void write_sr1(spiflash_obj_t *self, uint8_t value) { + uint8_t msg[2]; + msg[0] = COMMAND_WRITE_SR1; + msg[1] = value; + + mp_hal_pin_write(self->cs, 0); + spi_transfer(self->spi, 2, msg, NULL); + mp_hal_pin_write(self->cs, 1); +} + static void get_sfdp(spiflash_obj_t *self, uint32_t addr, uint8_t *buffer, int size) { uint8_t dummy[1]; write_addr(self, COMMAND_READ_SFDP, addr); @@ -155,27 +167,36 @@ static mp_obj_t spiflash_make_new(const mp_obj_type_t *type, size_t n_args, size mp_hal_pin_write(self->cs, 1); wait(self); - // Get the flash size from the device ID (default) uint8_t id[3]; get_id(self, id); + bool read_sfdp = true; + if (id[1] == 0x84 && id[2] == 1) { // Adesto self->size = 512 * 1024; - } else if (id[1] == 0x1f && id[2] == 1) { // Atmel / Renesas + } else if (id[0] == 0x1f && id[1] == 0x45 && id[2] == 1) { // Adesto/Renesas 8 MBit self->size = 1024 * 1024; + read_sfdp = false; + self->sectorsize = 4096; + self->addr_is_32bit = false; + // Globally unlock the sectors, which are locked after power on. + write_enable(self); + write_sr1(self, 0); } else { self->size = 1 << id[2]; } // Get the addr_is_32bit flag and the sector size - uint8_t buffer[128]; - get_sfdp(self, 0, buffer, 16); // get the header - int len = MIN(buffer[11] * 4, sizeof(buffer)); - if (len >= 29) { - int addr = buffer[12] + (buffer[13] << 8) + (buffer[14] << 16); - get_sfdp(self, addr, buffer, len); // Get the JEDEC mandatory table - self->sectorsize = 1 << buffer[28]; - self->addr_is_32bit = ((buffer[2] >> 1) & 0x03) != 0; + if (read_sfdp) { + uint8_t buffer[128]; + get_sfdp(self, 0, buffer, 16); // get the header + int len = MIN(buffer[11] * 4, sizeof(buffer)); + if (len >= 29) { + int addr = buffer[12] + (buffer[13] << 8) + (buffer[14] << 16); + get_sfdp(self, addr, buffer, len); // Get the JEDEC mandatory table + self->sectorsize = 1 << buffer[28]; + self->addr_is_32bit = ((buffer[2] >> 1) & 0x03) != 0; + } } self->commands = self->addr_is_32bit ? _COMMANDS_32BIT : _COMMANDS_24BIT; diff --git a/ports/webassembly/Makefile b/ports/webassembly/Makefile index 435636531cc26..9a673b757b29c 100644 --- a/ports/webassembly/Makefile +++ b/ports/webassembly/Makefile @@ -148,10 +148,10 @@ repl: $(BUILD)/micropython.mjs min: $(BUILD)/micropython.min.mjs test: $(BUILD)/micropython.mjs $(TOP)/tests/run-tests.py - cd $(TOP)/tests && MICROPY_MICROPYTHON_MJS=../ports/webassembly/$< ./run-tests.py --target webassembly + cd $(TOP)/tests && MICROPY_MICROPYTHON_MJS=../ports/webassembly/$< ./run-tests.py -t webassembly test_min: $(BUILD)/micropython.min.mjs $(TOP)/tests/run-tests.py - cd $(TOP)/tests && MICROPY_MICROPYTHON_MJS=../ports/webassembly/$< ./run-tests.py --target webassembly + cd $(TOP)/tests && MICROPY_MICROPYTHON_MJS=../ports/webassembly/$< ./run-tests.py -t webassembly ################################################################################ # Remaining make rules. diff --git a/py/misc.h b/py/misc.h index 96b13c151a6b9..8ac80bb7b3e34 100644 --- a/py/misc.h +++ b/py/misc.h @@ -380,6 +380,18 @@ static inline bool mp_check(bool value) { // mp_int_t can be larger than long, i.e. Windows 64-bit, nan-box variants static inline uint32_t mp_clz_mpi(mp_int_t x) { + #ifdef __XC16__ + mp_uint_t mask = MP_OBJ_WORD_MSBIT_HIGH; + mp_uint_t zeroes = 0; + while (mask != 0) { + if (mask & (mp_uint_t)x) { + break; + } + zeroes++; + mask >>= 1; + } + return zeroes; + #else MP_STATIC_ASSERT(sizeof(mp_int_t) == sizeof(long long) || sizeof(mp_int_t) == sizeof(long)); @@ -389,6 +401,7 @@ static inline uint32_t mp_clz_mpi(mp_int_t x) { } else { return mp_clzll((unsigned long long)x); } + #endif } #endif // MICROPY_INCLUDED_PY_MISC_H diff --git a/py/mpconfig.h b/py/mpconfig.h index caea0f4f7a3b8..b31da2a37c050 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -30,9 +30,9 @@ // as well as a fallback to generate MICROPY_GIT_TAG if the git repo or tags // are unavailable. #define MICROPY_VERSION_MAJOR 1 -#define MICROPY_VERSION_MINOR 24 +#define MICROPY_VERSION_MINOR 25 #define MICROPY_VERSION_MICRO 0 -#define MICROPY_VERSION_PRERELEASE 0 +#define MICROPY_VERSION_PRERELEASE 1 // Combined version as a 32-bit number for convenience to allow version // comparison. Doesn't include prerelease state. diff --git a/py/objdeque.c b/py/objdeque.c index 2ad771284df53..22c380a05a926 100644 --- a/py/objdeque.c +++ b/py/objdeque.c @@ -208,7 +208,7 @@ static mp_obj_t deque_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { size_t offset = mp_get_index(self->base.type, deque_len(self), index, false); size_t index_val = self->i_get + offset; - if (index_val > self->alloc) { + if (index_val >= self->alloc) { index_val -= self->alloc; } diff --git a/tests/README.md b/tests/README.md index 7bd7eb587177b..b39833aa096f4 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,7 +1,19 @@ # MicroPython Test Suite -This directory contains tests for various functionality areas of MicroPython. -To run all stable tests, run "run-tests.py" script in this directory. +This directory contains tests for most parts of MicroPython. + +To run all stable tests, run the "run-tests.py" script in this directory. By default +that will run the test suite against the unix port of MicroPython. + +To run the test suite against a bare-metal target (a board running MicroPython firmware) +use the `-t` option to specify the serial port. This will automatically detect the +target platform and run the appropriate set of tests for that platform. For example: + + $ ./run-tests.py -t /dev/ttyACM0 + +That will run tests on the `/dev/ttyACM0` serial port. You can also use shortcut +device names like `a` for `/dev/ttyACM` and `c` for `COM`. Use +`./run-tests.py --help` to see all of the device possibilites, and other options. Tests of capabilities not supported on all platforms should be written to check for the capability being present. If it is not, the test diff --git a/tests/basics/deque2.py b/tests/basics/deque2.py index 3552d5be37abe..ebc0872c7b4e0 100644 --- a/tests/basics/deque2.py +++ b/tests/basics/deque2.py @@ -31,6 +31,16 @@ d[3] = 5 print(d[3]) +# Access the last element via index, when the last element is at various locations +d = deque((), 2) +for i in range(4): + d.append(i) + print(i, d[-1]) + +# Write the last element then access all elements from the end +d[-1] = 4 +print(d[-2], d[-1]) + # Accessing indices out of bounds raises IndexError try: d[4] diff --git a/tests/cpydiff/modules_json_nonserializable.py b/tests/cpydiff/modules_json_nonserializable.py index ffe523786f501..d6a5660cadfdb 100644 --- a/tests/cpydiff/modules_json_nonserializable.py +++ b/tests/cpydiff/modules_json_nonserializable.py @@ -6,10 +6,7 @@ """ import json -a = bytes(x for x in range(256)) try: - z = json.dumps(a) - x = json.loads(z) - print("Should not get here") + print(json.dumps(b"shouldn't be able to serialise bytes")) except TypeError: print("TypeError") diff --git a/tests/feature_check/target_info.py b/tests/feature_check/target_info.py index df89496708aa9..f60f3b319192e 100644 --- a/tests/feature_check/target_info.py +++ b/tests/feature_check/target_info.py @@ -4,6 +4,7 @@ import sys +platform = getattr(sys, "platform", "minimal") sys_mpy = getattr(sys.implementation, "_mpy", 0) arch = [ None, @@ -19,4 +20,4 @@ "xtensawin", "rv32imc", ][sys_mpy >> 10] -print(arch) +print(platform, arch) diff --git a/tests/multi_espnow/10_simple_data.py b/tests/multi_espnow/10_simple_data.py index 1d218fe98130a..00d69f30d9158 100644 --- a/tests/multi_espnow/10_simple_data.py +++ b/tests/multi_espnow/10_simple_data.py @@ -14,7 +14,7 @@ def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/20_send_echo.py b/tests/multi_espnow/20_send_echo.py index 4a1d1624d8430..4c325bf68c513 100644 --- a/tests/multi_espnow/20_send_echo.py +++ b/tests/multi_espnow/20_send_echo.py @@ -61,7 +61,7 @@ def echo_client(e, peer, msglens): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/30_lmk_echo.py b/tests/multi_espnow/30_lmk_echo.py index ac8908049250d..2a6c77c6331db 100644 --- a/tests/multi_espnow/30_lmk_echo.py +++ b/tests/multi_espnow/30_lmk_echo.py @@ -92,7 +92,7 @@ def echo_client(e, peer, msglens): # Initialise the wifi and espnow hardware and software def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/40_recv_test.py b/tests/multi_espnow/40_recv_test.py index 46f4f78df4894..554db16ac1713 100644 --- a/tests/multi_espnow/40_recv_test.py +++ b/tests/multi_espnow/40_recv_test.py @@ -48,7 +48,7 @@ def client_send(e, peer, msg, sync): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/50_esp32_rssi_test.py b/tests/multi_espnow/50_esp32_rssi_test.py index 6a47b540d35ec..8aded1c099400 100644 --- a/tests/multi_espnow/50_esp32_rssi_test.py +++ b/tests/multi_espnow/50_esp32_rssi_test.py @@ -49,7 +49,7 @@ def client_send(e, peer, msg, sync): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/60_irq_test.py b/tests/multi_espnow/60_irq_test.py index 37fc57ce4ae73..db8b8168690e7 100644 --- a/tests/multi_espnow/60_irq_test.py +++ b/tests/multi_espnow/60_irq_test.py @@ -49,7 +49,7 @@ def client_send(e, peer, msg, sync): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/multi_espnow/80_asyncio_client.py b/tests/multi_espnow/80_asyncio_client.py index 4d58a451c1c01..8c34b98a3c4a6 100644 --- a/tests/multi_espnow/80_asyncio_client.py +++ b/tests/multi_espnow/80_asyncio_client.py @@ -50,7 +50,7 @@ def client_send(e, peer, msg, sync): def init(e, sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e.active(True) e.set_pmk(default_pmk) wlans[0].active(sta_active) diff --git a/tests/multi_espnow/81_asyncio_server.py b/tests/multi_espnow/81_asyncio_server.py index df1dbb2ac8aa1..e6002582c085d 100644 --- a/tests/multi_espnow/81_asyncio_server.py +++ b/tests/multi_espnow/81_asyncio_server.py @@ -30,7 +30,7 @@ def client_send(e, peer, msg, sync): def init(e, sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e.active(True) e.set_pmk(default_pmk) wlans[0].active(sta_active) diff --git a/tests/multi_espnow/90_memory_test.py b/tests/multi_espnow/90_memory_test.py index 5e80eb0fdf669..b59ff61b594a8 100644 --- a/tests/multi_espnow/90_memory_test.py +++ b/tests/multi_espnow/90_memory_test.py @@ -65,7 +65,7 @@ def echo_client(e, peer, msglens): def init(sta_active=True, ap_active=False): - wlans = [network.WLAN(i) for i in [network.STA_IF, network.AP_IF]] + wlans = [network.WLAN(i) for i in [network.WLAN.IF_STA, network.WLAN.IF_AP]] e = espnow.ESPNow() e.active(True) e.set_pmk(default_pmk) diff --git a/tests/net_hosted/connect_nonblock_xfer.py b/tests/net_hosted/connect_nonblock_xfer.py index dc4693cea6a8c..23620908afbc6 100644 --- a/tests/net_hosted/connect_nonblock_xfer.py +++ b/tests/net_hosted/connect_nonblock_xfer.py @@ -1,15 +1,24 @@ # 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, time, socket, errno, ssl +import errno +import select +import socket +import ssl -isMP = sys.implementation.name == "micropython" +# only mbedTLS supports non-blocking mode +if not hasattr(ssl, "MBEDTLS_VERSION"): + print("SKIP") + raise SystemExit -def dp(e): - # uncomment next line for development and testing, to print the actual exceptions - # print(repr(e)) - pass +# get the name of an errno error code +def errno_name(er): + if er == errno.EAGAIN: + return "EAGAIN" + if er == errno.EINPROGRESS: + return "EINPROGRESS" + return er # do_connect establishes the socket and wraps it if tls is True. @@ -22,112 +31,75 @@ def do_connect(peer_addr, tls, handshake): # print("Connecting to", peer_addr) s.connect(peer_addr) except OSError as er: - print("connect:", er.errno == errno.EINPROGRESS) - if er.errno != errno.EINPROGRESS: - print(" got", er.errno) + print("connect:", errno_name(er.errno)) # wrap with ssl/tls if desired if tls: ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) - if hasattr(ssl_context, "check_hostname"): - ssl_context.check_hostname = False - try: s = ssl_context.wrap_socket(s, do_handshake_on_connect=handshake) - print("wrap: True") + print("wrap ok: 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) + print("wrap er:", e) return s +# poll a socket and print out the result +def poll(s): + poller = select.poll() + poller.register(s) + print("poll: ", poller.poll(0)) + + # 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 +def test(peer_addr, tls, handshake): + # MicroPython plain and TLS sockets have read/write + hasRW = True - # 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) + # MicroPython plain sockets have send/recv + # MicroPython TLS sockets don't have send/recv + hasSR = not tls # connect + send + # non-blocking send should raise EAGAIN if hasSR: s = do_connect(peer_addr, tls, handshake) - # send -> 4 or EAGAIN + poll(s) try: ret = s.send(b"1234") - print("send:", handshake and ret == 4) + print("send ok:", ret) # shouldn't get here except OSError as er: - # - dp(er) - print("send:", er.errno in (errno.EAGAIN, errno.EINPROGRESS)) + print("send er:", errno_name(er.errno)) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("send:", True) # connect + write + # non-blocking write should return None 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.") + poll(s) + ret = s.write(b"1234") + print("write: ", ret) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("write:", True) + # connect + recv + # non-blocking recv should raise EAGAIN if hasSR: - # connect + recv s = do_connect(peer_addr, tls, handshake) - # recv -> EAGAIN + poll(s) try: - print("recv:", s.recv(10)) + ret = s.recv(10) + print("recv ok:", ret) # shouldn't get here except OSError as er: - dp(er) - print("recv:", er.errno == errno.EAGAIN) + print("recv er:", errno_name(er.errno)) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("recv:", True) # connect + read + # non-blocking read should return None 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.") + poll(s) + ret = s.read(10) + print("read: ", ret) s.close() - else: # fake it... - print("connect:", True) - if tls: - print("wrap:", True) - print("read:", True) if __name__ == "__main__": @@ -136,10 +108,8 @@ def test(peer_addr, tls=False, handshake=False): 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) + test(socket.getaddrinfo("micropython.org", 80)[0][-1], False, False) print("--- SSL sockets ---") test(socket.getaddrinfo("micropython.org", 443)[0][-1], True, True) diff --git a/tests/net_hosted/connect_nonblock_xfer.py.exp b/tests/net_hosted/connect_nonblock_xfer.py.exp new file mode 100644 index 0000000000000..c5498a03873a0 --- /dev/null +++ b/tests/net_hosted/connect_nonblock_xfer.py.exp @@ -0,0 +1,44 @@ +--- Plain sockets to nowhere --- +connect: EINPROGRESS +poll: [] +send er: EAGAIN +connect: EINPROGRESS +poll: [] +write: None +connect: EINPROGRESS +poll: [] +recv er: EAGAIN +connect: EINPROGRESS +poll: [] +read: None +--- SSL sockets to nowhere --- +connect: EINPROGRESS +wrap ok: True +poll: [] +write: None +connect: EINPROGRESS +wrap ok: True +poll: [] +read: None +--- Plain sockets --- +connect: EINPROGRESS +poll: [] +send er: EAGAIN +connect: EINPROGRESS +poll: [] +write: None +connect: EINPROGRESS +poll: [] +recv er: EAGAIN +connect: EINPROGRESS +poll: [] +read: None +--- SSL sockets --- +connect: EINPROGRESS +wrap ok: True +poll: [(, 4)] +write: 4 +connect: EINPROGRESS +wrap ok: True +poll: [(, 4)] +read: None diff --git a/tests/run-tests.py b/tests/run-tests.py index cd1d681af1ee5..d0feb4bcd6635 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -89,6 +89,91 @@ def open(self, path, mode): __import__('__injected_test') """ +# Platforms associated with the unix port, values of `sys.platform`. +PC_PLATFORMS = ("darwin", "linux", "win32") + +# Tests to skip on specific targets. +# These are tests that are difficult to detect that they should not be run on the given target. +platform_tests_to_skip = { + "esp8266": ( + "micropython/viper_args.py", # too large + "micropython/viper_binop_arith.py", # too large + "misc/rge_sm.py", # too large + ), + "minimal": ( + "basics/class_inplace_op.py", # all special methods not supported + "basics/subclass_native_init.py", # native subclassing corner cases not support + "misc/rge_sm.py", # too large + "micropython/opt_level.py", # don't assume line numbers are stored + ), + "nrf": ( + "basics/io_buffered_writer.py", + "basics/io_bytesio_cow.py", + "basics/io_bytesio_ext.py", + "basics/io_bytesio_ext2.py", + "basics/io_iobase.py", + "basics/io_stringio1.py", + "basics/io_stringio_base.py", + "basics/io_stringio_with.py", + "basics/io_write_ext.py", + "basics/memoryview1.py", # no item assignment for memoryview + "extmod/random_basic.py", # unimplemented: random.seed + "micropython/opt_level.py", # no support for line numbers + "misc/non_compliant.py", # no item assignment for bytearray + ), + "renesas-ra": ( + "extmod/time_time_ns.py", # RA fsp rtc function doesn't support nano sec info + ), + "rp2": ( + # Skip thread tests that require more that 2 threads. + "thread/stress_heap.py", + "thread/thread_lock2.py", + "thread/thread_lock3.py", + "thread/thread_shared2.py", + ), + "qemu": ( + # Skip tests that require Cortex-M4. + "inlineasm/asmfpaddsub.py", + "inlineasm/asmfpcmp.py", + "inlineasm/asmfpldrstr.py", + "inlineasm/asmfpmuldiv.py", + "inlineasm/asmfpsqrt.py", + ), + "webassembly": ( + "basics/string_format_modulo.py", # can't print nulls to stdout + "basics/string_strip.py", # can't print nulls to stdout + "extmod/asyncio_basic2.py", + "extmod/asyncio_cancel_self.py", + "extmod/asyncio_current_task.py", + "extmod/asyncio_exception.py", + "extmod/asyncio_gather_finished_early.py", + "extmod/asyncio_get_event_loop.py", + "extmod/asyncio_heaplock.py", + "extmod/asyncio_loop_stop.py", + "extmod/asyncio_new_event_loop.py", + "extmod/asyncio_threadsafeflag.py", + "extmod/asyncio_wait_for_fwd.py", + "extmod/binascii_a2b_base64.py", + "extmod/re_stack_overflow.py", + "extmod/time_res.py", + "extmod/vfs_posix.py", + "extmod/vfs_posix_enoent.py", + "extmod/vfs_posix_paths.py", + "extmod/vfs_userfs.py", + "micropython/emg_exc.py", + "micropython/extreme_exc.py", + "micropython/heapalloc_exc_compressed_emg_exc.py", + ), + "WiPy": ( + "misc/print_exception.py", # requires error reporting full + ), + "zephyr": ( + # Skip thread tests that require more than 4 threads. + "thread/stress_heap.py", + "thread/thread_lock3.py", + ), +} + def rm_f(fname): if os.path.exists(fname): @@ -115,6 +200,53 @@ def convert_regex_escapes(line): return bytes("".join(cs), "utf8") +def get_test_instance(test_instance, baudrate, user, password): + if test_instance.startswith("port:"): + _, port = test_instance.split(":", 1) + elif test_instance == "unix": + return None + elif test_instance == "webassembly": + return PyboardNodeRunner() + elif test_instance.startswith("a") and test_instance[1:].isdigit(): + port = "/dev/ttyACM" + test_instance[1:] + elif test_instance.startswith("u") and test_instance[1:].isdigit(): + port = "/dev/ttyUSB" + test_instance[1:] + elif test_instance.startswith("c") and test_instance[1:].isdigit(): + port = "COM" + test_instance[1:] + else: + # Assume it's a device path. + port = test_instance + + global pyboard + sys.path.append(base_path("../tools")) + import pyboard + + pyb = pyboard.Pyboard(port, baudrate, user, password) + pyboard.Pyboard.run_script_on_remote_target = run_script_on_remote_target + pyb.enter_raw_repl() + return pyb + + +def detect_test_platform(pyb, args): + # Run a script to detect various bits of information about the target test instance. + output = run_feature_check(pyb, args, "target_info.py") + if output.endswith(b"CRASH"): + raise ValueError("cannot detect platform: {}".format(output)) + platform, arch = str(output, "ascii").strip().split() + if arch == "None": + arch = None + + args.platform = platform + args.arch = arch + if arch and not args.mpy_cross_flags: + args.mpy_cross_flags = "-march=" + arch + + print("platform={}".format(platform), end="") + if arch: + print(" arch={}".format(arch), end="") + print() + + def prepare_script_for_target(args, *, script_filename=None, script_text=None, force_plain=False): if force_plain or (not args.via_mpy and args.emit == "bytecode"): if script_filename is not None: @@ -624,90 +756,18 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("extmod/ssl_poll.py") # Skip thread mutation tests on targets that don't have the GIL. - if args.target in ("rp2", "unix"): + if args.platform in PC_PLATFORMS + ("rp2",): for t in tests: if t.startswith("thread/mutate_"): skip_tests.add(t) - # Skip thread tests that require many threads on targets that don't support multiple threads. - if args.target == "rp2": - skip_tests.add("thread/stress_heap.py") - skip_tests.add("thread/thread_lock2.py") - skip_tests.add("thread/thread_lock3.py") - skip_tests.add("thread/thread_shared2.py") - elif args.target == "zephyr": - skip_tests.add("thread/stress_heap.py") - skip_tests.add("thread/thread_lock3.py") - # Some tests shouldn't be run on pyboard - if args.target != "unix": + if args.platform not in PC_PLATFORMS: skip_tests.add("basics/exception_chain.py") # warning is not printed skip_tests.add("micropython/meminfo.py") # output is very different to PC output - if args.target == "wipy": - skip_tests.add("misc/print_exception.py") # requires error reporting full - skip_tests.update( - { - "extmod/uctypes_%s.py" % t - for t in "bytearray le native_le ptr_le ptr_native_le sizeof sizeof_native array_assign_le array_assign_native_le".split() - } - ) # requires uctypes - skip_tests.add("extmod/heapq1.py") # heapq not supported by WiPy - skip_tests.add("extmod/random_basic.py") # requires random - skip_tests.add("extmod/random_extra.py") # requires random - elif args.target == "esp8266": - skip_tests.add("micropython/viper_args.py") # too large - skip_tests.add("micropython/viper_binop_arith.py") # too large - skip_tests.add("misc/rge_sm.py") # too large - elif args.target == "minimal": - skip_tests.add("basics/class_inplace_op.py") # all special methods not supported - skip_tests.add( - "basics/subclass_native_init.py" - ) # native subclassing corner cases not support - skip_tests.add("misc/rge_sm.py") # too large - skip_tests.add("micropython/opt_level.py") # don't assume line numbers are stored - elif args.target == "nrf": - skip_tests.add("basics/memoryview1.py") # no item assignment for memoryview - skip_tests.add("extmod/random_basic.py") # unimplemented: random.seed - skip_tests.add("micropython/opt_level.py") # no support for line numbers - skip_tests.add("misc/non_compliant.py") # no item assignment for bytearray - for t in tests: - if t.startswith("basics/io_"): - skip_tests.add(t) - elif args.target == "renesas-ra": - skip_tests.add( - "extmod/time_time_ns.py" - ) # RA fsp rtc function doesn't support nano sec info - elif args.target == "qemu": - skip_tests.add("inlineasm/asmfpaddsub.py") # requires Cortex-M4 - skip_tests.add("inlineasm/asmfpcmp.py") - skip_tests.add("inlineasm/asmfpldrstr.py") - skip_tests.add("inlineasm/asmfpmuldiv.py") - skip_tests.add("inlineasm/asmfpsqrt.py") - elif args.target == "webassembly": - skip_tests.add("basics/string_format_modulo.py") # can't print nulls to stdout - skip_tests.add("basics/string_strip.py") # can't print nulls to stdout - skip_tests.add("extmod/asyncio_basic2.py") - skip_tests.add("extmod/asyncio_cancel_self.py") - skip_tests.add("extmod/asyncio_current_task.py") - skip_tests.add("extmod/asyncio_exception.py") - skip_tests.add("extmod/asyncio_gather_finished_early.py") - skip_tests.add("extmod/asyncio_get_event_loop.py") - skip_tests.add("extmod/asyncio_heaplock.py") - skip_tests.add("extmod/asyncio_loop_stop.py") - skip_tests.add("extmod/asyncio_new_event_loop.py") - skip_tests.add("extmod/asyncio_threadsafeflag.py") - skip_tests.add("extmod/asyncio_wait_for_fwd.py") - skip_tests.add("extmod/binascii_a2b_base64.py") - skip_tests.add("extmod/re_stack_overflow.py") - skip_tests.add("extmod/time_res.py") - skip_tests.add("extmod/vfs_posix.py") - skip_tests.add("extmod/vfs_posix_enoent.py") - skip_tests.add("extmod/vfs_posix_paths.py") - skip_tests.add("extmod/vfs_userfs.py") - skip_tests.add("micropython/emg_exc.py") - skip_tests.add("micropython/extreme_exc.py") - skip_tests.add("micropython/heapalloc_exc_compressed_emg_exc.py") + # Skip platform-specific tests. + skip_tests.update(platform_tests_to_skip.get(args.platform, ())) # Some tests are known to fail on 64-bit machines if pyb is None and platform.architecture()[0] == "64bit": @@ -922,17 +982,38 @@ def main(): formatter_class=argparse.RawDescriptionHelpFormatter, description="""Run and manage tests for MicroPython. +By default the tests are run against the unix port of MicroPython. To run it +against something else, use the -t option. See below for details. + Tests are discovered by scanning test directories for .py files or using the specified test files. If test files nor directories are specified, the script expects to be ran in the tests directory (where this file is located) and the builtin tests suitable for the target platform are ran. + When running tests, run-tests.py compares the MicroPython output of the test with the output produced by running the test through CPython unless a .exp file is found, in which case it is used as comparison. + If a test fails, run-tests.py produces a pair of .out and .exp files in the result directory with the MicroPython output and the expectations, respectively. """, epilog="""\ +The -t option accepts the following for the test instance: +- unix - use the unix port of MicroPython, specified by the MICROPY_MICROPYTHON + environment variable (which defaults to the standard variant of either the unix + or windows ports, depending on the host platform) +- webassembly - use the webassembly port of MicroPython, specified by the + MICROPY_MICROPYTHON_MJS environment variable (which defaults to the standard + variant of the webassembly port) +- port: - connect to and use the given serial port device +- a - connect to and use /dev/ttyACM +- u - connect to and use /dev/ttyUSB +- c - connect to and use COM +- exec: - execute a command and attach to its stdin/stdout +- execpty: - execute a command and attach to the printed /dev/pts/ device +- ... - connect to the given IPv4 address +- anything else specifies a serial port + Options -i and -e can be multiple and processed in the order given. Regex "search" (vs "match") operation is used. An action (include/exclude) of the last matching regex is used: @@ -941,11 +1022,8 @@ def main(): run-tests.py -e async -i async_foo - include all, exclude async, yet still include async_foo """, ) - cmd_parser.add_argument("--target", default="unix", help="the target platform") cmd_parser.add_argument( - "--device", - default="/dev/ttyACM0", - help="the serial device or the IP address of the pyboard", + "-t", "--test-instance", default="unix", help="the MicroPython instance to test" ) cmd_parser.add_argument( "-b", "--baudrate", default=115200, help="the baud rate of the serial device" @@ -1029,43 +1107,11 @@ def main(): sys.exit(0) - LOCAL_TARGETS = ( - "unix", - "webassembly", - ) - EXTERNAL_TARGETS = ( - "pyboard", - "wipy", - "esp8266", - "esp32", - "minimal", - "nrf", - "qemu", - "renesas-ra", - "rp2", - "zephyr", - ) - if args.target in LOCAL_TARGETS: - pyb = None - if args.target == "webassembly": - pyb = PyboardNodeRunner() - elif args.target in EXTERNAL_TARGETS: - global pyboard - sys.path.append(base_path("../tools")) - import pyboard - - pyb = pyboard.Pyboard(args.device, args.baudrate, args.user, args.password) - pyboard.Pyboard.run_script_on_remote_target = run_script_on_remote_target - pyb.enter_raw_repl() - else: - raise ValueError("target must be one of %s" % ", ".join(LOCAL_TARGETS + EXTERNAL_TARGETS)) + # Get the test instance to run on. + pyb = get_test_instance(args.test_instance, args.baudrate, args.user, args.password) - # Automatically detect the native architecture for mpy-cross if not given. - if not args.mpy_cross_flags: - output = run_feature_check(pyb, args, "target_info.py") - arch = str(output, "ascii").strip() - if arch != "None": - args.mpy_cross_flags = "-march=" + arch + # Automatically detect the platform. + detect_test_platform(pyb, args) if args.run_failures and (any(args.files) or args.test_dirs is not None): raise ValueError( @@ -1081,7 +1127,7 @@ def main(): tests = [] elif len(args.files) == 0: test_extensions = ("*.py",) - if args.target == "webassembly": + if args.platform == "webassembly": test_extensions += ("*.js", "*.mjs") if args.test_dirs is None: @@ -1091,23 +1137,25 @@ def main(): "misc", "extmod", ) - if args.target == "pyboard": + if args.platform == "pyboard": # run pyboard tests test_dirs += ("float", "stress", "inlineasm", "ports/stm32") - elif args.target in ("renesas-ra"): + elif args.platform == "mimxrt": + test_dirs += ("float", "stress", "inlineasm") + elif args.platform == "renesas-ra": test_dirs += ("float", "inlineasm", "ports/renesas-ra") - elif args.target == "rp2": + elif args.platform == "rp2": test_dirs += ("float", "stress", "thread", "ports/rp2") if "arm" in args.mpy_cross_flags: test_dirs += ("inlineasm",) - elif args.target == "esp32": + elif args.platform == "esp32": test_dirs += ("float", "stress", "thread") - elif args.target in ("esp8266", "minimal", "nrf"): + elif args.platform in ("esp8266", "minimal", "samd", "nrf"): test_dirs += ("float",) - elif args.target == "wipy": + elif args.platform == "WiPy": # run WiPy tests test_dirs += ("ports/cc3200",) - elif args.target == "unix": + elif args.platform in PC_PLATFORMS: # run PC tests test_dirs += ( "float", @@ -1118,13 +1166,13 @@ def main(): "cmdline", "ports/unix", ) - elif args.target == "qemu": + elif args.platform == "qemu": test_dirs += ( "float", "inlineasm", "ports/qemu", ) - elif args.target == "webassembly": + elif args.platform == "webassembly": test_dirs += ("float", "ports/webassembly") else: # run tests from these directories diff --git a/tools/ci.sh b/tools/ci.sh index 6f0f23a1c4810..4e6f09b0bb853 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -39,12 +39,18 @@ function ci_c_code_formatting_run { # commit formatting function ci_commit_formatting_run { - git remote add upstream https://github.com/micropython/micropython.git - git fetch --depth=100 upstream master + # Default GitHub Actions checkout for a PR is a generated merge commit where + # the parents are the head of base branch (i.e. master) and the head of the + # PR branch, respectively. Use these parents to find the merge-base (i.e. + # where the PR branch head was branched) + # If the common ancestor commit hasn't been found, fetch more. - git merge-base upstream/master HEAD || git fetch --unshallow upstream master - # For a PR, upstream/master..HEAD ends with a merge commit into master, exclude that one. - tools/verifygitlog.py -v upstream/master..HEAD --no-merges + git merge-base HEAD^1 HEAD^2 || git fetch --unshallow origin + + MERGE_BASE=$(git merge-base HEAD^1 HEAD^2) + HEAD=$(git rev-parse HEAD^2) + echo "Checking commits between merge base ${MERGE_BASE} and PR head ${HEAD}..." + tools/verifygitlog.py -v "${MERGE_BASE}..${HEAD}" } ######################################################################################## @@ -63,25 +69,37 @@ function ci_code_size_build { PORTS_TO_CHECK=bmusxpdv SUBMODULES="lib/asf4 lib/berkeley-db-1.xx lib/btstack lib/cyw43-driver lib/lwip lib/mbedtls lib/micropython-lib lib/nxp_driver lib/pico-sdk lib/stm32lib lib/tinyusb" - # 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 master - # If the common ancestor commit hasn't been found, fetch more. - git merge-base upstream/master HEAD || git fetch --unshallow upstream master + # Default GitHub pull request sets HEAD to a generated merge commit + # between PR branch (HEAD^2) and base branch (i.e. master) (HEAD^1). + # + # We want to compare this generated commit with the base branch, to see what + # the code size impact would be if we merged this PR. + REFERENCE=$(git rev-parse --short HEAD^1) + COMPARISON=$(git rev-parse --short HEAD) + + echo "Comparing sizes of reference ${REFERENCE} to ${COMPARISON}..." + git log --oneline $REFERENCE..$COMPARISON + + function code_size_build_step { + COMMIT=$1 + OUTFILE=$2 + IGNORE_ERRORS=$3 + + echo "Building ${COMMIT}..." + git checkout --detach $COMMIT + git submodule update --init $SUBMODULES + git show -s + tools/metrics.py clean $PORTS_TO_CHECK + tools/metrics.py build $PORTS_TO_CHECK | tee $OUTFILE || $IGNORE_ERRORS + } + # build reference, save to size0 # ignore any errors with this build, in case master is failing - git checkout `git merge-base --fork-point upstream/master pull_request` - git submodule update --init $SUBMODULES - git show -s - tools/metrics.py clean $PORTS_TO_CHECK - tools/metrics.py build $PORTS_TO_CHECK | tee ~/size0 || true + code_size_build_step $REFERENCE ~/size0 true # build PR/branch, save to size1 - git checkout pull_request - git submodule update --init $SUBMODULES - git log upstream/master..HEAD - tools/metrics.py clean $PORTS_TO_CHECK - tools/metrics.py build $PORTS_TO_CHECK | tee ~/size1 + code_size_build_step $COMPARISON ~/size1 false + + unset -f code_size_build_step } ######################################################################################## @@ -767,5 +785,5 @@ function ci_zephyr_run_tests { docker exec zephyr-ci west build -p auto -b qemu_cortex_m3 -- -DCONF_FILE=prj_minimal.conf # Issues with zephyr tests: # - inf_nan_arith fails pow(-1, nan) test - (cd tests && ./run-tests.py --target minimal --device execpty:"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -monitor null -serial pty -kernel ../ports/zephyr/build/zephyr/zephyr.elf" -d basics float --exclude inf_nan_arith) + (cd tests && ./run-tests.py -t execpty:"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -monitor null -serial pty -kernel ../ports/zephyr/build/zephyr/zephyr.elf" -d basics float --exclude inf_nan_arith) } diff --git a/tools/mpremote/mpremote/commands.py b/tools/mpremote/mpremote/commands.py index 2b1acea438084..f86befd080349 100644 --- a/tools/mpremote/mpremote/commands.py +++ b/tools/mpremote/mpremote/commands.py @@ -129,8 +129,15 @@ def _remote_path_basename(a): def do_filesystem_cp(state, src, dest, multiple, check_hash=False): if dest.startswith(":"): - dest_exists = state.transport.fs_exists(dest[1:]) - dest_isdir = dest_exists and state.transport.fs_isdir(dest[1:]) + dest_no_slash = dest.rstrip("/" + os.path.sep + (os.path.altsep or "")) + dest_exists = state.transport.fs_exists(dest_no_slash[1:]) + dest_isdir = dest_exists and state.transport.fs_isdir(dest_no_slash[1:]) + + # A trailing / on dest forces it to be a directory. + if dest != dest_no_slash: + if not dest_isdir: + raise CommandError("cp: destination is not a directory") + dest = dest_no_slash else: dest_exists = os.path.exists(dest) dest_isdir = dest_exists and os.path.isdir(dest) diff --git a/tools/mpremote/mpremote/main.py b/tools/mpremote/mpremote/main.py index 8c0a6cd224c32..e6e397020fe85 100644 --- a/tools/mpremote/mpremote/main.py +++ b/tools/mpremote/mpremote/main.py @@ -547,7 +547,10 @@ def main(): return 0 except CommandError as e: + # Make sure existing stdout appears before the error message on stderr. + sys.stdout.flush() print(f"{_PROG}: {e}", file=sys.stderr) + sys.stderr.flush() return 1 finally: do_disconnect(state) diff --git a/tools/mpremote/mpremote/transport.py b/tools/mpremote/mpremote/transport.py index 0b22a61580a03..d845796953a29 100644 --- a/tools/mpremote/mpremote/transport.py +++ b/tools/mpremote/mpremote/transport.py @@ -151,9 +151,9 @@ def fs_writefile(self, dest, data, chunk_size=256, progress_callback=None): while data: chunk = data[:chunk_size] self.exec("w(" + repr(chunk) + ")") - written += len(chunk) data = data[len(chunk) :] if progress_callback: + written += len(chunk) progress_callback(written, src_size) self.exec("f.close()") except TransportExecError as e: diff --git a/tools/mpremote/tests/run-mpremote-tests.sh b/tools/mpremote/tests/run-mpremote-tests.sh index 11d82c9bb3801..b25b5fd7d2966 100755 --- a/tools/mpremote/tests/run-mpremote-tests.sh +++ b/tools/mpremote/tests/run-mpremote-tests.sh @@ -16,7 +16,7 @@ for t in $TESTS; do TMP=$(mktemp -d) echo -n "${t}: " # Strip CR and replace the random temp dir with a token. - if env MPREMOTE=${MPREMOTE} TMP="${TMP}" "${t}" | tr -d '\r' | sed "s,${TMP},"'${TMP},g' > "${t}.out"; then + if env MPREMOTE=${MPREMOTE} TMP="${TMP}" "${t}" 2>&1 | tr -d '\r' | sed "s,${TMP},"'${TMP},g' > "${t}.out"; then if diff "${t}.out" "${t}.exp" > /dev/null; then echo "OK" else diff --git a/tools/mpremote/tests/test_filesystem.sh b/tools/mpremote/tests/test_filesystem.sh index b6d5a7febcb84..afeb7c91da8d6 100755 --- a/tools/mpremote/tests/test_filesystem.sh +++ b/tools/mpremote/tests/test_filesystem.sh @@ -66,6 +66,16 @@ $MPREMOTE resume cp "${TMP}/a.py" :aaa $MPREMOTE resume cp "${TMP}/a.py" :bbb/b.py $MPREMOTE resume cat :aaa/a.py bbb/b.py +# Test cp -f (force copy). +echo ----- +$MPREMOTE resume cp -f "${TMP}/a.py" :aaa +$MPREMOTE resume cat :aaa/a.py + +# Test cp where the destination has a trailing /. +echo ----- +$MPREMOTE resume cp "${TMP}/a.py" :aaa/ +$MPREMOTE resume cp "${TMP}/a.py" :aaa/a.py/ || echo "expect error" + echo ----- $MPREMOTE resume rm :b.py c.py $MPREMOTE resume ls diff --git a/tools/mpremote/tests/test_filesystem.sh.exp b/tools/mpremote/tests/test_filesystem.sh.exp index 7220dc41e472c..82fe7d6bf78c5 100644 --- a/tools/mpremote/tests/test_filesystem.sh.exp +++ b/tools/mpremote/tests/test_filesystem.sh.exp @@ -38,6 +38,16 @@ print("World") print("Hello") print("World") ----- +cp ${TMP}/a.py :aaa +print("Hello") +print("World") +----- +cp ${TMP}/a.py :aaa/ +Up to date: aaa/a.py +cp ${TMP}/a.py :aaa/a.py/ +mpremote: cp: destination is not a directory +expect error +----- rm :b.py rm :c.py ls :