docs: Add a "Reset and Boot Sequence" reference page.

Previously individual ports documented these aspects to varying degrees,
but most of the information is common to all ports.

In particular, this adds a canonical explanation of `boot.py` and
`main.py`.

This work was funded through GitHub Sponsors.

Signed-off-by: Angus Gratton <angus@redyak.com.au>
This commit is contained in:
Angus Gratton
2024-10-02 19:16:39 +10:00
committed by Damien George
parent cbffe61f96
commit 0e7c3901b8
15 changed files with 311 additions and 74 deletions

View File

@@ -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
------------

View File

@@ -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 </reference/reset_boot>`) 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::

View File

@@ -170,6 +170,10 @@ Exceptions
.. exception:: KeyboardInterrupt
|see_cpython| `python:KeyboardInterrupt`.
See also in the context of :ref:`soft_bricking`.
.. exception:: KeyError
.. exception:: MemoryError

View File

@@ -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.

View File

@@ -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 <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
<filesystem>`. 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

View File

@@ -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 <hard_reset>` 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 <soft_reset>` of the interpreter, deleting all
Python objects and resetting the Python heap.
.. function:: reset_cause()

View File

@@ -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.

View File

@@ -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
---------

View File

@@ -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

View File

@@ -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 <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

View File

@@ -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
---------------------------

View File

@@ -0,0 +1,260 @@
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 </library/machine.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 <repl_soft_reset>` 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
</library/machine>`. There are very limited exceptions, for example
:doc:`machine.Pin </library/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
</library/network>` may indicate the network interface is still active with a
configured IP address, etc.
- An active :doc:`REPL <repl>` appears continuous before and after soft reset,
except in some unusual cases:
* If the :ref:`machine.USBDevice <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 </library/machine.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
<manifest>`. 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 <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
<filesystem>` using :doc:`mpremote <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 <repl>`,
:doc:`mpremote <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 <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 <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 <mpyfiles>` ``main.mpy`` and/or ``boot.mpy`` files to the
filesystem, or even :doc:`freeze <manifest>` them into the firmware build
instead.
- ``main.py`` execution is skipped when a soft reset is initiated from :ref:`raw
REPL mode <raw_repl>` (for example, when :doc:`mpremote <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 </pyboard/tutorial/reset>`
- :doc:`renesas-ra port instructions </renesas-ra/tutorial/reset>`
- :doc:`wipy port instructions </wipy/tutorial/reset>`
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

View File

@@ -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
-------------------------------------------

View File

@@ -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.

View File

@@ -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
---------