การทำงานกับ ROMFS

ภาพรวม

ROMFS (Read-Only Memory Filesystem) คือระบบไฟล์แบบอ่านอย่างเดียวที่มีน้ำหนักเบา ออกแบบมาสำหรับอุปกรณ์ MicroPython โดยเฉพาะ เหมาะสำหรับไมโครคอนโทรลเลอร์และระบบฝังตัวที่ต้องเก็บโค้ดและข้อมูลไว้ในหน่วยความจำแฟลช และเข้าถึงได้อย่างมีประสิทธิภาพโดยไม่ต้องคัดลอกเข้า RAM

ประโยชน์หลักของ ROMFS ได้แก่:

  • การนำเข้าแบบ Zero-copy: ไฟล์ไบต์โค้ด .mpy ที่เก็บอยู่ใน ROMFS สามารถรันได้โดยตรงจากหน่วยความจำแฟลช (memory-mapped) โดยไม่ต้องคัดลอกเข้า RAM ก่อน ซึ่งคล้ายกับวิธีการทำงานของ frozen modules แต่ไม่จำเป็นต้องแฟลช เฟิร์มแวร์ทั้งหมดใหม่

  • RAM overhead ต่ำ: อ็อบเจกต์ค่าคงที่ (สตริง, bytes เป็นต้น) ในไฟล์ .mpy ที่โหลดจาก ROMFS จะอ้างอิงโดยตรงจากแฟลช ไม่มีการซ้ำซ้อนใน RAM

  • การปรับใช้ที่ยืดหยุ่น: สามารถสร้างอิมเมจ ROMFS บน PC แม่ข่ายแล้วปรับใช้กับอุปกรณ์ผ่าน mpremote โดยไม่ต้องสร้าง เฟิร์มแวร์ใหม่

  • อินเทอร์เฟซระบบไฟล์มาตรฐาน: ROMFS ถูก mount ใน VFS และเข้าถึงได้ผ่านการดำเนินการไฟล์ Python ปกติ (open, os.listdir, import เป็นต้น)

ROMFS เป็นส่วนเสริมทั้งระบบไฟล์ FAT/LittleFS แบบอ่าน-เขียน (ซึ่งอยู่ในพาร์ทิชันแฟลชอื่น) และ frozen modules (ซึ่งถูกคอมไพล์เข้าไปใน เฟิร์มแวร์โดยตรง)

การรองรับบอร์ด

ROMFS ถูกเปิดใช้งานใน เฟิร์มแวร์ OpenMV บนบอร์ดกล้องทุกรุ่นที่มีพาร์ทิชัน ROMFS สำรองไว้ในเลย์เอาต์แฟลช บนบอร์ดเหล่านี้ พาร์ทิชัน ROMFS จะถูกตรวจจับอัตโนมัติตอนบูตและ mount ที่ /rom พร้อมกันนั้น /rom และ /rom/lib จะถูกเพิ่มเข้า sys.path เพื่อให้สามารถ import โมดูลที่เก็บอยู่ที่นั่นได้โดยตรง

บอร์ด

การรองรับ ROMFS

OpenMV Cam N6

ใช่

OpenMV AE3

ใช่

OpenMV Cam RT1062

ใช่

OpenMV Cam Pure Thermal

ใช่

OpenMV Cam M4 / M7 / H7 / H7 Plus

ใช่

Arduino Giga

ใช่

Arduino Portenta H7

ใช่

Arduino Nicla Vision

ใช่

Arduino Nano 33 BLE Sense

ไม่ (ไม่มีพาร์ทิชัน ROMFS)

Arduino Nano RP2040 Connect

ไม่ (ไม่มีพาร์ทิชัน ROMFS)

ดู romfs สำหรับตัวช่วยเฉพาะ OpenMV ที่ตรวจสอบ ROMFS ที่ mount ไว้ที่ /rom

ขั้นตอนการทำงาน

ขั้นตอนทั่วไปสำหรับการใช้ ROMFS มีดังนี้:

  1. สร้างไดเรกทอรีบน PC ของคุณพร้อมไฟล์ Python (หรือไฟล์ .mpy) ที่ต้องการปรับใช้

  2. ใช้ mpremote romfs deploy <directory> เพื่อสร้างและปรับใช้อิมเมจ ROMFS กับอุปกรณ์

  3. ROMFS จะถูก mount ที่ /rom ในการบูตครั้งถัดไป (หรือสามารถ mount ได้ทันทีหากรีบูตอุปกรณ์)

  4. โค้ด Python บนอุปกรณ์สามารถ import โมดูลจาก ROMFS ได้เหมือนกับระบบไฟล์อื่น ๆ

