Lucrul cu ROMFS

Prezentare generală

ROMFS (Read-Only Memory Filesystem) este un sistem de fișiere ușor, doar pentru citire, conceput pentru dispozitivele MicroPython. Este optimizat pentru microcontrolere și sisteme încorporate, unde codul și datele trebuie stocate în memoria flash și accesate eficient, fără a fi copiate în RAM.

Principalele avantaje ale ROMFS sunt:

  • Importuri zero-copy: fișierele bytecode .mpy stocate într-un ROMFS pot fi executate direct din memoria flash (memory-mapped), în loc să fie copiate mai întâi în RAM. Acest lucru este similar cu modul în care funcționează modulele înghețate, dar nu necesită reflasharea întregului firmware.

  • Consum redus de RAM: obiectele constante (șiruri de caractere, octeți etc.) din fișierele .mpy încărcate din ROMFS sunt referențiate direct din memoria flash, nefiind duplicate în RAM.

  • Implementare flexibilă: o imagine ROMFS poate fi construită pe un PC gazdă și implementată pe dispozitiv folosind mpremote, fără a reconstrui firmware-ul.

  • Interfață standard de sistem de fișiere: un ROMFS este montat în VFS și accesat prin operații normale Python pe fișiere (open, os.listdir, import etc.).

ROMFS este complementar atât sistemelor de fișiere citire-scriere FAT/LittleFS (care se află în alte partiții flash), cât și modulelor înghețate (care sunt compilate în firmware-ul propriu-zis).

Suport pentru plăci

ROMFS este activat în firmware-ul OpenMV pe fiecare placă de cameră care are o partiție ROMFS rezervată în structura sa flash. Pe aceste plăci, partiția ROMFS este detectată automat la pornire și montată la /rom; atât /rom cât și /rom/lib sunt adăugate în sys.path, astfel încât modulele stocate acolo să poată fi importate direct.

Placă

Suport ROMFS

OpenMV Cam N6

Da

OpenMV AE3

Da

OpenMV Cam RT1062

Da

OpenMV Cam Pure Thermal

Da

OpenMV Cam M4 / M7 / H7 / H7 Plus

Da

Arduino Giga

Da

Arduino Portenta H7

Da

Arduino Nicla Vision

Da

Arduino Nano 33 BLE Sense

Nu (fără partiție ROMFS)

Arduino Nano RP2040 Connect

Nu (fără partiție ROMFS)

Consultați romfs pentru un ajutor specific OpenMV care inspectează ROMFS-ul montat la /rom.

Flux de lucru

Fluxul de lucru tipic pentru utilizarea ROMFS este:

  1. Creați pe PC un director cu fișierele Python (sau fișierele .mpy) pe care doriți să le implementați.

  2. Folosiți mpremote romfs deploy <directory> pentru a construi și implementa imaginea ROMFS pe dispozitiv.

  3. ROMFS-ul va fi montat la /rom la următoarea pornire (sau poate fi montat imediat dacă dispozitivul este repornit).

  4. Codul Python de pe dispozitiv poate apoi să facă import de module din ROMFS exact ca din orice alt sistem de fișiere.

De exemplu:

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

După un soft-reset, dispozitivul va avea /rom/app.py (sau /rom/app.mpy dacă mpy_cross este instalat) disponibil pentru import.

Consultați secțiunea sub-comenzile mpremote romfs de mai jos pentru detalii complete despre sub-comenzile mpremote.

API-ul Python

API-ul Python pentru ROMFS este furnizat prin modulul vfs.

class vfs.VfsRom(buffer)

Creează un obiect sistem de fișiere ROMFS din buffer, care trebuie să fie un obiect ce suportă protocolul de buffer (de exemplu un obiect bytes, bytearray sau memoryview) și care conține o imagine ROMFS validă.

Constructorul validează faptul că buffer începe cu octeții magici ROMFS (b"\xd2\xcd\x31"). Dacă buffer-ul este prea mic sau nu este un ROMFS valid, se ridică OSError(ENODEV).

Obiectele create de acest constructor pot fi montate folosind vfs.mount().

Exemplu:

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')

