ROMFS の利用
概要
ROMFS (Read-Only Memory Filesystem) は、コードとデータをフラッシュメモリに格納し、RAM にコピーせずに効率よくアクセスする必要のあるマイクロコントローラや組み込みシステム向けに最適化された、軽量な読み出し専用ファイルシステムです。
ROMFS の主な利点は次のとおりです:
ゼロコピーの import: ROMFS に格納された
.mpyのバイトコードファイルは、まず RAM にコピーされるのではなく、フラッシュメモリから直接(メモリマップして)実行できます。これは 凍結モジュール の動作に似ていますが、ファームウェア全体を書き込み直す必要はありません。RAM のオーバーヘッドが小さい: ROMFS から読み込まれた
.mpyファイル内の文字列やバイト列の定数オブジェクトは、RAM に複製されず、フラッシュから直接参照されます。柔軟なデプロイ: ROMFS のイメージはホスト PC 上でビルドし、ファームウェアを再ビルドすることなく
mpremoteでデバイスにデプロイできます。標準的なファイルシステムのインターフェース: ROMFS は VFS にマウントされ、通常の Python のファイル操作(
open、os.listdir、importなど)でアクセスできます。
ROMFS は、(他のフラッシュパーティションに置かれる)読み書き可能な LittleFS/FAT のファイルシステムとも、(ファームウェア自体にコンパイルされる) 凍結モジュール とも補完的な関係にあります。
注釈
ROMFS を使うには、ROMFS のサポート ( MICROPY_VFS_ROM ) を有効にしてビルドしたファームウェアが必要です。すべてのポートやボードがデフォルトでこれを含んでいるわけではありません。ボードのドキュメントかビルドの設定を確認してください。
ポートのサポート状況
次のポートは ROMFS をサポートしています。これらのポートでは、ボードに ROMFS パーティションが設定されていれば、起動時に自動的に検出されて VFS の /rom にマウントされます。 /rom と /rom/lib は自動的に sys.path に追加されるので、そこに置いたモジュールを直接 import できます。
ポート |
備考 |
|---|---|
alif |
ROMFS パーティションが設定されたボードでサポートされます。 |
esp32 |
カスタムのパーティションテーブルでサポートされます。 |
esp8266 |
2MiB 以上のボードでサポートされます (ESP8266_GENERIC の FLASH_2M_ROMFS バリアント)。 |
mimxrt |
ROMFS パーティションが設定されたボードでサポートされます。 |
nrf |
ROMFS パーティションが設定されたボードでサポートされます。 |
qemu |
サポートされます (CI のテストに使われます)。 |
renesas-ra |
ROMFS パーティションが設定されたボードでサポートされます。 |
rp2 |
ROMFS パーティションが設定されたボードでサポートされます。 |
samd |
ROMFS パーティションが設定されたボードでサポートされます。 |
stm32 |
ROMFS パーティションが設定されたボードでサポートされます。 |
unix |
サポートされます (主にテスト用)。 |
ポートやボードで ROMFS を有効にする
ROMFS の実装は現時点ではポート固有であり、ここで説明するには詳細が多すぎます。
作業の流れ
ROMFS を使う一般的な流れは次のとおりです:
デプロイしたい Python のファイル(または
.mpyファイル)を置いたディレクトリを PC 上に作ります。mpremote romfs deploy <directory>を使って ROMFS のイメージをビルドし、デバイスにデプロイします。ROMFS は次回の起動時に
/romにマウントされます(デバイスをソフトリセットすればすぐにマウントすることもできます)。その後、デバイス上の Python のコードから、他のファイルシステムと同じように ROMFS のモジュールを
importできます。
たとえばホスト PC 上に、app.py を含むディレクトリ "myapp/" があるとします:
$ mpremote romfs deploy myapp/
ソフトリセットの後、デバイスでは /rom/app.mpy が import できるようになります( mpy_cross がインストールされていない場合は /rom/app.py )。
あるいは、先にホスト PC 上で ROMFS のイメージをビルドしてから、デバイスにデプロイすることもできます:
$ mpremote romfs build myapp
$ mpremote romfs deploy myapp.romfs
mpremote のコマンドの詳細は mpremote romfs commands の節を参照してください。
起動時の自動マウント
ファームウェアで ROMFS のサポートが有効になっている場合、MicroPython は初期化中( mp_init() の後)に、最初の ROM パーティションを /rom にマウントしようと自動的に試みます。パーティションに有効な ROMFS イメージがあればマウントされ、 /rom と /rom/lib が自動的に sys.path に追加されます。
つまり、 mpremote で ROMFS のイメージをデプロイした後は、ソフトリセットするだけで新しいモジュールを import できるようになります。
パーティションに有効な ROMFS のイメージが見つからない場合(書き込み直後のボードなど)は、マウントは何も表示せずにスキップされます。
mpremote を使った ROMFS の管理
mpremote の romfs コマンドで、ROMFS パーティションの問い合わせ、ROMFS イメージのビルド、接続したデバイスへのイメージのデプロイができます。コマンドの構文、オプション、例は mpremote ROMFS commands を参照してください。
ROMFS の使用例
単純なアプリケーションのデプロイ
次の構成のプロジェクトディレクトリ myapp/ があるとします:
── myapp
├── myapp.py
├── utils.py
└── lib
└── helper.py
これをデバイスの ROMFS にデプロイするには:
$ mpremote romfs deploy myapp/
$ mpremote tree
tree :
:/
└── rom
├── lib
│ └── helper.mpy
├── myapp.mpy
└── utils.mpy
ソフトリセットの後、マウントポイントと lib フォルダが sys.path に追加されているので、モジュールを ROMFS から import できます:
import myapp
import utils
import helper
Python から ROMFS の内容を一覧表示する
マウント後は、ROMFS の内容を他のファイルシステムと同じように調べられます:
import os
for entry in os.ilistdir('/rom'):
print(entry)
# あるいは単純に:
print(os.listdir('/rom'))
2つ目の ROMFS イメージを手動でマウントする
ROMFS のパーティションが複数ある場合は、デバイス上の別のパーティションから2つ目の ROMFS イメージをマウントできます。たとえば、2つ目の ROMFS パーティションが番号 2 にある場合、次のようにしてマウントできます:
import vfs
dev = vfs.rom_ioctl(2, 1) # 2つ目のパーティションを取得
vfs.mount(vfs.VfsRom(dev), "/rom2")
ファイルに格納された ROMFS イメージのマウント
ROMFS の中にファイルとして格納された ROMFS のイメージは、RAM にコピーすることなく入れ子のファイルシステムとしてマウントできます。たとえば /rom/data.romfs があれば、次のようにマウントできます:
# boot.py
import vfs
with open('/rom/data.romfs', 'rb') as f:
fs = vfs.VfsRom(f)
vfs.mount(fs, '/data')
その後ソフトリセットすると、入れ子の ROMFS が /data で利用できます:
$ mpremote tree
tree :
:/
├── boot.py
├── data
│ └── facts.db
├── main.py
└── rom
├── data.romfs
├── lib
│ └── helper.mpy
├── myapp.mpy
└── utils.mpy
ROMFS のファイルシステム形式
ROMFS は、読み出し専用でデータをメモリマップできるファイルを持つディレクトリ階層を表現するために設計された、柔軟で拡張可能なファイルシステム形式です。
概念:
varuint: 可変長のバイト数でエンコードされる符号なし整数です。ビッグエンディアンで格納され、後続のバイトがある場合はそのバイトの最上位ビットが立ちます。record: 型を持つ可変長の要素です。2つのvaruintとそれに続くペイロードとして格納されます。1つ目のvaruintはレコードの種類、2つ目のvaruintはペイロードの長さです(0 バイトのこともあります)。
ROMFS のファイルシステムは、レコード種別 0x14a6b1 のレコードです。この値は、エンコードすると 0xd2-0xcd-0x31 、つまり最初の2バイトの最上位ビットを立てた "RM1" になるように選ばれています。ROMFS レコードのペイロードが空でない場合、その中にはレコードが入っています。
レコードの種類:
0-- 未使用: ファイルシステムの破損の検出に使えます。1-- パディング/コメント: ペイロードには任意のデータを入れられます。2-- そのままのデータ: ファイルのデータの格納に使います。3-- 間接データ: ROMFS のペイロード内のオフセットを指します。4-- ディレクトリ: ペイロードにはディレクトリ名の長さ(バイト単位)を示すvaruintが入り、続いて名前と、ディレクトリの内容を表す省略可能な入れ子のレコード(省略可能なメタデータを含む)が入ります。5-- ファイル: ペイロードにはファイル名の長さ(バイト単位)を示すvaruintが入り、続いて名前と省略可能な入れ子のレコードが入ります。
未知のレコード種別は何も表示せずにスキップされるので、前方互換性が保たれます。
この形式は MicroPython のソースの extmod/vfs_rom.c で定義されています。 mpremote がイメージのビルドに使う Python の実装は tools/mpremote/mpremote/romfs.py にあります。
参考
ファイルシステムの利用 -- MicroPython の VFS と利用できるファイルシステムの種類の概要。
MicroPython マニフェストファイル -- Python のモジュールをファームウェアに凍結する方法。
MicroPython .mpy ファイル -- MicroPython の
.mpyバイナリファイル形式。MicroPython のリモート制御: mpremote --
mpremoteコマンドの完全なリファレンス。