Lorsque l’on essait de faire des modules VHDL génériques, on utilise massivement les paramètres generic map des modules.
Ces paramètres sont utilisés ensuite à la synthèse pour calculer des tailles de tableaux, des constantes et autre structures matérielles générées.
Pour déverminer cette partie du code on a besoin de «voir» les valeurs qui sont calculées. En VHDL il n’y a pas de printf pour ça, mais il y a report utilisé en simulation dans un process :
process is
begin
report "Affichons des trucs" severity note;
wait;
end process;
Sauf que nous ne somme pas en simulation, ce process ne sera même pas traité à la synthèse.
Pour pouvoir afficher du texte on peut utiliser les assertions en les mettant systématiquement à faux pour être sur qu’elles soient affichées :
assert false report "Affichons des trucs à la synthèse" severity note;
Mais rien ne s’affiche dans les log à la synthèse Vivado. Ça n’est pas un bug, c’est juste que Xilinx a désactivé ces messages par défaut. Pour les réactiver il suffit de taper cette ligne magique dans la console tcl avant de lancer la synthèse :
Le défis de Bruno Levy d’écrire un processeur RISCV en 100 lignes de codes semble avoir été relevé avec brio par Sylvain Lefebvre avec son langage HDL nommé Silice
Après avoir déballé le kit gowin proposé par trenz micro. Il faut trouver quelques chose à faire de plus avancé qu’un simple clignotement de LED. Pour le clignotement de LED et la prise en main des outils, le lecteur se référera à l’article Hackable 32.
Pourquoi ne pas tenter le Linux des FPGA, j’ai nommé LiteX (Prononcez Lahïtixe ) ? LiteX est un framework HDL basé sur Migen pour construire des systèmes matériel facilement en python.
LiteX inclut un langage de description matériel, mais également tous les outils permettant de faire des simulations, la synthèses et générer les bitstreams pour la plupart des FPGA du marché. Bien évidement, en ce qui concerne la synthèse et les bitstreams, LiteX pilote les outils propriétaires des constructeurs. Ce pilotage ne pose généralement pas trop de problème, car tous les outils constructeurs proposent des interfaces en ligne de commande.
La carte que nous allons tenter de faire fonctionner avec LiteX est donc la TEC0117-1 produite par Trenz electronic et munie d’un FPGA gowin .
D’après le wiki, la carte n’est pas encore officiellement supporté par LiteX puisqu’elle ne se trouve pas dans la liste du projet LiteX-Boards…
Pas dans le tableau de la documentation du moins, car en fouillant dans le code du projet, il semble qu’il y ait déjà un embryon de quelques chose ici. Voila qui est très engageant pour tester la carte.
Voyons donc voir les étapes nous permettant de construire un système avec LiteX.
$ wget https://raw.githubusercontent.com/enjoy-digital/litex/master/litex_setup.py
$ chmod +x litex_setup.py
$ ./litex_setup.py init install --user (--user to install to user directory)
Attention, si comme moi vous avez un pc qui commence à prendre sérieusement de l’âge, sachez que le script litex_setup.py va descendre beaucoup de projets annexes de LiteX. Ça va prendre quelques minutes.
On aura besoin également de gcc compilé pour RISC-V :
$ ./litex_setup.py gcc
Il faudra bien penser à l’exporter à chaque fois qu’on en aura besoin :
$ export PATH=$PATH:$(echo $PWD/riscv64-*/bin/)
Construire le système pour TEC0117
Pour construire un système pour la carte il faut ensuite se rendre dans le répertoire contenant la carte puis lancer le script python correspondant:
$ export PATH=$PATH:$(echo $PWD/riscv64-*/bin/)
$ cd litex-boards/litex_boards/targets
$ python3 trenz_tec0117.py
Pour synthétiser et générer le bitstream il faut d’abord ajouter le lien vers l’ide (1.9.7 minimum) de gowin :
Ou avec la bonne option litex (qui fait appel à openFPGALoader de toute manière):
$ python3 ../litex-boards/litex_boards/targets/trenz_tec0117.py --load
INFO:SoC: __ _ __ _ __
INFO:SoC: / / (_) /____ | |/_/
INFO:SoC: / /__/ / __/ -_)> <
INFO:SoC: /____/_/\__/\__/_/|_|
INFO:SoC: Build your hardware, easily!
INFO:SoC:--------------------------------------------------------------------------------
INFO:SoC:Creating SoC... (2021-07-12 19:05:10)
INFO:SoC:--------------------------------------------------------------------------------
INFO:SoC:FPGA device : GW1NR-LV9QN88C6/I5.
INFO:SoC:System clock: 25.00MHz.
INFO:SoCBusHandler:Creating Bus Handler...
...
INFO:SoC:Initializing ROM rom with contents (Size: 0x51cc).
INFO:SoC:Auto-Resizing ROM rom from 0x6000 to 0x51cc.
Parse /home/user/myapp/litex/myapp/build/trenz_tec0117/gateware/impl/pnr/project.fs:
checksum 0xa3a3
Done
erase SRAM Done
Flash SRAM: [==================================================] 100.000000%
Done
SRAM Flash: Success
La confirmation de la bonne configuration est donnée par le message de la console, mais également par le chenillard de LED rouge.
Console litex>
Le convertisseur USB-uart de la carte possède deux interfaces ttyUSB, la première vient d’être utilisée par openFPGALoader pour charger le bitstream, la seconde permet de se connecter à la console litex :
$ screen /dev/ttyUSB1 115200
litex> reboot
__ _ __ _ __
/ / (_) /____ | |/_/
/ /__/ / __/ -_)> <
/____/_/\__/\__/_/|_|
Build your hardware, easily!
(c) Copyright 2012-2021 Enjoy-Digital
(c) Copyright 2007-2015 M-Labs
BIOS built on Jul 9 2021 13:17:06
BIOS CRC passed (855636f6)
Migen git sha1: 3ffd64c
LiteX git sha1: 2b49430f
--=============== SoC ==================--
CPU: VexRiscv_Lite @ 25MHz
BUS: WISHBONE 32-bit @ 4GiB
CSR: 32-bit data
ROM: 24KiB
SRAM: 4KiB
L2: 0KiB
SDRAM: 8192KiB 16-bit @ 25MT/s (CL-2 CWL-2)
--========== Initialization ============--
Initializing SDRAM @0x40000000...
Switching SDRAM to software control.
Switching SDRAM to hardware control.
Memtest at 0x40000000 (2.0MiB)...
Write: 0x40000000-0x40200000 2.0MiB
Read: 0x40000000-0x40200000 2.0MiB
Memtest OK
Memspeed at 0x40000000 (2.0MiB)...
Write speed: 5.6MiB/s
Read speed: 6.2MiB/s
--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
Timeout
No boot medium found
--============= Console ================--
litex>
La commande help nous donnes les commandes disponibles :
litex> help
LiteX BIOS, available commands:
leds - Set Leds value
flush_l2_cache - Flush L2 cache
flush_cpu_dcache - Flush CPU data cache
crc - Compute CRC32 of a part of the address space
ident - Identifier of the system
help - Print this help
serialboot - Boot from Serial (SFL)
reboot - Reboot
boot - Boot from Memory
mem_speed - Test memory speed
mem_test - Test memory access
mem_copy - Copy address space
mem_write - Write address space
mem_read - Read address space
mem_list - List available memory regions
sdram_test - Test SDRAM
Photo traditionnelle
Avec LiteX, une tradition «twitter» veux que l’on fasse une photo du kit démarrant LiteX avec la console démarrée.
Et voila \o/
L’entrée en matière est incroyablement facile et fait honneur au slogan «Build your hardware, easily!».
Nous regarderons dans de futur articles ce que l’on peut faire avec.
Les constructeurs de FPGA fournissent des modèles VHDL de leurs primitives. Chez Xilinx cela se présente sous la forme d’une librairie nommée UNISIM.
Le logiciel libre de simulation GHDL n’inclut pas les sources VHDL de cette librairie dans son dépôt officiel car ça n’est pas sa propriété. Cependant, GHDL fourni des scripts permettant de les pré-compiler pour son projet.
Nous allons voir ici comment se servir des primitives disponible dans vivado avec une simulation utilisant CocoTB.
Les sources des primitives se trouvent dans le répertoire d’installation de Vivado
data/vhdl/src/unisims/
Pour les retrouver on peut utiliser la commande fd-find comme ceci :
Il peut être intéressant de mettre les librairies compilé à cet endroit pour y faire référence depuis tous ses projets ensuite :
cd data/vhdl
mkdir ghdl
cd ghdl
/usr/local/lib/ghdl/vendors/compile-xilinx-vivado.sh -all --vhdl2008
[...]
Pour compiler la librairie unisim pour vivado on utilisera le script suivant :
/usr/local/lib/ghdl/vendors/compile-xilinx-vivado.sh -a --vhdl2008
Loading environment...
Not all Xilinx primitives are VHDL-2008 compatible! Setting CONTINUE_ON_ERROR to TRUE.
Analyzing library 'unisim'...
Creating VHDL Library 'unisim'...
Analyzing files into library 'unisim'...
WARNING: /opt/Xilinx/Vivado/2020.2/data/vhdl/src/unisims/primitive/SYSMONE4.vhd:1536:44:warning: prefix of array attribute must be an object name [-Wattribute]
SCRIPT ERROR: Unfiltered line
v_str_time_length := time'image(now)'length;
SCRIPT ERROR: Unfiltered line
^
Warnings detected by filtering script.
Analyzing library 'secureip'...
Creating VHDL Library 'secureip'...
Analyzing files into library 'secureip'...
Analyzing library 'unimacro'...
Creating VHDL Library 'unimacro'...
Analyzing files into library 'unimacro'...
Analyzing library 'unifast'...
Creating VHDL Library 'unifast'...
Analyzing files into library 'unifast'...
Analyzing library 'secureip'...
Creating VHDL Library 'secureip'...
Analyzing files into library 'secureip'...
WARNING: /opt/Xilinx/Vivado/2020.2/data/vhdl/src/unifast/secureip/GTHE2_CHANNEL.vhd:29:1:warning: entity "gthe2_channel" was also defined in file "/opt/Xilinx/Vivado/2020.2/data/vhdl/src/unisims/secureip/GTHE2_CHANNEL.vhd" [-Wlibrary]
SCRIPT ERROR: Unfiltered line
library IEEE;
SCRIPT ERROR: Unfiltered line
^
Warnings detected by filtering script.
WARNING: /opt/Xilinx/Vivado/2020.2/data/vhdl/src/unifast/secureip/GTXE2_CHANNEL.vhd:34:1:warning: entity "gtxe2_channel" was also defined in file "/opt/Xilinx/Vivado/2020.2/data/vhdl/src/unisims/secureip/GTXE2_CHANNEL.vhd" [-Wlibrary]
SCRIPT ERROR: Unfiltered line
library IEEE;
SCRIPT ERROR: Unfiltered line
^
Warnings detected by filtering script.
--------------------------------------------------------------------------------
Compiling Xilinx Vivado libraries [SUCCESSFUL]
La compilation prend un temps infini, compter au moins 5 minutes sur un PC muni de 16 cœurs. Il suffira ensuite d’ajouter les lignes suivantes à son makefile CocoTB :
Le jeudi 3 juin 2021 a eu lieu, pour la seconde fois, le Live embedded event. Un ensemble de conférences totalement en ligne sur le sujet des systèmes embarqués et de l’open source.
J’ai eu l’honneur de pouvoir présenter une courte présentation sur les outils open-source pour le FPGA. La vidéo sera peut-être disponible dans quelques temps sur la chaîne youtube de l’événement. L’événement ayant été un succès, la technique a été relativement compliquée, avec peu de retour des «spectateurs» et quelque déboires filmatoires.
Pour ceux qui souhaitent retrouver les liens et les références de la présentation, je met le PDF de la présentation en ligne ici.
Ais-je bien téléchargé la dernière version de mon gateware dans ce montage ?
C’est un problème classique quand on fait de l’embarqué, entre l’éditeur de code, la génération du code bas niveau (Verilog dans le cas de Chisel) le logiciel de synthèse/placement-routage et le téléchargement du bitstream, il y a plein de façon de se tromper de version du logiciel s’exécutant réellement dans le montage. Et l’on peu passer des heures à chercher un bug dans son système en modifiant des lignes de code alors que le problème vient d’une même (vieille) version téléchargée éternellement.
Pour s’assurer que le gateware qui se trouve dans le FPGA soit bien le dernier généré il faut trouver une manière d’y enregistrer une valeur qui sera différente à chaque fois.
On pourrait mettre une version «en dur» dans nos sources, mais l’expérience montre que l’on oublie 4 fois sur 5 de l’incrémenter avant de générer le fichier. Toute bonne développeuse et tout bon développeur versionne son projet avec un gestionnaire de version. On pourrait du coup mettre le numéro de commit. Sauf que l’on ne commit pas toujours la modification avant de la tester.
Une autre astuce consiste à mettre la date EPOCH. De cette manière nous avons la date et l’heure précise de la synthèse du gateware. Cette solution est intéressante mais elle pose rapidement un problème d’occupation du FPGA. En effet, le temps en secondes filant assez rapidement nous avons besoin de registres de 32 bits minimum voir même 64bits. Ce qui est plutôt inutile pour compter les builds …
Compter les builds ?
La voila la solution toute simple, il suffit d’enregistrer un compteur dans un fichier texte. À chaque build, une fonction vient lire la valeur et l’incrémente. Pour rester à jour il nous suffit ensuite de versionner ce fichier avec le reste du code. Même si le nombre de build est grand, on n’aura rarement besoin d’en faire plus de quelques millier, ce qui rentrera très bien dans un registre de 16 bits voir moins.
Voici comment faire en Scala avec Chisel.
Dans un package avec les utilitaires «personnel» :
package myutil
On importe les packages d’entrées sorties pour lire/écrire dans les fichiers textes:
// Pour fromFile()
import scala.io.Source
// Pour PrintWriter()
import java.io._
Puis on crée une fonction de lecture écriture dans un objet personnel MyUtility :
def genCountVers(className: String = null): Option[Int] = {
if(className == null){
return None
}
println("generate counter for class " + className)
val filename = "src/main/scala/util/gen_count_vers.txt"
val versmap = scala.collection.mutable.Map[String,Int]()
/* read file to hashmap*/
val fp = Source.fromFile(filename)
for(v <- fp.getLines.map(_.split(",").map(_.trim))){
versmap(v(0)) = v(1).toInt
}
fp.close()
/* get and increment version */
val version = versmap.get(className)
if(version == None){
versmap(className) = 0
} else {
versmap(className) = version.get.toInt + 1
}
/* write back file*/
val fpw = new PrintWriter(new File(filename))
for ((hname, hvalue) <- versmap) {
fpw.write(hname + "," + hvalue + "\n")
}
fpw.close()
/* return version */
version match {
case Some(s) => Some(s.toInt + 1)
case None => Some(0)
}
}
La fonction genCountVers() va ouvrir le fichier texte gen_count_vers.txt, qui est un «CSV» composé du nom de classes et d’un numéro. Si le nom de la classe passé en paramètre n’existe pas la fonction va l’ajouter avec un compteur à zero.
Nous n’avons plus qu’a appeler la fonction genCounterVers() avec le nom de la classe (ou autre) de notre choix pour la fourrer dans un registre lisible via notre interface (spi, wisbone, i2c, jtag, …) directement sur le FPGA :
val buildnum = MyUtility.genCounterVers("MyTopClasse").get
La présentation de Jonathan Bachrach donne les bases d’utilisation des interfaces Decoupled() en Chisel. Cependant, elle date (2013). Il est donc nécessaire de se mettre un peu à jour.
Tout d’abord, il ne faut pas oublier d’inclure le package :
import chisel3.util._
Ensuite, l’interface possède en elle même l’information de direction. Il ne faut donc pas l’inclure dans un Output()/Input() lorsqu’on le déclare dans un IO(new Bundle {}) :
// Producteur
val data = Decoupled(UInt(10.W))
// Consommateur
val data = Flipped(Decoupled(UInt(10.W)))
Et pour finir, on ne déclare plus de Reg() comme donné à la fin de la présentation. Il faut utiliser RegNext() à la place :
GHDL est un logiciel écrit en ADA permettant de faire l’analyse, la compilation, la simulation ainsi que la synthèse du VHDL. Le VHDL, quant à lui, est un langage de description matériel très utilisé dans le développement sur FPGA ou ASIC. À l’origine, GHDL est un « side-project » de Tristan Gingold lui permettant de se faire la main avec ADA.
GHDL est devenu l’outil indispensable pour faire de la simulation VHDL aujourd’hui. Après presque 20 ans de développement, voici que sort en version 1.0.0 le logiciel de simulation VHDL nommé GHDL. En prime, GHDL s’offre un nouveau logo:
Peu d’informations ont filtré sur cette sortie pour le moment. On peut soupçonner que ce soit une sortie anniversaire pour marquer les 20 ans du logiciel. Il n’en reste pas moins que GHDL est devenu un maillon indispensable dans l’écosystème opensource du monde FPGA et des ASIC.
Cette version 1.0.0 supporte désormais complètement les standards 1987, 1993 et 2002 du langage défini par l’IEEE. Le support de VHDL 2008 est noté comme partiel pour le moment.
Depuis quelques années, le développement du projet s’est accéléré et supporte de mieux en mieux les projets tierces comme Yosys bien sûr mais également CocoTB pour les testbenchs écrit en Python ainsi que les standards de vérification comme UVVM, OSVVM, VUnit issues du standard d’accelera.
Un support partiel d’un langage de PSL (Properties Specification Language) est également inclus. Il permet de décrire les propriétés du système pour faire de la vérification formelle.
Et surtout, il est possible de faire de la Synthèse. Certes, l’extension ghdl-yosys-plugin est encore en développement, mais l’exemple TapTempo l’a montré : Il est tout à fait possible de faire de la synthèse VHDL avec.
Cette version mature est surtout une occasion de mettre en valeur cet outil indispensable dans le monde du développement numérique (gateware).