Tous les articles par Fabien Marteau

Les BlackBox et RawModule de Chisel3

Quelque soit le langage HDL utilisé il est très important de se garder la possibilité d’intégrer des modules provenant d’autre langages et/ou n’ayant pas de descriptions HDL.

C’est par exemple le cas des primitives matériel permettant d’instancier des modules intégrés au FPGA du constructeur au moyen de «template» Verilog : sérialiseur/désérialiseur, PLL, entrées/sorties spécifiques, …

BlackBox

Pour intégrer ce genre de module dans son projet Chisel3 on utilise des «BlackBox».  L’idée est de décrire les entrées/sorties du module ainsi que ses paramètres, et Chisel se chargera de convertir ça en une déclaration Verilog.

Le problème est assez classique sur les kits de développement de Xilinx qui sont cadencé par une horloge différentielle : Obligé d’instancier un buffer différentiel pour pouvoir récupérer l’horloge. Ce qui n’est pas prévu dans la classe Module de base de Chisel3 puisque l’horloge − tout comme le reset − est implicite.

D’après la documentation Xilinx, le buffer différentiel IBUFDS doit être instancié de la manière suivante en Verilog pour que le logiciel de synthèse le repère et l’instancie correctement:

IBUFDS #(
    .DIFF_TERM("TRUE"),
    .IOSTANDARD("DEFAULT")
) ibufds (
    .IB(ibufds_IB),
    .I(ibufds_I),
    .O(ibufds_O));

Cette instanciation est composée de deux paramètres «generic» et de trois entrées sorties.

Une BlackBox() se comporte comme un Module() sans les horloges et reset implicites. De plus le nom des IO est recopié tel quel par Chisel, il n’ajoute pas le préfixe «io_» comme pour un module normale.

Pour déclarer ce buffer différentiel en Chisel il suffira donc d’écrire le code suivant:

import chisel3._
import chisel3.util._
import chisel3.experimental._

class IBUFDS extends BlackBox(
    Map("DIFF_TERM" -> "TRUE",
        "IOSTANDARD" -> "DEFAULT")) {
    val io = IO(new Bundle {
        val O = Output(Clock())
        val I = Input(Clock())
        val IB = Input(Clock())})
}

Le Map en paramètre de la class BlackBox() permet d’ajouter les paramètres «generic» et les entrées sortie sont déclarés par la variable io.

Il suffira alors de l’instancier dans notre module top :

val ibufds = Module(new IBUFDS)
ibufds.io.I := clock_p
ibufds.io.IB:= clock_n

Pour que le code Verilog soit correctement écrit dans le fichier final.

Top RawModule

Maintenant que nous avons notre entrée d’horloge, notre but est d’aller faire clignoter une led (quelle originalité !) en utilisant un compteur. Avec le module suivant:

class Blink extends Module {
    val io = IO(new Bundle {
        val led = Output(Bool())
        })

    val MAX_COUNT = 100000000

    val count = Counter(MAX_COUNT)

    count.inc()

    io.led := 0.U
    when(count.value <= UInt(MAX_COUNT)/2.U){
    io.led := 1.U
    }
}

Ce module étant un module «normal» l’horloge et le reset sont implicite, alors comment allons nous faire pour qu’il soit cadencé par la sortie du buffer IBUFDS ?

On peut simplement les intégrer dans un Module() classique que l’on appellera Top :

class Top extends Module {
  val io = IO(new Bundle {
    val clock_p = Input(Clock())
    val clock_n = Input(Clock())
    val led     = Output(Bool())
  })

  val ibufds = Module(new IBUFDS)
  ibufds.io.I := io.clock_p
  ibufds.io.IB:= io.clock_n

  val blink = Module(new Blink)
  blink.clock := ibufds.io.O
  blink.reset := 1'0
  io.led := blink.io.led }

Notez que pour connecter explicitement l’horloge, la technique est en phase de développement mais il faut désormais utiliser la classe withClockAndReset()  pour faire les choses proprement . Plutôt que :

  val blink = Module(new Blink)
  blink.clock := ibufds.io.O
  blink.reset := false.B
  io.led := blink.io.led

Faire :

withClockAndReset(ibufds.io.O, false.B) {
    val blink = Module(new Blink)
    io.led := blink.io.led
  }

Cette méthode va fonctionner mais elle va nous ajouter les signaux clock et reset implicites. Signaux qui ne serviront pas à grand chose dans notre cas et généreront des warning pénible dans le logiciel de synthèse:

module Top(
  input   clock,
  input   reset,
  input   io_clock_p,
  input   io_clock_n,
  output  io_led
);
  wire  ibufds_IB;
  wire  ibufds_I;
  wire  ibufds_O;
  wire  blink_clock;
  wire  blink_reset;
  wire  blink_io_led;
  IBUFDS #(.DIFF_TERM("TRUE"), .IOSTANDARD("DEFAULT")) ibufds (
    .IB(ibufds_IB),
    .I(ibufds_I),
    .O(ibufds_O)
  );
  Blink blink (
    .clock(blink_clock),
    .reset(blink_reset),
    .io_led(blink_io_led)
  );
  assign io_led = blink_io_led;
  assign ibufds_IB = io_clock_n;
  assign ibufds_I = io_clock_p;
  assign blink_clock = ibufds_O;
  assign blink_reset = 1'h0;
endmodule

C’est pour cela qu’une nouvelle hiérarchie de classe est en développement pour les Module().

Un module Top est un module un peu spécial en conception HDL. En effet, ce type de module se contente simplement de «relier des boites entre elles». Ce n’est que du tire-fils, pas besoin d’horloge, de registres et autre structures complexes ici.

Dans la nouvelle hiérarchie des classes Module nous avons donc une nouvelle classe appelée RawModule qui apparaît.  Ce module n’a plus aucun signaux implicite et se contente de relier les fils. Dans le code Chisel précédent nous pouvons juste renommer Module en RawModule pour voir que les signaux reset et clock disparaissent:

class Top extends RawModule {

Nous obtenons alors une entête Verilog plus propre :

module Top(
  input   io_clock_p,
  input   io_clock_n,
  output  io_led
);

Nous avons tout de même ce préfixe «io_» disgracieux qui peu devenir pénible pour l’intégration, notamment dans certaine plate-forme où le pinout est déjà fourni pour des noms de pin précis.

Il est possible de les éviter avec les RawModule simplement en utilisant plusieurs variable IO() sans Bundle :

class Top extends RawModule {
  val clock_p = IO(Input(Clock()))
  val clock_n = IO(Input(Clock()))
  val led = IO(Output(Bool()))

  val ibufds = Module(new IBUFDS)
  ibufds.io.I := clock_p
  ibufds.io.IB:= clock_n

  withClockAndReset(ibufds.io.O, false.B) {
    val blink = Module(new Blink)
    led := blink.io.led
  }
}

De cette manière c’est le nom exact de la variable qui sera pris en compte pour générer le Verilog:

module Top(
  input   clock_p,
  input   clock_n,
  output  led
);

Et voila comment nous pouvons désormais faire un projet proprement écrit en Chisel de A à Z, ce qui n’était pas le cas avant où nous étions obligé d’encapsuler le projet dans des Top.v écrit à la main, et obligé de les modifier à chaque changement d’interface.

Le code décrit dans cet article se retrouve sur le Blinking Led Projet, dans le répertoire platform. Pour pouvoir le tester correctement, ne pas oublier de télécharger sa propre version de Chisel3 et de merger la branche modhier comme expliqué dans le README.

NVC, l’autre simulateur VHDL libre

Dans le domaine de la simulation libre du VHDL, on connait bien le simulateur GHDL qui est basé sur GCC, on connaît un petit peu le simulateur FreeHDL inclus dans le logiciel graphique de simulation électronique Qucs, mais on connaît moins le simulateur NVC qui pourtant est en train de faire son petit bonhomme de chemin.

NVC est écrit en langage C pur et compilé par défaut avec le compilateur LLVM concurrent de GCC. L’avantage de NVC par rapport à GHDL: c’est un programme indépendant de son compilateur là où ghdl n’est qu’une couche de GCC. Ce qui simplifie grandement la compilation de l’outils.

NVC n’a pas encore atteint sa première version stable, cependant le rythme des commits laisse penser que cela va venir. Et surtout il est déjà utilisable en l’état dans sa version git «master» .

L’outil a été intégré dans la partie VHDL du blp, il s’utilise de la même manière que ghdl avec une phase d’élaboration puis d’analyse avant d’être lancé en simulation.

Pour simuler le testbench permettant de tester le module anti-rebond du blp nous ferons les commandes suivantes dans le répertoire vhdl :