ตัวอย่าง:

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

หลังจาก soft-reset อุปกรณ์จะมี /rom/app.py (หรือ /rom/app.mpy หากติดตั้ง mpy_cross ไว้) พร้อมสำหรับการ import

ดูส่วน mpremote romfs sub-commands ด้านล่างสำหรับรายละเอียดทั้งหมดของ sub-commands mpremote

Python API

ROMFS Python API ให้บริการผ่านโมดูล vfs

class vfs.VfsRom(buffer)

สร้าง อ็อบเจกต์ระบบไฟล์ ROMFS จาก buffer ซึ่งต้องเป็นอ็อบเจกต์ที่รองรับ buffer protocol (เช่น อ็อบเจกต์ bytes, bytearray หรือ memoryview) ที่ประกอบด้วยอิมเมจ ROMFS ที่ถูกต้อง

คอนสตรักเตอร์จะตรวจสอบว่า buffer เริ่มต้นด้วย magic bytes ของ ROMFS (b"\xd2\xcd\x31") หาก buffer มีขนาดเล็กเกินไปหรือไม่ใช่ ROMFS ที่ถูกต้อง จะเกิด OSError(ENODEV)

อ็อบเจกต์ที่สร้างโดยคอนสตรักเตอร์นี้สามารถ mount ได้โดยใช้ vfs.mount()

ตัวอย่าง:

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

หรือ mount อิมเมจ ROMFS ที่เก็บอยู่ในไฟล์:

import vfs

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

เมธอดต่อไปนี้ใช้ได้กับอ็อบเจกต์ VfsRom:

VfsRom.open(path, mode)

เปิดไฟล์จาก ROMFS รองรับเฉพาะโหมดอ่าน ('', 'r', 'rt', 'rb') การพยายามเปิดไฟล์เพื่อเขียนจะเกิด OSError(EROFS)

อ็อบเจกต์ไฟล์ที่ส่งกลับมารองรับ read(), seek(), tell() และ close() สำหรับไฟล์ไบนารีที่เปิดในโหมดอ่าน อ็อบเจกต์ที่ส่งกลับมายังรองรับ buffer protocol เพื่อให้สามารถรับ memoryview ของข้อมูลไฟล์ได้ ซึ่งอ้างอิงโดยตรงไปยังหน่วยความจำ ROMFS (zero-copy)

VfsRom.ilistdir(path)

ส่งคืนตัววนซ้ำผ่านรายการในไดเรกทอรี path แต่ละรายการเป็น tuple (name, type, inode, size) โดย type คือ 0x8000 สำหรับไฟล์หรือ 0x4000 สำหรับไดเรกทอรี

VfsRom.stat(path)

ส่งคืน tuple 10 ค่าคล้าย os.stat สำหรับ path เกิด OSError(ENOENT) หากไม่พบ path

VfsRom.statvfs(path)

ส่งคืนสถิติระบบไฟล์ ขนาดบล็อกถูกรายงานเป็น 1 และจำนวนบล็อกแสดงขนาดรวมของอิมเมจ ROMFS เป็นไบต์ บล็อกว่างและไฟล์ว่างจะเป็น 0 เสมอ (ระบบไฟล์แบบอ่านอย่างเดียว)

VfsRom.chdir(path)

เปลี่ยนไดเรกทอรีภายใน ROMFS รองรับเฉพาะ root ('/') เท่านั้น การเปลี่ยนไปยัง subdirectory ใด ๆ จะเกิด OSError(EOPNOTSUPP)

VfsRom.getcwd()

ส่งคืนไดเรกทอรีทำงานปัจจุบันภายใน ROMFS จะส่งคืน '/' เสมอ

vfs.rom_ioctl(op, ...)

อินเทอร์เฟซระดับต่ำสำหรับเข้าถึงพาร์ทิชัน read-only memory (ROM) ของอุปกรณ์

