Praca z ROMFS

Przegląd

ROMFS (Read-Only Memory Filesystem) to lekki system plików tylko do odczytu, zaprojektowany dla urządzeń MicroPython. Jest zoptymalizowany pod kątem mikrokontrolerów i systemów wbudowanych, w których kod i dane muszą być przechowywane w pamięci flash i wydajnie dostępne bez konieczności kopiowania do pamięci RAM.

Najważniejsze zalety ROMFS to:

  • Importy bez kopiowania (zero-copy): pliki bajtkodu .mpy przechowywane w ROMFS mogą być wykonywane bezpośrednio z pamięci flash (mapowanej do pamięci) zamiast być najpierw kopiowane do pamięci RAM. Działa to podobnie do modułów zamrożonych, ale nie wymaga ponownego programowania całego oprogramowania układowego.

  • Niewielkie zużycie pamięci RAM: stałe obiekty (łańcuchy znaków, bajty itp.) w plikach .mpy wczytywanych z ROMFS są odwoływane bezpośrednio z pamięci flash, a nie powielane w pamięci RAM.

  • Elastyczne wdrażanie: obraz ROMFS można zbudować na komputerze hosta i wdrożyć na urządzeniu za pomocą mpremote, bez ponownego budowania oprogramowania układowego.

  • Standardowy interfejs systemu plików: ROMFS jest montowany w VFS i dostępny poprzez zwykłe operacje na plikach w Pythonie (open, os.listdir, import itp.).

ROMFS uzupełnia zarówno systemy plików FAT/LittleFS z możliwością odczytu i zapisu (które znajdują się w innych partycjach pamięci flash), jak i moduły zamrożone (które są kompilowane bezpośrednio do oprogramowania układowego).

Wsparcie dla płytek

ROMFS jest włączony w oprogramowaniu układowym OpenMV na każdej płytce kamery, która ma zarezerwowaną partycję ROMFS w swoim układzie pamięci flash. Na tych płytkach partycja ROMFS jest automatycznie wykrywana przy uruchomieniu i montowana w /rom; zarówno /rom, jak i /rom/lib są dodawane do sys.path, dzięki czemu przechowywane tam moduły można importować bezpośrednio.

Płytka

Wsparcie dla ROMFS

OpenMV Cam N6

Tak

OpenMV AE3

Tak

OpenMV Cam RT1062

Tak

OpenMV Cam Pure Thermal

Tak

OpenMV Cam M4 / M7 / H7 / H7 Plus

Tak

Arduino Giga

Tak

Arduino Portenta H7

Tak

Arduino Nicla Vision

Tak

Arduino Nano 33 BLE Sense

Nie (brak partycji ROMFS)

Arduino Nano RP2040 Connect

Nie (brak partycji ROMFS)

Zobacz romfs, aby poznać narzędzie pomocnicze specyficzne dla OpenMV, które analizuje zamontowany ROMFS w /rom.

Przepływ pracy

Typowy przepływ pracy podczas korzystania z ROMFS jest następujący:

  1. Utwórz na swoim komputerze katalog zawierający pliki Pythona (lub pliki .mpy), które chcesz wdrożyć.

  2. Użyj mpremote romfs deploy <directory>, aby zbudować i wdrożyć obraz ROMFS na urządzeniu.

  3. ROMFS zostanie zamontowany w /rom przy następnym uruchomieniu (lub może zostać zamontowany natychmiast po ponownym uruchomieniu urządzenia).

  4. Kod Pythona na urządzeniu może następnie importować (import) moduły z ROMFS tak samo jak z dowolnego innego systemu plików.

Na przykład:

# On the host PC, with a directory "myapp/" containing app.py:
$ mpremote romfs deploy myapp/

Po miękkim resecie urządzenie będzie miało dostępny do importu plik /rom/app.py (lub /rom/app.mpy, jeśli zainstalowano mpy_cross).

