Archives par mot-clé : xilinx

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.

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.

Compiler Torc sous Debian Jessie

Prérequis :

sudo apt-get install libboost-all-dev

Descendre le svn (avec git c’est mieux) :

git svn clone https://svn.code.sf.net/p/torc-isi/code/trunk torc

Faire un premier «make» dans src/, ce qui va créer un fichier nommé Makefile.local. Ouvrir ce fichier et ajouter les path vers boost :

BOOST_INCLUDE_DIR = /usr/include/boost/
BOOST_LIB_DIR = /usr/lib/

Faire un make (toujours dans src/) :

$ make

La configuration converti tous les warning en erreur (-Werror), et comme il reste des warnings visiblement dans le svn on arrive pas à tout compiler. Pour compiler malgré tout, virer l’option dans le fichier src/torc/Makefile.targets  (ligne 59):

-Werror \

 

Torc est un logiciel libre permettant de générer les bitstreams pour les fpga Xilinx:

http://torc-isi.sourceforge.net/documentation.php