Beaucoup de projets affirment avoir « porté le modèle en C ». Bien peu testent la seule chose qui compte : la sortie C correspond-elle à la référence Python, token par token ? Atome en fait un test plutôt qu'une promesse, et cette garantie est ce qui transforme une démonstration sur microcontrôleur en quelque chose que l'on peut réellement certifier et livrer.
Un moteur, deux puces
Le moteur d'inférence d'Atome est du C99 pur, avec un bloc à forme fixe : une LayerNorm, une convolution ternaire depthwise, un modèle d'état diagonal (SSM), une attention top-k et un routeur, dans cet ordre. Il vise l'ESP32 (le double cœur Xtensa LX7 avec SRAM interne) et les STM32 / Cortex-M de façon identique, car il ne suppose ni FPU, ni cache, ni système d'exploitation. Le même code se compile pour un STM32F103 à 2 $ et un ESP32-S3 à 5 $ ; seule la configuration à la compilation change.
Le contrat de parité
Parce que le modèle PyTorch et le moteur C sont construits sur la même forme de bloc fixe, la suite de tests peut exiger qu'ils produisent des nombres identiques. La parité de bout en bout entre Python et Cortex-M3 sous QEMU est de max |Δ| = 3,7×10⁻⁷, soit l'epsilon de la virgule flottante. La parité de génération multi-token est exacte : 48 tokens sur 48 pour le modèle 60K et 16 sur 16 pour le modèle 944K. La suite complète compte 146 tests, tous au vert sur HEAD.
Pourquoi la bit-exactitude compte pour livrer
Si la puce peut diverger silencieusement de votre implémentation de référence, vous ne pouvez pas certifier le comportement de l'appareil. La parité bit-exacte signifie que le comportement validé en Python est celui qui s'exécute sur la carte — précisément ce qu'exigent les domaines réglementés (médical, industriel, automobile), où « probablement pareil » n'est pas une réponse acceptable. Elle rend aussi le débogage tractable : un écart est un bug localisé, pas un mystère flottant à poursuivre entre deux langages.
Mémoire et flash, par puce
La configuration exécutable dépend de la SRAM du composant. D'après la table mesurée du dépôt : un STM32F103 (20 Ko de SRAM) exécute les petites configs de classifieur ; un RP2040 (264 Ko) exécute le modèle d'histoires en dimension 64 à environ 104 Ko de RAM ; la configuration « prod » 944K nécessite un composant de 512 Ko comme un STM32F7 ou un ESP32-S3. Le flash est rarement la limite — les poids empaquetés plus le moteur font de quelques dizaines à quelques centaines de kilooctets, largement dans un flash typique de 512 Ko à 4 Mo.
Une limite honnête
La garantie de parité tient entre la référence Python et le moteur C. Le playground intégré au site exécute une réimplémentation JavaScript distincte de la passe avant — mêmes poids, mais non couverte par la garantie bit-exacte, car l'ordre des opérations flottantes peut différer en JavaScript. Et tous les chiffres Cortex-M3 sont mesurés sous QEMU ; nous n'avons pas encore flashé de carte physique ni mesuré les joules par token. Quand ce sera fait, nous le publierons avec la même franchise que le reste.
De PyTorch à un blob flashable
Le chemin d'un modèle entraîné à quelque chose qu'un microcontrôleur exécute est court et explicite. Vous entraînez dans PyTorch, exportez les poids ternaires vers le format empaqueté ATOME01 — quatre trits par octet en base 3 — et le moteur C charge ce blob directement, sans conversion au démarrage. Parce que la structure de bloc du moteur reflète exactement le module PyTorch, il n'y a pas de couche de traduction susceptible d'introduire un écart ; les mêmes opérations se produisent dans le même ordre. C'est ce qui rend possible, en premier lieu, la garantie de parité bit-exacte : les deux implémentations ne sont pas des approximations l'une de l'autre, elles sont le même calcul exprimé deux fois.
Choisir entre ESP32 et STM32
Les deux familles conviennent à des produits différents. Les STM32 couvrent une gamme énorme, de la Blue Pill à 20 Ko qui n'exécute que les plus petites configs de classifieur au STM32F7 à 512 Ko qui héberge le modèle complet 944K, ce qui les rend idéaux quand vous voulez choisir exactement le bon point de coût et de taille. L'ESP32-S3 apporte 512 Ko de SRAM plus du sans-fil intégré, utile quand vous voulez un modèle embarqué pour le chemin sensible à la confidentialité et à faible latence, et une connectivité optionnelle pour le reste. Dans les deux cas, le code du moteur est le même ; vous choisissez la puce selon la configuration dont votre tâche a besoin et les périphériques que votre produit veut, puis vous confirmez la compatibilité face à la table RAM mesurée avant de vous engager.
Tester le portage avant de lui faire confiance
La parité bit-exacte ne vaut quelque chose que si vous lancez réellement les vérifications, le flux se termine donc par la vérification plutôt que par la supposition. Le dépôt fournit un test de parité qui compare la sortie du moteur C à la référence PyTorch, un test multi-token qui confirme que la génération reste identique sur une séquence, et un test QEMU qui exécute la compilation Cortex-M3 elle-même ; tout cela fait partie de la suite de 146 tests qui doit passer avant qu'un changement soit considéré comme terminé. Quand vous démarrez le moteur sur un nouveau composant, le bon réflexe est de reproduire ces vérifications dans votre environnement d'abord, de confirmer que les chiffres correspondent, et seulement ensuite de bâtir des fonctionnalités par-dessus. Un écart à ce stade est un bug localisé que vous pouvez corriger ; un écart découvert sur le terrain, après avoir supposé le portage fidèle, coûte bien plus cher. La discipline est simple : vérifiez la parité sur la puce avant de faire confiance à la puce.
En résumé
Le même moteur C99 portable tourne sur un ESP32 et sur toute la gamme STM32 parce qu'il ne suppose ni FPU, ni cache, ni système d'exploitation ; vous choisissez la puce selon la configuration dont votre tâche a besoin et confirmez la compatibilité face à la table RAM mesurée. Le chemin d'export de PyTorch vers un blob ATOME01 flashable est direct, et les tests de parité bit-exacte sont ce qui vous permet de faire confiance au fait que la puce se comporte exactement comme votre référence. Démarrez le moteur, reproduisez les vérifications de parité dans votre environnement, et seulement ensuite bâtissez des fonctionnalités par-dessus. Vérifiez la parité sur la puce avant de faire confiance à la puce.
Questions fréquentes
Un ESP32 peut-il faire tourner un modèle de langue ?
Oui — un ESP32-S3 a 512 Ko de SRAM, assez pour les configurations plus grandes d'Atome, et le moteur C99 sans tas fonctionne sans système d'exploitation. Les petites configs tournent aussi sur des STM32 jusqu'à environ 20 Ko de SRAM.
Que signifie la parité bit-exacte entre Python et C ?
Cela signifie que le moteur C produit les mêmes sorties que la référence PyTorch, vérifiées par des tests — max |Δ| = 3,7×10⁻⁷ de bout en bout et génération multi-token exacte. Le comportement validé est celui qui est livré.