Pełne szczegóły dotyczące podpoleceń mpremote znajdują się w sekcji podpolecenia mpremote romfs poniżej.

API Pythona

API ROMFS dla Pythona jest udostępniane poprzez moduł vfs.

class vfs.VfsRom(buffer)

Tworzy obiekt systemu plików ROMFS z buffer, który musi być obiektem obsługującym protokół buforów (np. obiektem bytes, bytearray lub memoryview) zawierającym prawidłowy obraz ROMFS.

Konstruktor sprawdza, czy buffer zaczyna się od magicznych bajtów ROMFS (b"\xd2\xcd\x31"). Jeśli bufor jest zbyt mały lub nie jest prawidłowym ROMFS, zgłaszany jest OSError(ENODEV).

Obiekty utworzone przez ten konstruktor można zamontować za pomocą vfs.mount().

Przykład:

import vfs

# Load a ROMFS image from flash into a memoryview.
dev = vfs.rom_ioctl(2, 0)   # get partition 0 as a memoryview
fs = vfs.VfsRom(dev)
vfs.mount(fs, '/rom')

Lub, aby zamontować obraz ROMFS przechowywany w pliku:

import vfs

with open('/flash/app.romfs', 'rb') as f:
    romfs_data = f.read()
fs = vfs.VfsRom(romfs_data)
vfs.mount(fs, '/rom2')

Na obiekcie VfsRom dostępne są następujące metody:

VfsRom.open(path, mode)

Otwiera plik z ROMFS. Obsługiwane są tylko tryby odczytu ('', 'r', 'rt', 'rb'). Próba otwarcia pliku do zapisu spowoduje zgłoszenie OSError(EROFS).

Zwracany obiekt pliku obsługuje read(), seek(), tell() oraz close(). W przypadku plików binarnych otwartych w trybie odczytu zwracany obiekt obsługuje również protokół buforów, dzięki czemu można uzyskać memoryview danych pliku, który odwołuje się bezpośrednio do pamięci ROMFS (bez kopiowania).

VfsRom.ilistdir(path)

Zwraca iterator po wpisach w katalogu path. Każdy wpis jest krotką (name, type, inode, size), gdzie type wynosi 0x8000 dla pliku lub 0x4000 dla katalogu.

VfsRom.stat(path)

Zwraca 10-elementową krotkę w stylu os.stat dla path. Zgłasza OSError(ENOENT), jeśli ścieżka nie istnieje.

VfsRom.statvfs(path)

Zwraca statystyki systemu plików. Rozmiar bloku jest raportowany jako 1, a liczba bloków reprezentuje całkowity rozmiar obrazu ROMFS w bajtach. Liczba wolnych bloków i wolnych plików zawsze wynosi 0 (system plików tylko do odczytu).

VfsRom.chdir(path)

Zmienia katalog w obrębie ROMFS. Obsługiwany jest tylko katalog główny ('/'); zmiana na dowolny podkatalog zgłasza OSError(EOPNOTSUPP).

VfsRom.getcwd()

Zwraca bieżący katalog roboczy w obrębie ROMFS. Zawsze zwraca '/'.

vfs.rom_ioctl(op, ...)

Niskopoziomowy interfejs dostępu do partycji pamięci tylko do odczytu (ROM) urządzenia.

Obsługiwane operacje to:

  • vfs.rom_ioctl(1) – Zwraca liczbę dostępnych partycji ROM.

  • vfs.rom_ioctl(2, id) – Zwraca partycję ROM o indeksie id jako obiekt memoryview. Pamięć można odczytywać, ale nie można jej bezpośrednio zapisywać.

  • vfs.rom_ioctl(3, id, length) – Przygotowuje partycję ROM do zapisu. Kasuje pierwsze length bajtów partycji o indeksie id. Zwraca minimalny rozmiar zapisu w bajtach (wyrównanie wymagane dla kolejnych zapisów).

  • vfs.rom_ioctl(4, id, offset, buf) – Zapisuje buf (obiekt typu bytes) do partycji ROM o indeksie id pod bajtem offset.

  • vfs.rom_ioctl(5, id) – Kończy sekwencję zapisu do partycji id (wykonuje wszelkie operacje finalizujące potrzebne po zapisie, takie jak opróżnienie pamięci podręcznej).

