ClaSH un HDL basé sur Haskell

La version 1.0 de CλaSH est sortie en septembre 2019. Profitons de cette sortie pour présenter ce langage de description matériel basé sur Haskell.

Logo de CλaSH

Mais qu’est-ce que CλaSH ? On pourrait commencer par le définir en décrivant ce qu’il n’est pas :

  • CλaSH (of cλaN ?) n’est pas un jeu vidéo se jouant en caressant un téléphone.
  • CλaSH n’est pas un groupe de musique se demandant s’il doit rester ou partir.
  • CλaSH n’est pas un HLS (High Level Synthesis). La fonction d’un logiciel HLS consiste à convertir un algorithme écrit dans un langage de programmation classique (très souvent du C) dans une architecture numérique en Verilog/VHDL.

Non.

CλaSH est un langage de description matériel basé sur le langage fonctionnel Haskell.

Le langage Haskell est né dans les années 90, fondé par une société secrète de mathématiciens qui voulait reprendre le contrôle des ordinateurs. En effet, vexés de voir tous ces geeks maîtriser cette machine mieux qu’eux ils décidèrent de se réunir de manière anonyme dans une cave (où il fallait entrer avec un mot de passe à base de développement limités sur un Algèbre commutatif isomorphe) pour fonder un nouveau langage compris par eux seuls. Le Haskell était né ! Enfin je crois 😉

Haskell est un langage fonctionnel, qui nous impose une autre façons de penser un programme informatique. Du dire des auteurs de CλaSH, le paradigme fonctionnel est plus à même de décrire du matériel que le paradigme impératif.

Un composant décrit en CλaSH est directement convertible en Verilog ou en VHDL (les deux sont gérés dans la version 1.0). Ce qui fait qu’un composant décrit en CλaSH est synthétisable avec n’importe quel logiciel de synthèse FPGA.

Dans le cas de ce type de langage on parle de générateur de code, un peu comme Chisel (basé sur Scala), Migen/Litex (basé sur Python) ou SpinalHDL (basé également sur Scala). Ces langages sont en fait des librairies ou des modules du langage auquel ils sont accolés.
Cette modularité permet de profiter d’un langage souvent bien établi avec énormément de fonctions optimisées (Scala, Python, Haskell…).

Le paradigme fonctionnel n’est pas du tout quelque chose qui coule de source pour un simple électronicien comme votre serviteur. Cette dépêche a donc été un peu différée le temps d’avaler les 100 premières pages de tutoriel permettant de faire un simple « hello world » en Haskell. Puis d’avaler le tutoriel CλaSH pour enfin faire clignoter cette fameuse LED sur un kit FPGA low cost de base.

Nous allons donc tenter un petit CλaSHtest permettant de faire clignoter une led.

Le type de base manipulé en CλaSH est le Signal. CλaSH ne permettant que des descriptions synchrones, un Signal représente une liste infinie de valeurs synchronisée sur une horloge et un reset pour la valeur initiale. L’horloge et le reset du signal sont décrit par le Domaine dom du signal. La forme d’un Signal est donc la suivante :

 Signal (dom :: Domain) a

En logique synchrone, le composant de base est le registre, nommé register:

register
     ( HiddenClockResetEnable dom dom
     , NFDataX a )
  => a
  -> Signal dom a
  -> Signal dom a

Son fonctionnement est assez basique : La valeur initiale (de reset) est donnée en premier argument, le second argument est le signal d’entrée qui est recopié sur le signal de sortie (valeur de retour de la fonction). On peut utiliser CλaSH en ligne de commande avec la commande clash.clashi.

$ clash.clashi
Clashi, version 1.0.1 (using clash-lib, version 1.0.1):
http://www.clash-lang.org/  :? for help
Clash.Prelude> 

Mais on préférera rapidement enregistrer notre module dans un fichiers source au format *.hs.

Pour faire clignoter une LED dans un FPGA, la première chose à mettre en place est un compteur :

Clash.Prelude> counter = register 0 counter + 1

Cette fonction counter que nous venons de créer retourne un registre initialisé à 0 et qui ajoute 1 à sa sortie à chaque coup d’horloge. La définition est récursive, c’est à dire qu’elle ajoute 1 à elle même. Et comme on commence à 0 et qu’on ajoute 1, la valeur initiale sera 1.
Cette fonction étant infinie, si nous voulons connaitre des valeurs il faut échantillonner :

Clash.Prelude> sampleN @System 10 counter
[1,1,2,3,4,5,6,7,8,9]

On constate que la première valeur est en double car c’est la valeur initiale, avant le premier coup d’horloge. @System désigne le domaine d’horloge/reset/enable utilisé. @System est le domaine général, nous pourrions appliquer un domaine (dom) spécifique à une architecture comme @XilinxSystem ou @IntelSystem.

Le problème avec ce compteur c’est qu’il incrémente à l’infini, nous on voudrait un compteur avec une limite et qui se remet à 0 à une certaine valeur comme ça :

Clash.Prelude> counter value = if(value < 100) then value + 1 else 0
Clash.Prelude> counter 10
11
Clash.Prelude> counter 0
1
Clash.Prelude> counter 101
0

[notes]
Je ne sais pas si je vais réussir à comprendre l’exemple de led qui clignote donné sur le blog de l’auteur de clash en fait.

Je suis parti un peu la fleur au fusil pour faire cette dépêche, mais je me rend compte que l’apprentissage de Clash (et surtout Haskell) est un loooong chemin.

Bref, je crois que je ne vais pas réussir à finir cette dépêche. Peut-être un jour aurais-je suffisamment de bouteille en Haskell pour comprendre Clash, mais là …

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*