Le FLF a été fondé a une époque obscure où il n’existait (quasiment) pas de solution open-source pour travailler sur des FPGA. Seul un petit village d’irréductibles simulateurs comme icarus, ghdl ou verilator maintenaient un semblant de liberté dans cet océan de sombre verrouillage. Il y avait bien VTR et Alliance, mais ils restaient très universitaire et un peu trop centré sur les ASIC.
Et pour boucler la boucle de développement, le logiciel libre de placement routage nextpnr a émergé ainsi que le logiciel de configuration universelle openFPGALoader.
Toutes ces initiatives ont énormément gagnées en qualité et en crédibilité depuis 3 ou 4 ans et sont en passe de devenir les références dans le monde du FPGA.
À ces nouvelles très réjouissantes se sont ajouté les annonces de plusieurs mise en production de FPGA avec les outils open-source supportés officiellement par les constructeurs. On pense notamment à :
Quicklogic et son microcontrôleur EOS S3 basé sur un cœur Cortex-M4 couplé à une «zone» de FPGA utilisable intégralement avec des logiciels open source.
CLEAR: un projet plus anecdotique consistant à générer un FPGA à partir de l’outil open source openFpga et à l’intégrer à la Caravel de la société eFabless pour la graver en 130nm. Pour le coup les outils pour développer le FPGA sont opensource ainsi que les outils pour développer sur le fpga.
CologneChip et son GateMate: un FPGA à l’architecture assez original notamment pour la partie calcul/dsp mais qui se défend assez bien niveau taille.
Avec toutes ces nouvelles on aurait pensé que cela allait se calmer et que l’on aurait le temps de digérer et faire clignoter quelques LED avant de passer à une vitesse encore supérieure.
ERREUR !
C’était sans compter sur cette société créé en 2020 (et oui toute neuve) et son annonce de sortie du projet Gemini (un poil prétentieux tout de même pour le nom 😉 de faire un SoC haute performances en se basant sur des outils open sources, et oui même pour la fabrication du FPGA. En parallèle du développement silicium, RapidSilicon développe un IDE open source nommé Raptor se basant sur tout un tas de logiciels libres cité au début de cet article :
Un premier échantillonnage de composants en 16nm est déjà sorti des fonderies de TSMC et nous rassure sur la réalité du produit. Nous ne sommes pas dans le cas d’une société qui fait des annonces «vaporware» juste pour faire des levées de fond.
Hâte de voir la datasheet
L’architecture du prototype est donnée dans la figure suivante:
On est donc dans le cas d’un gros SoC-FPGA avec les caractéristiques suivantes :
Arm A53 Dual-Core
contrôleur de DDR4
«Petit» core Risc-V 32bit pour le temps réel et la supervision
Les ports habituels d’un SoC : UART, USB, SPI, I²C, …
Du PCIe Gen4, XGMII (pour le réseau)
Des liens serdes (très) rapide jusqu’à 16Gbits/s
La zone FPGA n’est pas énorme, mais se défend face aux petit Zynq par exemple :
50-75K LUT (6 entrées)
Blocks DSP (combien ?)
Block de Ram double port 18kb (Combien ?)
Toutes ces parties sont liée ensemble au moyen d’un bloc d’interconnexion nommé FlexNOC.
Alors ?
Xilinx doit-elle trembler avec son Zynq ? et Microsemi avec son PolarFire SoC ?
Nous l’attendions depuis au moins deux ans, le FPGA européen GateMate des allemands de CologneChip est désormais disponible dans votre crémerie habituelle.
Ça y est il est arrivé, le kit de développement GateMate !
La dimension européenne de ce FPGA n’est pas la seule nouveauté, c’est également un des premier (mais pas le premier) à privilégier les outils open source pour son utilisation. Que cela soit pour la simulation ou pour la synthèse tous les exemples donnés dans la documentation utilisent des logiciels libres (Icarus, GHDL et surtout Yosys). Même pour la visualisation des chronogrammes, gtkwave est utilisé par défaut en exemple.
[À noter que le kit de développement m’a été offert gracieusement par CologneChip]
Caractéristiques du GateMate
Les caractéristiques du GateMate le positionne au niveau d’un petit Spartan7 de Xilinx ou d’un Trion T20 de Efinix.
La cellule de base est nommée CPE pour «Central Programming Element».
Structure du CPE (DS1001)
On notera l’absence remarqué de blocs multiplieurs. Cette absence est compensée par le «fast signal path routing» qui permet de chaîner les CPE afin de construire un multiplieur de dimension voulue.
Caractéristiques du kit
Pour le moment, seuls les trois petit FPGA de la gamme GateMate semblent être en production. CologneChip propose un kit de développement muni du plus petit GateMate : le CCGM1A1
Le schéma blocs de la carte de développement (source pdf officiel)
La carte de développement possède deux entrées USB:
une pour l’alimentation
et une pour la programmation et la communication avec le FPGA (FTDI 2232), mais qui peut également servir d’alimentation.
La magie du logiciel libre openFPGALoader permet de détecter le FPGA directement au branchement:
$ openFPGALoader --detect
Jtag frequency : requested 6.00MHz -> real 6.00MHz
index 0:
idcode 0x20000001
manufacturer colognechip
family GateMate Series
model GM1Ax
irlength 6
En effet, avant même la sortie du gatemate, CologneChip avait déjà proposé le support du composant sur le dépot openFPGALoader.
Hormis le logiciel de placement routage (GateMate), tous les outils sont des logiciels libre bien connus du monde du FPGA (source UG1002).
Tous les outils sont connus du monde du FPGA opensource et leurs installations sont intensivement décrites dans les différents dépôts des projets.
Si l’on souhaite éviter la case compilation, l’entreprise fournie même des versions binaires. Ces binaires ne sont téléchargeable que via un compte enregistré sur leur site pour le moment. Deux paquets de logiciels sont nécessaires :
Yosys compilé pour le gatemate: pour la synthèse Verilog
p_r: le logiciel de placement routage.
Pour le moment, la version binaire proposée en téléchargement sur le site n’est disponible que pour windows. Ces «.exe» s’exécutent cependant parfaitement sous Linux au moyen de l’émulateur wine bien connu des Linuxiens.
L’archive téléchargée se décompresse simplement avec unzip :
La version windows de openFPGALoader ne fonctionne pas bien en émulation wine, il est préférable d’en compiler une version à jour à partir des sources officiels.
Pour le reste, on peut s’affranchir de compiler yosys en utilisant celle fournie. Et pour p_r, les sources n’étant pas fournies pour le moment, cette version est la seule que nous pourrons utiliser.
Pour les installer, il suffit de les décompresser :
$ cd cc-toolchain-win/
$ unzip p_r-2022.04-001.zip
$ unzip yosys-win32-mxebin-0.15+57.zip
La notice d’utilisation des commandes est données dans le pdf de l’archive nommé ug1002-toolchain-install-2022-04.pdf.
C’est l’installation la plus simple que j’ai pu avoir à faire pour des outils de développement FPGA. La place occupée sur le disque dur de son ordinateur est plusieurs milliers de fois plus petites que les logiciels habituels :
$ cd p_r-2022.04-001/
$ du -sh .
24M .
$ cd yosys-win32-mxebin-0.15+57/
$ du -sh .
32M .
Évidemment, c’est pour seulement un modèle de FPGA, mais cela reste beaucoup plus petit.
Clignotons
Il est temps de rentrer dans le vif du sujet et de faire clignoter les LED. L’exemple donné dans le document ug1002 est trop rapide pour voir les LED clignoter. Nous allons donc faire un clignoteur plus traditionnel comme visible ci-dessous en Verilog :
`timescale 1ns / 1ps
module blink(
input wire clk,
input wire rst,
output reg led
);
localparam MAX_COUNT = 10_000_000;
localparam CNT_TOP = $clog2(MAX_COUNT);
wire i_clk;
reg [CNT_TOP-1:0] counter;
assign i_clk = clk;
always @(posedge i_clk)
begin
if (!rst) begin
led <= 0;
counter <= 0;
end else begin
if(counter < MAX_COUNT/2)
led <= 1;
else
led <= 0;
if (counter >= MAX_COUNT)
counter <= 0;
else
counter <= counter + 1'b1;
end
end
endmodule
Contrairement à beaucoup de FPGA, le GateMate ne définit pas d’états initial à 0 de ses registres. Une entrée reset est donc nécessaire.
Le pinout est décrit au moyen d’un fichier «ccf» :
## blink.ccf
Pin_in "clk" Loc = "IO_SB_A8" | SCHMITT_TRIGGER=true;
Pin_in "rst" Loc = "IO_EB_B0"; # SW3
Pin_out "led" Loc = "IO_EB_B1"; # D1
Une fois que ces deux fichiers sont prêt il suffit de lancer yosys pour la synthèse :
Wine génère tout un tas d’erreurs mais fini par nous lancer la synthèse tout de même
$ wine ../../cc-toolchain-win/yosys-win32-mxebin-0.15+57/yosys.exe -l yosys.log -p 'read_verilog blink.v; synth_gatemate -top blink -vlog blink_synth.v'
wine: created the configuration directory '/home/oem/.wine'
0012:err:ole:marshal_object couldn't get IPSFactory buffer for interface {00000131-0000-0000-c000-000000000046}
0012:err:ole:marshal_object couldn't get IPSFactory buffer for interface {6d5140c1-7436-11ce-8034-00aa006009fa}
0012:err:ole:StdMarshalImpl_MarshalInterface Failed to create ifstub, hres=0x80004002
0012:err:ole:CoMarshalInterface Failed to marshal the interface {6d5140c1-7436-11ce-8034-00aa006009fa}, 80004002
0012:err:ole:get_local_server_stream Failed: 80004002
0014:err:ole:marshal_object couldn't get IPSFactory buffer for interface {00000131-0000-0000-c000-000000000046}
0014:err:ole:marshal_object couldn't get IPSFactory buffer for interface {6d5140c1-7436-11ce-8034-00aa006009fa}
0014:err:ole:StdMarshalImpl_MarshalInterface Failed to create ifstub, hres=0x80004002
0014:err:ole:CoMarshalInterface Failed to marshal the interface {6d5140c1-7436-11ce-8034-00aa006009fa}, 80004002
0014:err:ole:get_local_server_stream Failed: 80004002
Could not find Wine Gecko. HTML rendering will be disabled.
Could not find Wine Gecko. HTML rendering will be disabled.
wine: configuration in L"/home/oem/.wine" has been updated.
On ne va pas recopier toute la trace de synthèse ici mais juste la partie ressources utilisées donnée en fin de synthèse :
2.49. Printing statistics.
=== blink ===
Number of wires: 49
Number of wire bits: 294
Number of public wires: 5
Number of public wire bits: 28
Number of memories: 0
Number of memory bits: 0
Number of processes: 0
Number of cells: 159
CC_ADDF 65
CC_BUFG 1
CC_DFF 25
CC_IBUF 2
CC_LUT1 24
CC_LUT2 5
CC_LUT4 36
CC_OBUF 1
2.50. Executing CHECK pass (checking for obvious problems).
Checking module blink...
Found and reported 0 problems.
2.51. Executing OPT_CLEAN pass (remove unused cells and wires).
Finding unused cells or wires in module \blink..
2.52. Executing Verilog backend.
2.52.1. Executing BMUXMAP pass.
2.52.2. Executing DEMUXMAP pass.
Dumping module `\blink'.
End of script. Logfile hash: dcf7a4084e
Yosys 0.15+57 (git sha1 207417617, i686-w64-mingw32.static-g++ 11.2.0 -Os)
Time spent: 1% 19x opt_clean (0 sec), 1% 18x opt_expr (0 sec), ...
Le fichier de sortie blink_synth.v est au format … Verilog également! C’était bien la peine de lancer Yosys tient !
Mais non, Verilog est un excellent format pour décrire une netlist autant que du RTL. Et de fait, le code Verilog généré n’est pas hyper lisible. Il est constitué d’une série d’instanciations et de connections des primitives du FPGA :
Code qui reste parfaitement simulable avec Icarus pour faire de la simulation post-synthèse comme expliqué dans la documentation officielle.
Maintenant que nous avons notre netlist passons aux choses sérieuses avec le placement routage :
$ wine ../../cc-toolchain-win/p_r-2022.04-001/p_r.exe -i blink_synth.v -o blink -lib ccag
GateMate (c) Place and Route
Version 4.0 (4 April 2022)
All Rights Reserved (c) Cologne Chip AG
...
Comme pour la synthèse, nous n’allons pas mettre tous les messages ici. Une des information qui nous intéresse en priorité pour le placement routage est la performance en vitesse.
...
Static Timing Analysis
Longest Path from Q of Component 25_1 to D-Input of Component 33/1 Delay: 18215 ps
Maximum Clock Frequency on CLK 230/3: 54.90 MHz
...
La vitesse d’horloge maximale est donc de 54.90Mhz. Cela peut sembler ridicule mais il faut prendre en compte que :
L’architecture du «clignoteur» avec un énorme compteur pour diviser l’horloge n’est absolument pas optimisée. Pour faire bien il faudrait pipeliner le compteur mais ça n’est pas le sujet ici. Ces mauvais résultats sont cohérent avec ce qu’on pourrait obtenir avec un autre FPGA «mainstream».
Ces performances sont «conservatrice» c’est le pire cas quand le FPGA est très chaud.
Bref, pour faire clignoter une LED, on peut raisonnablement doubler cette fréquence d’horloge si on veut 🙂 Mais comme nous n’utilisons pas de PLL ici, la fréquence d’entrée de 10Mhz rentre dans la specification.
Les statistiques d’utilisation du FPGA sont données à la fin de la synthèse :
CPE_USAGE_INPUT - CPE_COMBSEQ 1/8 : 0 / 21 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMBSEQ 2/8 : 11 / 21 ( 52.4%)
CPE_USAGE_INPUT - CPE_COMBSEQ 3/8 : 0 / 21 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMBSEQ 4/8 : 0 / 21 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMBSEQ 5/8 : 0 / 21 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMBSEQ 6/8 : 10 / 21 ( 47.6%)
CPE_USAGE_INPUT - CPE_COMBSEQ 7/8 : 0 / 21 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMBSEQ 8/8 : 0 / 21 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMB 1/8 : 3 / 3 ( 100.0%)
CPE_USAGE_INPUT - CPE_COMB 2/8 : 0 / 3 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMB 3/8 : 0 / 3 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMB 4/8 : 0 / 3 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMB 5/8 : 0 / 3 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMB 6/8 : 0 / 3 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMB 7/8 : 0 / 3 ( 0.0%)
CPE_USAGE_INPUT - CPE_COMB 8/8 : 0 / 3 ( 0.0%)
CPE_USAGE_INPUT - CPE COMBSEQ+COMB 1/8 : 3 / 24 ( 12.5%)
CPE_USAGE_INPUT - CPE COMBSEQ+COMB 2/8 : 11 / 24 ( 45.8%)
CPE_USAGE_INPUT - CPE COMBSEQ+COMB 3/8 : 0 / 24 ( 0.0%)
CPE_USAGE_INPUT - CPE COMBSEQ+COMB 4/8 : 0 / 24 ( 0.0%)
CPE_USAGE_INPUT - CPE COMBSEQ+COMB 5/8 : 0 / 24 ( 0.0%)
CPE_USAGE_INPUT - CPE COMBSEQ+COMB 6/8 : 10 / 24 ( 41.7%)
CPE_USAGE_INPUT - CPE COMBSEQ+COMB 7/8 : 0 / 24 ( 0.0%)
CPE_USAGE_INPUT - CPE COMBSEQ+COMB 8/8 : 0 / 24 ( 0.0%)
IO_USAGE : 3 / 144 ( 2.1%) of all IOs
IO_USAGE_TYPE - IBF : 2 / 144 ( 1.4%) of all IOs ( 66.7%) of used IOs
IO_USAGE_TYPE - OBF : 1 / 144 ( 0.7%) of all IOs ( 33.3%) of used IOs
IO_USAGE_TYPE - TOBF : 0 / 144 ( 0.0%) of all IOs ( 0.0%) of used IOs
IO_USAGE_TYPE - IOBF : 0 / 144 ( 0.0%) of all IOs ( 0.0%) of used IOs
CPE_USAGE_PHYS - CPE_COMB_ONLY : 73 / 20480 ( 0.4%) of all CPEs ( 60.3%) of occupied CPEs
CPE_USAGE_PHYS - CPE_SEQ_ONLY : 5 / 20480 ( 0.0%) of all CPEs ( 4.1%) of occupied CPEs
CPE_USAGE_PHYS - CPE_BRIDGE_ONLY : 0 / 20480 ( 0.0%) of all CPEs ( 0.0%) of occupied CPEs
CPE_USAGE_PHYS - CPE_CARRY_ONLY : 0 / 20480 ( 0.0%) of all CPEs ( 0.0%) of occupied CPEs
CPE_USAGE_PHYS - CPE_COMB+SEQ : 10 / 20480 ( 0.0%) of all CPEs ( 8.3%) of occupied CPEs
CPE_USAGE_PHYS - CPE_COMB+BRIDGE : 0 / 20480 ( 0.0%) of all CPEs ( 0.0%) of occupied CPEs
CPE_USAGE_PHYS - CPE_COMBSEQ : 21 / 20480 ( 0.1%) of all CPEs ( 17.4%) of occupied CPEs
CPE_USAGE_LOGIC - CPE_COMB : 8 / 20480 ( 0.0%) of all CPEs ( 6.6%) of occupied CPEs
CPE_USAGE_LOGIC - CPE_SEQ : 15 / 20480 ( 0.1%) of all CPEs ( 12.4%) of occupied CPEs
CPE_USAGE_LOGIC - CPE_COMBSEQ : 21 / 20480 ( 0.1%) of all CPEs ( 17.4%) of occupied CPEs
CPE_USAGE_LOGIC - CPE_BRIDGE : 0 / 20480 ( 0.0%) of all CPEs ( 0.0%) of occupied CPEs
CPE_USAGE_OVERALL : 121 / 20480 ( 0.6%) of all CPEs occupied
CPE_USAGE_LOGIC : 94 / 20480 ( 0.5%) of all CPEs used for customer logic
Component Statistics:
AND 32 19%
ADDF 2 1%
ADDF2 60 37%
C_OR 48 29%
Route1 4 2%
CP_route 15 9%
--------
Sum of COMB: 161
D 25 92%
C_0_1 2 7%
--------
Sum of SEQ: 27
Sum of all: 188
Et le bitstream généré est au format *.cfg (ascii) ou *.cfg.bit (binaire).
$ ls -lha blink_00.*
-rw-rw-r-- 1 oem oem 121K May 1 08:38 blink_00.cdf
-rw-rw-r-- 1 oem oem 1.4M May 1 08:38 blink_00.cfg
-rw-rw-r-- 1 oem oem 48K May 1 08:38 blink_00.cfg.bit
-rw-rw-r-- 1 oem oem 465 May 1 08:38 blink_00.pin
-rw-rw-r-- 1 oem oem 6.0K May 1 08:38 blink_00.place
-rw-rw-r-- 1 oem oem 65K May 1 08:38 blink_00.SDF
-rw-rw-r-- 1 oem oem 42K May 1 08:38 blink_00.used
-rw-rw-r-- 1 oem oem 79K May 1 08:38 blink_00.V
Pour configurer le FPGA nous utiliserons openFPGALoader en «natif» :
$ openFPGALoader -b gatemate_evb_jtag blink_00.cfg.bit
Jtag frequency : requested 6.00MHz -> real 6.00MHz
Load SRAM via JTAG: [==================================================] 100.00%
Done
Wait for CFG_DONE DONE
Le monde du FPGA (et de l’ASIC) regorge aujourd’hui de langages de description matériel. Au Verilog et VHDL s’ajoute maintenant tout un tas de langages comme Migen, Clash, BlueSpec, Amaranth, Chisel, SpinalHDL, Silice, … et j’en oublie plein. Tous ces langages permettent de générer du Verilog. Les possibilité de conversion de VHDL vers Verilog ne sont maintenant plus une utopie grâce aux évolutions de GHDL et de Yosys.
Bref, il existe désormais toujours une possibilité de convertir l’intégralité du projet en Verilog de manière à le simuler et synthétiser avec les outils conçus pour lui.
C’est quelque chose de très appréciable pour faire de la réutilisation de code. Il n’est plus nécessaire de re-concevoir un composant en VHDL tout ça parce que le contrôleur open source qui nous est nécessaire est codé en Verilog.
L’homogénéité de langage des sources d’un projet peut cependant être appréciable dans certain cas. Notamment quand le langage de description possède ses propres librairies de simulation comme c’est le cas en Chisel.
On peut certes instancier les «sous»-modules Verilog au moyen de BlackBox, mais ils ne seront pas simulable avec ChiselTest par exemple car treadle se cassera les dents dessus.
C’est là qu’intervient le nouveau projet nommé sv2chisel pour convertir du (system)Verilog en chisel.
Je vous propose ici de tester ensemble l’utilitaire dans un cas pratique. Je souhaite convertir le module Verilog fake_differential du projet d’exemple de l’ulx3s qui permet de générer un signal HDMI différentiel pour l’intégrer dans le projet HdmiCore écrit en Chisel. L’objectif étant de porter le projet HdmiCore sur la plate-forme ulx3s en restant dans du pure Chisel.
Installation de l’outil
Toutes les caractéristiques et limitations du convertisseur sont données sur le wiki. Pour l’installer nous allons cloner le projet github. Le projet n’en est pas à sa version 1.0, il est sans doute préférable de «travailler» sur le main du git plutôt que sur une release.
$ git clone https://github.com/ovh/sv2chisel.git
$ cd sv2chisel
Les étapes d’installations depuis les sources sont données dans le readme. Il faut publier localement sv2chisel ainsi que les «helpers» :
$ sbt publishLocal
Cette commande fonctionne pas chez moi, je pensais naïvement que c’était la même commande que celle consistant à lancer sbt puis taper «publishLocal» mais non 😉
Donc pour publier localement, on fera plutôt comme recommandé dans le readme :
Pour convertir en Chisel, on peut simplement donner les noms des fichiers sources en arguments:
$ ./sv2chisel fake_differential.v
[log] ---- Processing project ----
[log] ############# Parsing fake_differential.v #############
[log] ######### Elapsed time for fake_differential.v #########
[log] # Lexing+Parsing Time : 335.206505 ms
[log] # Mapping to IR Time : 126.985877 ms
[log] ######### Executing 18 transforms #########
[log] ####### sv2chisel.transforms.CheckUseBeforeDecl #######
[log] # Elapsed time : 26.214516 ms
[log] ####### sv2chisel.transforms.CheckScopes #######
[log] # Elapsed time : 7.222317 ms
[log] ####### sv2chisel.transforms.CheckBlockingAssignments #######
[log] # Elapsed time : 1.448437 ms
[log] ####### sv2chisel.transforms.InferDefLogicClocks #######
[info] Registering a new clock clk_shift for module fake_differential (non blocking assignment) at fake_differential.v:3:0>>54:0
[warn] Unable to find module module ODDRX1F instanciated as ddr_p_instance in current module fake_differential for clock inference processing. at fake_differential.v:33:10>>41:11
[warn] Unable to find module module ODDRX1F instanciated as ddr_n_instance in current module fake_differential for clock inference processing. at fake_differential.v:42:10>>50:11
[log] # Elapsed time : 41.125895 ms
[log] ####### sv2chisel.transforms.PropagateClocks #######
[warn] Module ODDRX1F referenced by instance ddr_p_instance cannot be found in current design. Clock & Reset management might be inaccurate. at fake_differential.v:33:10>>41:11
[warn] Module ODDRX1F referenced by instance ddr_n_instance cannot be found in current design. Clock & Reset management might be inaccurate. at fake_differential.v:42:10>>50:11
[log] # Elapsed time : 1.878423 ms
[log] ####### sv2chisel.transforms.FlowReferences #######
[info] Declaring actual port directions for module fake_differential at fake_differential.v:5:2->8
[info] Running FlowReference Transform another time on module fake_differential at fake_differential.v:3:0>>54:0
[log] # Elapsed time : 28.311422 ms
[log] ####### sv2chisel.transforms.InferUInts #######
[info] Converting in_clock to UInt based on its usage in the module at fake_differential.v:7:9->11
[info] Converting tmds[_] to UInt based on its usage in the module at fake_differential.v:11:10->12
[log] # Elapsed time : 30.181078 ms
[log] ####### sv2chisel.transforms.InferParamTypes #######
[log] # Elapsed time : 5.803376 ms
[log] ####### sv2chisel.transforms.TypeReferences #######
[critical] Unsupported Type 'Bool' for subindex expression 'out_n[i]' at fake_differential.v:47:15->22
[log] # Elapsed time : 18.082934 ms
[log] ####### sv2chisel.transforms.LegalizeExpressions #######
[warn] Unknown remote type for port #2 (Q) of instance ddr_p_instance of module ODDRX1F: casting by reference by default at fake_differential.v:38:12->23
[critical] Unsupported Type 'Bool' for subindex expression 'out_n[i]' at fake_differential.v:47:15->22
[warn] Unknown remote type for port #2 (Q) of instance ddr_n_instance of module ODDRX1F: casting by reference by default at fake_differential.v:47:12->23
[log] # Elapsed time : 29.292823 ms
[log] ####### sv2chisel.transforms.FixFunctionImplicitReturns #######
[log] # Elapsed time : 1.314473 ms
[log] ####### sv2chisel.transforms.NameInstancePorts #######
[log] # Elapsed time : 2.628666 ms
[log] ####### sv2chisel.transforms.RemovePatterns #######
[log] # Elapsed time : 4.862502 ms
[log] ####### sv2chisel.transforms.RemoveConcats #######
[log] # Elapsed time : 3.143862 ms
[log] ####### sv2chisel.transforms.AddDontCare #######
[log] # Elapsed time : 1.28653 ms
[log] ####### sv2chisel.transforms.LegalizeParamDefaults #######
[warn] Cannot find module ODDRX1F in current project at fake_differential.v:33:10>>41:11
[warn] Cannot find module ODDRX1F in current project at fake_differential.v:42:10>>50:11
[log] # Elapsed time : 4.072062 ms
[log] ####### sv2chisel.transforms.FixReservedNames #######
[log] # Elapsed time : 4.126536 ms
[log] ####### sv2chisel.transforms.ToCamelCase #######
[log] # Elapsed time : 0.830815 ms
[log] # Total Elapsed time running transforms : 216.675871 ms
[log] ######### EMISSION #########
[log] ######### CHISELIZING fake_differential.v #########
[info] At fake_differential.v:11: Emitting unpacked for node tmds
[info] At fake_differential.v:18: Emitting unpacked for node R_tmds_p
[info] At fake_differential.v:18: Emitting unpacked for node R_tmds_n
[log] # Elapsed time : 21.267262 ms
[log] ######### EMITTING to /fake_differential.scala #########
Exception in thread "main" java.io.FileNotFoundException: /fake_differential.scala (Permission denied)
at java.base/java.io.FileOutputStream.open0(Native Method)
at java.base/java.io.FileOutputStream.open(FileOutputStream.java:291)
at java.base/java.io.FileOutputStream.(FileOutputStream.java:234)
at java.base/java.io.FileOutputStream.(FileOutputStream.java:123)
at java.base/java.io.FileWriter.(FileWriter.java:66)
at sv2chisel.Emitter$.$anonfun$emitChisel$9(Emitter.scala:174)
at sv2chisel.Utils$.time(Utils.scala:185)
at sv2chisel.Emitter$.$anonfun$emitChisel$1(Emitter.scala:163)
at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:286)
at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:62)
at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:55)
at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:49)
at scala.collection.TraversableLike.map(TraversableLike.scala:286)
at scala.collection.TraversableLike.map$(TraversableLike.scala:279)
at scala.collection.AbstractTraversable.map(Traversable.scala:108)
at sv2chisel.Emitter$.emitChisel(Emitter.scala:134)
at sv2chisel.Driver$.emitChisel(Driver.scala:66)
at sv2chisel.Main$.$anonfun$new$10(Main.scala:159)
at scala.collection.immutable.List.foreach(List.scala:431)
at sv2chisel.Main$.delayedEndpoint$sv2chisel$Main$1(Main.scala:157)
at sv2chisel.Main$delayedInit$body.apply(Main.scala:55)
at scala.Function0.apply$mcV$sp(Function0.scala:39)
at scala.Function0.apply$mcV$sp$(Function0.scala:39)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
at scala.App.$anonfun$main$1$adapted(App.scala:80)
at scala.collection.immutable.List.foreach(List.scala:431)
at scala.App.main(App.scala:80)
at scala.App.main$(App.scala:78)
at sv2chisel.Main$.main(Main.scala:55)
at sv2chisel.Main.main(Main.scala)
Après de multiple messages plus ou moins critiques, la commande se termine sur une étonnante erreur java de fichier non trouvé. Visiblement il faut lui fournir le path complet en argument (sans doute un bug) :
./sv2chisel /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v
[log] ---- Processing project ----
[log] ############# Parsing /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v #############
[log] ######### Elapsed time for /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v #########
[log] # Lexing+Parsing Time : 337.962186 ms
[log] # Mapping to IR Time : 123.022396 ms
[log] ######### Executing 18 transforms #########
[log] ####### sv2chisel.transforms.CheckUseBeforeDecl #######
[log] # Elapsed time : 24.704003 ms
[log] ####### sv2chisel.transforms.CheckScopes #######
[log] # Elapsed time : 6.588446 ms
[log] ####### sv2chisel.transforms.CheckBlockingAssignments #######
[log] # Elapsed time : 1.838544 ms
[log] ####### sv2chisel.transforms.InferDefLogicClocks #######
[info] Registering a new clock `clk_shift` for module fake_differential (non blocking assignment) at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:3:0>>54:0
[warn] Unable to find module module ODDRX1F instanciated as ddr_p_instance in current module fake_differential for clock inference processing. at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:33:10>>41:11
[warn] Unable to find module module ODDRX1F instanciated as ddr_n_instance in current module fake_differential for clock inference processing. at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:42:10>>50:11
[log] # Elapsed time : 37.088031 ms
[log] ####### sv2chisel.transforms.PropagateClocks #######
[warn] Module ODDRX1F referenced by instance ddr_p_instance cannot be found in current design. Clock & Reset management might be inaccurate. at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:33:10>>41:11
[warn] Module ODDRX1F referenced by instance ddr_n_instance cannot be found in current design. Clock & Reset management might be inaccurate. at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:42:10>>50:11
[log] # Elapsed time : 1.73635 ms
[log] ####### sv2chisel.transforms.FlowReferences #######
[info] Declaring actual port directions for module fake_differential at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:5:2->8
[info] Running FlowReference Transform another time on module fake_differential at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:3:0>>54:0
[log] # Elapsed time : 25.221955 ms
[log] ####### sv2chisel.transforms.InferUInts #######
[info] Converting in_clock to UInt based on its usage in the module at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:7:9->11
[info] Converting tmds[_] to UInt based on its usage in the module at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:11:10->12
[log] # Elapsed time : 29.58874 ms
[log] ####### sv2chisel.transforms.InferParamTypes #######
[log] # Elapsed time : 5.646461 ms
[log] ####### sv2chisel.transforms.TypeReferences #######
[critical] Unsupported Type 'Bool' for subindex expression 'out_n[i]' at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:47:15->22
[log] # Elapsed time : 18.257447 ms
[log] ####### sv2chisel.transforms.LegalizeExpressions #######
[warn] Unknown remote type for port #2 (Q) of instance ddr_p_instance of module ODDRX1F: casting by reference by default at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:38:12->23
[critical] Unsupported Type 'Bool' for subindex expression 'out_n[i]' at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:47:15->22
[warn] Unknown remote type for port #2 (Q) of instance ddr_n_instance of module ODDRX1F: casting by reference by default at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:47:12->23
[log] # Elapsed time : 21.168114 ms
[log] ####### sv2chisel.transforms.FixFunctionImplicitReturns #######
[log] # Elapsed time : 0.631086 ms
[log] ####### sv2chisel.transforms.NameInstancePorts #######
[log] # Elapsed time : 1.467731 ms
[log] ####### sv2chisel.transforms.RemovePatterns #######
[log] # Elapsed time : 5.09245 ms
[log] ####### sv2chisel.transforms.RemoveConcats #######
[log] # Elapsed time : 2.749951 ms
[log] ####### sv2chisel.transforms.AddDontCare #######
[log] # Elapsed time : 1.251281 ms
[log] ####### sv2chisel.transforms.LegalizeParamDefaults #######
[warn] Cannot find module ODDRX1F in current project at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:33:10>>41:11
[warn] Cannot find module ODDRX1F in current project at /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:42:10>>50:11
[log] # Elapsed time : 3.092851 ms
[log] ####### sv2chisel.transforms.FixReservedNames #######
[log] # Elapsed time : 4.132691 ms
[log] ####### sv2chisel.transforms.ToCamelCase #######
[log] # Elapsed time : 0.890969 ms
[log] # Total Elapsed time running transforms : 195.82181 ms
[log] ######### EMISSION #########
[log] ######### CHISELIZING /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v #########
[info] At /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:11: Emitting unpacked for node tmds
[info] At /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:18: Emitting unpacked for node R_tmds_p
[info] At /opt/chiselmod/sv2chisel/utils/bin/fake_differential.v:18: Emitting unpacked for node R_tmds_n
[log] # Elapsed time : 25.374274 ms
[log] ######### EMITTING to //opt/chiselmod/sv2chisel/utils/bin/fake_differential.scala #########
[log] # Elapsed time : 17.429649 ms
Il y a sans doute beaucoup de chose a parametrer aux petits oignons pour bien utiliser l’outils, mais le résultat «en l’état» est déjà assez intéressant (commentaires FLF ajoutés):
package // FLF: à rajouter à la main ?
// FLF: on comprend pourquoi il voulait un path complet,
// il le prend pour le nom du package
package .opt.chiselmod.sv2chisel.utils.bin
import chisel3._
// FLF: commentaires gardés, bien.
// DDR mode uses Lattice ECP5 vendor-specific module ODDRX1F
class fake_differential() extends Module { // used only in DDR mode
// [1:0]:DDR [0]:SDR TMDS
val in_clock = IO(Input(UInt(2.W)))
val in_red = IO(Input(Bool()))
val in_green = IO(Input(Bool()))
val in_blue = IO(Input(Bool()))
// [3]:clock [2]:red [1]:green [0]:blue
val out_p = IO(Output(Vec(4, Bool())))
val out_n = IO(Output(Bool()))
// FLF: hmm, j'aurais pas traduit un tableau «wire» par une Mem()
// chisel, pas sûr que ça marche bien cette affaire.
val tmds = Mem(4,UInt(2.W))
tmds(3) := in_clock
tmds(2) := in_red
tmds(1) := in_green
tmds(0) := in_blue
// register stage to improve timing of the fake differential
val R_tmds_p = Mem(4,Vec(2, Bool()))
val R_tmds_n = Mem(4,Vec(2, Bool()))
// genvar i;
for(i <- 0 until 4){
R_tmds_p(i) := tmds(i).asBools
R_tmds_n(i) := ( ~tmds(i)).asBools
}
// output SDR/DDR to fake differential
// FLF: les generate sont détecté et traduit également.
// genvar i;
for(i <- 0 until 4){
// FLF: connexion des primitives sans broncher
// Il faudra tout de même inclure la définition
// de la blackbox à la main par la suite (import)
val ddr_p_instance = Module(new ODDRX1F)
ddr_p_instance.D0 := R_tmds_p(i)(0)
ddr_p_instance.D1 := R_tmds_p(i)(1)
out_p(i) := ddr_p_instance.Q.asTypeOf(out_p(i))
ddr_p_instance.SCLK := clock
ddr_p_instance.RST := 0.U
val ddr_n_instance = Module(new ODDRX1F)
ddr_n_instance.D0 := R_tmds_n(i)(0)
ddr_n_instance.D1 := R_tmds_n(i)(1)
out_n(i) := ddr_n_instance.Q.asTypeOf(out_n(i))
ddr_n_instance.SCLK := clock
ddr_n_instance.RST := 0.U
}
}
Conclusion
Visiblement, le code généré doit être passé en revue par un ou une humaine histoire de corriger quelques imprécisions.
Mais cette relecture est facile, le code est très lisible et bien indenté. On retrouve les noms des signaux, variables, registres, modules Verilog. On est loin des bouillies de conversion où le code généré ressemble plus à un binaire compilé qu’à un code source «versionnable». Et cette saine relecture est de toute manière indispensable si l’on souhaite se reposer sur ce nouveau code dans la suite de son projet.
C’est un outil qui va vite devenir indispensable lorsque le besoin de convertion de code open-source se fera sentir. Et c’est une belle passerelle pour tous les habitués du Verilog qui souhaiteraient se lancer dans ce langage de haut niveau qu’est Chisel.
Des dire de l’équipe, l’outil a été testé avec succès sur le code du processeur RISC-V PicoRV32 développé par Claire Clifford (autrice de Yosys) que l’on retrouve un peu partout dans les projets open-source hardware.
C’est également une surprise de voir que ce projet est né au sein du laboratoire OVHCloud. Où l’on découvre que le fleurons du Claude français (cocorico) finance la recherche sur le matériel libre. Ceux qui ont besoin d’un article plus académique pour découvrir l’outil iront lire le papier de l’équipe ici.
Ho mais je me rend compte que je n’avais encore rien écrit sur la carte ULX3S commandée l’été dernier. Il faut dire que les délais d’approvisionnement étant ce qu’ils sont aujourd’hui la carte a tout de même mis presque six mois à arriver. J’ai donc eu tout le temps de passer à autre chose.
Donc oui, après m’être posé la question, j’ai opté pour l’ULX3S au détriment de l’orangecrab. La carte, conçue par le Hackerspace Radiona de Zagreb (croatie) arrive dans un petit carton muni de quelques accessoire «pmod» pour ajouter un second port HDMI, des USB et autres header he10.
L’ULX3S et ses adaptateurs «pmod»
La carte a tout ce qu’il faut pour faire une console de jeux 🙂 Mais pour le moment, on va surtout s’intéresser à la sortie HDMI, après avoir déballé la bête.
Mise en route
Un guide de mise en route est donné sur le github officiel. Pour démarrer la carte il suffit de brancher l’ordinateur sur l’usb de gauche.
Branchement de l’usb pour programmer la carte (src : quickstartguide)
Ce qui a pour effet d’allumer quelques leds de toutes les couleurs
\o/ plein de LEDs multicolors
et de monter un driver tty sur le pc host :
$ dmesg
[1956889.190788] usb 1-1.1.2: new full-speed USB device number 16 using xhci_hcd
[1956889.300502] usb 1-1.1.2: New USB device found, idVendor=0403, idProduct=6015, bcdDevice=10.00
[1956889.300504] usb 1-1.1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[1956889.300506] usb 1-1.1.2: Product: ULX3S FPGA 85K v3.0.8
[1956889.300507] usb 1-1.1.2: Manufacturer: FER-RADIONA-EMARD
[1956889.300508] usb 1-1.1.2: SerialNumber: E20000
[1956889.308375] ftdi_sio 1-1.1.2:1.0: FTDI USB Serial Device converter detected
[1956889.308401] usb 1-1.1.2: Detected FT-X
[1956889.308597] usb 1-1.1.2: FTDI USB Serial Device converter now attached to ttyUSB0
N’oublions pas que le FPGA est un ECP5, il y a donc tous les outils opensource disponibles pour développer avec. De plus, radiona fournie la toolchain précompilé pour la carte, il n’y a plus qu’à la télécharger et l’installer comme expliqué dans le manuel (424Mo pour un pc x64).
$ cd /opt/
$ mkdir ulx3s
$ cd ulx3s/
$ wget https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2022-03-17/oss-cad-suite-linux-x64-20220317.tgz
$ tar xf oss-cad-suite-linux-x64-20220317.tgz
Si on veut faire clignoter vite vite vite, dans relancer de synthèse on peut tout simplement cloner le projet «blink» et télécharger le bitstream. À titre personnel je préfère utiliser openFPGALoader (avant même d’en tester un autre ;).
$ cd /opt/ulx3s
$ git clone https://github.com/ulx3s/blink.git
$ cd blink/
$ tree
.
├── blink_12f.bit
├── blink_45f.bit
├── blink_85f.bit
├── blinky.v
├── blinky.ys
├── LICENSE
├── Makefile
├── README.md
└── ulx3s_v20.lpf
0 directories, 9 files
$ openFPGALoader -bulx3s blink_85f.bit
Jtag probe limited to 3MHz
Jtag frequency : requested 6000000Hz -> real 3000000Hz
ret 0
Open file: DONE
Parse file: DONE
Enable configuration: DONE
SRAM erase: DONE
Loading: [==================================================] 100.00%
Done
Disable configuration: DONE
Le résultat n’est pas une LED qui clignote, mais 6 LEDs multicolors qui clignotent (qui compte en binaire).
Le projet blink fourni un makefile pour reconstruire le bitstream si l’on veut tester la toolchain :
$ export PATH=/opt/ulx3s/oss-cad-suite/:$PATH
$ make ulx3s.bit
$ openFPGALoader -bulx3s ulx3s.bit
Et voila \o/, c’est tout pour la prise en main !
La suite
La suite va consister à adapter le projet HdmiCore pour la sortie HDMI de la carte.
Ça y est, le colis contenant la carte de développement Tang Nano 9k est arrivé. Contrairement à la TangNano4k je ne l’ai pas reçue rapidement, il a fallu attendre environ deux mois. Mais elle est bien là maintenant.
La TangNano9k sortie de sa boite
La petite carte, muni d’un littlebee GW1NR-LV9 est tout de même plus grande que les petites sœur. Cette carte est plus «grande» que la 4k mais elle possède autant de PSRAM (64Mbits) et ne possède pas de cœur ARM. La carte possède cependant plus de LUT.
Tang Nano 9K, Tang Nano 4k et Tang Nano
La fiche descriptive d’aliexpress propose un tableau comparatif des trois cartes disponible pour environ $15.
Table de comparaison des différentes TangNano proposé sur aliexpress
Au branchement, les deux UART du BL702 apparaissent :
[1874406.879770] usb 1-1.1.3: new full-speed USB device number 96 using xhci_hcd
[1874406.980305] usb 1-1.1.3: not running at top speed; connect to a high speed hub
[1874406.981788] usb 1-1.1.3: New USB device found, idVendor=0403, idProduct=6010, bcdDevice= 5.00
[1874406.981794] usb 1-1.1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[1874406.981797] usb 1-1.1.3: Product: JTAG Debugger
[1874406.981800] usb 1-1.1.3: Manufacturer: SIPEED
[1874406.981803] usb 1-1.1.3: SerialNumber: FactoryAIOT Pro
[1874406.991536] ftdi_sio 1-1.1.3:1.0: FTDI USB Serial Device converter detected
[1874406.991586] usb 1-1.1.3: Detected FT2232C
[1874406.992995] usb 1-1.1.3: FTDI USB Serial Device converter now attached to ttyUSB0
[1874406.993170] ftdi_sio 1-1.1.3:1.1: FTDI USB Serial Device converter detected
[1874406.993213] usb 1-1.1.3: Detected FT2232C
[1874406.993538] usb 1-1.1.3: FTDI USB Serial Device converter now attached to ttyUSB1
Et les 6 LED rouges clignotent en même temps.
Pinout de la TangNano9k trouvé sur la fiche aliexpress
Le part number du FPGA est GW1NR-LV9QN88PC6/I5 et peut être sélectionné pour la synthèse et le placement-routage avec le logiciel de gowin version 1.9.8.03.
GHDL vient de sortir la version 2.0.0. Un an jour pour jours avec la version 1.0.0. Visiblement il n’y a pas de release note, mais le rythme de développement fait plaisir à voir pour ce simulateur VHDL opensource de référence.
CLEAR est un microcontrôleur RISC-V (VexRISCV) muni d’un eFPGA gravé en 130 nm et produit avec le programme chipignite de la société de production d’ASICEfabless. La totalité du « dossier de fabrication » du composant est publiée en open source.
Un FPGA est un type particulier de composant électronique. Il peut être vu comme un « kit » de portes logiques avec des connexions reconfigurables à l’infini. Le tout est assemblé dans un même composant. Les constructeurs de FPGA gardent jalousement secrète une partie de l’architecture de leurs composants et ne fournissent même pas la documentation permettant de les configurer. Il est nécessaire de passer par leurs logiciels (binaires) pour générer le fichier de configuration appelé bitstream. La conception et la fabrication d’un FPGA passent par les mêmes procédés que pour n’importe quel composant électronique numérique (processeur, périphérique, microprocesseur) : on part d’un modèle numérique du composant que l’on va transformer en un dessin « 3D » du circuit final appelé GDSII. Toute cette chaîne de fabrication utilise un tas de formats et de standards jalousement gardés secrets par les différents acteurs de cette industrie. Il est évident que les fabricants de FPGA gardent secret ces « dossiers de fabrication ».
Il y a quelques années, Claire Clifford libérait le format des bitstreams de configuration du FPGA ice40 par rétro-ingénierie avec le projet Icestorm. D’autres FPGA ont ensuite été libérés par ingénierie inverse, sans soutien des différents constructeurs.
Un pas de plus dans la libération des FPGA a été franchi avec la production du EOS S3 de la société QuickLogic. QuickLogic a publié le format du bitstream de la partie FPGA (eFPGA) de son composant et supporte officiellement les outils opensource.
Avec le CLEAR, on passe à une toute autre échelle de libération pour les FPGA. En effet — cette fois — c’est la totalité du « dossier de fabrication » qui est libéré. Commençons par le cœur du composant : openFPGA.
openFPGA : le générateur de FPGA
L’architecture utilisée dans le CLEAR est celle du projet openFPGA. Le projet propose de générer une structure de FPGA « prête à graver » à partir d’un simple fichier de configuration de l’architecture voulue. L’objectif étant de réduire le temps de développement d’un FPGA à 24 heures, au lieu des mois habituels.
L’architecture du eFPGA est constituée de 8 × 8 = 64 CLB. Chaque CLB contient quatre Blocs Logique Élémentaire (BLE) avec à chaque fois :
une bascule D (Flip-Flop) – un multiplexeur de sortie (MUX).
Ce type d’architecture se retrouve dans tous les FPGA avec une variation sur le nombre d’entrées de la LUT. Cela nous permet de comparer les FPGA en termes de nombre de LUT.
Avec le CLEAR nous avons donc un eFPGA de 256 LUT. Pour comparer on peut parler du EOS S3 qui possède 891 LUT ou du ICE40 dont la gamme débute à 384 LUT et monte jusqu’à environ 8000 LUT.
On parle donc d’un tout petit eFPGA. Avec une architecture de FPGA open source, il est plus évident de développer des outils libres pour travailler sur ces FPGA. L’outil de référence pour configurer les FPGA générés avec openFPGA est issu majoritairement du projet VTR (Verilog To Routing).
La partie FPGA ainsi générée est prête à être intégrée dans la caravelle.
Caravel : embarquez votre composant avec un processeur RISC-V
La lectrice ou le lecteur aura certainement remarqué que le titre de la dépêche ne parle pas de FPGA mais de eFPGA. e qui signifie simplement « embedded ». Le FPGA généré est embarqué dans un composant plus gros muni d’un microprocesseur 32 bits nommé VexRisc qui est openSource lui aussi.
Ce processeur — développé par Charles Papon avec le langage HDL SpinalHDL — est la base du SoC Caravel développé par la société eFabless et open source lui aussi.
L’objectif de Caravel est de proposer un SoC complet avec le CPU, quelques périphériques (UART, SPI, GPIO, RAM…) et une zone libre pour y graver un composant de son cru. Toute la partie « outils de déverminage » est ainsi standardisée avec tous les signaux nécessaires au bon fonctionnement du projet cible.
Avec cet outil libre, efabless veut démocratiser la fabrication de composant électronique et la rendre accessible aux petites PME, voire aux particuliers.
Ce kit de développement est proposé par la société efabless avec son programme chipignite.
Chipignite : les ASIC pour tous
Avec le programme Chipignite d’efabless, il est en effet possible de faire produire 100 composants (ou 300 selon le boîtier choisi) pour moins de 10 000 $ gravé en 130 nm. À ce prix-là, on a tous les outils de développement à disposition ainsi que les bibliothèques du kit de développement (PDK) en 130 nm du fabricant, sans avoir à signer d’accord de non divulgation (NDA) pour lire l’accord de non divulgation à signer.
Comme le nom de la société le laisse entendre, efabless ne possède pas d’usine de fabrication. Elle se contente de réserver des slots (shuttle) de fabrication dans l’usine américaine skywater.
Sky130 : le « kit ASIC » (pdk) libéré
Skywater est un petit fabricant de composants électroniques américain. C’est une ancienne usine de Cypress qui a pris son indépendance.
Cette entreprise fabrique des semi-conducteurs à des niveaux de gravure qui sont aujourd’hui considérés comme largement amortis. Le 130 nm par exemple est la finesse de gravure utilisée il y a 20 ans pour les processeurs Pentium III ou pour le processeur de la Gamecube.
Pour faire produire un composant électronique, il faut fournir un fichier de fabrication au format GDSII. Qui est un format ouvert de description des différentes couches de dopage et de connexions métal du composant.
Mais pour générer ce GDSII, l’entreprise qui conçoit des composants a besoin de connaître les propriétés du substrat fabriqué. Il faut un modèle pour simuler les composants que l’on « dessine ». À partir de ce modèle il est possible d’établir une bibliothèque de composants comme des transistors, bascules, mémoire et autres.
Toutes ces informations sont rassemblées dans ce qu’on appelle un PDK (Process Design Kit). Le PDK est intimement lié au fabricant et au process de fabrication. Sans lui, impossible de concevoir et générer le GDSII, malgré la disponibilité de nombreux (et vieux) logiciels libres dans le domaine.
Jusqu’à l’année dernière, il n’existait pas de PDK « ouvert » : impossible pour une petite entreprise d’y avoir accès sans payer des sommes folles en licences de développement. Sans compter sur les accords de non divulgation qui rendaient impossible la diffusion du GDSII en dehors de la production.
Pour dynamiser sa production, Skywater a donc décidé, avec l’aide de Google, de libérer son PDK pour la finesse de gravure de 130 nm. Le PDK se nomme Sky130 et est disponible à tous sous licence Apache 2.0 sur github.
Open MPW shuttle : les ASIC open source gratuits de google
10 000 $ ça ne se trouve pas sous le sabot d’un cheval. Il y a intérêt d’avoir un projet un peu plus solide qu’un simple TapTempo. Une PME peut prendre des risques avec cette somme mais de manière mesurée.
Pour favoriser l’émergence d’une communauté open source dans le développement matériel et pour encourager les développeuses et développeurs à prendre des risques et innover, Google a décidé de financer des productions de composants tous les six mois.
Pour être dans la « navette » nommée Open MPW shuttle il faut proposer un composant totalement open source et publier tous les scripts permettant de re-générer le GDSII. Si le projet est jugé intéressant, efabless fera fabriquer une série de composants gratuitement.
Et c’est ce qui s’est passé avec le projet Caravel-OpenFPGA-EF qui a été produit dans la première navette MPW-1. OpenFPGA a également été utilisé dans la seconde navette MPW-2 avec le projet SOFA.
Il y a d’autre projet de eFPGA dans les navettes, mais OpenFPGA semble être le plus utilisé et le plus facile d’accès.
La production de CLEAR en financement participatif
Pour montrer la possibilité de produire un eFPGA open source un financement participatif a été lancé sur le site groupgets.
Moyennant 75 $ (plus les frais de port plus certainement la douane) il est possible d’acquérir ce microcontrôleur soudé sur une carte de développement.
Une fois la levée de fonds bouclée, le projet sera embarqué dans la navette 2204C pour une production prévue en avril 2022.
Bien sûr, une carte de développement à ce prix pour un eFPGA aussi petit peut sembler inintéressante. Il faut plutôt le voir comme une somme accessible à (presque) tout le monde pour entrer dans le monde du silicium et des FPGA opensource. C’est, en tout cas la preuve, qu’il est possible de produire un FPGA (+ microcontrôleur) intégralement open source à des tarifs raisonnables.
openFPGALoader est un utilitaire en ligne de commande, écrit en C++ et sous licence Apache 2.0. Il permet de configurer des FPGA de toutes marques. L’objectif du projet est de pouvoir prendre en charge tous les FPGA du marché ainsi que tous les adaptateurs et sondes de configuration.
Pour développer sur un FPGA, nous avons besoin d’un ensemble de logiciels et de formats spécifiques. La chaîne de développement sur FPGA peut se résumer par la figure ci-dessous:
L’architecture du composant est décrite dans un langage de description (HDL pour Hardware Description Language) matériel. Cette description est convertie en un schéma électronique (Netlist) par un procédé appelé «synthèse». Les composants de la Netlist sont ensuite placés dans la matrice du FPGA (placement) puis connectés ensemble (Routage) pour former le composant décrit au début. Toutes ces informations sont ensuite décrites dans un fichier de configuration appelé bitstream (propriétaire). Et enfin, le fichier est transféré au FPGA pour le configurer.
À l’origine, toutes ces opérations sont réalisées par des logiciels privateurs, et les formats sont verrouillés. Quand on parle de libération des FPGA on aimerait bien sûr que toute la chaîne puisse être réalisée avec des logiciels libres et des formats ouverts. Mais le point le plus bloquant évoqué est souvent le format du bitstream, qui est LE maillon le plus critique de la chaîne jalousement gardé secret par (presque) tous.
Toutes ces étapes ont désormais des projets open source qui sont suffisamment avancés pour pouvoir développer sur FPGA librement. À condition de bien choisir le modèle.
Chargement du bitstream ou « configuration »
On en oublie souvent la dernière étape consistant à télécharger le bitstream dans le FPGA. Pourtant cette étape est également dépendante du constructeur qui propose l’adaptateur à vil prix (souvent une sonde USB-JTAG). Et le logiciel est en général intégré au lourd IDE du constructeur (binaire x86) qu’il n’est pas toujours facile de configurer sur sa distribution Linux et encore moins sur une architecture exotique (raspberryPi, Risc-V, …).
Le dessein d’openFPGALoader est donc d’être « l’anneau pour les programmer tous ». Pour cela il faut :
prendre en charge les différentes sondes de programmation du marché. La documentation indique les 14 (familles de) sondes qui le sont déjà.
gérer également les différentes cartes électroniques intégrant les FPGA ainsi que les différents modes de configuration (RAM de configuration, EEPROM maître/esclave, Flash, EEPROM interne…).
Avec cette version 0.6.0, le logiciel peut être considéré comme mature. C’est tellement devenu une référence qu’il est même intégré dans quelques distributions Linux, dans buildroot. Le logiciel fonctionne également sur Mac et Windows avec cependant plus de problème du fait du passage par le pilote usb zadig.
C’est aujourd’hui un automatisme pour configurer un FPGA, de le tester d’abord avec openFPGALoader. Avant même d’utiliser l’outil constructeur. Le logiciel apporte un confort d’utilisation et de configuration qui n’a rien à envier aux autres.
Quelques exemples
Pour illustrer, un peu l’utilisation d’openFPGALoader, supposons que nous ayons notre bitstream permettant de faire clignoter la led de la carte Tang Nano 4K. L’avantage de cette carte est que l’adaptateur de programmation est inclus et que tout passe par le même port USB. Une fois la carte branchée on peut commencer par détecter le FPGA avec --detect :
$ openFPGALoader --detect
write to ram
Jtag frequency : requested 6.00MHz -> real 6.00MHz
index 0:
idcode 0x100981b
manufacturer Gowin
family GW1NSR
model GW1NSR-4C
irlength 8
Le format de bitstream pour les gowin possède l’extension fs, on peut le configurer directement en donnant simplement le nom du fichier en argument :
$ openFPGALoader led_test/project/impl/pnr/led_test.fs
write to ram
Jtag frequency : requested 6.00MHz -> real 6.00MHz
Parse file Parse led_test/project/impl/pnr/led_test.fs:
Done
DONE
Jtag frequency : requested 2.50MHz -> real 2.00MHz
erase SRAM Done
Flash SRAM: [==================================================] 100.00%
Done
SRAM Flash: Success
Et si le bitstream nous satisfait on l’écrira dans la mémoire « flash » avec l’option -f pour qu’il se reconfigure à chaque allumage.
$ openFPGALoader ide/gbhdmi/impl/pnr/gbhdmi.fs -f
write to flash
Jtag frequency : requested 6.00MHz -> real 6.00MHz
Parse file Parse ide/gbhdmi/impl/pnr/gbhdmi.fs:
Done
DONE
Jtag frequency : requested 2.50MHz -> real 2.00MHz
erase SRAM Done
erase Flash Done
write Flash: [==================================================] 100.00%
Done
CRC check: Success
Les fichiers de configuration à télécharger dans le FPGA peuvent être assez volumineux pour certain gros FPGA. Les sondes de configuration n’étant pas toujours très rapide, il est intéressant de pouvoir envoyer le bitstream compressé. Cette option est bien sûr supporté par openFPGALoader.
Plutôt que de prendre le nom du fichier bitstream en argument, il est également possible de récupérer un «flux» sur l’entrée standard:
Méthode très pratique si l’on souhaite configurer son FPGA via le réseau par exemple:
# Carte connectée au FPGA
nc -lp port | openFPGALoader --file-type xxx [option
# Ordinateur distant
nc -q 0 host port < /path/to/bitstream.ext
Et si vous trouvez que cette dépêche manque (scandaleusement) de TapTempo, sachez qu’openFPGALoader fonctionne très bien sur le FPGA ECP5 présent sur la carte colorlight pour y configurer le bitstream TapTempo en VHDL :
À l’heure où cette dépêche est mise sous presse, openFPGALoader a sorti une version mineure 0.6.1 (principalement pour réduire le nombre d’« assets » dans l’archive.
openFPGALoader est maintenant bien installé dans la constellation des logiciels libres pour développer sur FPGA. Même s’il n’a pas encore atteint la version 1.0, il est désormais tout à fait mature pour une utilisation en « production ». Il méritait bien une dépêche sur LinuxFR.
Yosys est devenu le pivot opensource du développement des circuits intégrés FPGA et ASIC.
Le 27 septembre 2021 a été publiée la nouvelle version 0.10 de Yosys sur l’hébergeur de gestionnaire de versions GitHub. Cette sortie tardive (la 0.9 date de 2019) est l’occasion de parler de ce logiciel libre de synthèse Verilog, pivot de la libération des FPGA (et des ASIC). Plutôt que de simplement présenter les changements nous allons présenter le logiciel et le principe de la synthèse «RTL».
Le manuel de Yosys représente les différentes couches d’abstractions utilisées en conception numérique.
Les couches «System Level» et «High Level» sont généralement des modèles écrits en C/C++/OpenCV/Matlab/… puis convertis en modèles «Behavioral» par un humain. Il existe quelques logiciels dit de «synthèse de haut niveau» qui permettent cette conversion en automatique, mais ça n’est pas la panacée, et (très) souvent verrouillé sur une seule marque de FPGA.
Le niveau «Behavioral» est celui dans lequel on décrit le comportement du composant en Verilog ou VHDL. Il est également possible aujourd’hui d’utiliser des langages comme Clash, Chisel ou Migen. Ces derniers généreront ensuite du code Verilog au niveau «RTL». Comme son nom l’indique, la couche «RTL» est une description au niveau «transfert de registres». On y décrit le comportement des registres au rythme de l’horloge. Yosys utilise son propre langage nommé RTLIL pour cette étape, mais il est parfaitement possible d’utiliser Verilog ici. Dans la couche «physique» on décrit le système au moyen de portes logiques disponibles dans le FPGA cible. À ce niveau on parlera souvent de «netlist». Cette netlist est souvent décrite au format EDIF, mais il est possible de la décrire en Verilog ou VHDL. Yosys propose également le format JSON bien connu des développeurs JavaScript. Le niveau «switch level» n’est pas vraiment utilisé en synthèse sur FPGA. C’est la description des connexions entre transistors, elle ne fait pas vraiment de sens pour un FPGA dans la mesure où les cellules logiques sont figées dans le FPGA.
Arrivé au niveau portes (Physical Gate level) on quitte la synthèse – et donc yosys – pour passer à la mise en paquet (Pack), au placement puis au routage. Et on s’enfonce dans la jungle sombre et humide des logiciels gratuits-mais-pas-trop non libres fournis par les constructeurs. Il existe toutefois un logiciel libre nommé nextpnr, mais ça nous éloigne du sujet. Sachez juste que nextpnr est parfaitement utilisable pour certaines marques de FPGA (ICE40, Gowin, ECP5 notamment).
Yosys est un logiciel de synthèse Verilog, il prend en entrée un source Verilog dans la couche «Behavioral Level» pour descendre dans les couches jusqu’au niveau «Physical Gate».
commençons donc par l’exemple de l’inverseur synchrone suivant (fichier invert.v):
Par défault, Yosys se présente sous la forme d’une console «REPL» (Read Eval Print Loop) et se lance en tapant son nom :
$ yosys
/----------------------------------------------------------------------------\
| |
| yosys -- Yosys Open SYnthesis Suite |
| |
| Copyright (C) 2012 - 2020 Claire Xenia Wolf <claire@yosyshq.com> |
| |
| Permission to use, copy, modify, and/or distribute this software for any |
| purpose with or without fee is hereby granted, provided that the above |
| copyright notice and this permission notice appear in all copies. |
| |
| THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
\----------------------------------------------------------------------------/
Yosys 0.10+0 (git sha1 070cad5f4, clang 8.0.0-svn345496-1~exp1+0~20181029105533.852~1.gbpf10f36 -fPIC -Os)
yosys>
Premier changement de cette version : l’auteur n’est plus Clifford Wolf mais Claire Xenia Wolf. Si la date du début du copyright n’a pas changée c’est que les deux sont une seule et même personne, elle a «juste» changé de prénom et d’état civil.
La ligne de commande se comporte comme une ligne de commande shell classique, avec le rappel des commandes par flèche haut/bas, et la complétion avec Ctrl+R et tabulation. On charge son fichier source verilog avec la commande read_verilog.
yosys> read_verilog invert.v
1. Executing Verilog-2005 frontend: invert.v
Parsing Verilog input from `invert.v' to AST representation.
Generating RTLIL representation for module `\syncinvert'.
Successfully finished Verilog frontend.
Yosys nous informe ici qu’il a bien lu et analysé le fichier source. Le «gateware» est bien conforme au standard Verilog-2005 et a été converti dans le langage interne nommé RTLIL (RTL Intermediate Language).
On peut lister les modules chargés en mémoire grâce à la commande ls.
yosys> ls
1 modules:
syncinvert
Et afficher une vue graphique du module avec show:
yosys> show syncinvert
4. Generating Graphviz representation of design.
Writing dot description to `/home/fabien/.yosys_show.dot'.
Dumping module syncinvert to page 1.
Exec: { test -f '/home/user/.yosys_show.dot.pid' && fuser -s '/home/user/.yosys_show.dot.pid' 2> /dev/null; } || ( echo $$ >&3; exec xdot '/home/fabien/.yosys_show.dot'; ) 3> '/home/user/.yosys_show.dot.pid' &
Qui affiche l’image suivante
Cette simple lecture du fichier verilog nous a fait passer la première étape de la synthèse. Nous sommes presque descendus au niveau de la couche «RTL» de la synthèse. Presque, car il faut encore «mapper» la procédure que l’on voit au centre avec la commande proc:
yosys> proc
10. Executing PROC pass (convert processes to netlists).
10.1. Executing PROC_CLEAN pass (remove empty switches from decision trees).
Cleaned up 0 empty switches.
10.2. Executing PROC_RMDEAD pass (remove dead branches from decision trees).
Removed a total of 0 dead cases.
10.3. Executing PROC_PRUNE pass (remove redundant assignments in processes).
Removed 0 redundant assignments.
Promoted 0 assignments to connections.
10.4. Executing PROC_INIT pass (extract init attributes).
10.5. Executing PROC_ARST pass (detect async resets in processes).
10.6. Executing PROC_MUX pass (convert decision trees to multiplexers).
10.7. Executing PROC_DLATCH pass (convert process syncs to latches).
10.8. Executing PROC_DFF pass (convert process syncs to FFs).
10.9. Executing PROC_MEMWR pass (convert process memory writes to cells).
10.10. Executing PROC_CLEAN pass (remove empty switches from decision trees).
Cleaned up 0 empty switches.
10.11. Executing OPT_EXPR pass (perform const folding).
Optimizing module syncinvert.
Le résultat ressemble beaucoup plus à la bascule D que l’on connaît.
On peut optimiser ce graphe avec la commande opt, ce qui supprimera la branche inutile BUF -> $0\regval[0:0].
S’ensuit toute une série de commandes d’optimisation comme memory pour détecter les tableaux et utiliser des blocs de ram si possible ou techmap pour «mapper» les cellules d’une bibliothèque donnée.
yosys> opt; memory; opt; techmap; opt
Notez que toutes ces commandes étant des commandes d’optimisations, rien n’interdit de les relancer pour améliorer encore l’optimisation (ici les optimisation sont totalement inutiles vu la simplicité du schéma).
Beaucoup d’étapes dépendantes de la cible sont ensuite nécessaires pour obtenir une netlist correspondant au composant. Ces étapes étant différentes en fonction de l’architecture cible. Yosys inclut des scripts pour chaque composant supporté.
Par exemple, si nous voulons synthétiser pour icestick, le FPGA monté sur la carte est un ice40, nous pourrons donc utiliser le script nommé synth_ice40 :
yosys> synth_ice40
[...]
37.47. Printing statistics.
=== syncinvert ===
Number of wires: 6
Number of wire bits: 6
Number of public wires: 6
Number of public wire bits: 6
Number of memories: 0
Number of memory bits: 0
Number of processes: 0
Number of cells: 2
SB_DFFR 1
SB_LUT4 1
37.48. Executing CHECK pass (checking for obvious problems).
Checking module syncinvert...
Found and reported 0 problems.
Le lecteur attentif reconnaîtra la LUT 4 entrées et la basculeD de l’ICE40 dans le schéma généré :
Pour connaitre les scripts de synthèse disponibles dans yosys il suffit d’utiliser la complétion de commande avec synth_ :
Ce qui nous donne 16 modèles de FPGA supporté «officiellement» par Yosys.
Il n’est pas obligatoire d’avoir un script synth_ pour pouvoir synthétiser pour un modèle de FPGA du moment que l’on fournit la bibliothèque des primitives. Pepijn De Vos s’est par exemple amusé à synthétiser vers une bibliothèque de composants en logique discrète (la série des 74-* pour ceux qui connaissent un peu l’électronique). Il a ensuite produit, assemblé et soudé la carte électronique correspondante.
Mais! Pepijn est parti d’un gateware écrit en VHDL ! En quoi cela concerne-t-il yosys qui est censé être un logiciel de synthèse Verilog ?
Pepijn a utilisé pour cela une extension de yosys permettant de se connecter au logiciel GHDL. Avec cette extension (encore en bêta mais déjà bien avancée), le VHDL est décodé avec GHDL qui transmet ensuite les informations à Yosys pour la synthèse. Et comme yosys est capable de sortir une version verilog de sa représentation interne RTLIL, on peut s’en servir pour faire de la conversion VHDL->Verilog sans problème.
Notre inverseur est désormais synthétisé. Nous pouvons donc l’enregistrer au format de notre choix pour passer ensuite au placement routage.
On peut l’écrire au format Verilog par exemple si nous souhaitons le simuler ou tout simplement lire la netlist :
Les sources sont annotées avec des références au verilog initial. On repère bien la bascule SB_DFFR et la SB_LUT4 avec sa configuration. Et dans la mesure du possible, les noms de registres ont été gardés, ce qui rend le fichier assez facile à lire.
Pour le placement routage, si on utilise nextpnr il faudra «sortir» au format json :
Le fichier invert.json est beaucoup plus large que la sortie verilog (9779 lignes). Les différents formats de sortie disponibles sont visibles avec la complétion de commande write_ :
Cette possibilité de convertir dans différents formats le verilog initial fait de Yosys le couteau suisse du gateware. On peut s’en servir pour faire de la synthèse à destination des FPGA bien sûr, mais il est possible de «mapper» sur d’autres bibliothèques pour faire des PCB «discret» comme on l’a vu avec Pepijn De Vos. Il est également possible de faire de la synthèse à destination des ASIC. Yosys est d’ailleurs utilisé par la chaîne de développement opensource OpenLane.
Enfin, les modèles synthétisés par yosys peuvent servir à faire de la preuve formelle, à partir de sources verilog mais également VHDL.
En règle générale, il n’est pas nécessaire de connaître les détails des commandes de Yosys. L’utilisation de yosys est enfouie dans les scripts et autres Makefile des outils utilisés.
Bref, en quelques années, Yosys est devenu le pivot opensource du développement FPGA et ASIC. Il méritait bien une dépêche sur LinuxFr.org !
Historiquement le ICE40 soudé sur la carte icestick est le premier supporté par des outils libres.
Le célèbre icestick qui a libéré les FPGA
Il est maintenant possible d’utiliser plusieurs programmes open-source pour développer dessus. Voici une méthode avec yosys, nextpnr, icestorm et openFPGALoader.
Dans un premier temps, allez donc cloner, compiler makeInstaller les 4 programmes cités ci-avant :
Yosys: Logiciel de synthèse Verilog couteau suisse du monde du FPGA.
nextpnr: Logiciel de placement routage supportant de plus en plus de famille de FPGA
icestorm: La tempête à l’origine de la libération des ICE40 de Lattice.