14.2.2.2. Construindo uma imagem ROMFS¶
Uma imagem ROMFS é um sistema de arquivos somente leitura, residente na flash, que o runtime monta automaticamente em /rom. Ela resolve o problema dos ativos com que a página anterior terminou: arquivos de modelo de aprendizado de máquina, tabelas de rótulos, configuração JSON, modelos de imagem – qualquer coisa que a aplicação abre e lê, mas nunca grava – entram na compilação sem pagar o custo de serem embutidos como literais Python.
Três coisas tornam o ROMFS a ferramenta certa para ativos entregues:
O sistema de arquivos é parte da imagem do firmware. Os usuários finais não podem apagar um arquivo de
/rom, editá-lo ou substituí-lo por um próprio.Os arquivos em
/romsão acessíveis no local. Consumidores como o módulomlcarregando um arquivo de modelo obtêm uma visão direta na flash, sem cópia para a RAM – um modelo de vários megabytes em/romé “carregado” essencialmente de graça, ao passo que o mesmo arquivo em/sdcardé lido para a RAM no momento do carregamento e permanece lá durante toda a vida útil da referência. Oopen()+readcomum copia sob demanda: cada chamadaread(n)copianbytes da flash para a RAM no momento da chamada, com umread()simples pedindo o arquivo inteiro./rome/rom/libsão adicionados asys.pathno boot. Pacotes Python colocados na imagem são importáveis pelo nome; nada de especial no ponto de chamada.
14.2.2.2.1. Construindo uma imagem¶
As imagens ROMFS são criadas, editadas e gravadas através da IDE. Use-a como a fonte da verdade para o conteúdo de toda partição ROMFS entregue.
O motivo pelo qual isso importa: os arquivos de modelo vêm com requisitos de alinhamento que o carregador impõe em tempo de execução. Arquivos .tflite precisam ser preenchidos até limites de 16 bytes, e a NPU do N6 exige alinhamento de 32 bytes para modelos compilados. A IDE aplica esse preenchimento automaticamente ao gravar a imagem. Ferramentas que percorrem a árvore de código-fonte sem aplicar o preenchimento – o mpremote romfs em particular – produzem uma imagem que monta corretamente, mas cujos modelos falham na primeira chamada de inferência.
O editor ROMFS da IDE é uma visão interativa do conteúdo da imagem. Arquivos e pastas podem ser adicionados, renomeados e excluídos em memória; ao salvar, o resultado é gravado como um arquivo .img pronto para gravação. Uma estrutura típica para uma aplicação que entrega um modelo junto a alguns ativos e a um pacote Python se parece com:
model.tflite
labels.txt
config.json
templates/
calibration.jpg
lib/
mylib/
__init__.py
helpers.py
Dica
Tanto a IDE quanto o mpremote compilam de forma cruzada os arquivos .py para bytecode .mpy no caminho de entrada para uma imagem ROMFS, de modo que a câmera os importa sem pagar o custo de parsing no carregamento. Os arquivos de origem no editor permanecem .py; a imagem contém .mpy.
Uma vez gravada a imagem, a árvore fica visível a partir do MicroPython em /rom/
>>> import os
>>> os.listdir('/rom')
['model.tflite', 'labels.txt', 'config.json', 'templates', 'lib']
>>> import mylib
>>> mylib.helpers
<module 'mylib.helpers' from '/rom/lib/mylib/helpers.mpy'>
14.2.2.2.2. A maior parte da aplicação vive no ROMFS¶
O ROMFS é o lar certo para quase tudo que uma aplicação entrega: as bibliotecas que ela importa, os arquivos de modelo que ela carrega, a configuração que ela lê, qualquer ativo cuja saída veio de uma ferramenta de compilação que emite uma árvore de arquivos (conversores de modelo, pipelines de imagem, empacotadores de ativos) e – importante – o próprio código da aplicação.
O lado dos módulos congelados deve permanecer pequeno: boot.py para a configuração pré-REPL, main.py como um ponto de entrada enxuto e apenas as bibliotecas sem as quais a câmera genuinamente não consegue dar boot. Todo o resto vai para o ROMFS, onde iterar sobre ele é um novo .img salvo a partir da IDE e regravado – sem necessidade de recompilar o firmware, sem necessidade de ter um toolchain à mão para isso.
O padrão que emerge é um main.py que não faz nada além de delegar para a aplicação residente no ROMFS:
# main.py (frozen)
import app
app.run()
# /rom/app/__init__.py (in ROMFS)
def run():
...
Uma alteração em app é uma edição do ROMFS e uma regravação. A compilação do firmware permanece estável durante toda a vida útil do produto, a menos que algo no lado congelado realmente precise mudar.