  • analyse
nvc -a ../test/test_button_deb.vhd ../src/button_deb.vhd
  • élaboration
nvc -e button_deb_tb
  • simulation (run)
nvc -r button_deb_tb -w

L’option de simulation -w permet de générer un fichier de sortie (*.fst) pour être lu par gtkwave.

Synchronous Synthesizable Hardware Description Language (SSHDL)

Depuis quelques années, plusieurs nouveaux langages HDL basés sur des langages de programmations génériques émergent. Ces programmes peuvent être qualifiés de SSHDL pour Synchronous Synthesizable Hardware Description Language

HDL ?

Pour Hardware Description Langage, c’est un langage de description matériel. Un HDL permet de décrire le comportement d’un composant numérique, comme des bascule D, des ALU, ou des microprocesseurs complet.

Les deux HDL les plus connu sont bien sûr le VHDL et le Verilog. Se sont les seuls à être reconnu comme standard par tous les logiciels de synthèses du marché. C’est donc un passage obligé pour travailler sur les FPGA.

Synthesizable ?

Cela peut paraître étrange, mais VHDL/Verilog ont beau être supporté par tous les logiciels de synthèse du marché, se ne sont pas des langage que l’on peut considérer comme synthétisable. Seul un sous ensemble de ces deux langages l’est, le reste étant utilisé pour la simulation.

Une architecture décrite dans un langage synthétisable … sera synthétisable. Si une portion du code n’est pas synthétisable alors il y a une erreur de code.

Synchronous ?

C’est quelque chose qui est indispensable en conception HDL. Tout le design doit être cadencé avec la même horloge, même si nous cherchons à capturer un événements extérieur (comme une interruptions) il est nécessaire de le resynchroniser avec l’horloge principale. Dans un SSHDL, le fonctionnement synchrone est implicite. L’horloge qui cadence tout le design n’a pas a être indiqué à chaque registre.

Toutes personnes qui a travaillé sur un FPGA de taille raisonnable le sait, il est impossible de cadencer tout son design avec la même horloge, puisque certain sous ensembles comme les contrôleurs de RAM ou les sérialiseur/déserialiseur nécessitent leurs propres horloges qui n’est généralement pas synchrone avec l’horloge globale. Il est alors nécessaire d’introduire la notion de domaines d’horloges et de soigner la conception des franchissement de domaines d’horloges de nos signaux (Clock domain crossing) afin d’éviter la métastabilité.

C’est un des points sensible qui fait la qualité d’un SSHDL : comment est géré le franchissement de domaines d’horloges ?

Standards industriels ou joujoux universitaires ?

Pour que ces langages puissent avoir un minimum d’espoir d’être déployés dans l’industrie, il faut que l’on puisse les utiliser sur les FPGA du marché. Il faut donc  des logiciels capables de les synthétiser. Il est illusoire de croire que les gros fabricant de FPGA adoptent ces petits langages open-source pour leurs FPGA. Le SystemC est un bon exemple de langage qui n’a pas percé par manque de logiciel de synthèse (par contre il est très utilisé dans la simulation, car très rapide).

C’est pour cette raison que ces nouveaux langages ont choisi de générer leur designs en VHDL et/ou Verilog. Toutes la conception/simulation se fait donc avec ces nouveaux langages, et quand on veut faire la synthèse on lance la génération du VHDL/Verilog pour tester sur FPGA.

On peut ainsi considérer le VHDL/Verilog comme un langage «assembleur» du FPGA/ASIC.

Petites listes de SSHDL

La liste des SSHDL connus peut être trouvé dans la rubrique HDL de ce blog.

IceStudio, du schéma au verilog

IceStudio est un logiciel graphique permettant de concevoir un design FPGA à la manière d’un schéma électronique.

Le logiciel est encore largement expérimentale (version 0.2) et centré sur les FPGA ice40 de chez lattice.

En fait, IceStudio se veut une extension graphique au projet IceStorm — chaine de synthèse/place&route/bitstream opensource —.

Architecturé en javascript autour de Nodejs, le logiciel permet de dessiner son projet au moyen de blocs reliés entre eux par des signaux.

icestudio-0.2-crono_

Les blocs peuvent être pris dans une librairie fourni avec le logiciel, mais il est également possible de créer des blocs «vierges» dans lesquels on écrira le code verilog correspondant au comportement souhaité.

icestudio-0.2-counter-inspection

Le format de sauvegarde du projet est en  JSON, un outils de conversion permet ensuite de le transformer en code Verilog pour la synthèse.

Installation

Pour l’installer nous aurons besoin du paquet «npm» :

sudo apt-get install npm

Puis de télécharger le projet git:

git clone https://github.com/FPGAwars/icestudio.git

Et enfin de lancer la commande d’installation comme décrite dans le README.md:

cd icestudio
npm install

Pour l’exécuter lancer simplement

npm start

Et si comme pour votre serviteur, cela ne marche pas du tout 😉 allez plutôt chercher la release sur https://github.com/FPGAwars/icestudio/releases. Dézippez la

unzip Icestudio-0.2.0-beta2-linux64.zip

Puis lancez le simplement :

./Icestudio

Le projet est encore jeune mais très prometteur.  Espérons que nous verrons rapidement l’intégration de nouvelles plateformes/FPGA, voir une version FPGA-Agnostic.

 

SpinalHDL va-t-il remplacer Chisel ?

SpinalHDL est un langage HDL ressemblant à s’y méprendre à Chisel. Et pour cause, son créateur est un ancien utilisateur intensif de Chisel.

