Làm việc với ROMFS

Tổng quan

ROMFS (Hệ thống tệp bộ nhớ chỉ đọc) là một hệ thống tệp nhỏ gọn, chỉ đọc được thiết kế cho các thiết bị MicroPython. Nó được tối ưu hóa cho vi điều khiển và hệ thống nhúng, nơi mã và dữ liệu cần được lưu trữ trong bộ nhớ flash và truy cập hiệu quả mà không cần sao chép vào RAM.

Các lợi ích chính của ROMFS là:

  • Import không sao chép (zero-copy): Các tệp bytecode .mpy được lưu trong ROMFS có thể được thực thi trực tiếp từ bộ nhớ flash (ánh xạ bộ nhớ) thay vì phải sao chép vào RAM trước. Điều này tương tự như cách hoạt động của các mô-đun đóng băng, nhưng không yêu cầu nạp lại toàn bộ firmware.

  • Tốn ít RAM: Các đối tượng hằng số (chuỗi, bytes, v.v.) trong các tệp .mpy được tải từ ROMFS được tham chiếu trực tiếp từ flash, không nhân bản trong RAM.

  • Triển khai linh hoạt: Ảnh ROMFS có thể được xây dựng trên máy tính chủ và triển khai tới thiết bị bằng mpremote, mà không cần xây dựng lại firmware.

  • Giao diện hệ thống tệp chuẩn: ROMFS được gắn kết trong VFS và được truy cập thông qua các thao tác tệp Python thông thường (open, os.listdir, import, v.v.).

ROMFS bổ sung cho cả hệ thống tệp FAT/LittleFS có thể đọc-ghi (nằm trên các phân vùng flash khác) và cho các mô-đun đóng băng (được biên dịch trực tiếp vào firmware).

Hỗ trợ bo mạch

ROMFS được bật trong firmware OpenMV trên mọi bo mạch camera có phân vùng ROMFS được dành riêng trong bố cục flash. Trên các bo mạch này, phân vùng ROMFS được tự động phát hiện khi khởi động và gắn kết tại /rom; cả /rom/rom/lib đều được thêm vào sys.path để các mô-đun lưu trữ ở đó có thể được import trực tiếp.

Bo mạch

Hỗ trợ 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

Không (không có phân vùng ROMFS)

Arduino Nano RP2040 Connect

Không (không có phân vùng ROMFS)

Xem romfs để biết về trợ giúp dành riêng cho OpenMV kiểm tra ROMFS được gắn kết tại /rom.

Quy trình làm việc

Quy trình điển hình để sử dụng ROMFS là:

  1. Tạo một thư mục trên máy tính của bạn chứa các tệp Python (hoặc tệp .mpy) bạn muốn triển khai.

  2. Sử dụng mpremote romfs deploy <directory> để xây dựng và triển khai ảnh ROMFS tới thiết bị.

  3. ROMFS sẽ được gắn kết tại /rom khi khởi động lần tiếp theo (hoặc có thể gắn kết ngay lập tức nếu thiết bị được khởi động lại).

  4. Mã Python trên thiết bị sau đó có thể import các mô-đun từ ROMFS giống như từ bất kỳ hệ thống tệp nào khác.

Ví dụ:

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

Sau khi soft-reset, thiết bị sẽ có /rom/app.py (hoặc /rom/app.mpy nếu mpy_cross được cài đặt) sẵn sàng để import.

Xem phần các lệnh con romfs của mpremote bên dưới để biết chi tiết đầy đủ về các lệnh con mpremote.

API Python

API Python cho ROMFS được cung cấp thông qua mô-đun vfs.

class vfs.VfsRom(buffer)

Tạo đối tượng hệ thống tệp ROMFS từ buffer, phải là một đối tượng hỗ trợ giao thức buffer (ví dụ: đối tượng bytes, bytearray hoặc memoryview) chứa ảnh ROMFS hợp lệ.

Hàm khởi tạo xác thực rằng buffer bắt đầu bằng các byte kỳ diệu của ROMFS (b"\xd2\xcd\x31"). Nếu buffer quá nhỏ hoặc không phải là ROMFS hợp lệ, OSError(ENODEV) sẽ được phát sinh.

Các đối tượng được tạo bởi hàm khởi tạo này có thể được gắn kết bằng vfs.mount().

Ví dụ:

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

Hoặc, để gắn kết ảnh ROMFS được lưu trong một tệp:

import vfs

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

Các phương thức sau đây có sẵn trên đối tượng VfsRom:

VfsRom.open(path, mode)

