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.
La désormais célèbre société chinoise Sipeed vient de sortir un tout petit kit de développement autour d’un FPGA Gowin et muni d’une sortie HDMI ainsi que d’une entrée caméra : la Tang Nano 4K.
Principaux périphériques disponibles sur la Tang Nano 4K
Et non, elle n’est pas compatible avec des écrans HDMI 4K 😉 Le nom marketing bien choisi désigne le nombre de LUT disponibles.
Le pinout trouvé sur la fiche descriptive d’aliexpressAttention à certain IO du pinout, qu’il vaut mieux éviter d’utiliser… comme la sortie LED (IOT7A) ! Le document en question semble être celui là
Il est encore difficile de trouver des informations techniques sur cette carte, pourtant disponible (et rapidement livrée) sur aliexpress. Le wiki officiel est encore intégralement en chinois, mais on trouve le code d’exemple sur github. Le schéma de la carte est dispo sur le site de sipeed ici (ou sur le blog du flf si le firewall chinois fait des caprices 😉
À 18$ il n’était pas question de passer à coté, j’ai donc commandé une version avec caméra OV2640 ainsi qu’une version sans caméra :
Les deux kits tels que reçu dans ma boite aux lettres.
Le tout pour une quarantaine d’€.
Branchement
Un câble USB-C est fourni dans la boite, voyons ce qu’il se passe au branchement de la version sans caméra:
sudo dmesg -c
[34644.177355] usb 1-2.1.1: new full-speed USB device number 14 using xhci_hcd
[34644.283502] usb 1-2.1.1: not running at top speed; connect to a high speed hub
[34644.305274] usb 1-2.1.1: New USB device found, idVendor=0403, idProduct=6010, bcdDevice= 5.00
[34644.305288] usb 1-2.1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[34644.305292] usb 1-2.1.1: Product: JTAG Debugger
[34644.305295] usb 1-2.1.1: Manufacturer: SIPEED
[34644.305298] usb 1-2.1.1: SerialNumber: FactoryAIOT Pro
[34644.311295] ftdi_sio 1-2.1.1:1.0: FTDI USB Serial Device converter detected
[34644.311336] usb 1-2.1.1: Detected FT2232C
[34644.316362] usb 1-2.1.1: FTDI USB Serial Device converter now attached to ttyUSB0
[34644.316513] ftdi_sio 1-2.1.1:1.1: FTDI USB Serial Device converter detected
[34644.316548] usb 1-2.1.1: Detected FT2232C
[34644.322352] usb 1-2.1.1: FTDI USB Serial Device converter now attached to ttyUSB1
La version avec caméra donne exactement la même chose.
Le dmesg de Linux laisse apparaître deux interfaces tty. L’une est certainement un convertisseur jtag pour configurer le FPGA et l’autre une interface uart permettant de communiquer avec l’application comme on a pu le voir avec la Tang Nano «classique».
Si l’on branche un écran au HDMI on voit défiler quelques «pattern» avant de s’arrêter sur un écran vert sur fond bleu.
Gay friendly pattern
Prison pattern
gray pattern
Green pattern
Pour le moment je n’ai pas réussi à obtenir grand chose de ces deux uarts.
Par la suite j’éditerais cet article pour vous parler de la configuration du FPGA.
Synthèse et configuration
Le plus simple pour commencer avec le kit est d’aller voir les deux projets proposés par Sipeed sur github et de les cloner.
$ git clone https://github.com/sipeed/TangNano-4K-example.git
$ cd TangNano-4K-example/
Deux projets sont disponible dans ce dépôt, vus de loin l’un est pour faire clignoter une led et l’autre pour l’exemple HDMI programmé par défaut dans l’eeprom du kit :
$ ls
dk_video/ led_test/
Pour se faire la main il est donc naturel de commencer par la led.
Il faut d’abord lancer l’IDE officiel de gowin, comme décrit dans l’article de hackable-32 (le plus compliqué avec l’installation de gowin est d’obtenir la licence, après ça roule). Puis ouvrir le fichier de projet fourni nommé project/led_test.gprj et se trouvant dans le répertoire led_test/projet.
Le FPGA du kit se nomme GW1NSR-LV4CQN48PC7/I6 et le code Verilog de clignotement est le suivant. Quand au pinout le voici au format «cst» ci-dessous :
Le projet est déjà synthétisé, si on souhaite le relancer il suffit de faire un clic-droit «rerun» sur «Synthesize» . Le bitstream est généré au format «fs» et se nomme project/impl/pnr/led_test.fs.
$ 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
Il suffit donc de programmer le bistrteam (format fs) au moyen de la commande :
$ 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 pour l’écrire dans l’eeprom il suffit d’ajouter l’option -f:
$ 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
Le truc se programme et la led clignote.
Le HDMI
Le deuxième projet proposé dans le dépôt concerne le HDMI. Bon si on regarde le contenu du Top on se rend compte qu’il fait bien plus que le simple HDMI puisqu’il pilote également la ram, la Nor et la caméra.
Le générateur de pattern se charge également de générer les signaux de synchronisation vidéo. Mais ce qui nous intéresse particulièrement c’est le bloc qui pilote le HDMI. Ce bloc se prénomme DVI_TX_top et … il est encrypté 🙁
Le bloc est tout de même instanciable au moyen d’une BlackBox chisel. Ce qui permet d’adapter le projet GbVGA en GbHDMI
Pour la vidéo du montage en fonctionnement c’est par là.
La caméra OV2640
Je n’ai pas vu d’exemple de code utilisant la caméra ov2640 avec ce kit pour le moment. Il est probable que ça ne soit pas encore disponible.
Il y a quelques années, nous parlions de l’utilitaire vhdl2vl sur ce blog. Cette solution est intéressante mais limitée car le projet est relativement au point mort.
Depuis quelques mois une solution beaucoup plus «hype» est disponible, alliant le couteau suisse du Verilog Yosys, la référence en simulation libre en VHDL GHDL et le plugin ghdl-yosys-plugin. Cette solution permet dès à présent de convertir la plupart des codes VHDL en Verilog.
Voyons comment faire avec le module de réception uart proposé par nandland : UART_RX.vhd.
$ mkdir vhdlconv
$ cd vhdlconv
$ ls
UART_RX.vhd
Il faut tout d’abord compiler et installer Yosys et GHDL selon la procédure donnée sur les sites respectif.
Un fois fait il faut installer et compiler le plugin ghdl-yosys-plugin comme expliqué sur le dépot. Dans notre cas, cette compilation sera faite dans le répertoire /opt/ghdl-yosys-plugin.
Un fois l’installation effectuée nous pouvons nous lancer dans la conversion avec le plugin :
Le module verilog ainsi généré possède les même noms d’interfaces:
$ head -n 20 UART_RX.v
/* Generated by Yosys 0.9+4081 (git sha1 862e84eb, clang 10.0.0-4ubuntu1 -fPIC -Os) */
module UART_RX(i_Clk, i_RX_Serial, o_RX_DV, o_RX_Byte);
(* unused_bits = "7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" *)
wire [31:0] _00_;
wire [2:0] _01_;
wire [6:0] _02_;
wire _03_;
wire _04_;
wire _05_;
(* unused_bits = "3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31" *)
wire [31:0] _06_;
wire [2:0] _07_;
wire [2:0] _08_;
wire [2:0] _09_;
wire [6:0] _10_;
wire [2:0] _11_;
wire _12_;
wire [2:0] _13_;
wire _14_;
$ head -n 20 UART_RX.vhd
library ieee;
use ieee.std_logic_1164.ALL;
use ieee.numeric_std.all;
entity UART_RX is
generic (
g_CLKS_PER_BIT : integer := 115 -- Needs to be set correctly
);
port (
i_Clk : in std_logic;
i_RX_Serial : in std_logic;
o_RX_DV : out std_logic;
o_RX_Byte : out std_logic_vector(7 downto 0)
);
end UART_RX;
architecture rtl of UART_RX is
type t_SM_Main is (s_Idle, s_RX_Start_Bit, s_RX_Data_Bits,
Et même si le code n’est pas très lisible on retrouve ses petits avec le nom des signaux interne du module.
Ce qui est vraiment intéressant ici c’est que le code verilog généré est parfaitement synthétisable avec n’importe quel logiciel de synthèse verilog, on peut également utiliser Verilator pour accélérer nos simulation et enfin il est possible de faire de la preuve formelle avec Yosys.
Plus d’excuse pour ne pas mixer du code VHDL avec du Verilog maintenant puisque tout est convertible en Verilog !
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 .
La carte TEC0117-1 de trenz micro se pilote intégralement avec le port série.
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.
Photo trophée de la tec0117 avec LiteX
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.