14.1.1.3. Simulatori#
Due valori di TARGET compilano il firmware per essere eseguito all’interno di un simulatore sul tuo host, senza hardware collegato. Esistono affinché il firmware possa essere eseguito e testato ovunque.
| Core | NPU | Simulatore |
|---|---|---|---|
| Cortex-M7 | None | QEMU |
| Cortex-M55 | Ethos-U55 (256 MACs) | Arm FVP – Corstone SSE-300 |
MPS2_AN500 è il target Cortex-M7 di base senza NPU. Viene eseguito sotto QEMU, che è veloce da avviare ed è il modo leggero per esercitare il codice indipendente dalla piattaforma e la suite di test.
MPS3_AN547 è un Cortex-M55 con la NPU Ethos-U55, eseguito sui Fast Models di Arm – l’FVP, che modella il sottosistema di riferimento Corstone SSE-300. Questo è il target per esercitare la NPU e il percorso del modello ML in simulazione.
Entrambi forniscono il filesystem ROM e abbondante RAM (e la NPU sull’M55), così gli script di visione e ML vengono eseguiti senza modifiche.
Non c’è un sensore reale, ma il modulo csi funziona comunque con uno virtuale – csi.CSI.snapshot() restituisce un pattern di test sintetico e animato (una scacchiera scorrevole in scala di grigi, un gradiente in RGB565), non una scena dal vivo. Per elaborare contenuti di immagini reali, caricali invece da un file: un image.Image da una fixture nel filesystem ROM, oppure uno stream image.ImageIO per fotogrammi registrati.
14.1.1.3.1. Compilazione ed esecuzione#
Ogni simulatore è uno strumento host che installi tu stesso. Compili un target come qualsiasi altro (vedi Compilazione del firmware), poi lo esegui con run sotto il simulatore corrispondente – run compila l’immagine ROMFS, avvia il firmware e lo lascia in esecuzione con una connessione seriale a cui puoi collegarti.
14.1.1.3.1.1. MPS2_AN500 (QEMU)#
QEMU è disponibile nella maggior parte dei gestori di pacchetti come emulatore di sistema Arm:
Linux (Debian / Ubuntu) – installalo con
apt:sudo apt install qemu-system-armmacOS – installalo con Homebrew:
brew install qemu
Poi compila ed esegui il target:
make -j$(nproc) TARGET=MPS2_AN500
make TARGET=MPS2_AN500 run
14.1.1.3.1.2. MPS3_AN547 (Arm FVP)#
L’FVP è la Fixed Virtual Platform di Arm per il Corstone SSE-300, distribuita come parte di Arm Virtual Hardware. È disponibile solo per Linux – su un Mac, usa invece il target QEMU sopra.
Scarica il bundle, estrailo e metti la sua directory bin/ nel tuo 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"
L’FVP necessita anche delle librerie Python dell’SDK su LD_LIBRARY_PATH quando viene eseguito. Esportalo una volta per shell dalla radice del repo – leggere SDK_VERSION lo mantiene corretto man mano che il repo aggiorna l’SDK fissato:
export LD_LIBRARY_PATH="$HOME/openmv-sdk-$(cat SDK_VERSION)/python/lib:$LD_LIBRARY_PATH"
Poi compila ed esegui il target:
make -j$(nproc) TARGET=MPS3_AN547
make TARGET=MPS3_AN547 run
14.1.1.3.2. Esecuzione della suite di test#
I test unitari risiedono in scripts/unittest/tests/, con le loro immagini di fixture e i dati in scripts/unittest/data/. Vengono eseguiti con mpremote, che monta scripts/unittest/ sul simulatore in esecuzione ed esegue il suo run.py.
Il collegamento al simulatore è lento con mpremote standard, quindi usa la copia inclusa nel repo con la patch seriale applicata (applicala una volta):
patch -N -p1 -d lib/micropython < tools/mpremote-qemu-serial.patch
Per MPS2_AN500 sotto QEMU, avvia il target – stampa lo pseudo-terminale su cui si trova la sua porta seriale (ad esempio /dev/pts/5):
make TARGET=MPS2_AN500 run
Poi, da un’altra shell, punta il mpremote con la patch a quel dispositivo:
python3 lib/micropython/tools/mpremote/mpremote.py connect /dev/pts/5 \
mount scripts/unittest/ run scripts/unittest/run.py
Per MPS3_AN547 sotto l’FVP, la porta seriale è invece un socket telnet sulla 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
run.py individua ed esegue ogni test, stampando una riga PASSED / FAILED per ciascun test con i tempi e un riepilogo alla fine.
14.1.1.3.3. Aggiungere un test#
I test vengono individuati automaticamente. Per aggiungerne uno, inserisci un file in scripts/unittest/tests/ che definisca una funzione unittest(data_path, temp_path) che restituisce True quando il test passa e False quando fallisce:
# 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)
Gli argomenti data_path e temp_path puntano nei due filesystem che il simulatore vede mentre la suite è in esecuzione:
Path | Backed by |
|---|---|
| La directory |
| Il filesystem ROM di sola lettura della scheda, integrato nell’immagine del firmware da |
Quindi un nuovo test non necessita di ricompilazione, ma una nuova fixture sì: l’immagine ROM è basata su romfs_config.json anziché sui singoli file di dati, quindi forza la ricompilazione facendo touch del romfs_config.json della scheda (oppure make TARGET=<TARGET> clean) ed eseguendo di nuovo.
I valori attesi sono asseriti inline nel test, quindi non c’è un file di output atteso separato da mantenere. Per saltare un test a runtime – ad esempio quando necessita di hardware che il simulatore non modella – solleva un’eccezione il cui messaggio contiene "SKIPPED".