Mở một tệp từ ROMFS. Chỉ hỗ trợ chế độ đọc ('', 'r', 'rt', 'rb'). Cố gắng mở tệp để ghi sẽ phát sinh OSError(EROFS).

Đối tượng tệp trả về hỗ trợ read(), seek(), tell()close(). Đối với các tệp nhị phân được mở ở chế độ đọc, đối tượng trả về cũng hỗ trợ giao thức buffer để có thể lấy memoryview của dữ liệu tệp, tham chiếu trực tiếp vào bộ nhớ ROMFS (zero-copy).

VfsRom.ilistdir(path)

Trả về một iterator qua các mục trong thư mục path. Mỗi mục là một tuple (name, type, inode, size) trong đó type0x8000 cho tệp hoặc 0x4000 cho thư mục.

VfsRom.stat(path)

Trả về một tuple 10 phần tử giống os.stat cho path. Phát sinh OSError(ENOENT) nếu đường dẫn không tồn tại.

VfsRom.statvfs(path)

Trả về thống kê hệ thống tệp. Kích thước khối được báo cáo là 1 và số lượng khối biểu thị tổng kích thước của ảnh ROMFS tính bằng byte. Các khối trống và tệp trống luôn là 0 (hệ thống tệp chỉ đọc).

VfsRom.chdir(path)

Thay đổi thư mục trong ROMFS. Chỉ hỗ trợ thư mục gốc ('/'); thay đổi sang bất kỳ thư mục con nào sẽ phát sinh OSError(EOPNOTSUPP).

VfsRom.getcwd()

Trả về thư mục làm việc hiện tại trong ROMFS. Luôn trả về '/'.

vfs.rom_ioctl(op, ...)

Giao diện cấp thấp để truy cập phân vùng bộ nhớ chỉ đọc (ROM) của thiết bị.

Các thao tác được hỗ trợ là:

  • vfs.rom_ioctl(1) -- Trả về số lượng phân vùng ROM có sẵn.

  • vfs.rom_ioctl(2, id) -- Trả về phân vùng ROM với chỉ số id dưới dạng đối tượng memoryview. Bộ nhớ có thể đọc nhưng không thể ghi trực tiếp.

  • vfs.rom_ioctl(3, id, length) -- Chuẩn bị phân vùng ROM để ghi. Xóa length byte đầu tiên của phân vùng với chỉ số id. Trả về kích thước ghi tối thiểu tính bằng byte (căn chỉnh cần thiết cho các lần ghi tiếp theo).

  • vfs.rom_ioctl(4, id, offset, buf) -- Ghi buf (đối tượng kiểu bytes) vào phân vùng ROM với chỉ số id tại byte offset.

  • vfs.rom_ioctl(5, id) -- Hoàn thành chuỗi ghi vào phân vùng id (thực hiện mọi hoàn thiện cần thiết sau khi ghi, chẳng hạn như xả cache).

Các thao tác này được sử dụng nội bộ bởi mpremote để triển khai ảnh ROMFS. Hầu hết người dùng không cần gọi vfs.rom_ioctl() trực tiếp.

Ví dụ (truy vấn các phân vùng có sẵn):

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

Tự động gắn kết khi khởi động

Khi hỗ trợ ROMFS được bật trong firmware, MicroPython sẽ tự động cố gắng gắn kết phân vùng ROM đầu tiên tại /rom trong quá trình khởi tạo. Nếu phân vùng chứa ảnh ROMFS hợp lệ, nó sẽ được gắn kết và cả /rom lẫn /rom/lib đều được thêm tự động vào sys.path.

Điều này có nghĩa là sau khi triển khai ảnh ROMFS bằng mpremote, một lần soft-reset là đủ để các mô-đun mới có thể được import.

Nếu không tìm thấy ảnh ROMFS hợp lệ trong phân vùng (ví dụ: trên bo mạch mới được lập trình), quá trình gắn kết sẽ bị bỏ qua một cách im lặng.

Sử dụng mpremote để quản lý ROMFS

Công cụ mpremote cung cấp ba lệnh con để quản lý ảnh ROMFS trên thiết bị được kết nối.

romfs query

$ mpremote romfs query

Liệt kê tất cả các phân vùng ROMFS có sẵn trên thiết bị và kích thước của chúng. Cũng hiển thị 12 byte đầu tiên của mỗi phân vùng ở dạng hex và báo cáo xem có ảnh ROMFS hợp lệ hay không.

Ví dụ đầu ra:

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>

Xây dựng ảnh ROMFS từ thư mục source trên máy tính chủ. Ảnh được ghi vào output (mặc định: <source>.romfs).

