Archives de catégorie : blog

TapTempo en Verilog

Il y a plus de deux ans et demi maintenant, mzf publiait un journal sur le site LinuxFR parlant de son projet «TapTempo». L’objectif de son programme était simplement de mesurer la cadence d’une musique en tapant sur une touche de son clavier, le résultat s’affichant simplement dans la console.

Ce journal fut le point de départ d’une série de «projets TapTempo» proposé par les lecteurs du site dans à peu prêt tous les langages informatique possible… Mais pas le Verilog.

Voici donc la lacune comblée avec TapTempo en Verilog.

[Dépêche parue initialement sur LinuxFR]

Le projet TapTempo semble faiblir depuis quelques mois maintenant. En panne de langage informatique pour en faire une dépêche ?
Laissez‑moi vous présenter un langage assez particulier puisqu’il ne sert pas à faire de la programmation. Ce langage permet de décrire le comportement numérique d’un composant électronique (on parle alors de langage de description de matériel — HDL) : le Verilog.

C’est aussi un langage utilisé pour faire de la synthèse numérique sur les circuits logiques programmables (FPGA). Dans cet exemple, nous utiliserons la carte de développement à bas coût ColorLight 5A‑75B.

Vue d'ensemble du montage TapTempo

Sommaire

Le Verilog

Le Verilog est un langage conçu à l’origine pour rédiger des spécifications de circuits logiques en électronique numérique. Le langage permet de décrire le comportement de sortie par rapport à des entrées logiques.

Un peu comme les logiciels de saisie de schéma électronique, le Verilog est très hiérarchique, on décrit des modules avec leurs entrées-sorties. Que l’on assemble ensuite dans d’autres modules pour finir dans un module « top » qui décrit le composant final.

Dans le cas de TapTempo, le module « top » est déclaré comme ceci :

module taptempo #(
    parameter CLK_PER_NS = 40, // 25Mhz clock (ns)
    parameter TP_CYCLE = 5120, // timepulse cycle period (ns)
    parameter BPM_MAX = 250 // BPM max (bpm)
)(
    input clk_i,
    input btn_i,
    output pwm_o
);
//corps du module
endmodule

Le module possède deux entrées : l’horloge (clk_i) et le bouton (btn_i) ainsi qu’une sortie pwm (pwm_o) pour l’affichage. Les paramètres seront vus comme des constantes au moment de la simulation, ils permettent de configurer les composants en fonction de la cible.

Le changement de valeur des signaux se fait dans des processus qui sont déclenchés sur événement. Ces processus sont décrits au moyen du mot clef always@() en Verilog.
Par exemple, dans le code suivant:

/* Detect rising edge*/
reg btn_old, btn_rise;
always@(posedge clk_i)
begin
    btn_old <= btn_i;
    if(btn_old == 0 && btn_i == 1)
        btn_rise <= 1;
    else
        btn_rise <= 0;
end

L’événement déclencheur du process est le front montant de l’horloge clk_i. À chaque fois qu’un front montant d’horloge se présente, le processus est exécuté de manière séquentielle.

L’opérateur <= est l’opérateur d’affectation dit « non bloquant ». Cela signifie que la valeur ne sera effectivement appliquée qu’a la fin de l’exécution du process. Donc la valeur du signal btn_old ne sera pas nécessairement égale à btn_i à la ligne du if() comme on aurait pu instinctivement le croire.

Le langage Verilog a beaucoup de succès dans le monde du logiciel libre. En effet il est relativement peu verbeux et ressemble au C pour de nombreux aspects.

Il est par exemple possible de décrire des macros de la même manière qu’en C, il suffit de remplacer le symbole # par ` pour créer des constantes qui seront remplacées par le pré-processeur.

/* count tap period */
`define MIN_NS 60_000_000_000
`define BTN_PER_MAX (`MIN_NS/TP_CYCLE)
`define BTN_PER_SIZE ($clog2(1 + `BTN_PER_MAX))

Le Verilog reprend également les opérateurs booléen et binaire &,&&, |,||, etc. du C.

C’est le langage HDL le mieux supporté par les différents logiciels libres. Si l’on souhaite se lancer dans le domaine des FPGA et/ou des ASIC, il est préférable de commencer par lui. C’est également le langage « de sortie » de quasiment tous les générateurs de code HDL.

Architecture de TapTempo

L’outil indispensable pour commencer un projet en Verilog est… le papier et le crayon. Il est en effet indispensable d’avoir une vue d’ensemble assez claire de ce que l’on souhaite réaliser avant de se lancer dans le code.

Voici donc l’architecture générale du composant TapTempo :

Schema TapTempo au Crayon

Même si l’on doit revenir plusieurs fois (ce qui est le cas ici puisque les constantes ne sont pas à jour) sur ce schéma général en cours de développement, cette partie est très importante. Si elle est bien pensée, le reste coule de source.

Le composant va nécessiter quelques compteurs, mais l’horloge utilisée ici étant très rapide nous allons d’abord factoriser le comptage au moyen du module nommé timepulse, ce module va distribuer un pulse qui servira de base aux autres compteurs pour leur fonctionnement interne.

L’entrée utilisateur se compose d’un bouton (touche télégraphique « morse »). Les fronts montant et descendant de cette entrée n’étant pas synchronisés sur l’horloge du système nous allons devoir le faire au moyen de deux bascules en série pour éviter la métastabilité.

/* Synchronize btn_i to avoid metastability*/
reg btn_old, btn_s;
always@(posedge clk_i or posedge rst)
begin
    if(rst) begin
        btn_old <= 1'b0;
        btn_s <= 1'b0;
    end else begin
        btn_old <= btn_i;
        btn_s <= btn_old;
    end
end

Le second problème que pose notre entrée est que l’appui sur le bouton ne génère pas des changements francs de son état. Chaque « appui et relâche » génère une série de rebonds et donc une série de 0 et de 1 avant de se stabiliser. Pour lisser le signal il va donc falloir faire passer le signal dans le bloc « anti-rebond » debounce.

Le bloc percount va ensuite se charger de mesurer le temps entre deux appuis sur le bouton. Cette période va devoir être transformée en fréquence « BPM » (Beat Per Minute) via le module per2bpm puis en une valeur pseudo-analogique (PWM) grâce au module pwmgen.

La carte cible ne possédant pas de bouton « reset », il va falloir le générer grâce au module rstgen de manière à s’assurer de l’état de départ de notre système au démarrage.

Entrée sortie

La plupart des programmes TapTempo proposés jusqu’ici supposaient – en plus d’un CPU – la présence d’un clavier et d’une console texte de sortie (avec toute la pile de pilotes et de système d’exploitation associés). Ici, nous allons devoir tout définir dans le « portegramme » – Dans l’industrie on va parler d’IP pour Intellectual Property, quel horrible nom –.

L’idée est donc de simplifier au maximum l’entrée « clavier » et la sortie histoire de pouvoir les décrire simplement.

Pour l’entrée nous allons nous contenter d’un contact de type bouton, ou d’une touche de type télégraphe « morse ».

TapTempoEntreeMorse

Comme on peut le voir dans le schéma ci-dessus, quand la touche est appuyée, l’entrée « bouton » est mise à la masse et donne un niveau logique à 0 sur notre système. Lorsque l’on relâche le bouton, la résistance de tirage ramène le niveau de tension à Vcc pour avoir un niveau 1 sur l’entrée.

Pour la sortie, l’idée de mettre un écran complexifie énormément le système. En effet, il est nécessaire de faire une machine d’état assez complexe pour initialiser l’écran puis rafraîchir l’affichage. Il est souvent nécessaire d’ajouter un processeur « soft » rien que pour ça d’ailleurs. (Bon il est vrai que le VGA n’est pas si compliqué, mais il reste plus complexe que la solution proposée ici).

Non, l’idée ici est d’utiliser les graduations de l’antique voltmètre à aiguille trouvé dans une cave et qui gradue de 0 à 300 comme on peut le voir sur la photo :

vuemetretaptempo

Et comme un système numérique ne sort que des 0 et des 1 sur ses broches, on va « simuler » une valeur analogique au moyen d’une PWM (Pulse With Modulation). Il suffit de changer le rapport cyclique entre le temps haut et le temps bas de notre signal pour faire varier la tension moyenne qui sera vue par le voltmètre. Si on l’ajuste correctement avec une résistance en série, il est relativement facile de forcer la valeur maximale (5V) à 250.

pwm

La période de la pwm sera configurée suffisamment rapide pour que l’aiguille n’oscille pas.

Pulsation de temporisation (timepulse)

Le module ne prend pas de valeur d’entrée hormis l’horloge et le reset qui sont de rigueur dans tout le projet. Son signal de sortie tp_o est une pulsation de l’horloge émise toutes les 5120 ns :

module timepulse #(
    parameter CLK_PER_NS = 40,
    parameter PULSE_PER_NS = 5120
)(
    /* clock and reset */
    input clk_i,
    input rst_i,
    /* output */
    output tp_o);

Pour pouvoir compter des périodes de 5120ns on définit un registre de comptage :

`define MAX_COUNT (PULSE_PER_NS/CLK_PER_NS)
`define MAX_COUNT_SIZE ($clog2(`MAX_COUNT))

reg [`MAX_COUNT_SIZE-1:0] counter = 0;

Puis on compte de manière synchronisée avec l’horloge :

always@(posedge clk_i or posedge rst_i)
begin
    if(rst_i)
    begin
        counter <= 0;
    end else begin
        if (counter < `MAX_COUNT)
        begin
            counter <= counter + 1'b1;
        end else begin
            counter <= 0;
        end
    end
end

La pulsation est émise lorsque le compteur passe par 0 :

assign tp_o = (counter == 0);

Gestion des rebonds (debounce)

L’entrée de ce module est le signal de bouton préalablement synchronisé avec l’horloge du système btn_s. Le compteur utilisera la pulsation tp_i généré par le module timepulse décrit ci-avant.

La sortie du module est un signal btn_o proprement lissé. La période de temporisation de 20 ms est donné ici en paramètre DEBOUNCE_PER_NS.

module debounce #(
    parameter PULSE_PER_NS = 5120,
    parameter DEBOUNCE_PER_NS = 20_971_520
)(
    /* clock and reset */
    input clk_i,
    input rst_i,
    /* inputs */
    input tp_i,
    input btn_i,
    /* output */
    output btn_o
);

