14.1.1.3. Simuladores#

Dois valores de TARGET compilam o firmware para rodar dentro de um simulador no seu host, sem hardware conectado. Eles existem para que o firmware possa ser executado e testado em qualquer lugar.

TARGET

Core

NPU

Simulador

MPS2_AN500

Cortex-M7

None

QEMU

MPS3_AN547

Cortex-M55

Ethos-U55 (256 MACs)

Arm FVP – Corstone SSE-300

MPS2_AN500 é o alvo Cortex-M7 de base, sem NPU. Ele roda sob o QEMU, que é rápido de iniciar e é a forma leve de exercitar o código independente de plataforma e a suíte de testes.

MPS3_AN547 é um Cortex-M55 com a NPU Ethos-U55, executado nos Fast Models da Arm – o FVP, que modela o subsistema de referência Corstone SSE-300. Este é o alvo para exercitar a NPU e o caminho de modelos de ML em simulação.

Ambos fornecem o sistema de arquivos ROM e bastante RAM (e a NPU no M55), de modo que scripts de visão e de ML rodam sem modificação.

Não há sensor real, mas o módulo csi ainda funciona contra um virtual – csi.CSI.snapshot() retorna um padrão de teste sintético e animado (um tabuleiro de xadrez rolante em escala de cinza, um gradiente em RGB565), não uma cena ao vivo. Para processar conteúdo de imagem real, carregue-o de um arquivo: uma image.Image a partir de um fixture no sistema de arquivos ROM, ou um stream image.ImageIO para quadros gravados.

14.1.1.3.1. Compilando e executando#

Cada simulador é uma ferramenta de host que você mesmo instala. Você compila um alvo como qualquer outro (veja Compilando o firmware), depois o executa com run sob o simulador correspondente – o run compila a imagem ROMFS, inicia o firmware e o deixa rodando com uma conexão serial à qual você pode se conectar.

14.1.1.3.1.1. MPS2_AN500 (QEMU)#

O QEMU vem na maioria dos gerenciadores de pacotes como o emulador de sistema Arm:

  • Linux (Debian / Ubuntu) – instale-o com apt:

    sudo apt install qemu-system-arm
    
  • macOS – instale-o com o Homebrew:

    brew install qemu
    

Depois compile e execute o alvo:

make -j$(nproc) TARGET=MPS2_AN500
make TARGET=MPS2_AN500 run

14.1.1.3.1.2. MPS3_AN547 (Arm FVP)#

O FVP é a Fixed Virtual Platform da Arm para o Corstone SSE-300, distribuída como parte do Arm Virtual Hardware. Está disponível apenas para Linux – em um Mac, use o alvo QEMU acima.

Baixe o bundle, extraia-o e coloque o diretório bin/ dele no seu PATH:

wget https://artifacts.tools.arm.com/avh/11.31.28/avh-linux-x86_11.31_28_Linux64.tar.gz
mkdir -p ~/fvp
tar --strip-components=1 -xzf avh-linux-x86_11.31_28_Linux64.tar.gz -C ~/fvp
export PATH="$HOME/fvp/bin:$PATH"

O FVP também precisa das bibliotecas Python do SDK no LD_LIBRARY_PATH quando ele roda. Exporte-o uma vez por shell a partir da raiz do repositório – ler SDK_VERSION mantém o valor correto à medida que o repositório atualiza o SDK fixado:

export LD_LIBRARY_PATH="$HOME/openmv-sdk-$(cat SDK_VERSION)/python/lib:$LD_LIBRARY_PATH"

Depois compile e execute o alvo:

make -j$(nproc) TARGET=MPS3_AN547
make TARGET=MPS3_AN547 run

14.1.1.3.2. Executando a suíte de testes#

Os testes unitários ficam em scripts/unittest/tests/, com suas imagens de fixture e dados em scripts/unittest/data/. Eles são executados com o mpremote, que monta scripts/unittest/ no simulador em execução e executa o run.py dele.

O link do simulador é lento com o mpremote padrão, então use a cópia incluída no repositório com o patch serial aplicado (aplique-o uma vez):

patch -N -p1 -d lib/micropython < tools/mpremote-qemu-serial.patch

Para MPS2_AN500 sob o QEMU, inicie o alvo – ele imprime o pseudo-terminal em que sua porta serial está (por exemplo /dev/pts/5):

make TARGET=MPS2_AN500 run

Depois, a partir de outro shell, aponte o mpremote corrigido para esse dispositivo:

python3 lib/micropython/tools/mpremote/mpremote.py connect /dev/pts/5 \
    mount scripts/unittest/ run scripts/unittest/run.py

Para MPS3_AN547 sob o FVP, a porta serial é um socket telnet na porta 5555:

make TARGET=MPS3_AN547 run
python3 lib/micropython/tools/mpremote/mpremote.py connect socket://localhost:5555 \
    mount scripts/unittest/ run scripts/unittest/run.py

O run.py descobre e executa cada teste, imprimindo uma linha PASSED / FAILED por teste com tempo e um resumo ao final.

14.1.1.3.3. Adicionando um teste#

Os testes são descobertos automaticamente. Para adicionar um, coloque um arquivo em scripts/unittest/tests/ que defina uma função unittest(data_path, temp_path) que retorne True quando o teste passa e False quando ele falha:

# scripts/unittest/tests/circles.py
def unittest(data_path, temp_path):
    import image

    img = image.Image(data_path + "/shapes.ppm", copy_to_fb=True)
    circles = img.find_circles(threshold=5000, x_margin=30, y_margin=30, r_margin=30)
    return len(circles) == 1 and circles[0][0:] == (118, 56, 22, 5856)

Os argumentos data_path e temp_path apontam para os dois sistemas de arquivos que o simulador vê enquanto a suíte roda:

Path

Backed by

/remote

O diretório scripts/unittest/ do host, montado ao vivo sobre a conexão mpremote. O run.py e os tests/ que ele descobre ficam aqui, então adicionar ou editar um teste tem efeito na próxima execução sem recompilação. temp_path é /remote/temp no dispositivo, que é o scripts/unittest/temp/ do seu host – um diretório de rascunho gravável para testes que criam arquivos.

/rom

O sistema de arquivos ROM somente leitura da placa, embutido na imagem do firmware a partir de romfs_config.json: os fixtures de teste (de scripts/unittest/data/) e os modelos de ML incluídos. data_path é /rom – acesse um fixture como data_path + "/<file>".

Então um novo teste não precisa de recompilação, mas um novo fixture precisa: a imagem ROM é chaveada a partir de romfs_config.json em vez dos arquivos de dados individuais, então force a recompilação tocando o romfs_config.json da placa (ou make TARGET=<TARGET> clean) e executando novamente.

Os valores esperados são verificados inline no teste, então não há um arquivo de saída esperada separado para manter. Para pular um teste em tempo de execução – por exemplo quando ele precisa de hardware que o simulador não modela – lance uma exceção cuja mensagem contenha "SKIPPED".