Các tùy chọn:

  • -o <output>, --output <output>: Chỉ định đường dẫn tệp đầu ra.

  • -m, --mpy (mặc định): Tự động biên dịch các tệp .py sang .mpy bằng mpy_cross trước khi thêm chúng vào ảnh. Yêu cầu gói Python mpy_cross (pip install mpy_cross).

  • --no-mpy: Tắt biên dịch tự động các tệp .py.

Ví dụ:

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

Triển khai ảnh ROMFS tới thiết bị. source có thể là:

  • Một thư mục trên máy chủ: ảnh ROMFS được xây dựng trong bộ nhớ và triển khai trực tiếp.

  • Một tệp .romfs hoặc .img: ảnh được đọc từ đĩa và triển khai.

Các tùy chọn:

  • -p <partition>, --partition <partition>: Chỉ định chỉ số phân vùng đích (mặc định: 0).

  • -m, --mpy (mặc định): Biên dịch .py sang .mpy khi source là một thư mục.

  • --no-mpy: Tắt biên dịch tự động các tệp .py.

Sau khi triển khai, thiết bị phải được soft-reset để ROMFS mới được gắn kết tại /rom.

Ví dụ:

$ 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

Ví dụ

Triển khai một ứng dụng đơn giản

Giả sử bạn có thư mục dự án myapp/ với cấu trúc sau:

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

Để triển khai nó lên ROMFS của thiết bị:

$ mpremote romfs deploy myapp/

Sau khi soft-reset, các mô-đun có thể được import từ ROMFS:

import main
import utils
from lib import helper

Liệt kê nội dung ROMFS từ Python

Sau khi gắn kết, nội dung ROMFS có thể được khám phá như bất kỳ hệ thống tệp nào khác:

import os

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

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

OpenMV cũng đi kèm một trợ giúp nhỏ romfs in danh sách có định dạng bao gồm địa chỉ ánh xạ bộ nhớ và căn chỉnh của mỗi tệp:

from omv import romfs
romfs.ls_romfs()

Lồng ROMFS trong ROMFS

Ảnh ROMFS được lưu dưới dạng tệp trong ROMFS bên ngoài có thể được gắn kết như một hệ thống tệp lồng nhau. Ví dụ: nếu /rom/inner.romfs tồn tại. Vì /rom là ROMFS, các đối tượng tệp được mở từ nó hỗ trợ giao thức buffer, vì vậy có thể lấy trực tiếp 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'))

Định dạng ảnh ROMFS

Định dạng ảnh ROMFS là một định dạng nhị phân nhỏ gọn được thiết kế cho truy cập ánh xạ bộ nhớ trên vi điều khiển. Tổng quan ngắn gọn:

  • Ảnh bắt đầu bằng các byte kỳ diệu 0xd2 0xcd 0x31 (được mã hóa là "RM1" với các bit cao của hai byte đầu tiên được đặt).

  • Phần còn lại của ảnh bao gồm các bản ghi, mỗi bản ghi có thẻ loại (varuint), độ dài (varuint) và tải trọng.

  • Các loại bản ghi bao gồm: đệm, dữ liệu nguyên văn, con trỏ dữ liệu gián tiếp, thư mục, tệp.

  • Tên thư mục và tệp được lưu trữ dưới dạng chuỗi byte có tiền tố độ dài.

  • Dữ liệu tệp có thể được lưu trữ nguyên văn (nội tuyến) hoặc thông qua con trỏ gián tiếp đến nơi khác trong ảnh, điều này cho phép căn chỉnh để truy cập ánh xạ bộ nhớ.

  • Các loại bản ghi không rõ sẽ bị bỏ qua một cách im lặng, cung cấp khả năng tương thích về phía trước.

Định dạng này được định nghĩa trong extmod/vfs_rom.c trong nguồn MicroPython. Cài đặt Python được mpremote sử dụng để xây dựng ảnh nằm trong tools/mpremote/mpremote/romfs.py.

Xem thêm

Làm việc với hệ thống tệp -- Tổng quan về VFS MicroPython và các loại hệ thống tệp có sẵn.

Tệp manifest MicroPython -- Cách đóng băng các mô-đun Python vào firmware.

Các tệp .mpy của MicroPython -- Định dạng tệp nhị phân .mpy của MicroPython.

Điều khiển từ xa MicroPython: mpremote -- Tài liệu tham khảo lệnh mpremote đầy đủ.

romfs -- Trợ giúp OpenMV để kiểm tra hệ thống tệp /rom được gắn kết.