  • Tout comme Chisel, SpinalHDL est basé sur le langage Scala.
  • Tout comme Chisel, les entrées/sorties sont décrites au moyen de Bundles.
  • Tout comme Chisel, Spinal génère un langage HDL pour la synthèse.

Alors pourquoi ne pas utiliser Chisel ?

  • Car SpinalHDL génère du VHDL pour la synthèse, et non du Verilog
  • Car SpinalHDL gère les domaines d’horloges de manière élégante
  • Car la déclaration des entrées sortie est plus «naturelle» pour un habitué de VHDL/Verilog. En effet, pour déclarer les des signaux comme entrée ou sortie d’un module sur Chisel il faut faire :
    
       val io = new Bundle {
         val a = Bool(INPUT)
         val b = Bool(INPUT)
         val c = Bool(OUTPUT)
       }
    

    Alors que sur SpinalHDL on fera:

    
       val io = new Bundle {
         val a = in Bool
         val b = in Bool
         val c = out Bool
       }
    

    Ce qui est plus naturel.

  • La gestion des blackbox est mieux intégrée.

De plus, Charles (dolu1990) m’informe qu’il y a maintenant une implémentation de RISCV avec 5 étages de mul/div/interruptions fonctionnel en SpinalHDL:

Bonjour,

Flash info Spinal XD
Il y a maintenant une implémentation de RISCV, 5 stages mul/div/interrupt
fonctionnel codée en Spinal.
Le cpu égualement débuggable via JTAG, fork openOCD, GDB et eclipse. (c'est
a ma connaissance la seul implémentation RISCV qui cible les FPGA avec
cette fonctionnalité)

Au plaisir de libérer les FPGA de leur asservissement.
Charles

Bref pas mal de choses intéressantes qui j’espère dynamiserons le développement de Chisel également et peut-être fusionnerons à terme ?

Pour la documentation officielle c’est par là.

Comme d’habitude, le FOSDEM a eu sa moisson de conférences fort intéressantes à propos de la libération des FPGA.

Voici en vrac les slides qu’il ne faut pas louper:

Une nouvelle release d’icarus verilog est désormais disponible sur le ftp officiel.

Pour l’installer sous linux, rien de plus simple :

$ wget ftp://icarus.com/pub/eda/verilog/v10/verilog-10.0.tar.gz
$ tar -zxvf verilog-10.0.tar.gz
$ cd verilog-10.0/
$ ./configure
$ make
$ sudo make install

Vérifier la bonne installation avec :

$ iverilog -v
Icarus Verilog version 10.0 (stable) (v10_0)

Copyright 1998-2015 Stephen Williams

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

iverilog: no source files.

Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile]
                [-g1995|-g2001|-g2005|-g2005-sv|-g2009|-g2012] [-g]
                [-D macro[=defn]] [-I includedir]
                [-M [mode=]depfile] [-m module]
                [-N file] [-o filename] [-p flag=value]
                [-s topmodule] [-t target] [-T min|typ|max]
                [-W class] [-y dir] [-Y suf] source_file(s)

See the man page for details.

FPGA Prototyping By Verilog Examples: Xilinx Spartan-3 Version

 

185322_cover.indd

Très bon début pour commencer le Verilog pour le FPGA. Ce livre ne s’attarde pas trop sur les arcanes du langage, mais se contente de décrire les structures essentielles pour être rapidement opérationnel dans la conception de designs FPGA en Verilog.

Les exemples sont données pour le Spartan3 de chez Xilinx. Cependant ils sont de bonnes bases pour développer sur n’importe quel autre FPGA, même d’une autre marque.

Et si vous préférez vous mettre plutôt au VHDL, sachez qu’il existe exactement le même livre en version VHDL.

FPGA Prototyping by VHDL Examples: Xilinx Spartan-3 Version

vhdl_xilinx_book

Très bon début pour commencer le VHDL pour le FPGA. Ce livre ne s’attarde pas trop sur les arcanes du langage, mais se contente de décrire les structures essentielles pour être rapidement opérationnel dans la conception de designs FPGA en VHDL.

Les exemples sont données pour le Spartan3 de chez xilinx. Cependant ils sont de bonnes bases pour développer sur n’importe quel autre FPGA, même d’une autre marque.

Et si vous préférez vous mettre plutôt au Verilog, sachez qu’il existe exactement le même livre en version Verilog.