La gestion des rebonds est réalisée au moyen d’un compteur utilisé pour temporiser.

`define MAX_COUNT ((DEBOUNCE_PER_NS/PULSE_PER_NS)-1'b1)
`define MAX_COUNT_SIZE ($clog2(`MAX_COUNT))

/* Counter */
reg [`MAX_COUNT_SIZE-1:0] counter = 0;

Ainsi que d’une machine d’états à 4 états :

/* State machine */
localparam [1:0] s_wait_low  = 2'h0,
                 s_wait_high = 2'h1,
                 s_cnt_high  = 2'h2,
                 s_cnt_low   = 2'h3;

reg [1:0] state_reg, state_next;

Les transitions de la machine d’états sont données dans le code ci-dessous dans un processus dit « combinatoire » (always@*) par opposition à un processus « synchrone ».

always@*
begin
    case(state_reg)
        s_wait_low:
            if(btn_i)
                state_next = s_cnt_high;
            else
                state_next = s_wait_low;
        s_wait_high:
            if(!btn_i)
                state_next = s_cnt_low;
            else
                state_next = s_wait_high;
        s_cnt_high:
            /* verilator lint_off WIDTH */
            if(counter == `MAX_COUNT)
            /* verilator lint_on WIDTH */
                state_next = s_wait_high;
            else
                state_next = s_cnt_high;
        s_cnt_low:
            /* verilator lint_off WIDTH */
            if(counter == `MAX_COUNT)
            /* verilator lint_on WIDTH */
                state_next = s_wait_low;
            else
                state_next = s_cnt_low;
    endcase;
end

L’état de la machine est tout de même synchronisé dans un second processus :

always@(posedge clk_i or posedge rst_i)
    if(rst_i)
        state_reg <= s_wait_low;
    else
        state_reg <= state_next;

Le principe de « lissage » des rebonds est donc le suivant : Dans l’état initial s_wait_low on attend que le bouton passe à la valeur 1. Lorsque le signal passe à 1, on change d’état pour s_cnt_high.

Le passage dans l’état s_cnt_high a pour effet de faire passer le signal de sortie à 1 et déclencher le compteur. Tant que le compteur compte et n’a pas atteint la valeur MAX_COUNT, on reste dans cet état quelles que soient les variations du signal d’entrée.
Lorsque le compteur atteint la valeur maximale, la machine d’état passe dans l’état s_wait_high (en attente de valeurs hautes).

Dans l’état s_wait_high on surveille la valeur du bouton d’entrée, si elle passe à 0 on change d’état pour s_cnt_low.

De manière symétrique à s_cnt_high on déclenche donc le compteur en ignorant la valeur d’entrée. Et, lorsqu’elle atteint son maximum on passe à l’état initial s_wait_low.

La valeur « lissée » du bouton en sortie est donnée par l’état de la machine d’état :

assign btn_o = (state_reg == s_cnt_high) || (state_reg == s_wait_high);

Mesure de la période de tempo (percount)

L’interface du module percount se compose des entrées habituelles d’horloge clk_i, de reset rst_i ainsi que de la pulsation tp_i.

Le signal de mesure en entrée est btn_i et la sortie est un vecteur btn_per_o donnant la valeur mesurée. La valeur est considérée comme valide uniquement lorsque la sortie btn_per_valid est à 1. Cette astuce permet d’économiser un registre si la sauvegarde de la valeur mesurée est inutile comme c’est le cas ici.

`define MIN_NS 60_000_000_000
`define BTN_PER_MAX (`MIN_NS/TP_CYCLE)
`define BTN_PER_SIZE ($clog2(1 + `BTN_PER_MAX))

module percount #(
    parameter CLK_PER_NS = 40,
    parameter TP_CYCLE = 5120,
    parameter PULSE_PER_NS = 5120,
)(
    /* clock and reset */
    input clk_i,
    input rst_i,
    /* time pulse */
    input tp_i,
    /* input button */
    input btn_i,
    /* output period */
    output [(`BTN_PER_SIZE-1):0] btn_per_o,
    output btn_per_valid);

Maintenant que nous avons un signal de bouton btn_b propre et lissé, nous pouvons entamer la mesure de la période entre deux appuis au moyen de… devinez quoi ? D’un compteur pardi !

reg [($clog2(`BTN_PER_MAX+1)-1):0] counter = 0;
reg counter_valid = 0;

assign btn_per_valid = counter_valid;
assign btn_per_o = counter;

Il nous faut tout d’abord détecter le front descendant du bouton  :

reg btn_old;
wire btn_fall = btn_old & (!btn_i);

always@(posedge clk_i or posedge rst_i)
begin
    if(rst_i)
        btn_old <= 1'b0;
    else
        btn_old <= btn_i;     
end

Le signal btn_fall sert de remise à zéro du compteur ainsi que de validation de la valeur de sortie :

always@(posedge clk_i or posedge rst_i)
begin
    if(rst_i)
    begin
        counter <= 0;
    end else begin
        if(btn_fall) begin
            counter_valid <= 1'b1;
        end else if(counter_valid) begin
            counter <= 0;
            counter_valid <= 1'b0;
        end else begin
            /* stop counting if max, count tp_i */
            if(tp_i && counter < `BTN_PER_MAX)
                counter <= counter + 1'b1;
        end
    end
end

Le compteur compte le nombre de pulsations de tp_i jusqu’à atteindre la saturation BTN_PER_MAX. Si un front montant du bouton se présente avec btn_fall, on valide le compteur avec counter_valid. Et si le signal de validation passe à 1 (donc le coup d’horloge suivant) on remet le compteur à zéro et on recommence à compter.

Calcul de la fréquence en Beat Per Minute (per2bpm)

Avec le module per2bpm on arrive dans la partie critique du projet, car il va nous falloir faire une division. On entre une période dans le module :

    /* inputs */
    input [(`BTN_PER_SIZE-1):0] btn_per_i,
    input btn_per_valid,

Et on doit en ressortir une fréquence (BPM) :

    /* outputs */
    output [`BPM_SIZE - 1:0] bpm_o,
    output bpm_valid

Suivant la formule :

Il faut donc diviser la constante

par la variable btn\_per\_i

La division (tout comme la multiplication) est un point sensible en Verilog. En effet, l’opérateur de division existe bien dans le langage et il se peut que cela simule parfaitement.

C’est lorsque arrivera l’étape de la synthèse que l’on risque d’avoir quelques surprises. Il est possible que certains logiciels de synthèse réussiront à faire quelque chose en un coup d’horloge. Mais il est certain que cela se fera au prix de très mauvaises performances en matière de ressources utilisées et de fréquence d’horloge. Il est surtout probable que votre logiciel de synthèse jette l’éponge.

Pour réaliser cette division, nous allons donc en revenir aux fondamentaux appris au primaire et poser la division. Une division, c’est la recherche du Quotient et du Reste de l’équation suivante :

reg [(`REGWIDTH-1):0] divisor;
reg [(`REGWIDTH-1):0] remainder;
reg [(`REGWIDTH-1):0] quotient;

La taille des registres sera celle de la période en entrée BTN_PER_SIZE additionné à la constante à diviser.

`define DIVIDENTWITH ($clog2(1 + `MIN_NS/(TP_CYCLE)))
`define REGWIDTH (`BTN_PER_SIZE + `DIVIDENTWITH)

La division s’effectue avec une série de soustraction du reste (remainder) et de décalage du diviseur.

À l’étape initiale, on place le diviseur à gauche du registre divisor et le dividende dans le reste remainder :

     divisor <= {btn_per_i, (`DIVIDENTWITH)'h0};
     remainder <= `MIN_NS/TP_CYCLE;
     // le résultat est initialisé à 0:
     quotient <= 0;

Puis on effectue une série de comparaison-soustraction-décalage avec l’algorithme comme décrit ci-dessous :

  • si le diviseur (divisor) inférieur ou égal au reste (remainder), on soustrait le reste avec le diviseur et on décale le quotient à gauche en ajoutant 1 :
            if(divisor <= remainder)
            begin
                remainder <= remainder - divisor;
                quotient <= {quotient[(`DIVIDENTWITH-2):0], 1'b1};
  • si le diviseur (divisor) est supérieur au reste, on décale le quotient à gauche en ajoutant 0. On ne touche pas au reste :
                quotient <= {quotient[(`DIVIDENTWITH-2):0], 1'b0};
  • dans tous les cas, on décale le diviseur à droite.
            divisor <= {1'b0, divisor[(`REGWIDTH-1):1]};

La division est orchestrée par une machine à trois états :

localparam [1:0] s_init    = 2'h0,
                 s_compute = 2'h1,
                 s_result  = 2'h2;

reg [1:0] state_reg, state_next;

Et le résultat est disponible en sortie quand state_reg est dans l’état s_result:

assign bpm_o = quotient[(`BPM_SIZE-1):0];
assign bpm_valid = (state_reg == s_result);

Génération de la tension de sortie (pwmgen)

La génération du signal pseudo analogique décrite en introduction est presque la partie la plus simple.

On compte (oui encore) de 0 à 250 (BPM_MAX) :

/* count */
always@(posedge clk_i or posedge rst_i)
begin
    if(rst_i)
        count <= BPM_MAX;
    else begin
        if(tp_i)
        begin
            if (count == 0)
                count <= BPM_MAX;
            else
                count <= count - 1'b1;
        end
    end
end

Et on passe le signal de sortie pwm_o à 1 lorsque le compteur est inférieur à la fréquence demandée :

assign pwm_o = (count <= pwmthreshold);

Il y a juste une subtilité consistant à sauvegarder la valeur de la fréquence donnée en entrée dans deux registres pwmthreshold et bpm_reg :

reg [($clog2(BPM_MAX+1)-1):0] bpm_reg;
reg [($clog2(BPM_MAX+1)-1):0] pwmthreshold;

/* Latching bpm_i on bpm_valid */
always@(posedge clk_i or posedge rst_i)
begin
    if(rst_i)
    begin
        bpm_reg <= 0;
        pwmthreshold <= 0;
    end else begin
        if(bpm_valid)
            bpm_reg <= bpm_i;
        if(count == BPM_MAX)
            pwmthreshold <= bpm_reg;
    end
end

Le premier registre bpm_reg est mis à jour lorsque le signal d’entrée bpm_valid est à 1. Pour mémoriser la valeur d’entrée et pouvoir l’utiliser au moment où l’on en a besoin.
Et le second pwmthreshold est rafraîchi en fin de cycle d’une période de la pwm. Pour éviter d’avoir un changement de valeur en cours de période, et donc un rapport cyclique faux.

Simulation de l’ensemble avec Cocotb

Jusqu’ici nous avons décrit le comportement du composant final en Verilog. Toutes les développeuses ou développeurs HDL le savent très bien, il est impossible de réaliser un projet Verilog (ou autre HDL) sans faire un minimum de simulation.

Pour simuler le composant, il est nécessaire de décrire les stimuli en entrée du composant et de lire/valider les sorties. On va généralement créer un composant hiérarchiquement au-dessus du top de notre composant appelé « testbench » dans lequel nous décrirons les changements de valeurs des entrées au cours du temps. Cette partie peut tout à fait se faire en Verilog.

Cependant, l’idée de mélanger la partie banc de test et composant « synthétisable » n’est pas terrible. En effet on va très vite confondre les deux parties et mélanger les codes. L’exemple de la division est criant : l’opérateur diviser « / » fonctionne très bien dans la partie testbench mais elle pose de gros problèmes dans la partie « synthétisable ».

Pour éviter ce mélange des genres, une solution radicale consiste à utiliser un autre langage pour la partie banc de test. Le C++ et le SystemC sont utilisés depuis longtemps pour cela. S’ils sont utilisés en conjonction avec Verilator ils permettent d’atteindre des puissance/rapidité de simulation inégalées par les simulateurs « propriétaires ».

Une autre méthode consiste à piloter le simulateur Verilog avec un autre programme, on parle alors de cosimulation. C’est le cœur du fonctionnement du module python CocoTB. L’idée ici est d’écrire son banc de test en python, ce qui est nettement plus confortable que du Verilog ou même du C++ (SystemC est une librairie C++ également).

Le testbench pour simuler l’ensemble du projet taptempo se trouve dans le répertoire cocotb/test_taptempo. Pour le simuler il suffit de s’y rendre et d’y exécuter un make.
À condition cependant d’avoir installé cocotb (en python3) et Icarus pour la partie simulateur (On laissera l’appréciation de l’installation au lecteur en fonction de ses affinités linuxdistributive).

La simulation consiste à tester trois appuis sur le bouton à des intervalles différents :

@cocotb.test()
async def debounce_upanddown(dut):
    td = TestTapTempo(dut)
    td.log.info("Running test!")
    await td.reset()
    td.log.info("System reseted!")
    await Timer(1000, units="us")
    td.log.info("up")
    await td.bounce_up(10, bounce_per=(10000, "ns"))
    await Timer(24, units="ms")
    td.log.info("down")
    await td.bounce_down(10, bounce_per=(10000, "ns"))
    await Timer(300, units="ms")
    td.log.info("up")
    await td.bounce_up(10, bounce_per=(10000, "ns"))
    await Timer(30, units="ms")
    td.log.info("down")
    await td.bounce_down(10, bounce_per=(10000, "ns"))
    await Timer(800, units="ms")
    td.log.info("up")
    await td.bounce_up(10, bounce_per=(10000, "ns"))
    await Timer(30, units="ms")

    td.log.info("Wait stable")
    await Timer(1000, units="us")

Cela génère un fichier de « traces » au format VCD particulièrement volumineux de 2,3 Go (qui se compresse à 70 Mo avec xz !) permettant de visionner les signaux au cours du temps grâce à gtkwave:

$ gtkwave -g taptempo.vcd

Et donne la trace suivante :
simulation_taptempo_full

Cette simulation est particulièrement longue (il m’a fallu environ une heure et demie sur mon vieux T430) et génère un fichier de trace monstrueux. En phase de développement on va généralement lancer de petites simulations par modules comme on peut le voir pour le module debounce dans le répertoire cocotb/test_debounce. On changera également certaines constantes de temps pour limiter les «  pas » de simulation consommant inutilement du calcul processeur.

Il est également possible de laisser l’ordinateur écrire les stimuli grâce à la méthode de preuve formelle. C’est la méthode qui a été utilisée ici pour les modules. Les fichiers de configuration se trouvent dans le répertoire formal/*.

Synthèse sur ColorLight

La Colorlight n’est pas initialement une carte de développement pour les FPGA. C’est une carte permettant de piloter des panneaux de LED qui nous agressent un peu partout dans les rues commerçantes. Cependant, un petit malin s’est rendu compte qu’elle était munie d’un FPGA de chez Lattice : l’ECP5.

Ce FPGA possède deux gros avantages :

  • il est relativement gros, suffisamment pour posséder des multiplieurs câblés, des sérialiseurs-désérialiseurs…
  • on peut développer dessus avec une chaîne de développement intégralement opensource !

Jusqu’à la colorlight, les kits de développement ECP5 n’étaient pas donnés puisque les premières cartes débutaient à 100 $ minimum. Mais avec la colorlight, on tombe à 15 $, ce qui en fait un kit de développement ultra bon marché pour se faire la main avec des FPGA.

Et comme tout est opensource, il est aisé d’aller installer les logiciels permettant de synthétiser TapTempo sur sa distribution Linux préférée.
L’explication de l’installation des outils est hors de propos de cet article (un article détaillé sur la colorlight est disponible dans le Hackable 35), mais une fois les outils installés, il suffit de se rendre dans le répertoire synthesis/colorlight du projet et de faire make :

$ make
[...]
Info: Device utilisation:
Info:          TRELLIS_SLICE:   328/12144     2%
Info:             TRELLIS_IO:     3/  197     1%
Info:                   DCCA:     1/   56     1%
Info:                 DP16KD:     0/   56     0%
Info:             MULT18X18D:     0/   28     0%
Info:                 ALU54B:     0/   14     0%
Info:                EHXPLLL:     0/    2     0%
Info:                EXTREFB:     0/    1     0%
Info:                   DCUA:     0/    1     0%
Info:              PCSCLKDIV:     0/    2     0%
Info:                IOLOGIC:     0/  128     0%
Info:               SIOLOGIC:     0/   69     0%
Info:                    GSR:     0/    1     0%
Info:                  JTAGG:     0/    1     0%
Info:                   OSCG:     0/    1     0%
Info:                  SEDGA:     0/    1     0%
Info:                    DTR:     0/    1     0%
Info:                USRMCLK:     0/    1     0%
Info:                CLKDIVF:     0/    4     0%
Info:              ECLKSYNCB:     0/   10     0%
Info:                DLLDELD:     0/    8     0%
Info:                 DDRDLL:     0/    4     0%
Info:                DQSBUFM:     0/    8     0%
Info:        TRELLIS_ECLKBUF:     0/    8     0%
Info:           ECLKBRIDGECS:     0/    2     0%
[...]
ecppack --svf taptempo.svf taptempo_out.config taptempo.bit

On voit ici que les ressources utilisées pour TapTempo sont ridicules par rapport au FPGA utilisé. La curieuse ou le curieux qui voudra « voir » le placement routage dans le FPGA utilisera l’option --gui dans la commande NextPnR pour avoir l’interface graphique :

$ nextpnr-ecp5 --25k --package CABGA256 --speed 6 --json taptempo.json --textcfg taptempo_out.config --lpf taptempo.lpf --freq 25 --gui

Ce qui donne un autre aperçu du remplissage du FPGA.

taptempo routage

Pour télécharger le bitstream dans le FPGA, on pourra utiliser openFPGALoader en donnant simplement le nom du bitstream :

$ openFPGALoader taptempo.bit

Exercices de travaux pratiques

Pour celles et ceux qui ont suivi jusqu’ici et qui voudraient se faire la main avec ce projet, voici quelques propositions de « sujet de TP » :) :

  • utilisation d’un multiplieur câblé de l’ECP5 pour faire la division dans per2bpm ;
  • ajout un module de moyennage sur cinq échantillons pour coller à la spécification initiale de TapTempo ;
  • utilisation d’autres plates‑formes FPGA à bas coût : QuickFeather, FireAnt, Tang Nano

N’hésitez pas à me proposer des demandes d’intégration Git pour améliorer le projet.

Conclusion

On voit que dès que l’on passe dans le domaine de l’embarqué les choses se compliquent et prennent plus de temps. Alors que sur un PC on aurait pu faire ça en une ligne de code, quand on embarque ça dans un microcontrôleur, c’est déjà plus compliqué. Mais si l’on passe dans le monde des FPGA et des ASIC, le projet prend une toute autre dimension. C’est la raison pour laquelle il faut toujours se demander si un FPGA est bien à propos pour notre projet, non seulement cela coûtera plus cher en composant qu’une solution sur étagère, mais en plus le temps de développement (et donc le coût) sera nettement supérieur.

L’idée d’utiliser une touche de télégraphe pour mesurer le tempo n’était peut‑être pas la meilleure, compte tenu des rebonds qui sont relativement violents. Même avec le module lisseur de rebond (debounce), il subsiste quelques rebonds trop longs. Un tempo maximum à 250 n’est pas si rapide et l’on est vite frustré de l’atteindre alors qu’on pourrait mesurer des tempos de musiques plus… rythmées. On peut facilement passer à 300, mais ça reste lent. Si l’on veut un tempo plus rapide, il faut tout d’abord changer la graduation sur le voltmètre, puis modifier le paramètre BPM_MAX dans le code.

On a ici un modèle de projet qui est facile à synthétiser sur n’importe quel petit FPGA. C’est un projet qui peut être intéressant si l’on souhaite se sortir un peu les doigts des LED qui clignotent. La démonstration étant faite du fonctionnement de l’architecture globale, il est aisé de s’en servir pour la réécrire dans d’autres langages de description de matériel comme le VHDL, Chisel (même s’il y en a déjà une pour taptempo), Migen/Litex, MyHDL, Clash (en plus, ça permettrait de débloquer la dépêche LinuxFr.org sur le sujet)…

Pour le curieux, ou la curieuse, qui sera allé voir le code sur le projet GitHub, ce projet a été développé avec une dose de preuves formelles grâce au logiciel libre Yosys-SMTBMC.

CocoTB 1.4.0, la maturité

[Dépêche publiée initialement sur LinuxFR]

C’est dans la soirée du 8 juillet que l’annonce est tombée : la version 1.4.0 de CocoTB est sortie. Cette nouvelle version est une belle évolution de Cocotb avec une bonne intégration dans le système de paquets de Python ainsi que l’abandon de la prise en charge de Python 2. On peut aujourd’hui dire que CocoTB est une alternative sérieuse pour écrire ses bancs de test HDL.

Sommaire

Mais qu’est‑ce que c’est ?

CocoTB est une bibliothèque de cosimulation permettant d’écrire (en Python) des bancs de test pour la simulation numérique HDL (Hardware Description Language). Historiquement, les deux langages de descriptions HDL que sont Verilog et VHDL embarquent tout le nécessaire pour écrire des stimuli permettant de tester le composant en simulation. Cela permet d’avoir un seul langage pour décrire le composant et le tester. Le simulateur exécutera tout cela sans problème.

Mais cela amène beaucoup de confusion entre la partie du langage utilisable pour la simulation uniquement et la partie « description du matériel ». Dans le cas de la partie « matériel » on parle alors de code « synthétisable ». Cette confusion entre du code synthétisable ou non est source de grandes frustrations au moment de passer à la synthèse. En effet, cette belle structure de données que l’on aura développée et testée aux petits oignons s’écroulera au moment de la synthèse quand on se rendra compte que le code n’est pas synthétisable. Il faudra tout reprendre.

Une des idées derrière CocoTB est donc de changer de langage pour la simulation, comme cela les choses sont claires : on utilise le VHDL ou Verilog pour la partie du composant qui est synthétisable, et on passe au Python pour le banc de test. Ce n’est pas le seul logiciel à proposer ce genre d’approche. Avec Verilator, par exemple, on va écrire toute la partie banc de test en C++ ou en SystemC. La partie synthétisable sera écrite en Verilog et convertie en un objet C++ par Verilator.

La seconde idée de CocoTB est de ne pas réinventer la roue en réécrivant un énième simulateur HDL. Ce qui évite également d’avoir à choisir son camp entre Verilog et VHDL ou les deux (simulation mixte). Non, CocoTB va se contenter de piloter les simulateurs disponibles sur le marché. Les simulateurs libres que sont GHDL, Icarus et Verilator sont naturellement pris en charge, même si dans le cas de Verilator c’est très récent. La plupart des simulateurs commerciaux le sont également, ce qui est un argument pour l’introduire dans son bureau d’étude. En effet, les managers sont en général moyennement chauds pour virer un logiciel acquis à grands frais. Et l’on peut continuer à profiter des interfaces proposées par notre simulateur habituel pour exécuter le simulateur, visionner les chronogrammes, faire de la couverture de tests, etc.
La version 1.4 de CocoTB introduit la gestion complète du simulateur Aldec Active HDL qui vient s’ajouter aux classiques de Cadence et de Mentor, Modelsim…

Les changements dans le code

Un gros changement initié depuis quelque versions déjà est l’utilisation du mot clef async en lieu et place du yield et du décorateur @coroutine. Python 3 gérant désormais l’asynchronisme, CocoTB l’utilise et le prend désormais complètement en charge. L’exemple donné dans le courriel de la publication est le suivant :

    @cocotb.test()
    async def my_first_test(dut):
         """Try accessing the design."""

         dut._log.info("Running test!")
         for cycle in range(10):
             dut.clk <= 0
             await Timer(1, units='ns')
             dut.clk <= 1
             await Timer(1, units='ns')
         dut._log.info("Running test!")

Qui se serait écrit comme cela dans « l’ancien système » :

    @cocotb.test()
    def my_first_test(dut):
        """Try accessing the design."""
         dut._log.info("Running test!")
         for cycle in range(10):
             dut.clk <= 0
             yield Timer(1, units='ns')
             dut.clk <= 1
             yield Timer(1, units='ns')
         dut._log.info("Running test!")

Cette écriture restant cependant valable.

Le gros avantage de cette nouvelle écriture est de ne plus avoir a réinventer la roue avec des décorateurs inutiles. Avec async et await, on utilise des interfaces intégrées à Python 3, ce qui évite tout un travail de gestion.

Installation

CocoTB est, depuis maintenant un certain temps, partie intégrante du système de gestion de paquets de Python pip. Et vous pouvez dès à présent l’installer sur votre système via la commande pip install :

    $ python -m pip install cocotb
    # Pour celles et ceux qui ont installé la version précédente n’oubliez pas le --upgrade
    $ python -m pip install --upgrade cocotb

Et on peut vérifier la version grâce à la commande cocotb-config suivante :

cocotb-config --version
1.4.0

En plus de votre composant écrit en VHDL ou Verilog, deux fichiers supplémentaires sont nécessaires pour tester avec CocoTB : le Makefile et le script Python de test proprement dit.

Avec cette nouvelle version, le Makefile a encore été simplifié puisqu’il n’est plus nécessaire d’intégrer les en‑têtes C++. Ces en‑têtes sont nécessaires pour compiler les interfaces VPI/VHPI/FLI qui permettent de piloter les simulateurs. On compile désormais cette partie à l’installation de CocoTB. Dans les précédentes version, cette compilation ce faisait à chaque fois que l’on relançait les tests.

Si l’on prend l’exemple de l’antirebond en Verilog du Blinking Led Project, nous avons le Makefile suivant :

    SIM=icarus                                   # Nom du simulateur
    export COCOTB_REDUCED_LOG_FMT=1              # Pour avoir des traces de log qui rentre dans l’écran
    VERILOG_SOURCES = $(PWD)/../src/button_deb.v # Inclusion des fichiers HDL
    TOPLEVEL=button_deb                          # Nom de l’entité 
    MODULE=test_$(TOPLEVEL)                      # Nom du script Python de test
    include $(shell cocotb-config --makefile)/Makefile.sim

L’exemple est un composant permettant de ne pas compter les rebonds d’un bouton comme des appuis successifs.
Le script de test en Python se trouve dans le dépôt Git du projet et se nomme test_buton_deb.py. Pour le lancer, il suffit de se rendre dans le répertoire blp/verilog/cocotb/ et de taper make :

     $ make
     [...]
     0.00ns INFO     Running test!
     0.00ns INFO     freq value : 95000 kHz
     0.00ns INFO     debounce value : 20 ms
     0.00ns INFO     Period clock value : 10000 ps
     0.02ns INFO     Reset complete

Un fichier de traces (chronogrammes) button_deb.vcd au format VCD est créé. Il peut être visionné en « temps réel » alors même que la simulation n’est pas terminée, grâce au visualiseur gtkwave :

   $ gtkwave button_deb.vcd
Vue de traces VCD avec GTKWave

Une organisation qui tourne

Le projet CocoTB est chapeauté par la FOSSi foundation qui fournit le « chef de projet » Philipp Wagner ainsi que des moyens financiers pour faire tourner des machines virtuelles de tests ainsi que pour payer les licences des simulateurs commerciaux.

Les statistiques de modification de cette version sont les suivantes :

  • 346 fichiers modifiés, 14 012 insertions (+), 10 356 suppressions (−) ;
  • 554 commits ;
  • 31 contributeurs ;
  • 2 nouveaux mainteneurs : Colin Marquardt et Kaleb Barrett.

Ces chiffres montrent que CocoTB est un projet qui fédère désormais une grosse communauté. C’est un projet mature qui compte dans le paysage des logiciels libres pour le matériel (FPGA et ASIC).

EOS S3, le bitstream libéré !

[Dépêche initialement paru sur LinuxFR]

Pour configurer les différentes connexions des blocs de logiques contenus dans un FPGA il faut lui fournir un fichier de configuration appelé «bitstream». Quand on parle de libération des FPGA, on pense principalement à la publication de ces spécifications .
Jusqu’à présent, cette « libération » s’est faite, pour une poignée de FPGA (majoritairement Lattice), par ingénierie inverse. Donc jamais à l’initiative du constructeur, ce dernier n’ayant même pas toujours connaissance de projet d’ingénierie inverse à destination de ses produits. Et il faut aller fouiller dans d’obscurs fils Twitter et autre forums de bidouilleurs pour les découvrir.

Mais la libération s’accélère, et une petite société peu connue dans le monde du FPGA vient de lancer un produit basé sur des outils opensource pour le développement : l’EOS S3.

Comme on peut le voir dans le diagramme bloc ci-dessous, le produit est en fait un microcontrôleur Cortex-M4 qui possède une zone périphérique «de FPGA» appelé eFPGA.

EOS S3 Block Diagram

La société Quicklogic a considéré que développer des logiciels de synthèse et de placement routage n’était pas son métier. Elle s’est donc « contenté » de l’adapter aux logiciels open source de la suite symbiflow.

Pour la première fois dans l’histoire des FPGA, nous avons donc une société qui affirme documenter son « bitstream » et qui propose des outils libres pour le développement. C’est un événement que beaucoup attendaient depuis des dizaines d’années !

Bon le (tout petit) FPGA ne concerne qu’une partie du composant. Mais c’est un bon début, et l’utilisation de logiciels libres reste la philosophie de la société pour le développement de ce produit. Comme dit dans les avantages de la fiche marketing du kit de développement :

« No more multi Gigabyte software installs, no more of the hassles associated with proprietary tools, no more vendor-specific hardware incompatible with the industry. »

eFPGA

La datasheet nous dit que la partie FPGA (celle qui nous intéresse ici) est composée de :

  • 891 cellules logiques
  • 8 blocs de RAM double ports de 8Kbits
  • 2 multiplieurs câblés de 32×32 bits
  • 32 I/O configurables

Alors certes, on est très très bas dans la gamme des FPGA du marché. Mais on peut déjà envisager faire des petites choses intéressantes avec. Surtout qu’il n’y a pas que le FPGA dans ce microcontrôleur.

Kit de développement QuickFeather

Le kit est encore en phase de lancement, même s’il semble que certaines développeuses aient déjà reçu la carte pour faire des tests. Le tarif de $50 n’est pas prohibitif pour en envisager l’acquisition à des fin de tests. Les frais de port de $80 par contre posent problème, surtout s’il faut ajouter des frais de douane.

Bref, ça n’est pas du vaporware puisque les composants existent, mais il est pour l’instant difficile d’en dire plus concernant les outils. Dans tous les cas une nouvelle très rafraîchissante, et une accélération de la libération des FPGA qui fait plaisir !

[Édition le 17 juin 2020]

L’entreprise qui est derrière ce nouveau produit est Antmicro. Une entreprise qui fait de la conception FPGA/ASIC à base de logiciel libre.
Il semble également qu’ils aient été aidé par google.

Le communiqué de Antmicro.

Et en plus du EOS S3, Quicklogic lance une gamme de FPGA «discret» : le PolarPro 3E. Également basé sur une chaîne de développement libre \o/

La libération des FPGA et des ASIC bien engagée pour 2020

[Dépêche publiée initialement sur LinuxFR.]

En début d’année 2019 se posait la question de savoir si ce serait l’année de la libération des FPGA. En ce début d’année 2020, essayons de faire un bilan.

FPGA, ASC, HDL, RISC‑Ⅴ et PCB sont les chapitres que nous allons découvrir dans la suite de cet article. Si vous connaissez déjà ces sigles et acronymes, vous allez adorer ; mais si vous ne les connaissez pas, c’est indispensable car ces vocables sont à la base de la culture universelle de notre siècle.

Nous sommes actuellement arrivés à un moment clé pour le matériel informatique. Il en est au même point que le logiciel libre en était en 2000, quand il est devenu mature. Le mouvement est lancé et les projets deviennent utilisables. On ne rêve plus…


FPGA

À condition de choisir son FPGA cible, il est aujourd’hui possible de faire son développement intégralement à base de logiciels libres. Tout cela principalement grâce à Yosys et Nextpnr.

Les grandes avancées de Yosys

Yosys est un logiciel libre de synthèse [[Verilog]]. Il permet de convertir un modèle Verilog en une netlist. La netlist est tout simplement un schéma électronique comme on peut en faire avec un logiciel de saisie de schéma. On relie entre eux des connecteurs d’entrées‐sorties de composants pour réaliser un circuit électronique.

Cependant, en général, un logiciel de synthèse cible des FPGA ou des ASIC qui ont leurs propres bibliothèques de composants. Et la netlist générée est au format texte, même si une fonction de Yosys permet d’afficher le « schéma » au moyen de Graphviz.

Yosys augmente le nombre des FPGA officiellement pris en charge avec les FPGA de Gowin. L’ingénierie inverse du Gowin n’est pas encore terminée mais elle est déjà utilisable. C’est tout le travail de Pepijn De Vos avec son Project Apicula.

Plusieurs gammes de FPGA de Lattice sont désormais prises en charge. En plus du ICE40 initial, les ECP5 sont maintenant parfaitement utilisables et les nouveaux CrossLink (Nexus) sont en cours de « reverse engineering » (rétro‑ingénierie, voir ci‑dessous) avec le Project Oxide de David Sha.

Hormis la partie placement routage et bitstream, les FPGA de la série 7 de Xilinx sont assez bien gérés par Yosys (mais Yosys ne fait pas le placement‐routage). Et Google a fait un petit cadeau à la communauté libre en annonçant financer la prise en charge des (pas si) vieux Spartan3 et Spartan6.

NextPnR, le placement‐routage libre

Nextpnr est un logiciel libre permettant de faire le [placement‐routage(https://fr.wikipedia.org/wiki/Placement-routage). Le principe est assez simple, un FPGA disposant d’une matrice de composants gravés sur la puce, il faut décider quel composant de la netlist générée par le logiciel de synthèse ira sur quel composant présent dans le FPGA. Une fois les composants placés, il faut router les entrées‐sorties en réalisant les connexions.

Nextpnr est aujourd’hui parfaitement utilisable pour les FPGA ICE40 et ECP5 de Lattice. Pour les FPGA de Gowin, cela ne saurait tarder à mon avis.

Rétro‑ingénierie

Pour configurer un FPGA (établir les liens entre les bascules) il faut télécharger un bitstream. Le format de ce bitstream n’est documenté par aucun constructeur de FPGA. Nous sommes obligés de passer par les outils (gratuits, en général) fournis par le constructeur pour le générer.
Bien que n’étant pas documenté, le format n’est pas non plus chiffré, il est donc parfaitement possible de l’étudier par ingénierie inverse pour le documenter.
De plus en plus de projets de FPGA par ingénierie inverse de bitstream voient le jour. Votre serviteur tente de maintenir une liste de ces projets sur son blog en donnant l’état d’avancement des projets.
On décompte au moins neuf projets plus ou moins avancés de rétro‑ingénierie :

  • icestorm : les ICE40 de Lattice ;
  • X-Ray : la série 7 de Xilinx : Artix7, Spartan7 et Virtex7 ;
  • prjoxide : les CrossLink‑NX de Lattice ;
  • rodinia : les CPLD AGM ;
  • mistral : le Cyclone Ⅴ d’Intel (anciennement Altera) ;
  • Apicula : les GW1N de Gowin ;
  • OpenFpga : un mélange de CPLD de différentes marques GreenPAK4, CoolRunner Ⅱ, PSoC 5LP (Silego, Xilinx et Cypress) ;
  • Trellis : les ECP5 de Lattice ;
  • prjbureau : les ATF1502AS de Microchip.

Notons que la marque Lattice est très représentée, alors que Microsemi est absent (à ma connaissance) de ces projets.

ASIC

Les ASIC ne sont pas des FPGA. Une fois que l’on a envoyé nos fichiers de production au fondeur, les composants ne sont plus modifiables. Et comme la facture est en général particulièrement salée pour produire une série, il faut en produire beaucoup et surtout ne pas se planter.

Une (vénérable) suite de logiciels libres appelée QFlow existe depuis plus de trente ans pour concevoir ces circuits intégrés spécialisés. Mais le site officiel fait particulièrement peur, et laisse croire que le logiciel est à l’abandon depuis bien longtemps.
Il n’en est rien, ce logiciel est toujours maintenu et est utilisé par de plus en plus de concepteurs ASIC pour produire des puces libres. On pense notamment au Raven à base de PicoRV32 (RISC‑Ⅴ) qui avait été décrit dans les colonnes de LinuxFr.org. On pense également au projet de FPGA libre kFPGA décrit également dans ces colonnes.

Un autre composant à destination des amateurs de rétro‑informatique est en cours de production par Staf Verhaegen avec le projet Chip4Makers. L’idée de Staf est que la production de composants ASIC coûte très cher à l’unité, il n’est donc pas possible de concurrencer les composants du marché avec un composant conçu « dans son garage ».
Cependant, il existe une frange de hobbyistes prête à payer plus cher pour retrouver leur vieux processeur 6502 ou Z80. Ce sont donc ces processeurs que Staf a inclus dans un unique composant, et la pré‑série a été produite d’après un de ses tweets. Les sources du composant en question sont disponibles sur sa projet GitLab.

D’autres instituts et fondations s’intéressent de très près à l’émergence d’outils libres pour réaliser des microprocesseurs et ASIC. On pense notamment à :

  • DARPA, qui finance le projet OpenRoad ;
  • l’université de Zurich et son projet PULP ;
  • l’université de Barcelone, qui a annoncé la sortie prochaine d’un processeur RISC‑V libre.
  • l’université Paris Ⅵ, qui fait bien trop peu de publicité de sa suite libre Alliance (synthèse [[VHDL]], pour faire des ASIC) — Mais pourquoi ce projet est-il si peu connu ?

HDL (Hardware Description Languages)

Yosys était jusqu’ici réservé à la synthèse Verilog. Mais grâce au travail de Tristan Gingold et Pepijn De Vos (principalement), il est désormais possible d’utiliser Yosys en conjonction de GHDL pour faire de la synthèse GHDL. Le projet est encore en beta‑test, mais Pepijn s’en sert pour faire de la synthèse TTL de ses porte‑grammes VHDL ainsi que de la vérification formelle.

Principalement grâce à Yosys, il est désormais tout à fait possible de faire de la vérification formelle pour valider ses composants. C’est le cheval de bataille de Dan Guisselquist, avec son projet de processeur nommé ZipCPU.

Le langage de haut niveau Chisel est maintenant relativement mature. Le projet fait partie de la fondation Linux et la conférence annuelle CCC (non pas Chaos Communication Camps mais Chisel Community Conference) est soutenu par des gros industriels comme Western Digital ou Cadence.
Toute la gamme des processeurs développés par SiFive est écrite avec Chisel, Google a utilisé le langage Chisel pour son processeur d’intelligence embarqué Edge TPU.

Le langage nMigen basé, lui, sur Python essaime aussi pas mal, mais surtout dans le milieu de la recherche.

CλaSH est sortie en version 1.0. Cela faisait des années qu’il se traînait avec des version 0.x, le passage à 1.0 est un signe de maturité. CλaSH est basé sur le langage au paradigme fonctionnel [[Haskell]]. Je ne peux hélas pas vous en dire plus aujourd’hui car je n’ai par réussi à percer le secret de cette logique de matheux qu’est le paradigme fonctionnel. :)

Cocotb a désormais un vrai rythme de développement et est utilisé en production pour de « grosse » IP comme l’USB. La version 1.3 est sortie en ce début d’année. Cocotb est un module Python permettant d’écrire des bancs de test HDL. Cocotb a la particularité de se connecter à un simulateur « du marché » pour lire et écrire les valeurs de signaux. Cela permet de garder son simulateur HDL parfois acquis à grands frais.

Verilator, le simulateur Verilog le plus rapide du « marché » (plus rapide que tous les simulateurs commerciaux) continue à être activement développé. Les récents commits permettent aujourd’hui de l’utiliser avec Cocotb. Et son passage à la version 4.0 permet une pleine utilisation des multiples cœurs de nos PC actuels, améliorant encore ses performances.

RISC‑Ⅴ

On peut aujourd’hui dire sans sourcilier que l’année de libération des processeurs est passée grâce au jeu d’instructions RISC‑Ⅴ.
Il n’est plus nécessaire de présenter ce jeu d’instructions aujourd’hui, et nous pouvons nous procurer tout un tas de microcontrôleurs basés sur RISC‑Ⅴ pour une somme d’argent (plus ou moins) modique.
Voici une petite liste de microprocesseurs RISC‑Ⅴ disponibles sur le marché :

Hormis l’U540 et, dans une certaine mesure, le K210, tous ces processeurs sont des microcontrôleurs orientés basse consommation. La question qui est sur toutes les lèvres aujourd’hui, c’est : RISC‑Ⅴ va‑t‑il percer dans le monde du serveur et du calcul parallèle ?

Circuits imprimés

Kicad est un logiciel de conception électronique pour fabriquer des circuits imprimés, également appelés PCB. C’est un logiciel initialement développé par un français (cocorico) qui inclut toute la suite de logiciels nécessaires à l’électronicien :

  • la schématique ;
  • le routage ;
  • et même maintenant la simulation de la gestion des coûts en composants (BOM) ;
  • etc.

Kicad est longtemps resté un logiciel anecdotique (mais parfaitement fonctionnel), jusqu’à ce que le CERN s’y intéresse et finance des ingénieurs pour améliorer la partie routage. Aujourd’hui, Kicad est soutenu par la Fondation Linux et a lui aussi sa conférence annuelle prestigieuse : la KiCon.

Ils sont emprisonnés depuis trop longtemps, mais nous ne les avons pas oubliés !

Pour conclure, nous pouvons affirmer que la libération des FPGA est maintenant bien engagée. Et nous assistons aujourd’hui à l’émergence du matériel libre du point de vue du cœur de la puce : le silicium.
La liberté dans ce monde stagnait depuis des dizaines d’années, mais les choses décollent aujourd’hui. Et on entend le même refrain contre le Libre que l’on entendait dans les années 2000 sur le logiciel. Pour quelqu’un qui chercherait un projet libre sur lequel se lancer pour faire ses armes, comme pour la conquête de l’ouest, l’espace est encore vierge et c’est le moment de se lancer.

Tang Nano, déballage

Sipeed continue dans sa course à l’échalote des kit FPGA low cost en proposant un kit Gowin à $4.90. Évidemment à ce prix là c’était trop tentant d’en prendre un. Bon en vrai vu que les frais de port ne sont pas négligeable j’ai également pris l’écran proposé et je m’en suis finalement sortie pour une vingtaine d’€. Ce qui reste néanmoins raisonnable.

Le petit kit Tang Nano à $4.90

Le kit est fourni avec des headers males (pattes) non soudés. Ils ne sont pas nécessaire pour faire clignoter la LED ou pour jouer avec l’écran, mais c’est quand même utile.

Le dessous de la carte avec le pinout.

Premier boulot en recevant le truc donc : souder les headers.

Pour 13$ de plus on a l’écran compatible avec le connecteur

Le FPGA soudé sur la carte est un GW1N-LV1, assez petit donc, mais il reste raisonnable puisque de la même taille que le ice40 soudé sur le icestick. C’est d’ailleurs le kit utilisé actuellement par Pepijn de Vos son projet d’ingénierie inverse nommé Apicula (mais chuuut le projet n’est pas encore public !).

Le branchement se fait au moyen d’un câble USB-C non fourni. Au premier branchement, la LED rouge qui semble être celle de l’alimentation s’allume et la led RGB du centre se met à clignoter en allumant les trois couleurs à la suite.

Pimp my blinker !

Les messages noyau m’affichent le traditionnel double tty typique d’un convertisseur USB-Série habituel (CH552T, un microcontrôleur chinois):

$ sudo dmesg -c
[365812.686837] usb 3-2: new full-speed USB device number 25 using xhci_hcd
[365812.838484] usb 3-2: New USB device found, idVendor=0403, idProduct=6010, bcdDevice= 5.00
[365812.838490] usb 3-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[365812.838492] usb 3-2: Product: Sipeed-Debug
[365812.838494] usb 3-2: Manufacturer: Kongou Hikari
[365812.838496] usb 3-2: SerialNumber: 85522A1A47
[365812.840468] ftdi_sio 3-2:1.0: FTDI USB Serial Device converter detected
[365812.840534] usb 3-2: Detected FT2232C
[365812.841192] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB0
[365812.841373] ftdi_sio 3-2:1.1: FTDI USB Serial Device converter detected
[365812.841427] usb 3-2: Detected FT2232C
[365812.841727] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB1

On remarquera que cette fois le numéro de série n’est pas en chinois 😉

La connexion au ttyUSB0 (en 115200) fournie un echo du clavier un peu bizarre :

�n�a�u�r�s�i�t�e�n�a�s�u�t�i�e�n�a�s�u�t�i�e�n�s�a�u�t�i�e�n�r�a�s�u�t�i�e�n�r�s�a�u�t�i�e�n�r�s�a�t�u�i�e

Et le ttyUSB1 semble ne pas «fonctionner».

Il est fort probable que le kit soit entièrement utilisable avec des logiciels libre à Noël lors de la grand messe allemande : le Chaos Communication Congress à Liepnitz.

Pour le moment nous allons nous contenter de l’IDE chinois fourni, que j’avais déjà installé pour le little bee. Pour le code, il y a des exemples fournis sur le github de sipeed. Pour la documentation c’est par ici. Et comme d’habitude avec les trucs chinois, quand la doc en anglais semble trop limitée, ne pas hésiter à aller faire un tour sur la version chinoise à coup de google traduction.

Trucs:

Si le floorplanning ne veut pas se lancer c’est qu’il faut bien configurer sa variable LD_LIBRARY_PATH avant de lancer l’appli:

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/flf/myapp/gowin/IDE/lib
$ ./gw_ide -gui

Ressources

Nano board pinout (blog)

Le point Gowin

Oui je sais c’est nul 😉

Arrivée dans un énorme carton, la carte électronique se trouve dans le tout petit «tube» blanc.

Je viens donc de recevoir ma carte petite abeille (littlebee) munie d’un FPGA du chinois GOWIN.

La carte «LittleBee» munie d’un FPGA de chez Gowin

La carte produite et vendue par la société allemande Trenz Electronic permet de se faire la main avec le composant pour moins de 40€ (un peu plus avec les frais de ports UPS …).

Branchement

Au branchement à la sortie du carton les huit leds rouge s’allument ainsi qu’une led verte que je suppose de «power».

Les messages noyau nous donnent deux ports séries ttyUSBx :

$ dmesg
[630417.919258] usb 3-1: new high-speed USB device number 35 using xhci_hcd
[630418.059577] usb 3-1: New USB device found, idVendor=0403, idProduct=6010
[630418.059581] usb 3-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[630418.059583] usb 3-1: Product: Dual RS232-HS
[630418.059584] usb 3-1: Manufacturer: FTDI
[630418.060116] ftdi_sio 3-1:1.0: FTDI USB Serial Device converter detected
[630418.060155] usb 3-1: Detected FT2232H
[630418.060352] usb 3-1: FTDI USB Serial Device converter now attached to ttyUSB0
[630418.060499] ftdi_sio 3-1:1.1: FTDI USB Serial Device converter detected
[630418.060528] usb 3-1: Detected FT2232H
[630418.060648] usb 3-1: FTDI USB Serial Device converter now attached to ttyUSB1

Si on se connecte au port série ttyUSB1 on obtient un affichage de la résolution du problème des philosophes.

Philosopher 0 [P: 3] THINKING [ 750 ms ]
Philosopher 1 [P: 2] HOLDING ONE FORK
Philosopher 2 [P: 1] EATING [ 450 ms ]
Philosopher 3 [P: 0] STARVING nabled
Philosopher 4 [C:-1] HOLDING ONE FORK get back,
Philosopher 5 [C:-2] EATING [ 375 ms ]

Il est probable que nous ayons ici un RISC-V dans le tiroir.

La connexion d’un terminal sur le port ttyUSB0 ne donne rien par contre.

IDE

Voila pour le déballage, maintenant il va falloir installer les outils pour faire clignoter ces leds !

[To Be Edited …]

Un hack pour intégrer Wavedrom dans LibreOffice

Wavedrom est un outils magique pour générer de très beaux chronogrammes à partir d’une base texte (JSON). Il existe un outils en ligne de commande pour générer des rendu en SVG ou PNG. Cependant, Wavedrom reste très lié au web, pas facile de l’intégrer dans un document wysiwyg comme libreoffice.

On peut bien sûr générer l’image puis l’intégrer à son document, mais cela éparpille très vite le nombre de fichiers source à gérer. Or, un des intérêt d’un document libreoffice est d’inclure toute les sources permettant de générer et modifier le document.

L’idéal serait d’avoir un plugin Libreoffice pour wavedrom, mais pour l’instant cela n’existe pas.

Krispy propose une solution/hack sur la mailing list de wavedrom.

Cette solution nécessite d’avoir un accès web et de faire son chronograme avec l’éditeur en ligne de wavedrom. Cet éditeur permet de «stocker» la description du chronograme dans l’URL. Il suffit pour cela de cliquer sur le menu sandwich en bas à droite et de sélectionner «expand url» pour avoir le contenu du chronogramme dans l’url comme ceci.

Il n’est pas utile de comprendre ce qui est écrit dans l’url, il suffit de cliquer dessus pour avoir le texte «lisible».

Pour l’intégrer à son document libreoffice il suffit de:

  • générer l’image dans le format de son choix avec l’éditeur en ligne
  • de l’intégrer à son document libreoffice
  • Puis de faire un lien web sur l’image avec l’url complète contenant le source du chronograme.

De cette manière, le source du chronogramme est bien embarqué dans le document. Il faudra certe refaire une manip légèrement fastidieuse à chaque modification, mais nous avons tout de même une solution viable.


C’est évident quand on y pense, mais très piégeux :

        if(state == OPU_RSC || (state == OPU_WSTR))
            if(timetick_pulse) begin
                pwr_counter <= pwr_counter + 1;
            end
        else
            pwr_counter <= 0;

On pense que le else se rapporte au premier if … et bien non !

Il faut écrire :

        if(state == OPU_RSC || (state == OPU_WSTR)) begin
            if(timetick_pulse) begin
                pwr_counter <= pwr_counter + 1;
            end
        end else
            pwr_counter <= 0;

Voila voila, si on peut vous éviter des heures de déverminage inutiles c’est cadeaux 😉

Un ASIC conçu intégralement avec des logiciels libres

Les FPGA sont très liés aux ASIC. En effet, la plupart des outils utilisés en FPGA pour la synthèse HDL, la preuve formel, le placement routage ou l’analyse des timings sont les même que ceux à destination des ASIC. Seuls les librairies et les configurations changent. La grosse différence (de taille) avec les FPGA c’est que l’ASIC n’est pas reconfigurable, et les «frais d’initialisations» sont très élevés. Les délais de productions sont très long également (on parle en trimestre voir en semestre de délais).

Avec de telles contraintes, on comprend pourquoi les développeurs ne se mouillent pas trop avec des logiciels exotiques et restent sur ceux qu’ils connaissent. Vu les tarif de production, le coût des licences des logiciels est assez négligeable. Pourquoi «grenouiller» avec des outils open-source dans ce cas ?

Vue «silicium» du Raven, un microcontrôleur Risc-V conçu avec des outils open-sources

Toutes ces contraintes n’ont pas découragé Tim Edwards de se lancer dans la conception et la fabrication d’un microcontrôleurs intégralement avec des outils open-sources.

Synoptique du Raven avec ses différents périphériques

C’est comme cela qu’est né le Raven, un microcontrôleur basé sur un cœur picoRV32 (conçu par Clifford Wolf) et réalisé principalement avec les outils qflow d’opencircuitdesign.com :

Grande surprise quand on se plonge dans ces outils open-source : Beaucoup sont très vieux. Les pages web de ses outils sont encore codé en web95 avec des frames et autre fonds hideux datant de l’époque frontpage.

Pourtant à y regarder de plus prêt, ces outils semblent toujours activement maintenus.

Mais alors pourquoi aucun fondeur FPGA ne les proposent dans leurs IDE ?

Une première série du microcontrôleur gravé en 180nm a été produite en mai 2018. Le composant est désormais fonctionnel avec les caractéristiques suivantes:

  • Cadencé à 100 MHz
  • 16 GPIO
  • 2 ADCs
  • 1 DAC
  • 1 Comparateur
  • Alarme de température
  • Oscillateur RC de 100 kHz
  • Fonction configurables pour les sorties GPIO
  • Interruptions configurable sur les entrées GPIO

Il n’est pas possible d’acheter le composant pour se faire un montage chez soit pour le moment. Par contre l’«IP» est disponible dans la bibliothèque du fondeur efabless et peut être utilisé comme base pour réaliser son propre composant selon les besoins.

Les traces VCD se compressent bien

La simulation HDL génère assez vite des traces (waves) de plusieurs centaines de méga-octets, voir des dizaines de giga.

Ces traces sont le plus souvent au format VCD, qui n’est qu’un fichier texte répétant les même schéma au court du temps. Ces fichiers ayant énormément de redondance, ils se compressent très bien.

Si je prend par exemple un fichier vcd messignaux.vcd faisant ~500Mo :

$ ls messignaux.vcd
-rw-r--r-- 1 fabien fabien 504M avril 3 10:17 messignaux.vcd

Je compresse avec zip en environ 30 secondes:

$ zip -c messignaux.zip messignaux.vcd
adding: messignaux.vcd (deflated 89%)
$ ls -lha messignaux.zip
-rw-r--r-- 1 fabien fabien 56M avril  5 14:16 messignaux.zip

Et je réduis mon fichier à 56Mo soit un rapport de presque 10.

Avec gzip on gagne la même chose à une queue de vache près :

$ gzip messignaux.vcd
$ ls -lha messignaux.gz
-rw-r--r-- 1 fabien fabien 57M avril  3 10:17 messignaux.vcd.gz

Et avec xz alors là on réduit environs à 20 fois plus petit, … mais il faut plus de 5 minutes à xz pour faire la compression:

$ xz -z messignaux.vcd
$ ls -lha messignaux.vcd.xz
-rw-r--r-- 1 fabien fabien 27M avril  3 10:17 messignaux.vcd.xz

Bref, si vos traces de simulation prennent trop de place sur votre pc, n’hésitez pas à les compresser.