- Avec l’objectif de créer un ordinateur minimal assemblable à la maison, ce système de classe 8 Mo de RAM et 1 MIPS, capable d’exécuter Debian Linux, vi, gcc et make, est réalisé uniquement avec des composants à 8 broches
- La carte finale se compose de trois puces : STM32G031, 8 Mo de SPI PSRAM et un pont USB-série PL2303GL ; la contrainte du boîtier à 8 broches ne laisse que 6 E/S utilisables
- Le manque de broches est la principale difficulté de conception : la RAM utilise du SPI classique, la carte SD partage les broches de la RAM via 1-bit SDIO, et l’émission UART est gérée en bit-banging
- Côté logiciel, le projet réutilise un émulateur MIPS existant basé sur de l’assembleur ARMv6M, tandis qu’un bootloader de 8 Ko met à jour le firmware depuis FIRMWARE.BIN sur le système de fichiers FAT de la carte SD
- Le STM32G031 est overclocké au-delà des 64 MHz officiels via le réglage VOS0 ; avec un CPU hôte à 148 MHz, il atteint environ le niveau d’un MIPS R3000 à 1,65 MHz et démarre Debian en moins d’une minute
Un ordinateur Linux minimal conçu uniquement avec des puces à 8 broches
- L’objectif est de créer un ordinateur moderne en kit, facile à assembler chez soi, uniquement avec des puces à 8 broches
- Le critère minimal d’un ordinateur moderne est défini comme la capacité à exécuter Debian Linux, vi, gcc et make
- Sur la base d’expériences précédentes, les spécifications minimales nécessaires pour faire tourner Linux sont fixées à 8 Mo de RAM et 1 MIPS CPU
- Le stockage utilise une carte SD, et la console passe par une connexion USB-série
- La carte a une petite forme circulaire, avec un connecteur USB-C en bord de carte sur le dessus
- Le nombre de composants et de broches a été réduit afin que même une personne ayant très peu d’expérience en soudure puisse l’assembler avec un fer à souder de 45 W
Choix des composants
- Pour la connexion USB, le PL2303GL a été choisi
- C’est un pont USB-série qui fonctionne sans composant externe et fournit aussi une sortie régulée 3,3 V de 100 mA
- Il dispose de pilotes pour les principaux systèmes d’exploitation ; sous macOS, une installation depuis l’App Store est nécessaire
- Une implémentation USB basée sur ATTINYx5 et V-USB a aussi été envisagée comme alternative
- Le low-speed USB ne peut pas, selon la spécification, utiliser d’endpoint bulk, mais les principaux OS ne l’imposent pas, ce qui permet à une implémentation de port série ACM de fonctionner
- V-USB consomme beaucoup de temps CPU, de flash et de RAM, ce qui le rend trop lourd pour ce projet
- La RAM utilise de la SPI PSRAM en SOIC-8
- ISSI, APMEMORY, Vilsion et d’autres fabriquent des composants de ce type, et les composants de 8 Mo sont disponibles chez des distributeurs classiques
- Des puces de 16 Mo avaient été promises par plusieurs fournisseurs, mais l’auteur estime qu’elles n’ont pas été réellement livrées
- Les familles de microcontrôleurs PIC16F, RL78, PSoC1, eZ8, S08CPUv2, STM8, MSP430, AVR, PSoC4, MSPM0C, CH32V003, CH570E et STM32G0 ont été comparées
- Le choix final s’est porté sur la famille STM32G031J4M6/STM32G031
- Elle fournit un cœur Cortex-M0+, 64 MHz officiels, 32 Ko de flash et 8 Ko de RAM
- En boîtier 8 broches, elle était plus avantageuse que les autres candidats en performances et en mémoire
- La qualité des documents d’errata des puces STM suscitait des inquiétudes, mais la conception utilisant très peu de périphériques intégrés, ce choix restait possible
Conception matérielle dans la limite de 6 broches d’E/S
-
UART console
- Les lignes UART RX et TX sont difficiles à combiner avec d’autres fonctions
- Si RX est partagée, des données reçues peuvent être perdues pendant d’autres opérations ; si TX est partagée, même de brèves impulsions low peuvent être interprétées comme des caractères par le PC
- Pour cette raison, 2 des 6 E/S sont affectées à la console UART
- Dans le brochage final, la broche 8 sert de USART2 RX, et la broche 7 assure l’UART TX en bit-banging
- Pendant l’émission UART, toute l’exécution s’arrête ; la vitesse aussi élevée que possible, 115 200 bps, est donc utilisée
- L’envoi d’un caractère prend environ 87 microsecondes, et la carte n’émet rien la plupart du temps, ce qui rend cette approche acceptable
-
Connexion de la RAM
- La SPI PSRAM prend en charge QSPI, mais QSPI nécessite 6 broches et ne peut donc pas être utilisé
- Le dual-SPI pourrait être deux fois plus rapide que le SPI classique sans utiliser de broches supplémentaires, mais le STM32G031 ne prend pas en charge le dual-SPI
- Il a été jugé difficile de dépasser la combinaison SPI matériel + DMA même en faisant du dual-SPI en bit-banging avec le CPU
- En conséquence, la RAM est connectée en SPI classique, et cette connexion utilise à elle seule les 4 broches restantes
-
Connexion de la carte SD
- Utiliser la carte SD en mode SPI aurait nécessité une broche chip select supplémentaire, mais il ne restait plus de broche disponible
- Inverser RAM nCS avec un inverseur pour l’utiliser comme SD card nCS posait problème avec certaines cartes et nécessitait aussi un composant supplémentaire
- Le partage de la broche UART TX comme SD card nCS avec un filtre passe-bas a également été envisagé, mais il aurait nécessité un UART à 300 bps ou moins et se révélait fragile lorsque la carte SD était lente
- La solution finale consiste à implémenter directement le protocole 1-bit SDIO de la carte SD
- RAM nCS est partagé avec SD CLK, RAM CLK avec SD CMD, et RAM MOSI avec SD DAT
- Un accès RAM ressemble à un bit d’état idle pour la carte SD, et un accès SD ressemble pour la RAM à une simple alternance de sélection et désélection, ce qui permet un fonctionnement sûr
- En revanche, aucun accès RAM ne peut s’intercaler au milieu d’une transaction SD ; les lectures/écritures multiblocs ne peuvent donc pas être utilisées
- En raison du brochage du STM32G031, SDIO ne peut pas être utilisé comme périphérique matériel, donc tous les accès SD sont effectués en bit-banging
- L’implémentation en assembleur atteint un débit d’environ 14 cycles CPU par bit
Bootloader et séquence de démarrage de Linux
-
Émulateur
- Le projet réutilise l’émulateur MIPS du projet existant LinuxCard
- L’émulateur est écrit en assembleur ARMv6M et pouvait démarrer Linux
- Un JIT MIPS-to-ARMv6M a aussi été écrit pour accélérer l’exécution, mais sa taille de code de 46 Ko était trop importante et le gain de vitesse dans un cache de traduction de 6 Ko n’était pas suffisant ; il n’est donc pas utilisé
- Les 32 Ko de flash du STM32G031 sont divisés entre un bootloader de 8 Ko et 24 Ko de code principal
-
Bootloader pour la mise à jour du firmware
- Comme il n’était pas possible de conserver des broches de débogage, un bootloader capable de mettre à jour le firmware depuis la carte SD était nécessaire
- Le bootloader contient un pilote SDIO, un pilote de système de fichiers FAT, du code d’écriture flash, de la journalisation et du code d’émission UART en bit-banging
- Sa taille réelle est d’environ 6,5 Ko, mais il utilise une zone de 8 Ko à cause de la granularité des blocs de flash
- Il recherche FIRMWARE.BIN sur la carte SD et applique la mise à jour si les vérifications de base passent et que la version augmente
- Le word à l’offset 16 de l’image applicative sert de numéro de version
- L’octet à l’offset 8 du bootloader correspond à la version du bootloader et n’est pas utilisé, sauf pour l’affichage du texte de démarrage de l’application principale
- Le bootloader recherche aussi, dans le système de fichiers FAT, un fichier ou répertoire dont le nom commence par CLOCK
- Les chiffres qui suivent sont utilisés comme fréquence d’horloge de l’application principale ; si la valeur est absente ou hors de la plage 32–200 MHz, 132 MHz sont utilisés
-
Partitions de la carte et chargement du noyau
- La séquence de démarrage est conçue de façon similaire à celle d’un PC
- Le premier secteur de la carte SD est lu au début de la RAM, puis l’exécution y saute
- Le code de première étape cherche une partition de type 0xBB, la charge à 0x80001000, puis saute dessus
- Le bootloader de deuxième étape monte en FAT16 la partition marquée active et charge le fichier VMLINUX comme ELF
- La ligne de commande du noyau est intégrée au bootloader
- root est
/dev/pvd3, init est/sbin/uMIPSinit /dev/pvd1tente d’être monté sur/boot- L’ordre des partitions du projet est : partition FAT, partition bootloader, puis rootfs
- Windows et macOS montant la première partition, la partition FAT permet d’ajouter et retirer facilement des fichiers
- Une fois Linux démarré, cette partition apparaît également comme
/boot
Performances et overclocking
- La fréquence officielle de fonctionnement du STM32G031 est de 64 MHz, mais des horloges plus élevées ont été essayées en utilisant le réglage de tension interne
- La documentation STM mentionne les réglages VOS2 à 1,0 V et VOS1 à 1,2 V ; avec VOS1, le fonctionnement n’est pas stable au-delà d’environ 75 MHz
- L’utilisation du réglage VOS0 1,35 V, mentionné dans de vieux documents et dans la documentation de puces similaires, augmente fortement la marge d’overclocking
- La plupart des puces fonctionnent bien à 136 MHz, et certaines atteignent 180 MHz
- La mémoire flash ne devient pas plus rapide ; il faut donc gérer correctement les flash wait states
- Avec un CPU hôte à 148 MHz, le CPU MIPS émulé est comparable à un MIPS R3000 d’environ 1,65 MHz avec FPU désactivée
- Le système démarre en environ une minute, et vi, make, objdump et gcc fonctionnent
- Comme il s’agit d’un système Debian complet, des paquets
.debpeuvent être importés via/bootpuis installés
Assemblage et premier démarrage
-
Déroulement de l’assemblage
- Les fichiers de conception sont fournis afin de permettre la fabrication de la carte, et l’auteur cherche une société pour vendre des kits
- L’assemblage se fait dans l’ordre suivant : socket de carte SD, condensateurs, résistances, STM32G031, puis PL2303GL
- R101, R102, R201 et R202 ne sont pas montés au départ
- Le bootloader doit d’abord être écrit dans le STM32
- R101 et R201 sont pontées pour configurer le câblage série du bootloader ROM
- Cette étape se fait sans carte SD insérée et avant de souder la puce RAM
- Après l’écriture du bootloader, les ponts R101/R201 sont retirés et R102/R202 sont pontées
- Il suffit ensuite de souder une puce RAM APS6408 ou VTI7064 à l’emplacement U2 pour terminer l’assemblage matériel
-
Firmware et premier boot
- La carte SD doit faire au moins 1 Go et l’image disque fournie doit y être écrite
- L’image contient un bootloader MIPS de première étape, un bootloader MIPS de deuxième étape, une partition contenant le noyau Linux et une copie du firmware, ainsi qu’un rootfs Debian
- Si FIRMWARE.BIN est placé dans la partition FAT, le bootloader flashe lui-même le firmware au premier démarrage
- Le terminal série doit être réglé sur 115 200 bps, 8N1
- Lors de la première exécution, les fuses du STM32 sont programmés, et il peut être nécessaire de débrancher puis rebrancher le câble USB-C
- Après environ 20 secondes, les messages de démarrage du noyau Linux commencent, et le démarrage complet prend environ une minute
- Comme il n’y a que 8 Mo de RAM, il est fortement recommandé d’exécuter
swapon /swapfilecomme première commande - L’activation du swap prend plusieurs dizaines de secondes, mais permet ensuite d’exécuter davantage de programmes
Fichiers à télécharger et utilisation
- Le téléchargement principal est uMIPS.8PL.zip
- L’archive contient les fichiers nécessaires à la fabrication de la carte et à son exécution
schematics: schémasgerbers: fichiers Gerber pour la fabrication de la cartesrcs: sources de l’émulateur et du bootloaderbinaries/SD.img: image à écrire sur la carte SDBOOTLOADER.BIN: bootloader à écrire dans la puce pendant l’assemblageFIRMWARE.BIN: image firmware précompilée
- Après le démarrage, le shell par défaut est
sh, etbashpeut aussi être lancé - Pour éviter de manquer de RAM, il est conseillé d’activer le swapfile inclus dans l’image avec
swapon /swapfile - En supposant que le MCU fonctionne à 120 MHz, la vitesse CPU effective est d’environ 1,5 MHz
- Compiler un petit programme C avec gcc prend quelques minutes, mais cela fonctionne
- Des générateurs Mandelbrot en virgule flottante et en virgule fixe sont fournis en exemple, sous forme de sources et de binaires
- Les outils installés incluent vim, make et gcc, et des paquets Debian peuvent être ajoutés via la partition FAT16 partagée de la carte SD
1 commentaires
Avis de Hacker News
La partie où, après s’être demandé quelles broches pouvaient être combinées avec les trois broches du SDIO, la solution a consisté à utiliser le nCS de la RAM comme CLK de la carte SD, le CLK de la RAM comme CMD de la carte SD, et le MOSI de la RAM comme DAT de la carte SD, est vraiment un hack superbe.
En examinant les interactions possibles avec chaque périphérique, l’argument selon lequel cela fonctionne en toute sécurité est convaincant, et ça mérite largement sa place sur Hacker News.
Je trouve toujours un peu dommage que l’option par défaut, pour se connecter en USB, soit d’utiliser une puce séparée.
L’USB est un protocole tellement complexe qu’au-delà du niveau basique de V-USB, qui permet de faire tourner de l’USB 1.1 basse vitesse, cela semble généralement difficile sans matériel spécialisé et une pile logicielle assez volumineuse.
À l’inverse, le SPI est absurdement simple : le minimum matériel nécessaire se résume à peu près à un registre à décalage capable de recevoir une horloge assez rapide.
L’époque des anciens PC de bureau et portables, avec des ports série/parallèle exposés vers l’extérieur permettant ce type de communication bas niveau, me manque.
Pour les périphériques simples, on aurait pu utiliser UART, I2C ou du SPI multidrop sur de courtes distances, avec quelques fréquences d’horloge standard et un connecteur unique ; et pour les appareils riches en données comme les moniteurs ou les disques externes, passer directement à IEEE 802.3 Ethernet. Peut-être qu’il aurait alors suffi de prendre en charge un lien Ethernet, sans devoir gérer séparément USB et Ethernet.
SPI ne prend pas en compte nombre des commodités qu’offre l’USB, comme l’alimentation, le branchement à chaud, la découverte de périphériques ou les erreurs de bits.
Il est utile, pour les développeurs logiciel, de comprendre les idiomes SPI et la manière dont les concepteurs matériel utilisent SPI.
En général, SPI sert à remplir les registres de périphériques, et c’est d’une nature différente de la communication asynchrone de haut niveau que l’on voit souvent avec USB ou Ethernet et les couches d’abstraction qui les surplombent.
Il n’existe pas de standard universel pour les trames SPI, mais il y a des motifs idiomatiques, et cela a suffi dans un nombre incalculable d’applications.
En pratique, des protocoles simples comme SPI et I2C ne suffisent pas.
Ils ne sont pas rapides, utilisent une signalisation asymétrique très sensible au bruit, et n’ont pas de correction d’erreurs.
Ces protocoles conviennent très bien à leur usage prévu, c’est-à-dire relier des CI entre eux sur un PCB, mais si l’on expose vers l’extérieur des ports sans terminaison, il est difficile de garantir quoi que ce soit.
Même dans les PC modernes, ces protocoles et leurs variantes sont très utilisés, mais uniquement comme bus internes.
Je n’ai pas examiné en détail la spécification USB, mais le principal problème du bit-banging est probablement la vitesse requise.
Un microcontrôleur n’est pas assez rapide pour basculer les broches tout en décodant le protocole et en gérant la correction d’erreurs ; il faut donc du matériel dédié.
On peut rencontrer le même problème en faisant du bit-banging avec I2C.
Avec un CPU à 20 MHz, l’horloge maximale qu’on peut obtenir est d’environ 250 kHz, soit un peu plus de la moitié de la vitesse maximale courante de 400 kHz, et la version à 1 MHz est pratiquement impossible.
Les PHY existent parce qu’il est de très loin moins coûteux de déléguer le protocole de communication au matériel.
Sinon, il faudrait surdimensionner nettement le CPU afin de disposer des ressources nécessaires pour gérer manuellement les communications ; c’est pourquoi les microcontrôleurs modernes intègrent du matériel pour I2C, SPI et la communication série.
En conclusion, les protocoles série simples comme SPI, I2C et UART sont de très mauvais choix pour des périphériques externes.
Ils ont du mal à fonctionner à une vitesse correcte et ne supportent pas les longs câbles ni le bruit.
Même en mettant RS-232 à part, puisque ce n’est pas de l’UART, la nature et la conception de ces protocoles font qu’on ne peut pas les utiliser de cette façon ; et si l’on modifiait leurs spécifications pour les prendre en charge ainsi, on finirait par réinventer l’USB.
Les écrans de signalétique ou de salles de réunion proposent aussi souvent du RS-232 pour un contrôle plus flexible que HDMI-CEC.
Il est rare d’avoir besoin d’un débit supérieur à 9600 bps, et le connecteur le plus courant est un bornier à vis à 3 broches avec Tx, Rx et GND.
Les installations actuelles comportent généralement au moins un adaptateur RS232-USB quelque part, et dans les grandes salles, on bridge le RS232 sur Ethernet.
Quand je suis arrivé dans ce domaine, cela m’a surpris, mais c’est compréhensible quand on pense que beaucoup d’installations ont plusieurs décennies et que les composants ont été remplacés un par un.
Cette puce coûte environ 0,10 dollar et offre 2 Ko de RAM, 16 Ko de Flash, 48 MHz, et fonctionne à 1 CPI.
Le nouveau CH570 est lui aussi autour de 0,10 dollar en SOIC8, avec 100 MHz, 16 Ko de RAM, 256 Ko de Flash, USB et même une radio à paquets 2,4 GHz ; j’ai commandé une carte de développement.
Il serait utile d’indiquer aussi l’épaisseur de PCB nécessaire pour ceux qui veulent fabriquer directement la carte.
Si je me souviens bien, c’est autour de 0,8 mm, et il faut cette épaisseur pour qu’un « connecteur edge USB-C » s’insère correctement dans la prise.
C’est un excellent article, mais je me demande si tout aurait été beaucoup plus simple en assouplissant un peu l’exigence des 8 broches.
Quelques broches de plus auraient sans doute fortement réduit la complexité du projet, tout en n’ajoutant que très peu de temps de soudure.
Il existe beaucoup de puces bien plus rapides avec USB intégré.
Allwinner V3s peut aussi être soudé à la main, intègre de la RAM et démarre très bien Linux en natif.
RP2350 est aussi une bonne option, avec une excellente interface QSPI RAM intégrant du cache et la prise en charge de l’USB.
On est presque plus proche d’un projet à 2 puces
L’une n’est qu’un circuit intégré USB-série, et si on ne compte pas la carte SD, on revient à 3 avec la carte SD incluse
Le nombre total de broches est tellement faible que ça donne envie d’essayer de le réaliser en dead bug
Un adaptateur microSD-vers-SD fait un support microSD assez utilisable pour la soudure
Faire une astuce similaire en le ciblant serait aussi un hack amusant
Techniquement, c’est un projet très cool, mais il semble être allé à un extrême qui s’écarte quelque peu de l’objectif de créer un nouveau kit d’ordinateur pour débutants
Pour un débutant, souder un SOIC8 ou un SOIC28 ne change pas grand-chose
Un SOIC28 est à peu près aussi facile, ou aussi difficile, qu’un SOIC8
Avec une puce plus grande, on pourrait ajouter un minimum de son, un clavier, puis plus tard une vraie sortie moniteur de type VGA, ce qui donnerait un ordinateur bien plus utile
La difficulté de soudure n’augmenterait presque pas, tout en offrant une bonne base d’extension aux utilisateurs qui y prendraient goût
Si tu veux le construire dans cette direction, tu peux utiliser mon code
Moi, je l’ai fait parce que la contrainte artificielle des 8 broches m’amusait
J’ai une étrange envie de même supprimer le circuit imprimé et d’en faire une sculpture électronique
Je ne suis ni artiste ni sculpteur, donc je n’ai pas osé m’y risquer
Ce serait mignon d’utiliser une Flash SPI 8 broches comme stockage à la place de la carte SD
Il a dit être « allergique à RISC-V pour des raisons personnelles », et je suis curieux de savoir pourquoi
Je ne veux pas lancer une flamewar, c’est juste une opinion personnelle, mais assez tranchée
RISC-V a été conçu suffisamment tard dans l’histoire pour pouvoir tirer parti d’une grande partie des connaissances existantes, mais je considère qu’il ne l’a presque pas fait
Du coup, plusieurs extensions sont proposées pour corriger des choses qui auraient dû être bien faites dès le départ
À force d’ajouts, il commence seulement, dix ans plus tard, à se rapprocher d’une forme qui a du sens
Je n’accepte pas non plus l’excuse selon laquelle il fallait une phase d’apprentissage
Les informations nécessaires existaient dès le début, et les erreurs étaient évidentes pour la plupart d’entre nous
Certaines extensions ne sont que des pansements collés sur des problèmes de conception fondamentaux
Par exemple, shadd2 est un pansement pour l’absence d’un vrai mode d’adressage pour l’accès aux tableaux
La réponse habituelle à cela consiste à promettre une fusion d’instructions magique dans le cœur, souvent promise mais jamais vraiment fournie
C’est encore moins le cas sur les processeurs bon marché, qui semblent pourtant être la seule cible de RISC-V
L’absence d’instructions d’extraction et d’insertion de champs de bits est aussi une erreur d’amateur, d’où l’existence d’une extension pour la corriger
Mais il aurait dû être évident dès le départ que cette fonctionnalité était nécessaire
Les instructions de branchement conditionnel sur un bit particulier dans un registre apparaissent aussi fréquemment, c’était donc une fonctionnalité évidente à envisager dès le début
Une simple analyse du logiciel moderne l’aurait montré
Ce qui est agaçant, c’est que les informations existaient déjà
On savait ce que faisait le logiciel moderne, mais tout a été ignoré, et au final on a eu, selon moi, un MIPS-1 légèrement remis au goût du jour
Désormais, les extensions se sont accumulées et la fragmentation est devenue importante
On peut viser quelque chose comme RV23, un résultat final à peu près raisonnable, mais il n’existe pas de matériel qui l’implémente ; sinon, il faut viser le plus petit dénominateur commun, qui tourne partout mais fonctionne très mal
Il existe des problèmes de conception encore plus sérieux quand on veut utiliser RISC-V pour du calcul réellement haute performance, mais je garde ça pour le prochain rant
Un autre jeu d’instructions conçu à peu près à la même période a réellement exploité les connaissances sur l’état du logiciel moderne, et le résultat se voit : aarch64
Le projet lui-même est très cool, mais cette page est aussi une excellente ressource pour obtenir des informations sur les petits microcontrôleurs
La famille WLCSP n’y figure pas, mais elle renvoie aussi vers la page de l’émulateur MIPS pour ARM https://dmitry.gr/?r=05.Projects&proj=33.%20LinuxCard, qui a l’air assez intéressante