Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/ports_unix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ jobs:
# This avoids the annoying situation of the job failing on every push to a fork (if no token is set).
fail_ci_if_error: ${{ secrets.CODECOV_TOKEN != '' || github.repository_owner == 'micropython' }}
verbose: true
flags: unix-coverage-64bit
name: unix-coverage-64bit
# note: when a fork opens a PR into MicroPython repo, the pull_request trigger can't access
# secrets so this token value will be empty (codecov will do a 'tokenless' upload).
token: ${{ secrets.CODECOV_TOKEN }}
Expand All @@ -125,6 +127,18 @@ jobs:
run: tools/ci.sh native_mpy_modules_32bit_build
- name: Test importing .mpy generated by mpy_ld.py
run: tools/ci.sh unix_coverage_32bit_run_native_mpy_tests
- name: Run gcov coverage analysis
run: |
(cd ports/unix && gcov -o build-coverage/py ../../py/*.c || true)
(cd ports/unix && gcov -o build-coverage/extmod ../../extmod/*.c || true)
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v6
with:
fail_ci_if_error: ${{ secrets.CODECOV_TOKEN != '' || github.repository_owner == 'micropython' }}
verbose: true
flags: unix-coverage-32bit
name: unix-coverage-32bit
token: ${{ secrets.CODECOV_TOKEN }}
- name: Print failures
if: failure()
run: tests/run-tests.py --print-failures
Expand Down
5 changes: 5 additions & 0 deletions docs/library/machine.RTC.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ Methods

Availability: esp32, esp8266 ports.

.. note::

For cross-port persistent storage, see :data:`machine.backup_memory`
which is available on more ports and provides direct memoryview access.

Constants
---------

Expand Down
84 changes: 84 additions & 0 deletions docs/library/machine.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,90 @@ Example use (registers are specific to an stm32 microcontroller):
# read PA3
value = (machine.mem32[GPIOA + GPIO_IDR] >> 3) & 1

.. data:: backup_memory

A writable `memoryview` over persistent hardware memory that survives
restarts, including :ref:`soft_reset` and `machine.deepsleep()`.

The element type depends on the port's hardware alignment requirements:
``'B'`` (unsigned byte) on ports with byte-addressable backup memory,
``'I'`` (unsigned 32-bit) on ports backed by word-sized registers.
Use ``machine.backup_memory.itemsize`` to discover the access granularity
at runtime.

The total size in bytes is ``len(mem) * mem.itemsize``, where ``len(mem)``
is the number of elements and ``mem.itemsize`` is the size of each element.
For example, on a port with 4 word-sized registers, ``len(mem)`` is 4 and
``mem.itemsize`` is 4, giving 16 bytes total. On a port with 4096 bytes
of byte-addressable backup SRAM, ``len(mem)`` is 4096 and ``mem.itemsize``
is 1.

Usage::

import machine

mem = machine.backup_memory
mem[0] = 0x12345678 # write element 0
print(hex(mem[0])) # read element 0
print(len(mem)) # number of elements
print(mem.itemsize) # bytes per element
print(len(mem) * mem.itemsize) # total bytes available

The total byte size and backing hardware vary by port:

====== =================================== ============= ==============
Port Backing storage Total bytes Battery-backed
====== =================================== ============= ==============
alif Backup SRAM 4096 yes
esp32 RTC slow memory 2048 no
mimxrt SNVS LPGPR registers (4-8 per chip) 16-32 yes
rp2 Watchdog scratch registers (8) 32 no
samd Backup RAM (SAMD51 only) 8192 yes
stm32 Backup SRAM (F4/F7/H5/H7/U5/N6) 2048-8192 yes
stm32 RTC BKP registers (other families) 20-128 yes
====== =================================== ============= ==============

.. note::

On esp32 and rp2, data persists across :ref:`soft_reset` and
`machine.deepsleep()` wake but is lost on power-off and on
poweron-style resets. On esp32 in particular this includes pressing
the EN/RESET button on most dev boards, which the chip reports as a
power-on reset.

Some registers may be reserved by the system depending on port and board
configuration. These are exposed in the memoryview but should not be
overwritten by application code:

====== ============== =========================================================
Port Register(s) Used by
====== ============== =========================================================
mimxrt LPGPR[3] UF2 bootloader double-tap (when ``USE_UF2_BOOTLOADER``)
rp2 scratch[4-7] pico-sdk ``watchdog_reboot()`` / ``machine.deepsleep()``
stm32 BKP0R Arduino bootloader (Portenta H7, Giga, Opta, Nicla)
stm32 BKP16R-BKP18R ``rfcore_firmware.py`` on STM32WB
stm32 last BKP reg clock frequency (``MICROPY_HW_CLK_LAST_FREQ``)
stm32 BKP31R (N6) mboot bootloader entry
====== ============== =========================================================

The buffer allows direct register access and can be combined with
``uctypes`` for structured layouts::

import machine, uctypes

mem = machine.backup_memory

# Structured access via uctypes (check len(mem) for your board)
layout = {
"flags": (0 * 4, uctypes.UINT32), # register 0
"counter": (1 * 4, uctypes.UINT32), # register 1
}
regs = uctypes.struct(uctypes.addressof(mem), layout)
regs.flags = 0x01
print(regs.counter)

Availability: alif, esp32, mimxrt, rp2, samd, stm32 ports.

Reset related functions
-----------------------

Expand Down
1 change: 1 addition & 0 deletions extmod/extmod.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ set(MICROPY_SOURCE_EXTMOD
${MICROPY_EXTMOD_DIR}/btstack/modbluetooth_btstack.c
${MICROPY_EXTMOD_DIR}/machine_adc.c
${MICROPY_EXTMOD_DIR}/machine_adc_block.c
${MICROPY_EXTMOD_DIR}/machine_backup_memory.c
${MICROPY_EXTMOD_DIR}/machine_bitstream.c
${MICROPY_EXTMOD_DIR}/machine_can.c
${MICROPY_EXTMOD_DIR}/machine_i2c.c
Expand Down
1 change: 1 addition & 0 deletions extmod/extmod.mk
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
SRC_EXTMOD_C += \
extmod/machine_adc.c \
extmod/machine_adc_block.c \
extmod/machine_backup_memory.c \
extmod/machine_bitstream.c \
extmod/machine_can.c \
extmod/machine_i2c.c \
Expand Down
55 changes: 55 additions & 0 deletions extmod/machine_backup_memory.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2026 Andrew Leech
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "py/mpconfig.h"

#if MICROPY_PY_MACHINE_BACKUP_MEMORY

#include "py/obj.h"
#include "py/objarray.h"

// Validate port configuration at compile time.
#if !MICROPY_PY_BUILTINS_MEMORYVIEW
#error "backup_memory requires MICROPY_PY_BUILTINS_MEMORYVIEW"
#endif
#if MICROPY_HW_BACKUP_MEMORY_ITEMSIZE != 1 && MICROPY_HW_BACKUP_MEMORY_ITEMSIZE != 4
#error "MICROPY_HW_BACKUP_MEMORY_ITEMSIZE must be 1 or 4"
#endif

// Select memoryview typecode based on item size. The 0x80 flag marks the
// memoryview as read-write.
#if MICROPY_HW_BACKUP_MEMORY_ITEMSIZE == 1
#define BACKUP_MEMORY_TYPECODE ('B' | 0x80)
#elif MICROPY_HW_BACKUP_MEMORY_ITEMSIZE == 4
#define BACKUP_MEMORY_TYPECODE ('I' | 0x80)
#endif

const MP_DEFINE_MEMORYVIEW_OBJ(machine_backup_memory_obj,
BACKUP_MEMORY_TYPECODE, 0,
MICROPY_HW_BACKUP_MEMORY_BYTES / MICROPY_HW_BACKUP_MEMORY_ITEMSIZE,
MICROPY_HW_BACKUP_MEMORY_ADDR);

#endif // MICROPY_PY_MACHINE_BACKUP_MEMORY
3 changes: 3 additions & 0 deletions extmod/modmachine.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ static const mp_rom_map_elem_t machine_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) },
{ MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) },
#endif
#if MICROPY_PY_MACHINE_BACKUP_MEMORY
{ MP_ROM_QSTR(MP_QSTR_backup_memory), MP_ROM_PTR(&machine_backup_memory_obj) },
#endif

// Miscellaneous functions.
#if MICROPY_PY_MACHINE_BARE_METAL_FUNCS
Expand Down
6 changes: 6 additions & 0 deletions extmod/modmachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ extern const machine_mem_obj_t machine_mem8_obj;
extern const machine_mem_obj_t machine_mem16_obj;
extern const machine_mem_obj_t machine_mem32_obj;

// Object for machine.backup_memory (static memoryview over persistent storage).
#if MICROPY_PY_MACHINE_BACKUP_MEMORY
#include "py/objarray.h"
extern const mp_obj_array_t machine_backup_memory_obj;
#endif

// These classes correspond to machine.Type entries in the machine module.
// Their Python bindings are implemented in extmod, and their implementation
// is provided by a port.
Expand Down
11 changes: 11 additions & 0 deletions ports/alif/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,17 @@
#define MICROPY_PY_MACHINE_UART (1)
#define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/alif/machine_uart.c"
#define MICROPY_PY_MACHINE_UART_IRQ (1)
#ifndef MICROPY_PY_MACHINE_BACKUP_MEMORY
#define MICROPY_PY_MACHINE_BACKUP_MEMORY (1)
#endif
#if MICROPY_PY_MACHINE_BACKUP_MEMORY
#define MICROPY_HW_BACKUP_MEMORY_BYTES (4096)
// The 4KB region at 0x4902C000 is in peripheral space and does not
// support sub-word writes (byte writes zero unaddressed bytes of the
// containing word). Expose as uint32 memoryview to enforce word access.
#define MICROPY_HW_BACKUP_MEMORY_ITEMSIZE (4)
#define MICROPY_HW_BACKUP_MEMORY_ADDR ((void *)0x4902C000)
#endif
#define MICROPY_PY_NETWORK (CORE_M55_HP)
#ifndef MICROPY_PY_NETWORK_HOSTNAME_DEFAULT
#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "mpy-alif"
Expand Down
4 changes: 0 additions & 4 deletions ports/esp32/machine_rtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ typedef struct _machine_rtc_obj_t {
If MICROPY_HW_RTC_USER_MEM_MAX is set to 0, the RTC.memory() functionality will be not
be compiled which frees some extra flash and RTC memory.
*/
#ifndef MICROPY_HW_RTC_USER_MEM_MAX
#define MICROPY_HW_RTC_USER_MEM_MAX 2048
#endif

// A board can enable MICROPY_HW_RTC_MEM_INIT_ALWAYS to always clear out RTC memory on boot.
// Defaults to RTC_NOINIT_ATTR so the user memory survives WDT resets and the like.
#if MICROPY_HW_RTC_MEM_INIT_ALWAYS
Expand Down
13 changes: 13 additions & 0 deletions ports/esp32/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,19 @@
#define MICROPY_PY_MACHINE_UART_IRQ (1)
#define MICROPY_PY_MACHINE_WDT (1)
#define MICROPY_PY_MACHINE_WDT_INCLUDEFILE "ports/esp32/machine_wdt.c"
#ifndef MICROPY_HW_RTC_USER_MEM_MAX
#define MICROPY_HW_RTC_USER_MEM_MAX 2048
#endif
// machine.backup_memory exposes the RTC user memory as a byte memoryview.
// RTC.memory() coexists but uses separate length tracking; writes via
// backup_memory won't update RTC.memory()'s length, and vice versa.
#if MICROPY_HW_RTC_USER_MEM_MAX > 0
extern uint8_t rtc_user_mem_data[];
#define MICROPY_PY_MACHINE_BACKUP_MEMORY (1)
#define MICROPY_HW_BACKUP_MEMORY_BYTES (MICROPY_HW_RTC_USER_MEM_MAX)
#define MICROPY_HW_BACKUP_MEMORY_ITEMSIZE (1)
#define MICROPY_HW_BACKUP_MEMORY_ADDR ((void *)&rtc_user_mem_data[0])
#endif
#ifndef MICROPY_PY_NETWORK
#define MICROPY_PY_NETWORK (1)
#endif
Expand Down
8 changes: 8 additions & 0 deletions ports/mimxrt/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,14 @@ uint32_t trng_random_u32(void);
#endif
#define MICROPY_PY_ONEWIRE (1)
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
#ifndef MICROPY_PY_MACHINE_BACKUP_MEMORY
#define MICROPY_PY_MACHINE_BACKUP_MEMORY (1)
#endif
#if MICROPY_PY_MACHINE_BACKUP_MEMORY
#define MICROPY_HW_BACKUP_MEMORY_BYTES (SNVS_LPGPR_COUNT * 4)
#define MICROPY_HW_BACKUP_MEMORY_ITEMSIZE (4)
#define MICROPY_HW_BACKUP_MEMORY_ADDR ((void *)&SNVS->LPGPR[0])
#endif

// fatfs configuration used in ffconf.h
#define MICROPY_FATFS_ENABLE_LFN (2)
Expand Down
9 changes: 9 additions & 0 deletions ports/rp2/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "hardware/flash.h"
#include "hardware/spi.h"
#include "hardware/sync.h"
#include "hardware/structs/watchdog.h"
#include "pico/binary_info.h"
#include "pico/multicore.h"
#include "mpconfigboard.h"
Expand Down Expand Up @@ -191,6 +192,14 @@
#define MICROPY_PY_MACHINE_UART_IRQ (1)
#define MICROPY_PY_MACHINE_WDT (1)
#define MICROPY_PY_MACHINE_WDT_INCLUDEFILE "ports/rp2/machine_wdt.c"
#ifndef MICROPY_PY_MACHINE_BACKUP_MEMORY
#define MICROPY_PY_MACHINE_BACKUP_MEMORY (1)
#endif
#if MICROPY_PY_MACHINE_BACKUP_MEMORY
#define MICROPY_HW_BACKUP_MEMORY_BYTES (sizeof(watchdog_hw->scratch))
#define MICROPY_HW_BACKUP_MEMORY_ITEMSIZE (4)
#define MICROPY_HW_BACKUP_MEMORY_ADDR ((void *)&watchdog_hw->scratch[0])
#endif
#define MICROPY_PY_MACHINE_FREQ_NUM_ARGS_MAX (2)
#define MICROPY_PY_ONEWIRE (1)
#define MICROPY_VFS (1)
Expand Down
9 changes: 9 additions & 0 deletions ports/samd/mcu/samd51/mpconfigmcu.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ unsigned long trng_random_u32(void);

#define VFS_BLOCK_SIZE_BYTES (2048) //

#ifndef MICROPY_PY_MACHINE_BACKUP_MEMORY
#define MICROPY_PY_MACHINE_BACKUP_MEMORY (1)
#endif
#if MICROPY_PY_MACHINE_BACKUP_MEMORY
#define MICROPY_HW_BACKUP_MEMORY_BYTES (BKUPRAM_SIZE)
#define MICROPY_HW_BACKUP_MEMORY_ITEMSIZE (1)
#define MICROPY_HW_BACKUP_MEMORY_ADDR ((void *)BKUPRAM_ADDR)
#endif

#ifndef MICROPY_HW_UART_TXBUF
#define MICROPY_HW_UART_TXBUF (1)
#endif
Expand Down
Loading
Loading