14.2.2.1. Congelando scripts no firmware¶
Um módulo congelado (frozen) é um arquivo .py compilado para bytecode e vinculado à imagem do firmware no momento da compilação. O runtime importa um módulo congelado diretamente da flash, sem nunca consultar o sistema de arquivos em disco. Para um produto que será enviado, este é o lugar certo para o código da aplicação: nada para o usuário final apagar, nada que um .py desatualizado no cartão SD possa sobrescrever, e a câmera executa o mesmo código a cada boot, independentemente do que (se é que há algo) esteja em suas unidades.
Esta página aborda a sequência de inicialização que a câmera segue e, em seguida, como o manifest.py e a diretiva freeze incorporam uma aplicação à compilação.
14.2.2.1.1. A sequência de inicialização¶
O que executa, e quando, em uma câmera saindo do reset:
O bootloader. A energização entra em uma breve janela DFU que a IDE usa para enviar atualizações de firmware. A janela se fecha após alguns segundos e o bootloader passa o controle ao MicroPython. Um script em execução pode reentrar nessa janela sob demanda chamando
machine.bootloader().Inicialização do sistema de arquivos congelado. Antes que qualquer código de aplicação execute, o runtime ativa os sistemas de arquivos. A flash interna é montada em
/flash(e formatada em branco se não houver nada lá). Se um cartão SD estiver presente e um arquivo marcador chamadoSKIPSDnão existir na flash interna, o cartão SD é montado em/sdcard. O ROMFS, quando a compilação o inclui, é montado automaticamente em/rom. O diretório de trabalho é definido como o diretório de boot (/sdcardse o cartão foi montado,/flashcaso contrário), esys.pathé preenchido com/flash,/flash/lib,/sdcard,/sdcard/lib,/rome/rom/lib. A configuração residente na flash é tratada por um módulo congelado chamado_boot.py– infraestrutura de port e de placa, não um gancho de aplicação. As aplicações não personalizam o_boot.py; a compilação faz isso. Colocar um arquivoSKIPSDna flash a partir da IDE é a forma suportada de fazer a câmera dar boot a partir da flash interna em vez do cartão SD.Configuração pré-REPL. O
boot.pyexecuta em todo soft reset – boot a frio,Ctrl-Da partir do REPL, o término do script em execução e a recuperação por watchdog – antes de o REPL ficar acessível. Sua função é preparar o ambiente em que o restante do sistema executa: o tipo de configuração que o REPL, a aplicação e qualquer ferramenta de recuperação precisam que esteja em vigor para funcionar. Não é onde a aplicação em si reside. Omain.pyé o ponto de entrada da aplicação.Loop principal. O
main.pyé o loop principal da aplicação. Executa uma vez no boot a frio, imediatamente após oboot.py. Não é reexecutado em soft resets subsequentes – em vez disso, a câmera cai no REPL. Essa assimetria importa para o desenvolvimento (um Ctrl-D cai no REPL sem reexecutar o loop, permitindo que o desenvolvedor inspecione o estado), mas não para a produção: uma câmera em campo vê resets de energização, de watchdog e resets físicos, que são todos resets de hardware que reentram no caminho de boot a frio e executam omain.pynovamente.
14.2.2.1.2. Congelando no firmware¶
O conjunto de módulos congelados de uma placa é declarado em boards/<TARGET>/manifest.py na árvore do firmware. O manifesto é um pequeno arquivo Python que chama um punhado de diretivas:
freeze("$(OMV_LIB_DIR)/", "foo.py")– incorpora um únicofoo.pyà compilação.package("mylib", base_path="...")– incorpora um pacote Python de múltiplos arquivos, preservando seu layout de diretórios sob o caminho base informado.include("...")– inclui outro arquivo de manifesto. Os manifestos das placas usam isso para compartilhar conjuntos de módulos comuns.require("logging")– inclui, pelo nome, um módulo upstream domicropython-lib.
Um manifesto mínimo de aplicação adiciona uma linha freeze por script de nível superior e uma linha package por pacote do qual a aplicação depende.
14.2.2.1.2.1. Onde o código-fonte fica¶
O código-fonte da aplicação fica em scripts/libraries/ na árvore do firmware, junto aos módulos que a compilação já congela. A variável de manifesto $(OMV_LIB_DIR) se expande para esse caminho, de modo que as entradas do manifesto permanecem curtas. Editar o manifesto já é uma operação dentro da árvore, então manter o código-fonte na árvore evita ter que conciliar um repositório de projeto separado na resolução de caminhos.
Um layout típico para uma aplicação que entrega um único main.py mais um pacote de apoio:
scripts/libraries/
main.py
my_lib/
__init__.py
helpers.py
E no boards/<TARGET>/manifest.py da placa, uma linha freeze para o script e uma linha package para o pacote:
freeze("$(OMV_LIB_DIR)/", "main.py")
package("my_lib", base_path="$(OMV_LIB_DIR)/my_lib")
Scripts de arquivo único – main.py aqui, mas a mesma regra se aplica a boot.py ou a qualquer auxiliar autônomo – usam freeze. Pacotes de múltiplos arquivos usam package. Adicionar outro script é mais uma linha freeze; adicionar outro pacote é mais uma linha package.
14.2.2.1.2.2. Compilando e gravando¶
Uma vez que o manifesto esteja no lugar, compile o firmware exatamente como o capítulo sobre firmware descreve:
make -j$(nproc) -C lib/micropython/mpy-cross # once, builds the cross-compiler
make -j$(nproc) TARGET=<TARGET> # builds the firmware
A saída fica em build/<TARGET>/bin/
build/<TARGET>/bin/
firmware.bin # flash through the IDE
romfs0.img # flash through the IDE in a separate step
Gravar o .bin e o .img através da IDE resulta em uma câmera cuja aplicação faz parte da compilação.
A sequência de inicialização acima é o que torna a incorporação eficaz: o runtime resolve boot.py e main.py para as cópias congeladas antes mesmo de verificar o sistema de arquivos, de modo que uma câmera entregue executa o código da compilação mesmo que o cartão SD contenha um boot.py desatualizado deixado durante o desenvolvimento.
14.2.2.1.2.3. Ordem de busca¶
A semântica de sobrescrita é diferente para o caminho de execução de boot.py / main.py e para instruções import comuns. Saber qual é qual importa tanto para a produção quanto para o desenvolvimento:
Para
boot.pyemain.py: o runtime procura primeiro uma cópia congelada, depois o sistema de arquivos. Umboot.pycongelado não pode ser sobrescrito colocando um no cartão SD – quem estiver com a câmera não consegue mudar o ponto de entrada sem regravar o firmware.Para
import foo: o runtime busca primeiro emsys.path– que cobre/flash,/sdcard,/rome seus subdiretórioslib– depois os módulos congelados. Umfoo.pyde mesmo nome na flash ou no SD sobrescreve umfoocongelado. Esse é o recurso para desenvolvimento: coloque um módulo corrigido no cartão, dê um soft reset e veja a mudança sem regravar o firmware.
Um produto entregue que queira suprimir o comportamento de sobrescrita do congelado pelo sistema de arquivos para imports pode limpar sys.path logo no início do boot.py
import sys
sys.path.clear()
Com sys.path vazio, todos os imports são resolvidos apenas a partir dos módulos congelados; nada na flash, no SD ou no ROMFS pode ocultá-los.
14.2.2.1.2.4. O problema dos ativos¶
O congelamento é ótimo para código. Não é ótimo para grandes ativos binários: arquivos de modelo de aprendizado de máquina, tabelas de rótulos, configuração JSON, modelos de imagem. Embutir esses como literais Python infla o código-fonte, recompila lentamente e desperdiça o contêiner de bytecode com dados que o interpretador vai apenas ler de forma bruta de qualquer maneira. A página Construindo uma imagem ROMFS aborda o sistema de arquivos somente leitura residente na flash que preenche essa lacuna.