ROMFS 사용하기¶
개요¶
ROMFS(Read-Only Memory Filesystem)는 MicroPython 장치를 위해 설계된 경량 읽기 전용 파일시스템입니다. 코드와 데이터를 플래시 메모리에 저장하고 RAM으로 복사하지 않고도 효율적으로 접근해야 하는 마이크로컨트롤러 및 임베디드 시스템에 최적화되어 있습니다.
ROMFS의 주요 이점은 다음과 같습니다:
제로 카피(zero-copy) 임포트: ROMFS에 저장된
.mpy바이트코드 파일은 먼저 RAM으로 복사되지 않고 플래시 메모리에서 직접(메모리 매핑) 실행될 수 있습니다. 이는 프리즈된 모듈이 동작하는 방식과 유사하지만, 전체 펌웨어를 다시 플래시할 필요가 없습니다.낮은 RAM 오버헤드: ROMFS에서 로드된
.mpy파일 내의 상수 객체(문자열, 바이트 등)는 RAM에 복제되지 않고 플래시에서 직접 참조됩니다.유연한 배포: ROMFS 이미지는 호스트 PC에서 빌드하여 펌웨어를 다시 빌드하지 않고도 mpremote를 사용해 장치에 배포할 수 있습니다.
표준 파일시스템 인터페이스: ROMFS는 VFS에 마운트되며 일반적인 Python 파일 연산(
open,os.listdir,import등)을 통해 접근합니다.
ROMFS는 읽기-쓰기 FAT/LittleFS 파일시스템(다른 플래시 파티션에 존재함)과 frozen modules(펌웨어 자체에 컴파일되어 포함됨) 양쪽 모두를 보완합니다.
보드 지원¶
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에 대한 쓰기 시퀀스를 완료합니다(캐시 플러시 등 쓰기 후 필요한 마무리 작업을 수행합니다).
이러한 연산은
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")
부팅 시 자동 마운트¶
펌웨어에서 ROMFS 지원이 활성화되어 있으면, MicroPython은 초기화 중에 첫 번째 ROM 파티션을 /rom에 자동으로 마운트하려고 시도합니다. 파티션에 유효한 ROMFS 이미지가 포함되어 있으면 마운트되고 /rom과 /rom/lib 모두 sys.path에 자동으로 추가됩니다.
즉, mpremote로 ROMFS 이미지를 배포한 후에는 소프트 리셋만으로 새 모듈을 임포트할 수 있게 됩니다.
파티션에서 유효한 ROMFS 이미지를 찾지 못하면(예: 갓 프로그래밍된 보드), 마운트는 조용히 건너뜁니다.
mpremote로 ROMFS 관리하기¶
mpremote 도구는 연결된 장치에서 ROMFS 이미지를 관리하기 위한 세 가지 하위 명령을 제공합니다.
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(처음 두 바이트의 상위 비트가 설정된"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 헬퍼.