การดำเนินการที่รองรับมีดังนี้:

  • vfs.rom_ioctl(1) -- ส่งคืนจำนวนพาร์ทิชัน ROM ที่มีอยู่

  • vfs.rom_ioctl(2, id) -- ส่งคืนพาร์ทิชัน ROM ที่มี index id เป็นอ็อบเจกต์ memoryview หน่วยความจำสามารถอ่านได้แต่ไม่สามารถเขียนโดยตรงได้

  • vfs.rom_ioctl(3, id, length) -- เตรียมพาร์ทิชัน ROM สำหรับการเขียน ลบ length ไบต์แรกของพาร์ทิชันที่มี index id ส่งคืนขนาดการเขียนขั้นต่ำเป็นไบต์ (การจัดตำแหน่งที่จำเป็นสำหรับการเขียนครั้งถัดไป)

  • vfs.rom_ioctl(4, id, offset, buf) -- เขียน buf (อ็อบเจกต์คล้าย bytes) ไปยังพาร์ทิชัน ROM ที่มี index id ที่ byte offset

  • vfs.rom_ioctl(5, id) -- เสร็จสิ้นลำดับการเขียนไปยังพาร์ทิชัน id (ดำเนินการสรุปผลที่จำเป็นหลังการเขียน เช่น การล้างแคช)

การดำเนินการเหล่านี้ถูกใช้ภายในโดย mpremote เพื่อปรับใช้อิมเมจ ROMFS ผู้ใช้ส่วนใหญ่ไม่จำเป็นต้องเรียก vfs.rom_ioctl() โดยตรง

ตัวอย่าง (การสอบถามพาร์ทิชันที่มีอยู่):

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

การ mount อัตโนมัติตอนบูต

เมื่อเปิดใช้งานการรองรับ ROMFS ใน เฟิร์มแวร์ MicroPython จะพยายาม mount พาร์ทิชัน ROM แรกที่ /rom โดยอัตโนมัติระหว่างการเริ่มต้นระบบ หากพาร์ทิชันมีอิมเมจ ROMFS ที่ถูกต้อง ระบบจะ mount และเพิ่มทั้ง /rom และ /rom/lib เข้า sys.path โดยอัตโนมัติ

ซึ่งหมายความว่าหลังจากปรับใช้อิมเมจ ROMFS ด้วย mpremote การ soft-reset เพียงครั้งเดียวก็เพียงพอที่จะทำให้โมดูลใหม่ import ได้

หากไม่พบอิมเมจ ROMFS ที่ถูกต้องในพาร์ทิชัน (เช่น บนบอร์ดที่เพิ่งโปรแกรมใหม่) การ mount จะถูกข้ามอย่างเงียบ ๆ

การใช้ mpremote เพื่อจัดการ ROMFS

เครื่องมือ mpremote มี sub-commands สามรายการสำหรับจัดการอิมเมจ ROMFS บนอุปกรณ์ที่เชื่อมต่อ

romfs query

$ mpremote romfs query

แสดงรายการพาร์ทิชัน ROMFS ทั้งหมดที่มีอยู่บนอุปกรณ์และขนาดของแต่ละพาร์ทิชัน นอกจากนี้ยังแสดง 12 ไบต์แรกของแต่ละพาร์ทิชันในรูปแบบ hex และรายงานว่ามีอิมเมจ ROMFS ที่ถูกต้องหรือไม่

ตัวอย่างผลลัพธ์:

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>

สร้างอิมเมจ ROMFS จากไดเรกทอรี source บน PC แม่ข่าย อิมเมจจะถูกเขียนไปยัง output (ค่าเริ่มต้น: <source>.romfs)

ตัวเลือก:

  • -o <output>, --output <output>: ระบุ path ไฟล์ผลลัพธ์

  • -m, --mpy (ค่าเริ่มต้น): คอมไพล์ไฟล์ .py เป็น .mpy โดยอัตโนมัติโดยใช้ mpy_cross ก่อนเพิ่มเข้าอิมเมจ ต้องติดตั้ง Python package mpy_cross (pip install mpy_cross)

  • --no-mpy: ปิดการคอมไพล์ไฟล์ .py อัตโนมัติ

ตัวอย่าง:

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

ปรับใช้อิมเมจ ROMFS ไปยังอุปกรณ์ source สามารถเป็น:

  • ไดเรกทอรีบนเครื่องแม่ข่าย: อิมเมจ ROMFS ถูกสร้างในหน่วยความจำและปรับใช้โดยตรง

  • ไฟล์ .romfs หรือ .img: อิมเมจถูกอ่านจากดิสก์และปรับใช้