Sau, pentru a monta o imagine ROMFS stocată într-un fișier:

import vfs

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

Următoarele metode sunt disponibile pentru un obiect VfsRom:

VfsRom.open(path, mode)

Deschide un fișier din ROMFS. Sunt suportate doar modurile de citire ('', 'r', 'rt', 'rb'). Încercarea de a deschide un fișier pentru scriere va ridica OSError(EROFS).

Obiectul fișier returnat suportă read(), seek(), tell() și close(). Pentru fișierele binare deschise în mod citire, obiectul returnat suportă de asemenea protocolul de buffer, astfel încât se poate obține un memoryview al datelor fișierului, care referențiază direct memoria ROMFS (zero-copy).

VfsRom.ilistdir(path)

Returnează un iterator peste intrările din directorul path. Fiecare intrare este un tuplu (name, type, inode, size), unde type este 0x8000 pentru un fișier sau 0x4000 pentru un director.

VfsRom.stat(path)

Returnează un tuplu cu 10 elemente, similar cu os.stat, pentru path. Ridică OSError(ENOENT) dacă calea nu există.

VfsRom.statvfs(path)

Returnează statistici despre sistemul de fișiere. Dimensiunea blocului este raportată ca 1, iar numărul de blocuri reprezintă dimensiunea totală a imaginii ROMFS în octeți. Blocurile libere și fișierele libere sunt întotdeauna 0 (sistem de fișiere doar pentru citire).

VfsRom.chdir(path)

Schimbă directorul în cadrul ROMFS. Este suportat doar rădăcina ('/'); schimbarea către orice subdirector ridică OSError(EOPNOTSUPP).

VfsRom.getcwd()

Returnează directorul de lucru curent în cadrul ROMFS. Returnează întotdeauna '/'.

vfs.rom_ioctl(op, ...)

Interfață de nivel scăzut pentru accesarea partiției(lor) de memorie doar pentru citire (ROM) ale dispozitivului.

Operațiile suportate sunt:

  • vfs.rom_ioctl(1) – Returnează numărul de partiții ROM disponibile.

  • vfs.rom_ioctl(2, id) – Returnează partiția ROM cu indicele id ca obiect memoryview. Memoria poate fi citită, dar nu scrisă direct.

  • vfs.rom_ioctl(3, id, length) – Pregătește o partiție ROM pentru scriere. Șterge primii length octeți ai partiției cu indicele id. Returnează dimensiunea minimă de scriere în octeți (alinierea necesară pentru scrierile ulterioare).

  • vfs.rom_ioctl(4, id, offset, buf) – Scrie buf (un obiect de tip bytes) în partiția ROM cu indicele id la octetul offset.

  • vfs.rom_ioctl(5, id) – Finalizează o secvență de scriere către partiția id (efectuează orice finalizare necesară după scriere, cum ar fi golirea cache-ului).

Aceste operații sunt utilizate intern de către mpremote pentru a implementa imagini ROMFS. Majoritatea utilizatorilor nu au nevoie să apeleze direct vfs.rom_ioctl().

Exemplu (interogarea partițiilor disponibile):

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")

Montare automată la pornire

Când suportul ROMFS este activat în firmware, MicroPython va încerca automat să monteze prima partiție ROM la /rom în timpul inițializării. Dacă partiția conține o imagine ROMFS validă, aceasta este montată, iar atât /rom cât și /rom/lib sunt adăugate automat în sys.path.

Aceasta înseamnă că, după implementarea unei imagini ROMFS cu mpremote, un soft-reset este suficient pentru a face noile module importabile.

Dacă nu se găsește nicio imagine ROMFS validă în partiție (de exemplu, pe o placă proaspăt programată), montarea este omisă în mod silențios.

Utilizarea mpremote pentru gestionarea ROMFS

Instrumentul mpremote oferă trei sub-comenzi pentru gestionarea imaginilor ROMFS de pe un dispozitiv conectat.

romfs query

$ mpremote romfs query

Listează toate partițiile ROMFS disponibile de pe dispozitiv și dimensiunile acestora. De asemenea, afișează primii 12 octeți ai fiecărei partiții în format hexazecimal și raportează dacă este prezentă o imagine ROMFS validă.