Operacje te są używane wewnętrznie przez mpremote do wdrażania obrazów ROMFS. Większość użytkowników nie musi bezpośrednio wywoływać vfs.rom_ioctl().

Przykład (odpytywanie dostępnych partycji):

import vfs

n = vfs.rom_ioctl(1)
print("Number of ROM partitions:", n)
for i in range(n):
    dev = vfs.rom_ioctl(2, i)
    print(f"  Partition {i}: {len(dev)} bytes")

Automatyczne montowanie przy uruchomieniu

Gdy wsparcie dla ROMFS jest włączone w oprogramowaniu układowym, MicroPython podczas inicjalizacji automatycznie spróbuje zamontować pierwszą partycję ROM w /rom. Jeśli partycja zawiera prawidłowy obraz ROMFS, jest on montowany, a zarówno /rom, jak i /rom/lib są automatycznie dodawane do sys.path.

Oznacza to, że po wdrożeniu obrazu ROMFS za pomocą mpremote wystarczy miękki reset, aby nowe moduły stały się dostępne do importu.

Jeśli w partycji nie zostanie znaleziony prawidłowy obraz ROMFS (np. na świeżo zaprogramowanej płytce), montowanie jest po cichu pomijane.

Zarządzanie ROMFS za pomocą mpremote

Narzędzie mpremote udostępnia trzy podpolecenia do zarządzania obrazami ROMFS na podłączonym urządzeniu.

romfs query

$ mpremote romfs query

Wyświetla wszystkie dostępne partycje ROMFS na urządzeniu wraz z ich rozmiarami. Pokazuje również pierwsze 12 bajtów każdej partycji w postaci szesnastkowej i informuje, czy obecny jest prawidłowy obraz ROMFS.

Przykładowe wyjście:

ROMFS0 partition has size 131072 bytes (32 blocks of 4096 bytes each)
  Raw contents: d2:cd:31:XX:XX:XX:XX:XX:XX:XX:XX:XX ...
  ROMFS image size: 1234

romfs build

$ mpremote romfs [-o <output>] build <source>

Buduje obraz ROMFS z katalogu source na komputerze hosta. Obraz jest zapisywany do output (domyślnie: <source>.romfs).

Opcje:

  • -o <output>, --output <output>: Określa ścieżkę pliku wyjściowego.

  • -m, --mpy (domyślnie): Automatycznie kompiluje pliki .py do .mpy za pomocą mpy_cross przed dodaniem ich do obrazu. Wymaga pakietu Python mpy_cross (pip install mpy_cross).

  • --no-mpy: Wyłącza automatyczną kompilację plików .py.

Przykład:

$ mpremote romfs build myapp/
Building romfs filesystem, source directory: myapp/
/
|-- main.py -> .mpy
\-- lib/
    \-- helper.py -> .mpy
Writing 2048 bytes to output file myapp.romfs

romfs deploy

$ mpremote romfs [-p <partition>] deploy <source>

Wdraża obraz ROMFS na urządzeniu. source może być jednym z:

  • Katalog na hoście: obraz ROMFS jest budowany w pamięci i wdrażany bezpośrednio.

  • Plik .romfs lub .img: obraz jest odczytywany z dysku i wdrażany.

Opcje:

  • -p <partition>, --partition <partition>: Określa indeks partycji docelowej (domyślnie: 0).

  • -m, --mpy (domyślnie): Kompiluje .py do .mpy, gdy source jest katalogiem.

  • --no-mpy: Wyłącza automatyczną kompilację plików .py.

Po wdrożeniu urządzenie musi zostać poddane miękkiemu resetowi, aby nowy ROMFS został zamontowany w /rom.

