ROMFS の利用¶
概要¶
ROMFS(Read-Only Memory Filesystem)は、MicroPython デバイス向けに設計された軽量な読み取り専用ファイルシステムです。コードやデータをフラッシュメモリに格納し、RAM にコピーすることなく効率的にアクセスする必要があるマイクロコントローラや組み込みシステム向けに最適化されています。
ROMFS の主な利点は次のとおりです。
ゼロコピーインポート: ROMFS に格納された
.mpyバイトコードファイルは、いったん RAM にコピーされるのではなく、フラッシュメモリから直接(メモリマップして)実行できます。これは フリーズモジュール の仕組みに似ていますが、ファームウェア全体の再フラッシュを必要としません。低い RAM オーバーヘッド: ROMFS から読み込まれた
.mpyファイル内の定数オブジェクト(文字列、バイト列など)は、RAM に複製されるのではなくフラッシュから直接参照されます。柔軟なデプロイ: ROMFS イメージはホスト PC 上でビルドし、ファームウェアを再ビルドすることなく mpremote を使ってデバイスにデプロイできます。
標準的なファイルシステムインターフェース: ROMFS は VFS にマウントされ、通常の Python のファイル操作(
open、os.listdir、importなど)でアクセスできます。
ROMFS は、読み書き可能な FAT/LittleFS ファイルシステム(他のフラッシュパーティションに存在します)と フリーズモジュール(ファームウェア自体にコンパイルされます)の両方を補完するものです。
ボードサポート¶
ROMFS は、フラッシュレイアウト内に ROMFS パーティションが予約されているすべてのカメラボードの OpenMV ファームウェアで有効になっています。これらのボードでは、起動時に ROMFS パーティションが自動検出されて /rom にマウントされます。また、/rom と /rom/lib の両方が sys.path に追加されるため、そこに格納されたモジュールを直接インポートできます。
ボード |
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 パーティションなし) |
/rom にマウントされた ROMFS を検査する OpenMV 固有のヘルパーについては romfs を参照してください。
ワークフロー¶
ROMFS を使用する際の一般的なワークフローは次のとおりです。
デプロイしたい Python ファイル(または
.mpyファイル)を含むディレクトリを PC 上に作成します。mpremote romfs deploy <directory>を使用して ROMFS イメージをビルドし、デバイスにデプロイします。ROMFS は次回起動時に
/romにマウントされます(デバイスを再起動すればすぐにマウントすることもできます)。その後、デバイス上の Python コードは、他のファイルシステムと同じように ROMFS からモジュールを
importできます。
例えば次のようになります:
# On the host PC, with a directory "myapp/" containing app.py:
$ mpremote romfs deploy myapp/
ソフトリセット後、デバイスでは /rom/app.py(mpy_cross がインストールされている場合は /rom/app.mpy)がインポート可能になります。
mpremote のサブコマンドの詳細については、後述の mpremote romfs サブコマンド セクションを参照してください。
Python API¶
ROMFS の Python API は vfs モジュールを介して提供されます。
- class vfs.VfsRom(buffer)
buffer から ROMFS ファイルシステムオブジェクトを作成します。buffer は、有効な ROMFS イメージを含む、バッファプロトコルをサポートするオブジェクト(例:
bytes、bytearray、memoryviewオブジェクト)でなければなりません。コンストラクタは、buffer が ROMFS のマジックバイト(
b"\xd2\xcd\x31")で始まることを検証します。バッファが小さすぎる場合や有効な ROMFS でない場合はOSError(ENODEV)が発生します。このコンストラクタで作成したオブジェクトは
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')
または、ファイルに格納された 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()をサポートします。読み取りモードで開かれたバイナリファイルの場合、返されるオブジェクトはバッファプロトコルもサポートするため、ファイルデータのmemoryviewを取得できます。これは ROMFS のメモリを直接参照します(ゼロコピー)。
- VfsRom.ilistdir(path)
ディレクトリ path 内のエントリを反復処理するイテレータを返します。各エントリはタプル
(name, type, inode, size)で、type はファイルの場合0x8000、ディレクトリの場合0x4000です。
- VfsRom.stat(path)
path に対して
os.statのような 10 要素のタプルを返します。パスが存在しない場合はOSError(ENOENT)が発生します。
- VfsRom.statvfs(path)
ファイルシステムの統計情報を返します。ブロックサイズは 1 として報告され、ブロック数は ROMFS イメージの合計サイズ(バイト単位)を表します。空きブロックと空きファイルは常に 0 です(読み取り専用ファイルシステムのため)。
- VfsRom.chdir(path)
ROMFS 内でディレクトリを変更します。サポートされているのはルート(
'/')のみで、サブディレクトリへの変更はOSError(EOPNOTSUPP)を発生させます。
- VfsRom.getcwd()
ROMFS 内の現在の作業ディレクトリを返します。常に
'/'を返します。
- vfs.rom_ioctl(op, ...)
デバイスの読み取り専用メモリ(ROM)パーティションにアクセスするための低レベルインターフェースです。
サポートされている操作は次のとおりです。
vfs.rom_ioctl(1)-- 利用可能な ROM パーティションの数を返します。vfs.rom_ioctl(2, id)-- インデックス id の ROM パーティションをmemoryviewオブジェクトとして返します。このメモリは読み取りできますが、直接書き込むことはできません。vfs.rom_ioctl(3, id, length)-- ROM パーティションを書き込み用に準備します。インデックス id のパーティションの先頭 length バイトを消去します。最小書き込みサイズ(後続の書き込みに必要なアライメント)をバイト単位で返します。vfs.rom_ioctl(4, id, offset, buf)-- インデックス id の ROM パーティションのバイトオフセット offset に buf(bytes ライクなオブジェクト)を書き込みます。vfs.rom_ioctl(5, id)-- パーティション id への書き込みシーケンスを完了します(キャッシュフラッシュなど、書き込み後に必要な最終処理を実行します)。
これらの操作は、ROMFS イメージをデプロイするために
mpremoteによって内部的に使用されます。ほとんどのユーザーは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")
起動時の自動マウント¶
ファームウェアで ROMFS サポートが有効になっている場合、MicroPython は初期化中に最初の ROM パーティションを /rom に自動的にマウントしようとします。パーティションに有効な ROMFS イメージが含まれていれば、それがマウントされ、/rom と /rom/lib の両方が自動的に sys.path に追加されます。
つまり、mpremote で ROMFS イメージをデプロイした後は、ソフトリセットを行うだけで新しいモジュールをインポート可能にできます。
パーティションに有効な ROMFS イメージが見つからない場合(例: プログラム書き込み直後のボード)は、マウントは何も告げずにスキップされます。
mpremote を使用した ROMFS の管理¶
mpremote ツールは、接続されたデバイス上で ROMFS イメージを管理するための 3 つのサブコマンドを提供します。
romfs query¶
$ mpremote romfs query
デバイス上で利用可能なすべての ROMFS パーティションとそのサイズを一覧表示します。また、各パーティションの先頭 12 バイトを 16 進数で表示し、有効な 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>
ホスト PC 上のディレクトリ source から ROMFS イメージをビルドします。イメージは output(デフォルト: <source>.romfs)に書き込まれます。
オプション:
-o <output>、--output <output>: 出力ファイルのパスを指定します。-m、--mpy(デフォルト): イメージに追加する前に、mpy_crossを使用して.pyファイルを.mpyに自動的にコンパイルします。mpy_crossPython パッケージ(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>: 対象パーティションのインデックスを指定します(デフォルト:0)。-m、--mpy(デフォルト): source がディレクトリの場合に.pyを.mpyにコンパイルします。--no-mpy:.pyファイルの自動コンパイルを無効にします。
デプロイ後、新しい ROMFS を /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/
ソフトリセット後、モジュールは ROMFS からインポート可能になります:
import main
import utils
from lib import helper
Python から ROMFS の内容を一覧表示する¶
マウント後、ROMFS の内容は他のファイルシステムと同じように調べることができます:
import os
for entry in os.ilistdir('/rom'):
print(entry)
# Or simply:
print(os.listdir('/rom'))
OpenMV には、各ファイルのメモリマップアドレスとアライメントを含む整形済みの一覧を表示する小さな romfs ヘルパーも付属しています:
from omv import romfs
romfs.ls_romfs()
ROMFS 内での ROMFS のネスト¶
外側の ROMFS 内にファイルとして格納された ROMFS イメージは、ネストされたファイルシステムとしてマウントできます。例えば、/rom/inner.romfs が存在する場合です。/rom は ROMFS であるため、そこから開いたファイルオブジェクトはバッファプロトコルをサポートし、ゼロコピーの memoryview を直接取得できます:
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 イメージフォーマットは、マイクロコントローラ上でのメモリマップアクセス向けに設計されたコンパクトなバイナリフォーマットです。簡単に説明すると次のようになります。
イメージはマジックバイト
0xd2 0xcd 0x31(先頭 2 バイトの上位ビットを立てて"RM1"としてエンコードされたもの)で始まります。イメージの残りの部分は レコード で構成され、それぞれが型タグ(varuint)、長さ(varuint)、ペイロードを持ちます。
レコードの型には、パディング、verbatim データ、間接データポインタ、ディレクトリ、ファイルがあります。
ディレクトリ名とファイル名は、長さプレフィックス付きのバイト文字列として格納されます。
ファイルデータは、verbatim(インライン)で格納するか、イメージ内の別の場所への間接ポインタを介して格納できます。後者はメモリマップアクセスのためのアライメントを可能にします。
不明なレコードの型は何も告げずにスキップされ、前方互換性を提供します。
このフォーマットは MicroPython のソースの extmod/vfs_rom.c で定義されています。mpremote がイメージをビルドするために使用する Python 実装は tools/mpremote/mpremote/romfs.py にあります。
参考
ファイルシステムの利用 -- MicroPython の VFS と利用可能なファイルシステムタイプの概要。
MicroPython マニフェストファイル -- Python モジュールをファームウェアにフリーズする方法。
MicroPython .mpy ファイル -- MicroPython の .mpy バイナリファイルフォーマット。
MicroPython リモートコントロール: mpremote -- mpremote コマンドの完全なリファレンス。
romfs -- マウントされた /rom ファイルシステムを検査するための OpenMV ヘルパー。