Exemplu de ieșire:

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>

Construiește o imagine ROMFS din directorul source de pe PC-ul gazdă. Imaginea este scrisă în output (implicit: <source>.romfs).

Opțiuni:

  • -o <output>, --output <output>: Specifică calea fișierului de ieșire.

  • -m, --mpy (implicit): Compilează automat fișierele .py în .mpy folosind mpy_cross înainte de a le adăuga în imagine. Necesită pachetul Python mpy_cross (pip install mpy_cross).

  • --no-mpy: Dezactivează compilarea automată a fișierelor .py.

Exemplu:

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

Implementează o imagine ROMFS pe dispozitiv. source poate fi fie:

  • Un director pe gazdă: imaginea ROMFS este construită în memorie și implementată direct.

  • Un fișier .romfs sau .img: imaginea este citită de pe disc și implementată.

Opțiuni:

  • -p <partition>, --partition <partition>: Specifică indicele partiției țintă (implicit: 0).

  • -m, --mpy (implicit): Compilează .py în .mpy când source este un director.

  • --no-mpy: Dezactivează compilarea automată a fișierelor .py.

După implementare, dispozitivul trebuie să fie supus unui soft-reset pentru ca noul ROMFS să fie montat la /rom.

Exemplu:

$ 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

Exemple

Implementarea unei aplicații simple

Să presupunem că aveți un director de proiect myapp/ cu următoarea structură:

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

Pentru a-l implementa în ROMFS-ul dispozitivului:

$ mpremote romfs deploy myapp/

După un soft-reset, modulele pot fi importate din ROMFS:

import main
import utils
from lib import helper

Listarea conținutului ROMFS din Python

După montare, conținutul ROMFS poate fi explorat ca orice alt sistem de fișiere:

import os

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

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

OpenMV include de asemenea un mic ajutor romfs care afișează o listare formatată ce include adresa memory-mapped și alinierea fiecărui fișier:

from omv import romfs
romfs.ls_romfs()

Imbricarea unui ROMFS în interiorul unui ROMFS

O imagine ROMFS stocată ca fișier în interiorul unui ROMFS exterior poate fi montată ca sistem de fișiere imbricat. De exemplu, dacă /rom/inner.romfs există. Deoarece /rom este un ROMFS, obiectele fișier deschise din acesta suportă protocolul de buffer, astfel încât se poate obține direct un memoryview zero-copy:

import vfs

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

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

Formatul imaginii ROMFS

Formatul imaginii ROMFS este un format binar compact, conceput pentru acces memory-mapped pe microcontrolere. O scurtă prezentare:

  • Imaginea începe cu octeții magici 0xd2 0xcd 0x31 (codificați ca "RM1" cu biții superiori ai primilor doi octeți setați).

  • Restul imaginii este compus din înregistrări, fiecare având o etichetă de tip (varuint), o lungime (varuint) și o sarcină utilă.

  • Tipurile de înregistrări includ: umplutură, date verbatim, pointer de date indirect, director, fișier.

  • Numele directoarelor și fișierelor sunt stocate ca șiruri de octeți prefixate cu lungime.

  • Datele fișierului pot fi stocate verbatim (inline) sau printr-un pointer indirect către altă locație din imagine, ceea ce permite alinierea pentru accesul memory-mapped.

  • Tipurile de înregistrări necunoscute sunt omise în mod silențios, asigurând compatibilitatea în avans.

Acest format este definit în extmod/vfs_rom.c în sursa MicroPython. Implementarea Python folosită de mpremote pentru a construi imagini se află în tools/mpremote/mpremote/romfs.py.

Vezi și

Lucrul cu sistemele de fișiere – Prezentare generală a VFS-ului MicroPython și a tipurilor de sisteme de fișiere disponibile.

Fișierele manifest MicroPython – Cum să înghețați module Python în firmware.

Fișiere .mpy MicroPython – Formatul fișierelor binare .mpy MicroPython.

Control la distanță MicroPython: mpremote – Referința completă a comenzilor mpremote.

romfs – Ajutor OpenMV pentru inspectarea sistemului de fișiere montat /rom.