Przykład:

$ mpremote romfs deploy myapp/
Building romfs filesystem, source directory: myapp/
/
|-- main.py -> .mpy
\-- lib/
    \-- helper.py -> .mpy
Image size is 2048 bytes
ROMFS0 partition has size 131072 bytes (32 blocks of 4096 bytes each)
Preparing ROMFS0 partition for writing
Deploying ROMFS to ROMFS0 partition
ROMFS image deployed

$ mpremote soft-reset

Przykłady

Wdrażanie prostej aplikacji

Załóżmy, że masz katalog projektu myapp/ o następującej strukturze:

myapp/
    main.py
    utils.py
    lib/
        helper.py

Aby wdrożyć go do ROMFS urządzenia:

$ mpremote romfs deploy myapp/

Po miękkim resecie moduły można importować z ROMFS:

import main
import utils
from lib import helper

Wyświetlanie zawartości ROMFS z poziomu Pythona

Po zamontowaniu zawartość ROMFS można przeglądać tak jak każdy inny system plików:

import os

for entry in os.ilistdir('/rom'):
    print(entry)

# Or simply:
print(os.listdir('/rom'))

OpenMV dostarcza również niewielkie narzędzie pomocnicze romfs, które wyświetla sformatowaną listę zawierającą adres mapowany do pamięci oraz wyrównanie każdego pliku:

from omv import romfs
romfs.ls_romfs()

Zagnieżdżanie ROMFS w ROMFS

Obraz ROMFS przechowywany jako plik wewnątrz zewnętrznego ROMFS można zamontować jako zagnieżdżony system plików. Na przykład, jeśli istnieje /rom/inner.romfs. Ponieważ /rom jest systemem ROMFS, obiekty plików otwierane z niego obsługują protokół buforów, dzięki czemu można uzyskać bezpośrednio memoryview bez kopiowania:

import vfs

with open('/rom/inner.romfs', 'rb') as f:
    inner = vfs.VfsRom(memoryview(f))
vfs.mount(inner, '/inner')

print(os.listdir('/inner'))

Format obrazu ROMFS

Format obrazu ROMFS to zwarty format binarny zaprojektowany do dostępu mapowanego do pamięci na mikrokontrolerach. Krótki przegląd:

  • Obraz zaczyna się od magicznych bajtów 0xd2 0xcd 0x31 (zakodowanych jako "RM1" z ustawionymi najstarszymi bitami dwóch pierwszych bajtów).

  • Pozostała część obrazu składa się z rekordów, z których każdy zawiera znacznik typu (varuint), długość (varuint) oraz ładunek.

  • Typy rekordów obejmują: wypełnienie, dane dosłowne, wskaźnik pośredni do danych, katalog, plik.

  • Nazwy katalogów i plików są przechowywane jako łańcuchy bajtów z prefiksem długości.

  • Dane pliku mogą być przechowywane dosłownie (w treści) lub poprzez wskaźnik pośredni do innego miejsca w obrazie, co umożliwia wyrównanie na potrzeby dostępu mapowanego do pamięci.

  • Nieznane typy rekordów są po cichu pomijane, co zapewnia zgodność w przód.

Format ten jest zdefiniowany w extmod/vfs_rom.c w źródłach MicroPython. Implementacja w Pythonie używana przez mpremote do budowania obrazów znajduje się w tools/mpremote/mpremote/romfs.py.

Zobacz także

Praca z systemami plików – Przegląd VFS w MicroPython oraz dostępnych typów systemów plików.

Pliki manifestu MicroPython – Jak zamrażać moduły Pythona w oprogramowaniu układowym.

Pliki .mpy w MicroPython – Format pliku binarnego .mpy w MicroPython.

Zdalne sterowanie MicroPython: mpremote – Pełny opis poleceń mpremote.

romfs – Narzędzie pomocnicze OpenMV do inspekcji zamontowanego systemu plików /rom.