ตัวเลือก:

  • -p <partition>, --partition <partition>: ระบุ index พาร์ทิชันเป้าหมาย (ค่าเริ่มต้น: 0)

  • -m, --mpy (ค่าเริ่มต้น): คอมไพล์ .py เป็น .mpy เมื่อ source เป็นไดเรกทอรี

  • --no-mpy: ปิดการคอมไพล์ไฟล์ .py อัตโนมัติ

หลังการปรับใช้ อุปกรณ์ต้องถูก soft-reset เพื่อให้ ROMFS ใหม่ถูก mount ที่ /rom

ตัวอย่าง:

$ 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

ตัวอย่าง

การปรับใช้แอปพลิเคชันธรรมดา

สมมติว่าคุณมีไดเรกทอรีโปรเจกต์ myapp/ ที่มีโครงสร้างดังนี้:

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

เพื่อปรับใช้กับ ROMFS ของอุปกรณ์:

$ mpremote romfs deploy myapp/

หลังจาก soft-reset โมดูลสามารถ import ได้จาก ROMFS:

import main
import utils
from lib import helper

การแสดงเนื้อหา ROMFS จาก Python

หลังจาก mount สามารถสำรวจเนื้อหา ROMFS ได้เหมือนกับระบบไฟล์อื่น ๆ:

import os

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

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

OpenMV ยังมี helper romfs ขนาดเล็กที่แสดงรายการที่จัดรูปแบบรวมถึงที่อยู่ memory-mapped และการจัดตำแหน่งของแต่ละไฟล์:

from omv import romfs
romfs.ls_romfs()

การซ้อน ROMFS ภายใน ROMFS

อิมเมจ ROMFS ที่เก็บเป็นไฟล์ภายใน ROMFS ภายนอกสามารถ mount เป็นระบบไฟล์ซ้อนกันได้ ตัวอย่างเช่น หากมี /rom/inner.romfs เนื่องจาก /rom เป็น ROMFS อ็อบเจกต์ไฟล์ที่เปิดจากมันจะรองรับ buffer protocol ดังนั้นจึงสามารถรับ 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'))

รูปแบบอิมเมจ ROMFS

รูปแบบอิมเมจ ROMFS เป็นรูปแบบไบนารีขนาดกะทัดรัดออกแบบมาสำหรับการเข้าถึงแบบ memory-mapped บนไมโครคอนโทรลเลอร์ ภาพรวมสั้น ๆ:

  • อิมเมจเริ่มต้นด้วย magic bytes 0xd2 0xcd 0x31 (เข้ารหัสเป็น "RM1" โดยมีบิตสูงของสองไบต์แรกถูกตั้งค่า)

  • ส่วนที่เหลือของอิมเมจประกอบด้วย records แต่ละรายการมี type tag (varuint), ความยาว (varuint) และ payload

  • ประเภทของ record ได้แก่: padding, verbatim data, indirect data pointer, directory, file

  • ชื่อไดเรกทอรีและไฟล์ถูกเก็บเป็น byte strings ที่นำหน้าด้วยความยาว

  • ข้อมูลไฟล์สามารถเก็บแบบ verbatim (inline) หรือผ่าน indirect pointer ไปยังตำแหน่งอื่นในอิมเมจ ซึ่งช่วยให้จัดตำแหน่งสำหรับการเข้าถึงแบบ memory-mapped

  • ประเภท record ที่ไม่รู้จักจะถูกข้ามอย่างเงียบ ๆ เพื่อรองรับความเข้ากันได้ในอนาคต

รูปแบบนี้ถูกกำหนดไว้ใน extmod/vfs_rom.c ในซอร์สโค้ดของ MicroPython การใช้งาน Python ที่ mpremote ใช้สร้างอิมเมจอยู่ใน tools/mpremote/mpremote/romfs.py

See also

การทำงานกับระบบไฟล์ -- ภาพรวมของ MicroPython VFS และประเภทระบบไฟล์ที่รองรับ

ไฟล์แมนิเฟสต์ของ MicroPython -- วิธีการ freeze โมดูล Python เข้าใน เฟิร์มแวร์

ไฟล์ .mpy ของ MicroPython -- รูปแบบไฟล์ไบนารี .mpy ของ MicroPython

MicroPython การควบคุมระยะไกล: mpremote -- เอกสารอ้างอิงคำสั่ง mpremote ฉบับสมบูรณ์

romfs -- helper ของ OpenMV สำหรับตรวจสอบระบบไฟล์ /rom ที่ mount ไว้