Category Archives: programmation

Utiliser VMPK et Cardinal avec PIpeWire, Jack et les baies de brassage Helvum et Qpwgraph

Sommaire
VMPK, clavier de piano MIDI virtuel
Cardinal, fork de VCV Rack, synthétiseur modulaire Eurorack
PipeWire et les baies de brassage
Petit ensemble simple sous Cardinal

Arch Linux, ainsi que de nombreuses autres distributions Linux, ont définitivement basculé la pile audio sous PipeWire. Celui-ci à l’avantage de centraliser de façon simple Jack (temps réel pour l’audio de qualité studio, nécessite des réglages en fonction des besoins) et PulseAudio (plus simple, pour l’utilisation en informatique de bureau ou de jeu). PipeWire, permet aussi de synchroniser plusieurs sources et destinations video de différentes tailles et format, en même temps que le son. Les baies de brassage Helvum et Qpwgraph permettent d’établir simplement à la souris les connexions. QJackCtl ne semble plus fonctionner pour cette tâche.

VMPK, clavier de piano MIDI virtuel

VMPK (Virtual MIDI Piano Keyboard, signifiant clavier de piano MIDI virtuel) est un contrôleur MIDI logiciel, permettant d’utiliser le clavier de l’ordinateur, la souris ou autre dispositif de pointage (tablette, écran tactile) sur un clavier de piano logiciel, et d’envoyer des signals MIDI soit via l’interface MIDI du système, soit aux synthétiseurs logiciels General MIDI FluidSynth (paquet fluidsynth) ou SoniVoxEAS. Les distributions Linux contiennent des fontes sonores libres compatibles General MIDI, (sous Arch, paquet soundfont-fluid, complet, mais de qualité moyenne et freepats-general-midi, moins étendue en nombre d’instruments, mais de bien meilleure qualité). Si vous avez besoin de sélectionner leur emplacement, ces fontes sont placées dans le dossier /usr/share/soundfonts/ après installation. Le site de Freepats regroupe également des fontes spécialisées par instruments d’encore meilleures qualité, mais dont le poids des fichiers sont beaucoup plus grands.

réglage de la fonte midi ou de la sortie MIDI

Pour choisir le mode synthétiseur logiciel ou MIDI system, en suivant l’image ci-dessus :
* 1. Menu Éditer -> Paramètres MIDI
* 2. choisir dans le champs Pilote MIDI OUT, choisir soit Alsa pour le signal MIDI système, soit FluidSynth pour le synthétiseur MIDI logiciel permettant de produire directement les sons.
* 3. Dans le cas de FluidSynth, si c’est votre premier réglage, il faut cliquer sur le bouton [...]; à droite du bouton-menu ou vous avez sélectionné FluidSynth dans le champs Paramètres MIDI OUT.
La fenêtre Paramètres du pilote FluidSynth s’ouvre alors, il est possible dans le champs Pilote Audio de choisir Alsa, Jack, PulseAudio, SDL et d’autres.
# Dans le champs Soundfont, vous pouvez choisir la fonte sonore en ouvrant un sélecteur de fichier avec [...] ou de taper le chemin directement. Comme dis précédemment, les fontes sont placées par défaut dans le dossier /usr/share/soundfonts/.

Cardinal, fork de VCV Rack, synthétiseur modulaire Eurorack

Voici un aperçu des nombres plugins du synthétiseur modulaire Cardinal, il s’agit d’un fork, par le projet DISTRHO (dont le but est de regrouper les systèmes de plugins audio) de VCV Rack, intégrant de base tous les modules sous license libre en un seul paquet, et comme vous pouvez voir sur cette vidéo de la version d’avril 2022, ils sont nombreux :

VCV Rack est une version logicielle de synthétiseur modulaire au format standardisé ouvert de modules de synthétiseurs analogiques, Eurorack créé par l’allemand Dieter Döpfer1. Il est d’ailleurs possible d’interfacer, notamment via MIDI, des modules matériel de ce format. Les échanges s’y font principalement par un signal électrique analogique en utilisant des câbles avec connecteurs jack, mais les modules peuvent faire des traitements numériques ou utiliser des connexions numériques de type MIDI ou différents formats audio comme la connexion optique S/PDIF. Certains modules logiciels de VCV Rack sont d’ailleurs fait par des créateurs de modules matériels, à l’image des auteurs des excellents modules Mutable Instruments qui en ont porté certains sous le nom de Audible Instruments ou encore de Befaco. Ceux-ci comportement notamment de la synthèse granulaire et de la simulation numérique de sons. La version Cardinal supporte également différents formats de plugins audio LV2, VST, etc..

Je maintien les paquets Arch AUR de VCVRack-git en version 1.x qui ont l’avantage de pouvoir être utilisé avec ALSA directement (plus simple que Jack) et de comporter directement un module de saisie au clavier. Ce que ne propose pas encore Cardinal, mais cela devrait arriver ce mois-ci avec l’ajout des modules Fundamentals.

À ce propos, David Louapre, l’auteur de la chaîne Youtube de vulgarisation scientifique « Science étonnante » à fait une vidéo explicative des principes des synthétiseurs analogiques et modulaires intitulée « La Science des synthétiseurs ».

PipeWire et les baies de brassage

La baie de brassage Helvum permet de gérer de façon graphique et interactive, les connexions des différents éléments audio ou vidéo au sein de PipeWire, il est bien évidement également possible de les gérer par des programmes ou scripts directement dans PipeWire.

Helvum affiche les différents éléments sous forme de boîte et les liaisons via des courbes ou fils. D’après ce que j’en ai compris, les courbes en pointillées montrent les connexions automatiques de la partie PulseAudio, et les courbes pleine, montrent les connexions manuelles pour Jack.

Pour ajouter une connexion, il suffit de cliquer sur une boîte, puis de déplacer le petit icône de fichier affiché vers l’autre point de liaison. Voici comment faire dans le cas de VMPK avec Cardinal et de Cardinal avec la sortie audio :

Qpwgraph, à l’avantage de détecter d’avantages de choses. Il a notamment détecté VokoscreenNG que j’ai utilisé ici pour la capture vidéo du bureau. Mais ça n’est pas un problème, celui-ci ayant permis de sélectionner l’entrée audio sans doute via pulseaudio à utiliser. Il comporte également des annuler/refaire, et l’ouverture et sauvegarde de la configuration en fichier, ce qui peut rapidement devenir indispensable.

Voici donc les manipulations équivalentes sous Qpwgraph :

À noter que dans cet vidéo j’avai une sortie MIDI de VMPK, mais dans un autre cas, comme sur cette capture d’écran, je ne l’avais pas. VMPK étant connecté au bridge (passerelle) MIDI du port 0, il suffit alors de connecter la sortie de la passerelle MIDI du port 0 pour avoir le même résultat.

Utilisation du bridge MIDI du port 0 pour récupérer la sortie de VMPK

Petit ensemble simple sous Cardinal

Voici un exemple simple d’ensemble fonctionnant avec VMPK et cette configuration. Cela permet de comprendre le fonctionnement de base de Cardinal/VCV Rack.

Les sorties de contrôle MIDI sont récupérées vers les entrées de contrôle du rack « Modal Synthesizer » d’Audible Instrument dont j’aime bien la sonorité, proche de carillons :
* La sortie V/Oct (volt/octave) vers l’entrée V/Oct. Elle donne la hauteur de la note en fonction de la tension électrique (ici virtuelle).
* La sortie Gate (porte) vers l’entrée Gate. Elle transmet le moment où une touche est pressée (signal 0 relâchée ou 1 pressée). Il est intéressant de noter dans ce montage que lorsque la touche suivante est pressée avant que la touche précédente ne soit relâché on a un glissendo, plutôt qu’une nouvelle percussion de la note avec attaque initiale.
* La sortie velocity (vélocité) permet de donner de l’expression au son en variant l’attaque, sur un clavier texte AZERTY d’ordinateur, il n’y a pas ce genre de paramètres, mais il est possible de le lier à l’entrée strength (force), du module, et de lui transmettre ainsi l’expression du touché.

Pour la sortie du module d’Audible Instrument, c’est simple, la sortie gauche (L comme left) vers le port de sortie audio 1 et la sortie droite (R comme right) sur le port audio 2, comme nous avions définit dans la baie de brassage. Il ne reste plus qu’à presser les touches du clavier virtuel pour entendre les notes et tourner les molettes pour modifier les effets.

Comme on peut le voir sur cette vidéo, la saisie directe du clavier comme le permettent les modules Fundamentals ou un veritable instrument midi sont tout de même plus pratique pour ne pas avoir à passer sans arrêt de l’un à l’autre. Mais cela permet déjà de faire des enregistrement ou contrôle simple. Différents outil comme Qjackctl permettent d’enregistrer une séquence MIDI puis de la retransmettre. Il est aussi possible de passer un morceau midi en fichier .MID dans la passerelle MIDI et de lui ajouter des effets avec Cardinal et/ou des nombreux modules au format LV2 ou VST.

Principe de synthétiseur analogique modulaire

Les synthétiseurs analogiques modulaires suivent le principe similaire à UNIX, du KISS (keep It Simple and Stupid), garder les choses simples et stupides. Les modules sont très simples, et la force du système réside dans la possibilité d’échanger entre ces différents éléments. Les échanges se font principalement via des câbles électrique via un courant qui peut varier en tension (en Volt) de façon analogique (continue), plutôt que numérique (discrète). Il est tout de même possible de transmettre un signal numérique (1 ou 0) et c’est ce que fait la fonction gate (porte).

Les différents modules de base sont généralement (je donne les termes anglais et leurs abréviations utilisée, ainsi que la traduction du terme en français entre parenthèses :
* VCO Voltage Controled Oscillator (oscillateur commandé en tension), Il s’agit d’un générateur de signal, généralement sinusoïdal, comme le faisait le premier synthétiseur, le Thérémine, du nom de son inventeur, le Russe Lev Sergueïevitch Termen. Certains plus avancés permettent de fournir des singaux carrés, en dent de scie, différentes couleurs de bruits (blanc, rose, etc).
* VCF Voltage Controled Filter (filtre contrôlé en tension), permet de filtrer l’entrée en fonction de fréquences définies.
* VCA Voltage Controled Amplifier (Amplificateur commandé en tension). Permet d’amplifier le signal.
* ADSR Attack, Delay, Sustain, Release (Attaque, délai, soutient, relâchement. Il s’agit des paramètres standard d’enveloppe sonore, dont les principes ont été crées par Vladimir Ussachevsky. Pour faire une analogie avec les instruments acoustiques, L’attaque correspond à la montée au moment ou l’on actionne le sonophore de l’instrument. La corde frappée d’un piano ou pincée d’une guitare ou d’un clavecin. L’archer qui commence à frotter la corde, La percussion sur la peau d’un tambour, l’impulsion d’un trompettiste. Le délais est la durée de l’attaque, c’est à dire le délai de l’attaque initiale. le Sustain/soutien, et la longueur pendant la vibration produisant le son continue sur son palier. et le relâchement, l’arrêt du son.
* LFO Low Frequency Oscillator (Oscillateur basse fréquence), il est utilisé principalement pour les modulation de l’instrument, comme un trémolo ou une trille par exemple.
* Arpeggiator (arpégiateur). Il est destiné à produire une succession de note. Un accord correspond à plusieurs notes produites simultanément, et un arpège est son équivalent avec cet ensemble de notes décalées dans le temps. Par exemple les notes do mi sol do jouées successivement correspondant à un accord de do majeur. Cela permet de produire simplement des éléments de base de mélodies.

Pour aller plus loin

La page PipeWire du wiki d’Arch.

Quelques essais avec le moteur de jeu LÖVE en Lua

* This post is also available in English.

Dessiné avec Pencil2D et MyPaint, animé en utilisant un maillage dans le moteur de jeu LÖVE (enlangage Lua), J’ai également fait quelques essais en C avec libSDL, mais cela ma paraît plus rapide de prototyper en Lua avec LÖVE, quitte à ajouter des bibliothèques en FFI pour les parties ayant besoin de beaucoup de calcul (dans cet exemple, les calculs sont légers et GL est utilisé pour le rendu), ou de le porter en C/SDL dans un second temps. Tous ces logiciels sont des logiciels libres, utilisés sur un système GNU/Linux.

Ce premier exemple utilise un simple maillage sur une image, et la déforme en utilisant des mouvement circulaires, comme je l’avais fait sur TIC-80 en juillet 2021.

Voici l’image utilisée comme texture du maillage, réalisé avec la branche MyPaint de Pencil2D et affiné avec MyPaint lui même :

Voici une version modifiée avec un crâne rigide, comme il est anthropomorphe. Je garde les 2 versions de l’algorithme le premier restant intéressant pour représenter de façon plus réaliste des invertébrés.

Cet autre test agglomère différentes tests d’ombrage (shaders). J’applique un seuil sur certains, au dessus de ce seuil une couleur apparaît et en dessous une autre couleur, sans ce seuil il y aurait des dégradés. Cela permet d’avoir des effets 2 couleur dans un style très épurés. J’ai mis le code source de ces ombrages pour LÖVE à disposition sur Framagit (Fichier demo01.love depuis ce site).
.

Cet autre test utilise un maillage sur une image en 2 étapes pour la texture. Ces images sont interchangées permettant l’animation de la bouche. Contrairement à la première animation, il ne s’agit pas d’une déformation utilisant des fonctions sin() de façon relativement homogène et indépendante sur la courbe. Le maillage est courbé en incrémentant l’angle à chaque étape de sa construction. Une petite onde sinusoïdale est ajoutée pour simulé un déglutition au travers du corps, à la façon de serpents qui avalent une grosse proie.

Le liquide que le monstre rejette est une autre texture. Celle ci est copiée sur un Canvas (toile) intermédiaire, afin de permettre une rotation cyclique le long de la texture (comme un scrolling sur un ruban), Cette partie ma été inspiré par les boss de fin de niveau du jeu « Conan Chop Chop ». Mais j’ai amélioré un peu le principe : Un Ombrage est alors ajouté sur le canvas au moment dfe l’afficher à l’écran, il zoom le jet sur sa longueur et ajoute un légère onde sinusoïdale. L’éclaboussure à la fin est la superposition de plusieurs fois la même animation en 3 étapes faite sous Pencil2D.

Voici le monstre dessiné rapidement avec seulement 2 étapes, pour la mise à l’épreuve du concept. Cela pourrait être optimisé sur disque en n’ayant que les 2 têtes dans le fichier et en les dupliquant dans la texture au chargement de l’image, LÖVE ne me semble pas aussi pratique que libSDL pour les maillages texturés :
bidule spritesheet

L’image utilisée comme texture pour la partie sur laquelle est appliquée l’ombrage :
flux de vomit intense

L’éclaboussure au bout du jet, chaque partie est affiché séquentiellement en boucle, et a été animé à l’aide de Pencil2D (branche MyPaint):
Éclaboussure de vomit

L’animation est d’abord réalisée avec Pencil2D, puis sauvegarde avec une image (PNG avec transparence) par image temporelle, puis à l’aide de la commande « montage » du paquet ImageMagick assemblé dans une feuille de sprite de la façon suivante:

montage sprite0001.png sprite0006.png sprite0010.png -tile 3x1 -geometry 64x128 spritesheet.png

Mise à jour du 19 mars :

En se basant sur les mêmes méthodes, j’ai amélioré le code pour gérer des animations dessinées à main levée, de manière un peu plus générique, comme texture d’un maillage. J’ai donc fait un croquis animé de l’ouverture d’une fleur pour tester ce principe.

Fleur qui s'ouvre

Elle comporte 8 images distinctes, l’ordonnancement des images, dont le temps d’apparition peut varier pour la dynmique est gérée via une table comportant le nombre d’image temporelle par image :

table = {1,1,2,2,2,3,3,4,4,4,5,5,6,6,7,8,8,8,8,8}

Je peux cycler facilement dans la table en utilisant une fonction du type math.floor( (time()*fps) % #table) ou juste lire la table pour une animation unique, pulmonique a une fleur qu’il s’ouvre ici.

Je l’ai donc utilisé comme élément décoratif. Maintenant que je vois que cela fonctionne, l’animation peut être affinée, et le code également pour être encore plus générique et avoir le besoin d’ajouter de moins en moins d’information pour ajouter rapidement de nouvelles animations.

Après être satisfait du premier jet de l’animation au sein du programme, j’ai un peu plus nettoyé, amélioré les couleurs, et me suis aperçu qu’il manquait une frame dans mon animation, la dernière était dupliquée de l’avant dernière. Dans tous les cas, il faut que je supprime l’espace inutile et que je sépare la queue de la fleur, la feuille de sprite en png fait 180~200 Ko (150 Ko recompressée avec zopflipng), c’est trop gros, multiplié par le nombre d’image et d’objets on peut rapidement se retrouver avec des dizaines de Mo de PNG sur disque et encore pire en RAM, de quoi exploser tous les caches. Je pense donc que je vais séparer la fleur de sa tige, et essayer de la cadrer plus précisément, un 64×64 devrait faire l’affaire.

Fleur qui s'ouvre améliorée

 »’Mise à jour mai 2022  »’
* Vous pouvez tester la dernière version et voir les sources à l’adresse https://framagit.org/popolon/reforest
* Les fichiers LÖVE de la dernières version, prêts à l’emploi, sont disponibles à l’adresse https://framagit.org/popolon/reforest/-/releases (Version 0.2 depuis ce site).

capture d'écran de Reforest 0.2

Playing with button and LEDs on RISC-V based ESP32-C3 NodeMCU board with ESP-IDF (FreeRTOS)

Table of Content


* Introduction
* Circuit
** Components
** Breadboard
** Choose GPIO ports and their board pins

** LED part
** Resistors
** Switch button part
* The Software
** Initialisation
** Main loop
** ISR (Interrupt Service Routine)
** Debouncing
*** ESP timer

Introduction

Update: I wrote, following this one, another article to teache the usage of a potentiometer and an OLED screen..

After ArchLinux upgrade from python 3.9 to 3.10, tools need to be reinstalled by:

cd ~/esp/esp-idf
git pull
git submodule update --init --recursive
./install.sh esp32c3

If you never used ESP-IDF, you can read the previous introduction article to ESP-IDF on RISC-V based ESP32-C3, how to install it and start environment for compiling and flashing code. I also wrote article about using ESP32-C3 with Apache NuttX POSIX OS, but it will be useless here.

This article is about, on ESP32 (more specifically a less than 3.5€ ESP32-C3 based NodeMCU board, but it should work about the same way on other ESP based boards) :
* How to blink an external LED using GPIO, including how to know LED needed voltage, amperage, and compute needed resistor, with several possible means.
* Explanations about resistors values colours bands and computation of parallel mounted resistors. I also give link to free and open source softwares I wrote to help to compute resistors (depending on led, and desired intensity).
* How to connect an external switch to GPIO, and which resistor is needed. How to receive and manage it’s state a good way. By debouncing physical human pressure on switch, and use software interruption (that’s more easy that it could sounds).
* How to blink included RGB LED and stop/start it by using switch, an asynchronous way.

Hardware: The Circuit


After 20 years without practising electronics and searching about how to make the circuitry, for the LED and for the button, I found several one for each part. I finally found a some article that give explanations for this board, but using Arduino, and with lot of deep errors. Too strong resistor, not at the good place, after gathering of informations of lot of sources and after made lot of tests and looking back to source, I decided to write a complete tutorial for real beginners like me. This could help me to understand again all needed bases when needed, and I hope it will be useful for other people too.

Components

A basic tool to test and learn on circuits is a breadboard, it allow to test without needing to solder anything. It can so also be used by children, as 3,3 V is not dangerous at all.

* We need also a 5mm LED, that can be found on old electronics circuit or are really cheap, we use a RED that is perfect to represent an ON or OFF state and with 1,8V can be managed by 3,3V board.
* We also need a switch button. A cap is more comfortable but not required to make it work.
* We also need a resistor, if you have a 75 ohms (75Ω), or a 100Ω + a 330Ω, that’s perfect, else a 100 ohm alone is just nice, above 100 and until 330Ω there will still have light, but it could not be very bright.

You can also use a 10KΩ resistor for the switch, called pull-up (or pull-down) to have a more perfect signal, but external resistor is not required for this kind of board, as it includes internal pull-up/pull-down resistors, that is more efficient and possible to enable or disable by software with the esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull). We will not need to use the Open drain mode, used with SPI, where pull-up/pull-down is used as changing active/inactive signal.

Breadboard

half width Breadboard
Breadboard is a very convenient board that allow to test circuits without soldering. Any kind of components can be easily inserted and removed, and contact just work. The holes follow the weird electronics standard space of 2.54mm. This is because 2.54 mm is 1 inch of the imperial units, as modern electronics was made in the US, and this is the only one country that still uses this very complex units system. System International (SI), made during French Revolution, is far easier for any kind physics computation.


half width Breadboard with electric links drawn
In brown, electric links hidden inside breadboard

The plastic made board contains underlying electric links. They are divided on three parts, large central part and two side part.
* The two side part are just long line generally used for Ground (GND) and Voltage (V or Vcc). Blue (for GND) and red (for Vcc) lines are drawn to help to avoid to mix both VCC and GND on the same line and so avoid short circuits. Having one VCC+GND backbone one each side is just perfect for microcontrollers that can have both 5V and 3.3V or to have easier access to each side of the board. More efficient boards only use 3.3V.
* In the middle part, electric links are perpendicular to the side ones, separated in the middle, and are used to place electronics components. Some hole counters every 5 holes, and Letter at both extremities of the breadboard are wrote to copy a circuit from a board map an easier way.

Choose GPIO ports and their board pins

Here is the pineout of the board, we need to choose ports used for the circuitry, some pins have GPIO including only digital signal (0 or 1), analog support with analog to digital conversion (ADC) input, or digital to analog conversion (DAC) output. Some pins are to add voltage to electronic components (3,3V ports in RED on the picture) Warning to not connect them directly to GPIO port, you could definitively damage your board..


NodeMCU ESP32-C3-32S-kit pineout
NodeMCU ESP32-C3-32S-kit pineout

Here is the pinout mapping for NodeMCU ESP32-C3-32S-kit. Drawing, thanks to J-C François.

You can generally found them in the documentation of your board. founding this kind of schemas is not always easy, but they are often provided y board vendors. List of ESP32-C3 SoC GPIO are on the doc, but every board vendors can choose to map or not them on the board pins.

We choose here GPIO 1 (GPIO_NUM_1 in the API) for the button, and GPIO 2 (GPIO_NUM_2) for the LED. For this specific board, they are respectively second and third pins, on the left side starting from the top.

ADC0_CH1, GPIO1 and GPIO2
In the source code, as we will see later:

#define EXTERN_BUT GPIO_NUM_1 // BUTTON on GPIO1
#define EXTERN_LED GPIO_NUM_2 // LED on GPIO2

LED part

Anode (+) and cathode (-) on a diode symbolWe will use here a red LED. LED means « light-emitting diode », this is so a special kind of diode that emit light. A diode work only in one direction, start at a determined voltage, and shouldn’t be powered over a top voltage to avoid damages. If it is powered in the wrong direction (and not too much), nothing happen.


Diode and LED symbols
Diodes (at left) and LED (at right, with arrows) symbols.


Anode leg is longer than cathode one
Anode leg is longer than cathode one, the leds rolled by themselves on the flat side.


led bottom ring is flat at the cathode side
led bottom ring is flat at the cathode side

For LED part we need a 330Ω resistor. electronics LED work with different ranges. About 10 to 50mA, 20 to 30mA is generally safe, but warning, sometime less than mA. Generally with lower value than 20mA, the LED will not be very bright. Voltage depend on LED colour. Try to keep in the middle of these values to have enough light and to not burn your LED. The best is to follow the specifications of your LED vendor.
* IR 1,2 to 1.6V
* Red 1,8 to 2.1V
* Orange/yellow 1.9 to 2.2V
* Green 1.8 to 3.1V
* White/UV 3 to 3.4V
* Blue 3 to 3.7V
* RGB, each colour pin has his own colour voltage.

You can test the « Forward Voltage » of a LED with a digital multimeter. Forward because this is the direction where it lights.


Digital multimeter in diode testing position
Digital multimeter in diode testing position

Select the diode mode of the multimeter (as on above picture), and touch the Anode (longer leg, to the rounded side of the LED base) with the red connector, and the cathode (shorter leg, to the flat side of the LED base) with the black connector.

We use a red LED, that consume about 1,8V. The ESP32C3 has 3.3V output, so there is a difference of :

3.3 - 1.8 = 1.5V

We need a resistor to avoid an over-voltage of the LED.

Resistors

After Ohm law, U = RI, where U is voltage (V), R=resistance (Ohms or Ω) and I intensity (A). So :

R = U/I
 1.5/0.02 = 75Ω


We choose the nearest resistor equal or above this result.

The resistors are painted with rings indicating their resistance. There is in general 4 or 5 rings (or bands). That can be read from left to right as: resistance (2 rings), multiplier (one ring) and tolerance % (1 or 2 rings). Here is a good 5 bands calculators.

A good mnemonic-technique to memorize colours order in English is "Bad Beer Rots Our Young Guts But Vodka Goes Well (in) Silver Goblets". for value rings they start by 0, then 1, etc..., for multiplier by 1, then 10, etc....

Black 0 0 x1
Brown 1 1 x10 ±1%
Red 2 2 x100 ±2%
Orange 3 3 x1000=x1K
Yellow 4 4 x10K
Green 5 5 x100K ±0.5%
Blue 6 6 x1000K=x1M ±0.25%
Violet 7 7 x10M ±0.1%
Gray 8 8 x100M ±0.05%
White 9 9 x1000M=1G x1
Gold ±5%
Silver x0.01 ±10%

Warning, to the light when you look at the bands, some bands can be confused, and it can have disastrous consequences, especially if that's for the multiplier.


Resistors with flash at left and ambient light shadow at right
Resistors with flash at left and ambient light shadow at right. As we can see on the light blue resistor at left, the second painted ring is orange, where it seems brown with the shadow of the natural light et right.

If you have some doubt about their value, you can still use a digital multimeter on their resistance position displayed by a greek Omega character (Ω).


Digital multimeter in resistor testing position
Digital multimeter in ohm (resistor) testing mode position

Some suggest 330 ohms with 5V, this is really too much, and even for 3.3V, that result in

1.5/330 = 0.01A = 1mA

1mA is 1/20th of ideal light, this is still light up but with far less intensity.

I have a 47ohms resistor that would result in a too bit too high value:

1.5/47 = 31.91mA

See this video (the difference is visible on the ambient light, as camera focus on the light. Here 330 ohms (resulting to 1mA) and 100 ohms (resulting to 15mA) resistors are connected in parallel, a better solution (see below), and then 330 ohms only, we can see the ambient light change only as phone sensor wasn't in HDR mode. This difference of light means that the LED is not powered enough.

I made a simple resistor calculator tool with TIC80 in Lua You can use it online or download it to use on your computer or phone, it's Open Source with GPLv3 license:



Screenshot of resisor_calculator

28 december 2021 update: I discovered after this post that the famous free and open source electronic design suite, KiCad contains a tool called PCB Calculator that allow to compute lot of electronics related things, including resistors, and has a resistor color table. The version 6.0.0 of Kicad had been released on 25 december and contains several years of work huge improvements.

Another Ohm law say that When linking resistors in parallel called "parallel_resistor.lua", the computation is:

1/R = 1/R₁ + 1/R₂ + ... + 1/R₉ + ...

So

R = 1 / (1/R₁ + 1/R₂ + ... + 1/R₉ + ...)

And we have with 330Ω and 100Ω resistors in parallel:

R = 1 / (1/100 + 1/330) = 1 / 76.744Ω
I = 1.5/76.744 = 0.01954A = 20mA

That's just near perfect.

This formula make everything in one path:

I = U * 1 / (1/R₁ + 1/R₂ + ... + 1/R₉ + ...)

So here:

1.5 / (1/100 + 1/330)

I also made a simple command line calculator tool for resistors in parallel, available here https://framagit.org/popolon/electronhelp beside the tic80 single resistor calculator. I still need to implement, parallel calculation in tic80 version, and unique resistor calculation in command line version.

Switch button part

Pull-up resistor
For the button, the principle is just a switch, that cut current by default and make a short-circuit when the button is pressed. This switch is on a circuit between one of the GPIO pin and the ground (GND) pin. Due to the signal noise, a pull-up/pull-down resistor is needed to stabilize the signal. Pull-up/pull-down resistor is just a very low current that goes out of short circuit current. A 10KΩ resistor connected to the 3.3V pin is used for this. It is called pull-up if the resistor is connected to the button circuit between the button and the GPIO (3.3V too) pin, or pull-down if it is connected between the button and the ground.

Today most MCU boards, including ESP32-C3 include internal pull-up/pull-down resistors. They can be activated by software.

The legs connexion inside switch should be counter-intuitive at first, but legs on the same side are unconnected, each leg is connected with the one on the opposite side. To be clear, on the schema about the pull-up resistor, the two left legs are always connected together and the the two right legs are connected together, the button make the connection between left legs and right legs.

The Software

We want to manage:
* A main loop that blink the RGB LED of the board
* An external red LED that light when blinking is off.
* A switch button, that can at anytime, asynchronously, switch between these two states. This button can't be locked down, so we change state at each time it is push down. Releasing it doesn't have any effect.

To manage all this an asynchronous way, we need interrupts.
* Timer interrupt for waiting between LED blink steps
* Interrupt when on/off switch is pushed down, to change state.
* At this level we need also another asynchronous timer interrupt used for what is called debouncing. When the button is push down, it physically bounds, and the contact is on/off several time. The same effect is produced at the electrical level but the analogue current, is managed by electronic components, we can see at our level the current with just digital logical 1 (on) or 0 (off) state.

There is an included example with queueing in ESP-IDF, but it was not very clear for me about it's goal. After managing the switch, I looked it again, it allow to test queuing and interrupt, just by connecting 2 pins to 2 other pins, two pin send signal, the two other receive it, following by serial terminal output to understand what is running, is also of great value. Anyway, after searching a lot about how to use switch button, I found this topic helped me for the software part but was not complete, especially for debouncing. So I wrote here a working one and how it works. All the functions about GPIO of ESP-IDF are described here.

I made a copy of the esp-idf/examples/get-started/blink example as a starting project. See the previous article about ESP-IDF for this and how to compile it.

Just copy the blink example, and replace blink/main/blink.c by my version, that you can download here.

You can as an helper compile and flash it a first time, you will then only have to relplace the blink.c file and compile and flash it again.

To initialise the environnement for an ESP32-C3:

. $HOME/esp/esp-idf/export.sh
idf.py set-target esp32c3

Then to build and flash it (you can only apply build of flash depending on your needs) :

idf.py build flash

Initialisation

Set the state = 1 meaning internal LED will blink and RED Led the opposite way will be off.
* Choose used GPIO, reset all GPIO, set LED ones as output, button as input
* Initialise interrupt and timer fur button, we set it as POSEDGE (positive edge), this is the time when the current move up from 0 to 1, when liaison contact become active inside button.

In the default blink project, the blue intern led is defined as BLINKING LED, we instead start from it's internal GPIO, and the same for other internal colours of the internal RGB LED (respectively R=3. G=4. B=5). I use here connector GIO1 pin for the button and GPIO2 for the external LED (line 19).

#define RED_GPIO   GPIO_NUM_3
#define GREEN_GPIO GPIO_NUM_4
#define BLUE_GPIO  GPIO_NUM_5

#define EXTERN_BUT GPIO_NUM_1 // BUTTON on GPIO1
#define EXTERN_LED GPIO_NUM_2 // LED on GPIO2

We define a variable called state that define the current state of the machine at 1. 1 = on, 0 = off (line 31):

// set initial state to blinking led on
static int state = 1;

We need to first reset all the pin we use (line 61):

  gpio_reset_pin(RED_GPIO); // reset internal RGB LED GPIO
  gpio_reset_pin(GREEN_GPIO);
  gpio_reset_pin(BLUE_GPIO);
  gpio_reset_pin(EXTERN_LED); // reset external GPIO
  gpio_reset_pin(EXTERN_BUT);

We then create the timer, by sending to the appropriate function the previously defined structure (line 67).

  // create the timer
  esp_timer_create(&debounce_timer_args, &debounce_timer);

We set all the characteristics of the button GPIO. Could be set outside of the code, and send it to the gpio_config function (line 70).

  if (ISR_MODE == 1) {  // Interrupt mode
    gpio_config_t btn_conf;
    btn_conf.intr_type = GPIO_INTR_POSEDGE;  // Interrupt at Posedge only
    btn_conf.mode = GPIO_MODE_INPUT;           // Use INPUT mode
    btn_conf.pin_bit_mask = EXTERN_BUT_MASK;   // MASK of the button pin
    btn_conf.pull_up_en = GPIO_PULLUP_DISABLE;    //Disable pullup
    btn_conf.pull_down_en = GPIO_PULLDOWN_ENABLE; //Enable pulldown
    gpio_config(&btn_conf); // send config
  } else {               // Naive mode
    gpio_set_direction(EXTERN_BUT, GPIO_MODE_INPUT);
  }
  printf("button configured\n");

We now set the interruption associated with the button at the GPIO EXTERN_BUT to the isr_button_pressed() handler function (line 83).

  if (ISR_MODE == 1) {
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); // install GPIO interrupt
    gpio_isr_handler_add(EXTERN_BUT, isr_button_pressed, (void*) EXTERN_BUT); //Add handler of interrupt
    printf("Interrupt configured\n");
  } // end of ISR initialistaion

Then we set the OUTPUT direction of the LED and change the external red LED to 1 - state = 0, or off, we don't set the 3 internal LED at off, as they will be set just after, at the beginning of the loop (line 89)

  /* Set the LED GPIO as a push/pull output */
  gpio_set_direction(BLUE_GPIO, GPIO_MODE_OUTPUT);
  gpio_set_direction(RED_GPIO, GPIO_MODE_OUTPUT);
  gpio_set_direction(GREEN_GPIO, GPIO_MODE_OUTPUT);
  gpio_set_direction(EXTERN_LED, GPIO_MODE_OUTPUT);
  printf("LED output configured\n");

  gpio_set_level(EXTERN_LED, 1 - state); // 1-1=0 1-0=1

Main loop

In this infinite loop (while(1)), we basically light one colour of the LED, wait a bit using interrupt, to avoid power consumption, then light off the LED. Some informations bout the current state are send to the serial console like the state of the system, the begining of the loop, and when the internal red LED is lighted on.

We choose here to make the loop and to send the state value to the LED, then to wait, then to change to 0 value the LED to stop lighting it. It would have be better to stop the loop as soon as the system is off, ant to wait to be at on again to loop.

The loop start by printing the current state and light off RGB (line 98):

while(1) {
  printf("starting cycle by turning off the LEDs\n");
  gpio_set_level(RED_GPIO, 0);
  gpio_set_level(GREEN_GPIO, 0);
  gpio_set_level(BLUE_GPIO, 0);

then if we are in naive mode without interruption get the GPIO of the button state with gpio_get_level() function, and in any case display it. This allow to see if we currently connected the button to the appropriate GPIO pin. We then wait 1 second (1000 millisecond) all LEDs off (line 104):

  if ( ISR_MODE == 0 ) { // naive mode
    state = gpio_get_level(EXTERN_BUT);
  }
  printf("state=%d\n",state); // display current state on console
  vTaskDelay(1000 / portTICK_PERIOD_MS); // wait with lights off

We then light the blue LED, wait 200 milliseconds (0,2 s) and turn it off (line 110):

  gpio_set_level(BLUE_GPIO, state); // light on blue if state up
  vTaskDelay(200 / portTICK_PERIOD_MS);
  gpio_set_level(BLUE_GPIO, 0);     // light off blue

Then we do the same with the green LED (line 114), then the red (line 118), and then the green at the same time, lighting a yellowish colour (line 122). We wait for the last time 200ms (line 124) and the cycle restart by turning off all the LEDs (line 100):

  gpio_set_level(GREEN_GPIO, state);// light on green
  vTaskDelay(200 / portTICK_PERIOD_MS);
  gpio_set_level(GREEN_GPIO, 0);    // light off green

  gpio_set_level(RED_GPIO, state);  // light on red
  vTaskDelay(200 / portTICK_PERIOD_MS);
  gpio_set_level(RED_GPIO, 0);      // light off red

  gpio_set_level(RED_GPIO, state);  // light on red
  gpio_set_level(GREEN_GPIO, state);// and green => yellow!!
  vTaskDelay(200 / portTICK_PERIOD_MS); // end loop
}

ISR (Interrupt Service Routine)

gpio_isr_register() to register interruption function.

gpio_install_isr_service() and gpio_isr_handler_add().

Interrupt allocation. One of interesting aspect to know is that you can keep interruptions in IRAM (Instruction RAM) and use datas in DRAM (Data RAM), allowing less latency in interrupt and keep them independent from flash read/write. About Memory in ESP32-C3), IRAM and DRAM can be read/write in parallel.

Example without interruption.

Debouncing


This article illustrate the problem of bouncing and 2 methods for debouncing with examples on FPGA, and that's really more simple to implement on an FPGA than on a general purpose microprocessor or microcontroller.

The main app already use the main timer vTaskDelay, so we can't use it as I first done, else it will change the return address of the function, and so break the main loop. We could create a new xApp, but the more elegant way, is to use ESP Timer, it will solve all our problems an easy way.

ESP timer

We will follow the second one, and use an High Resolution Timer, ESP Timer for this. ESP-IDF include an example in examples/system/esp_timer/. This is not that we care about high resolution, ms is good enough here, but, this has the advantage to manage the timer by interruption and to easily avoid conflicting call, as this is needed due to bounces.

We need to create the structure to be sent to the function , that initialize the interrupt, and so the header of the callback function must be predefined (line 34):

// button manager with debounce timer definition
static esp_timer_handle_t debounce_timer;
static void test_button(void* arg);
const esp_timer_create_args_t debounce_timer_args = {
  .callback = &test_button,
  /* argument specified here will be passed to timer callback function */
  .arg = NULL,
  .name = "test_button"
};

The timer function, is called by an interruption at the end of the timer. It verify if the button is still pushed, and then switch state (1-0=1, 1-0=1). The external LED is set at the opposite of the actual state, if board LED blinks, then external RED LED is off (line 44).

static void test_button(void* arg) {
  if (gpio_get_level(EXTERN_BUT) == 1)
  {
    state = 1 - state;
    gpio_set_level(EXTERN_LED, 1 - state);
  }
}

The isr_button_pressed callback function is called when the button is pushed down. (at the positive edge). Due to the bounce it can be called several times for one human analogue pressure. We still need to double check we are really at push down state (high voltage) at the end of a short delay, else goes out. We then verify that the timer wasn't started before. If it wasn't (!) the case then we start once the timer that will start test_button function after 1 millisecond (1000 microseconds). This avoid bounce but still allow quick repeated push (for fast Morse code typing, of furious action games) (line 52):

static void isr_button_pressed(void* args) {
  if (gpio_get_level(EXTERN_BUT) == 0) 
    return; // we only want to manage pushed button
  if (!esp_timer_is_active(debounce_timer)) { // start only if the timer isn't active
    ESP_ERROR_CHECK(esp_timer_start_once(debounce_timer, 1000)); // 1ms = 1Kµs
  }
}

p.s.: I found an article about another method for debouncing specifically with RTOS capabilities too.

Courte introduction pratique à FPGA, Verilog et Verilator et quelques mots à propos de SystemVerilog

→ English version here

Attention, Je suis un débutant de moins d’un mois en Verilog, Verilator et FPGA, que j’ai étudié comme un loisir, si il y a des erreurs je les corrigerais. Vous pouvez me contacter sur le Fediverse.

Sommaire


* Introduction
** Process complet dans le monde réel
** Choses à connaître et comprendre
** Comment se programme un FPGA
* Verilog
** Valeurs
** Types
** Portes
** Modules
** Exemple simple, écrire une porte « et »
** Blocs initial et always
* Exemple simple en Verilator
** Effectuer le test avec Verilator
** Tracer l’exemple avec GTKWave
** À propos des exemples de Verilator
** Exemple pratique de base avec Verilator
* Pour aller plus loin

The text in strong are here to help diagonal reading.

Introduction

Je continue de descendre dans les couches avec le mode des FPGA (en anglais, « Field Processors Gateway Array », il existe différentes traductions). Ce sont des circuits itnégrés programmables, permettant de réaliser des circuilts logiques comme des processeurs. Ils sont notamment utilisés pour développer et tester des processeurs. Lorsque le processeur est validé en FPGA, ils peuvent être produit en ASIC (en anglais : « Application-specific integrated circuit »), processeur finaux, avec circuits cablés en durs et n’étant plus modifiables, tels qu’on les trouves dans les ordinateurs et périphériques informatiques de tous les jours. Les FPGA sont également utilisés tels quels dans différentes applications industrielles (avionique, traitement audio, vidéo, etc…) pour leur parallélisme et rapidité supérieure à un ASIC comportant un processeur général interprétent un logiciel, dans ces domaines et la possibilité de les mettre à jour facilement en cas de problème. Ce billet est une petite intrduction aux FPGA, au langage HDL (en anglais : « Hardware description language », signifiant, « langage de description de matériel ») standard IEEE, appelé Verilog, et la façon de le tester avec un logiciel libre à sources ouvertes (FOSS), le simulateur Verilator. Si vous désirez utiliser VHDL, GHDL est un simulateur libre équivalant VHDL.

Process complet dans le monde réel

Les étapes de l’implémentation d’un circuit sur un FGPA après sa conceptions son (des logiciels libres sont donnés comme outils de référence à chaque étape) :
* Implémentation de la logique (dans un HDL), donc Verilog ici, n’importe quel éditeur de text ou de programme peut être utilisé (j’utilise les très ampibien VIM et NeoVIM et parfois Geany, un environnement de programmation à interface très léger, mais également très puissant.
* SimulationVerilator ici, permettant de faire des bancs d’essai (anglais : testbench, parfois traduit en banc de test). C’est également un bon premier pas dans la vérification.
* Vérification formelleYoSYS est un système de vérification formelle et de synthèse de circuit. C’est une vérification plus formelle que celle de Verilator (vérification du temps d’execution et de la cohérence notamment). On ne va pas aborder ce point ici.
, Synthesis — créer les route et placer les blocs logiques, avec les contraites physiques et de temps (voir les principes de base des blocs logiques (CLB) et implémentations aujourd’hui) blocs depuis les sources en HDL et sortie d’un bitstream au format spécifique au FPGA utilisé, YoSYS de nouveau.
* Programmation/flashage du FPGAopenFPGAloader fait le travail sur la plupart des FPGA (utiliser la version Git si votre carte n’est pas autodetectée).
* Test dans le monde réel avec le FPGA.

On va se content de tester sur simulateur ici, pour comprendre les bases. Lorsque vous les aurez compris, vous pourrez aller plus loin, chosir un FPGA qui vous convient, et commencer à travailler avec.

Choses à connaître et comprendre

Les principaux concepts à comprendre et à savoir manipuler pour produire un circuit de FPGA de base sont:
* 0, 1 : Électricité passant par un circuit, ou non, pouvant être interprété en numérique par 1 ou 0, vrai ou faux, etc…
* Opéraeurs booléens (pour les portes logiques), qui est l’outil principal dans leurs manipulations. Vous n’avez pas besoin de notion d’électronique plus poussées. Les portes les plus basiques sont ou (OR), et (AND), non (NOT), et ou exclusif (XOR). Vous pouvez réaliser toutes ces portes en chaînant uniquement la porte non-et (NAND == AND+NOT), mais ça n’est probablement pas la façon la plus efficace de le faire. Si vous maîtrisez les propriétés algébriques des booléens cela pourra vous aider à simplifier les circuits, et donc de les rendre plus efficace, maiss ça n’est pas indispensable pour réaliser un circuit.
* Additionneur (ALU) (arithmetics logical unit), permettant des simples additions
* Bascule D (flip-flop) : Circuit spécial utilisé pour mémoriser les infromations (états) et les changer en fonction du signal d’enetrée et des changements d’état de l’horloge.
* Verrou (Latch) : Circuit logique séquentiel ne dépendant pas de l’horloge
* Horloge (Clock) : Donnant le rythme du temps. Les opérations sont dépendantes du temps, et ont besoin d’être synchronisées pour avoir un comportement cohérent. Les horloges sont comme un percussionniste dans un groupe de musique, Ils donne le rythme de base nécessaire à toute construction relative au temps. Les autres instruments, les chanteurs et les danceurs suivent son signal de temps. Appellés signaux d’horloge ici.
* Multipléxeurs (Multiplexers or MUX) qui peuvent aggréger différents signaux en un seul, et les démultipléxeurs (demultiplexer) qui font l’opposé.
Toutes ces fonctions sont intégrées dans une chaque bloc logique d’un FPGA.

Les autres parties importantes d’un FPGA sont :
* Un a quelques PLL (anglais : « Phase-locked loop », signifiant boucle, vérouillé sur la phase). Ils permettent d’avoir des signaux d’horloge secondaires, soit à des multiples de la fréquence de l’horloge principale, soit pour se synchroniser à des horloges (et donc signaux) externes.
* A Mémoire flash permettant de garder le bitstream (circuit) sur la carte après sa mise hors tension. Le bitstream sera automatiquement chargé au démarrage.
* De nombres blocs d’entrées/sorties (I/O blocks) périphériques à la puce contenant le FPGA, pour communiquer avec le monde extérieur.

Comment se programme un FPGA

Il existe trois principales méthodes pour développer un circuit de FPGA, et elles peuvent être mélangées, selon les besoins:
* Tracer le schéma avec outil graphique representant les opérateurs logiques et autres éléments de base (bascules, MUX, etc,) donnés plus haut.
* En utilisant un HDL (hardware description language), comme Verilog ou VHDL pour les plus connus. Les synthétiseurs sont là pour construire le circuit correspondant au programme en HDL, à la façon d’un assembleur pour transformer un du langage assembleur, facilement lisible par un humain en langage machine, ou comme les compilateurs ou interpréteurs des langages de plus haut niveau (C/C++, Python, Lua, JavaScript, FORTRAN, BASIC, Pascal, Camel, LISP, etc) qui les convertissent également en langage machine. Il existe également des langage de plus haut niveau pour les FPGA, tels que Chisel, un lanage HDL de très haut niveau qui est transpilé en Verilog ou VHDL, pour être ensuite synthétisé par les outils habituels.
* En utilisant OpenCL (Open Computing Language). Un langage conçu à la base pour le calcul intensif, pouvant être réparti simultanément sur les GPGPU et CPU .

Lors de l’utilisation de HDL, des outils sont donc là pour calculer automatiquement le routage du circuit pour vous. Des outils tels que VTR (Verilog To Routing) et NextPNR, optimisent également leur placement pour vous.

Symbiyosys est un outil de vérification.

OpenFPGAloader est un logiciel libre pour programmer/flasher le bitstream ainsi généré sur le FPGA lui même.

Il est possible de d’obtenir des résultats intéressants quelques heures après avoir commencé à étudier un HDL, Il permet de crée un circuit simple avec du code, proche de certains langages de haut niveau, avec des variables, constantes, registres, tests conditionnels, ainsi que des calculs de booléens des additions et des manipulations de bits et champs de bits. Il faut comprendre qu’un registre est déjà lui même un circuit de plusieurs portes logiques. Ce type d’outil est souvent construit sous forme d’ASIC au sein des blocs logiqus des FGPA, afin de gagner en performances et consommation électrique.

Verilog

Nous choisissons donc ici Verilog (Specifications officielles IEEE Std 1364-2001), c’est un langage très répandu avec de nombreux outils, VHDL est également un langage très répandu. Quoi qu’il en soit, il est toujours intéressant de regarder à des alternatives qui pourraient avoir des aspects intéressants. Je l’ai choisi comme langage de démarrage, parce qu’il est utilisé dans les quelques projets et exemples qui m’intéressent, et parce qu’il y a tous les outils en Logiciels libres et open source (FOSS) nécessaire pour écrire, simuler, vérifier, ainsi que différentes implémentation légères du processeur RISC-V pour l’embarqué (SERV and PicoRV32 (Github) (dont l’implémentation pour Sipeed Lichee Tang (carte FPGA à 20 €) implementation (J’ai personnellement choisi la Sipeed Tang Nano 4K (à 12 €) comme première carte, la Tang Nano coûte 3 € mais n’a que 1000 blocs logiques). Il y a également le très pratique et efficace simulateur Verilator et l’optimiseur de route VTR (Verilog to Routing.

Valeurs

Par défaut, les nombres sont des entiers en Verilog. Il est possible de forcer le nombre de bits (en le précisant avant le caractère apostrophe ') et de le choisir signé (caractère s, signé signifiant pouvant voir un signe négatif) ou non-signé (c’est le cas par défaut). En binaire b, octal o, hexadécimal h, ou encore de forcer décimal d (c’est déjà le cas par défaut). Les données peuvent également être données sous forme de chaînes de caractères.

32'hFFFF00FF  // Valeur RVBA 32 bits non signée en hexadécimal, correspondant à du jaune opaque ( rouge+vert+alpha à 100 %)
8'b00101010   // 42 (binaire non-signé sur 8 bits)
8'b0010_1010  // 42 (binaire non-signé sur 8 bits) version plus facile à lire
8'sb1010_1010 // -86 (binaire signé sur 8 bits)
-8'sd42       // -42 (décimal signé sur 8 bits)
12'o0754      // -rwxr-x-r-- pour les droits (octal non signé sur 12 bits) sur les systèmes de fichiers compatible Unix.
"Hello"       // Une chaine de caractères

Les constantes peuvent être définies en les précédent par une apostrophe suivie du terme define : 'define:

'define mavaleur -8'sd42;

Types

Il existe deux classes différentes de types réseau et logique. Nous ne couvrons ici que :
* wire, qui est de type réseau, implémente juste un fil électrique (wire) pour lier deux composants.
* reg, qui est de type logique, qui peut être défini ou indéifini pour mémoriser des valeurs (comme un registre).

wire w; // un simple fil, appelé w, entre 2 composants
reg r0; // Une mémoire (registre) de 1 bit appelé r0 (registre 0)

La valeur d’un wire est modifiée dynamiquement par les composants qui lui sont liés, mais il est également possible de leur donner une valeur permanente en dur avec la fonction assign :

assign a = 1;

Il existe également des types de plus haut niveau qui agglomèrent plusieurs des éléments précédents, comme par exemple :

integer i,j; // Deux entiers 32 bis signés, appelés i et j
real num;    // Un nombre flottant 64 bits appelé num
time t;      // Un nombre entier non-signé 64 bits représentant le temps (en signaux d'horloge)
realtime rt; // Un nombre flottant 64 bits représentant le temps

À propos du temps, il est intéressant de savoir que le terme #entier peut être utiliser pour attendre n signaux d’horloge. Il ne faut pas mettre ici de point-virgule (;) après la valeur, mais à la place, sur la même ligne, l’instruction qui sera executée après le délai définit. Par exemple :

#20 out = a & b;// Attend 20 signaux, puis calcule la valeur de a ET b

Les éléments de bit simples, wire et reg peuvent être organisés en vecteurs, les bits sont alors notés par convention, du MSB (anglais : « most significant bit » traduit par, « bit de plus fort poids » ) au LSB (anglais : « less significant bit », traduit par : « bit de plus faible poids »).

wire [7:0] Bus0; // Un bus 8 bits appelé Bus0 et connecté au système
reg [31:0] R0;   // un registre 32 bits appelé R0

Il est possible d’utiliser des expressions arithmétiques pour les définire, dans ce cas, l’utilisation de tableaux (voir ci-dessous) est plus pertinent:

reg[8*256:1] texte;   // Une chaîne de caractères de 256 octets. Pour simplfier son utilisation nous commencons à 1, comme nous n'y accéderons pas bits à bits
reg[16*16-1:0] sprite; // un sprite monochrome de 16×16 pixels, il est mieux d'utiliser -1 et de commencer par 0 dans ce cas

Après ces déclarations on peut accéder à des bits en particuliers ou des groupes de bits de la façon suivante :

Bus0[0];   // Accès au LSB de Bus0
Bus0[7:5]; // Accès aux 3 MSB de Bus0 (défini comme 8 bits de 0 à 7)

Verilog permet également de concaténer plusieurs ensembles de bits vers un autre. Par example, ici, on convertir une valeur 16 bit encodée au format de donnée Little Endian en valaur 16 bit au format Big Endian:

BigEndian[15:0] = {LittleEndian[7:0], LittleEndian[15,8]};

Si le bus de destination est de 16 bits ici [15:0] n’est alors pas nécessaire, puisque cela correspond à l’ensemble des bits fournis en entrée (8 + 8).

Et tous ces types peuvent être organisés en tableaux.

Tous les types complexes (dans le sens évolués, pas dans le sens mathématique) et vecteurs peuvent être organsiés en tableaux.

reg[7:0] curseur[0:7];            // Bitmap nonochrome d'un curseur de 8x8 pixels. Ici, curseur est le nom donné
reg[15:0] sprite[0:15];          // Le bitmap monochrome d'un sprite de 16*16 pixels
reg[31:0] palette[0:15];         // Palettes de 16 couleus RGBA8 (RVBA8)
reg[31:0] FrameBuffer[0:307199]; // Un tampon graphique d'écran de 640×480 pixels RGBA8

Les tableaux, contrairement aux vecteurs de bits, sont notés de leur adresse la plus basse à leur plus haute.

Portes

Voicil les portes (et opérateurs) logiques disponibles dans ce langage, ils opèrent sur un bit, mais on peut l’appliquer aux bits d’un vecteur en parallèle:

~a     // NON (NOT)
a & b  // ET (AND)
a | b  // OU (OR)
a ^ b  // OU-x (XOR)
a ~^ b // X-N-OU (XNOR), peut également être écrit ^~

tables de vérité des portes logiques

Il exxiste également deux opérateurs de décalage de bits :

>> // Décalage à droite
<< // Décalage à gauche

Modules

Verilog est organisé par module (un peu comme des classes en programmation objet, ou bien un circuit intégré sur un circuit imprié). Un module comporte :
* Un nom de module
* Son interface entre l’intérieur et l’extérieur du module (penser aux variables ou méthodes publiques en programmation objet, ou aux branches d’un CI en électronique), est une liste de ports définis entre parenthèses comme pour les fonctions dans les langages fonctionnels module module_name (wire a, wire b, wire out); ... endmodule.
* Par défaut, les déclarations sont des blocs logiques exécutés en parallèle.
* Pour éxecuter du code séquentiellement et conditionnellement, les sous-blocs always (et initial dans les bancs d’essai) doivent être utilisés (vir plus bas pour d’avantage de détails).

Exemple simple, écrire une porte « et »

AND
Les opérateurs de bases sont déjà présent en Verilog, mais voici comment ils pourraient être écrits. Dans les fonctions booléennes de Verilog, l’argument de gauche est la sortie (valeur retournée). Une porte « et » (AND), peut donc être réimplémentée de la façon suivante :

module and(f,a,b); // Le caractère ; n'est pas une erreur ici
  output f;
  input a,b;

  assign f = a & b; // ^ est une porte AND
endomdule

Cet exemple devrait être mis dans un fichier nommé "and.v".
* Le type par défaut est wire
* Depius Verilog 2005, les entrées (input) et sorties (output) peuvent être placer dans l’entête du module comme ceci, vous trouverez les 2 versions selon les goûts des développeurs :

module and(output f, input a,b);

Cet exemple est une porte permanente inconditionnelle, il est possible de les utiliser pour faire des circuits complets, mais Verilog est une langage de haut niveau, permettant de programmer des choes plus complexes de façon simple. Les blocs always pour des porticiels (anglais : gatewares) plus complexes.

Blocs initial et always

Il existe deux types de blocs à connaître pour commencer Verilog :
* Les blocs always (toujours) peuvent être synthétisés, et donc utilisés dans les circuits et sont executés à chaque fois que les conditions sont remplies, donc déclenchés par un évenement (en anglais : event), donné entre parenthèses always @(event).
* Les blocs initial (initial) ne peuvent être synthétisés, ils sont donc utilisés uniquement dans les bancs d’essai et exécués une seule fois et inconditionnellement. Ils permettent de donner des condtions et valeurs initiales au test. Il est possible d’avoir plusieurs blocs initial. Cela permet également de tester ou prototyper une fonction simple rapidement grace à la sortie texte du vérificateur/simulateur, avant de le mettre dans un bloc always d’un circuit.

Ils permenttent tous deux de :Front montant (Posedge) et descendant (negedge) sur un signal d'horloge
* exécuter un code séquentiel
* des conditionnels, if else
* des boucles while, for, etc

Un élément typique pour déclencher un always peut être par exemple :
* Un front montant d’horloge (positive clock edge (posedge)
* Un front descendant d’horloge negative clock edge (negedge)

always @(posedge clk)
  ...
end

Mais peut également être un test logique conditionnel :

always @(a or b)
  ...
end

Exemple simple en Verilator

Un exemple simple fourni avec Verilator (j’ai traduit les commentaires) :

module our (clk);
  input clk;  // L'horloge est requise pour obtenir une activation initiale
  always @(posedge clk)
    begin $display("Hello World!"); $finish;
  end
endmodule

Ici, le bloc always est déclench au premier front montant (posedge), il sort à l’écran ($display) une chaîne de caractères « Hello World! », puis termine ($finish) la simulation.

Il est posssible de tester et valider de nombreuses choses avec Verilator avant d’aller plus loin dans le processus de création du FPGA (vérification formelle, conversion en bitstream (qui peut être très long) et écriture sur le FPGA). Une bonne façon de démarrer est de faire une copie de l’exemple donné ci-dessus, puis de modifier le fichier top.v en jouant avec.

Effectuer le test avec Verilator

Les exemples sont installés sur :
* Arch Linux ou Manjaro, dans : /usr/share/verilator/examples/
* Debian ou Ubuntu, dans : /usr/share/doc/verilator/examples/

Donc pour les tester, copier simple le dossier dans votre répertoir personnel ou de travail, où vous avez un accès en écriture, puis construisez le modèle et executez le avec un simple make (Verilator le transpile en C puis le compile, ce qui permet d’avoir une simulation très rapide):

Sur les distributions basés Arch Linux :

cp -a /usr/share/verilator/examples/make_hello_c ~/

Sur les distributions basées sur Debian:

cp -a /usr/share/doc/verilator/examples/make_hello_c ~/

Puis, sur ma distribution:

cd ~/make_hello_c
make

make permet de reconstruire depuis les sources si nécessaire, et exécute immédiatement le banc d’essai.

La sortie du banc est de ce type:

-- RUN ---------------------
obj_dir/Vtop
Hello World!
- top.v:11: Verilog $finish
-- DONE --------------------

Le reste du texte est à propos de la compilation et une proposition d’uitliser le tuto suivant.

Tracer l’exemple avec GTKWave

GTKwave est un outil compagnon à Verilator. Il permet de voir et vérifier le chronographe résultant de la simulation, afin de comprendre le fonctionnement temporel du circuit, et de visualiser ce que pourrait être la source d’un problème possible. Il utilise le fichier .vcd (Value Change Dump), pour tracer. Ce fichier est utilisé comme source pour tracer le chronogramme (Le résultat de sortie de make_tracing_c se trouve dans le sous-répertoire logs/. Le fichier est logs/vlt_dump.vcd, crée après avoir utilisé la commande make).

Pour utiliser l’exemple de tracé avec GTKwave, vous devez avoir installé le paquet GTKwave, puis, comme dans le premier exemple, avoir dupliqué le dossier dans un répertoire dans lequel vous avez les droits en écriture:
* Sur les distributions basées sur Arch Linux :

cp -a /usr/share/verilator/examples/make_tracing_c ~/

* Sur les distributions basées sur Debian :

cp -a /usr/share/doc/verilator/examples/make_tracing_c ~/

Puis, dans tous les cas :

cd ~/make_tracing_c
make
gktwave logs/vlt_dump.vcd

GTKwave
La sortie texte pendant la phase make fournit également des informations intéressantes :

-- RUN ---------------------
obj_dir/Vtop +trace
[1] Tracing to logs/vlt_dump.vcd...

[1] Model running...

[1] clk=1 rstl=1 iquad=1234 -> oquad=1235 owide=3_22222222_11111112
[2] clk=0 rstl=0 iquad=1246 -> oquad=0 owide=0_00000000_00000000
[3] clk=1 rstl=0 iquad=1246 -> oquad=0 owide=0_00000000_00000000
[4] clk=0 rstl=0 iquad=1258 -> oquad=0 owide=0_00000000_00000000
[5] clk=1 rstl=0 iquad=1258 -> oquad=0 owide=0_00000000_00000000
[6] clk=0 rstl=0 iquad=126a -> oquad=0 owide=0_00000000_00000000
[7] clk=1 rstl=0 iquad=126a -> oquad=0 owide=0_00000000_00000000
[8] clk=0 rstl=0 iquad=127c -> oquad=0 owide=0_00000000_00000000
[9] clk=1 rstl=0 iquad=127c -> oquad=0 owide=0_00000000_00000000
[10] clk=0 rstl=1 iquad=128e -> oquad=128f owide=3_22222222_11111112
[11] clk=1 rstl=1 iquad=128e -> oquad=128f owide=3_22222222_11111112
[12] clk=0 rstl=1 iquad=12a0 -> oquad=12a1 owide=3_22222222_11111112
[13] clk=1 rstl=1 iquad=12a0 -> oquad=12a1 owide=3_22222222_11111112
[14] clk=0 rstl=1 iquad=12b2 -> oquad=12b3 owide=3_22222222_11111112
[15] clk=1 rstl=1 iquad=12b2 -> oquad=12b3 owide=3_22222222_11111112
[16] clk=0 rstl=1 iquad=12c4 -> oquad=12c5 owide=3_22222222_11111112
*-* All Finished *-*
- sub.v:29: Verilog $finish
[17] clk=1 rstl=1 iquad=12c4 -> oquad=12c5 owide=3_22222222_11111112

On peut voir ici, chaque étape de l’exécution avec l’horloge (clk) oscillant entre l’état 0 et l’état 1.

Le temps est en tic d’horloge, et celle du banc d’essai peut être défini par la directive timescale, permettant un temps granulaire de plus grande précision. Cela ne nous est pas utile pour le moment.

Le fichier de trace .vcd (Value Change Dump) est défini dans top.v:

$dumpfile("logs/vlt_dump.vcd");

Toutes les étapes se trouvent dans le dossier logs/annotated/ et le fichier de rapport (coverage) dans logs/coverage.dat:

-- COVERAGE ----------------
verilator_coverage --annotate logs/annotated logs/coverage.dat
Total coverage (2/31) 6.00%
See lines with '%00' in logs/annotated

-- DONE --------------------

Le dernier est définit dans sim_main.cpp.

À propos des exemples de Verilator

Dans chaque exemple, il y a un petit code d’interface en C++. La modification du code C++ n’est pas nécessaire pour les tests de base. Lorsque les concepts de bases sont acquis, la partie C++ peut être adaptée pour s’interfacer avec des bibliothèques système, et permetter de simuler les échanges avec des périphériques de communications, audio, graphisme, vidéo etc.

Dans ces simples exemples d’introduction, les plus intéressant pour débuter sont tous disponible en Verilog :
* make_hello_c Hellow World Verilog avec Makefile
* make_hello_sc Hello World Verilog utilisant SystemC et un Makefile
* cmake_hello_c Hello World Verilog avec cmake.
* cmake_hello_sc Hello World Verilog utilisant SystemC et cmake.

SystemC est un ensemble de classes et macros C++ fournissant une interface de simulation pilotée par les évenements, simule les processus concurrents dans un environnement temps-réel.

Les autres exemples sont (avec leurs variantes cmake|make et c|sc) :
* make_tracing_c pour le tracage de l’execution (comme dit au dessus avec l’exemple GTKWave).
* make_protect_lib pour créer une bibliothèque protégée DPI (Direct Programming Interface), une interface entre SystemVerilog et des fonctions dans un langage comme C ou C++.

System Verilog est une évolution de Verilog, c’est donc un HDL avec d’avantage de fonctionnalités, et également classé comme un HVL (Hardware Verification Language), il ajoute différents types et blocs séquenciels, et permet la programmation orientée objet avec une syntaxe proche de C sur certains points.

Quelques extensions de fichiers que vous trouverez dans le domaine de Verilog, System Verilog et simulateurs liés sont :
* .v comme Verilog.
* .vc (verilog ???) ou .f comme File, utilisé pour les projets importants, comportant des argiments à passer à Verilator (ou un autre simulateur) tels que les drapeaux (flags), des dossiers d’include, et des bibliothèques liées pour la simulation.
* .vcd comme Value Change Dump file, contient les sorties de traces de la simulation pour leur analyse (voir au dessus).
* .vo comme Verilog Output file.
* .sv comme System Verilog,

Exemple pratique de base avec Verilator


Nous allons donc réutiliser le simple exemple avec Make (vous pouvez choisirles exemples avec cmake à la place, dans ce cas, vous n’aurez qu’à invoquer la commande cmake à la place de la commande make.

J’ai dans ce cas, copié l’exemple make_hello_c dans un repertoire avec droits en écriture :

cp -a /usr/share/verilator/examples/make_hello_c verilator_test
cd verilator_test

puis ai édité top.v ( vim, emacs, gedit, ou n’importe quel éditeur de texte de votre choix fait l’affaire), puis remplacé son contenu.

Voici un simple exemple que j’ai utilisé pour mes tests. Je voulais comprendre comment fonctionnait les accès aux registres Verilog simples, et à différent types de données, dont un sprite (celui affiché à côté du titre de cette section), vous povuez décharger cette version de top.v ici. Je traduis ici les commentaires :

module top;
 reg [8*11:1] str1;
 reg [8*25:1] str2;  // remplit à gauche par des espaces
 reg a,b,c;
 reg [7:0] sprite[0:7];  // utilise un octet par ligne
 reg [15:0] sprite2;  // sprite sous forme de champs de bits
 integer i;

 initial begin
   str1 = "Hello World"; // tests d'initialisations de chaîne de caractère
   str2 = "Hello World";

   a = 1'b1; // Tests d'initialisation de bits
   b = a^1;
   c = a^0;

   sprite[0] = 8'b10011000; // Tests d'initialisation de sprites
   sprite[1] = 8'b00100100;
   sprite[2] = 8'b01000010;
   sprite[3] = 8'b10011001;
   sprite[4] = 8'b10011001;
   sprite[5] = 8'b01000010;
   sprite[6] = 8'b00100100;
   sprite[7] = 8'b00011000;

   sprite2[ 7:0] = 8'b10011000;
   sprite2[15:8] = 8'b00100100;

   $display ("str1 = %s", str1); // Affichage des chaînes de caractères
   $display ("str2 = %s", str2);

   $display ("a = %d", a);       // Affichage des bits
   $display ("b = a^1 = %d", b);
   $display ("c = a^0 = %d", c);

   for ( i=0; i<8; i=i+1) begin  // Affichage des sprites
    $display ("sprite[%2d] = %b",i,sprite[i]);
   end
   for ( i=0; i<2; i=i+1) begin  // essaie de lire des rangées de bits, en tantant des sorties du domaine
    $display ("sprite2[8*i(%1d) +: 8] = %8b",i,sprite2[8*i +:8]);
    $display ("sprite2[8*i(%1d) -: 8] = %8b",i,sprite2[8*i -:8]);
    $display ("sprite2[4*i(%1d) -: 8] = %8b",i,sprite2[4*i -:8]);
   end
 end 
endmodule

Pour le tester, lancer juste la commande make. Ici, les commentaires en bleu sont des commentaires de la sorti uniquement dans ce billet de blog, il ne seront pas visible à l’execution:

make
-- VERILATE & BUILD -------- 
[...]  Cette partie est le log de compilation, je ne l'ai pas collé
-- RUN ---------------------   C'est ici que c'est intéressant
obj_dir/Vtop
str1 = Hello World
str2 =               Hello World    str2 de 25 caractères rempli par des espaces
a = 1
b = a^1 = 0
c = a^0 = 1
sprite[ 0] = 10011000    Huits lignes du sprite
sprite[ 1] = 00100100
sprite[ 2] = 01000010
sprite[ 3] = 10011001
sprite[ 4] = 10011001
sprite[ 5] = 01000010
sprite[ 6] = 00100100
sprite[ 7] = 00011000
sprite2[8*i(0) +: 8] = 10011000      Quelques acces de tests à des plages de bits
sprite2[8*i(0) -: 8] = x0010010      Nous sortons des domaines du registre ici, des x sont affichés
sprite2[4*i(0) -: 8] = x0010010
sprite2[8*i(1) +: 8] = 00100100
sprite2[8*i(1) -: 8] = 01001100
sprite2[4*i(1) -: 8] = xxxxx001

Pour aller plus loin

Documentation relativement complètes en ligne :

À propos de Verilog :
* Anglais : Un manuel Verilog complet sur Chip Verify
* Anglais : Tutorial Verilog pour les débutants sur ReferenceDesigner.com
* Anglais : Survol de Verilog en quelques écrans de présentation (format PDF) sur euler.ecs.umass.edu
* Anglais : Verilog Quick Reference Card.pdf
* Anglais :
Verilog HDL Quick reference Guide
* Français : Syntaxe de Verilog à hdl.telecom-paristech.fr

Bancs d’essai en Verilog, SystemC et System Verilog:
* Anglais : Comment écrire un banc d’essai de base en Verilog, en utilisant un template en C++. Ce site, FPGA tutorial, à trois sujets principaux, VHDL, Verilog & System Verilog.
* Anglais : Tutoriels sur Systemverilog et SystemC.
* Anglais : Quelques tutoriels sur System Verilog.
* Français : SystemVerilog en 13 minutes sur sen.enst.fr
* Verilog Simulation with Verilator and SDL Utilisation de la bibliothèque SDL pour simuler une sortie graphique VGA, avec Verilator. Utilise Verilog et un peu de SystemVerilog.

Quelques projets FPGA intéressants:
* Anglais : From Nand To Tetris (De Nand à Tetris), toutes les étapes pour faire, en quelques exercices, un ordinateur basé sur un CPU 16 bits, en n’utilisant que des portes NAND (jeu intéressant et formateur), puis construction d’un jeu Tetris avec ce petit système. Ils utilisent un HDL développé par cette université, mais il existe déjà des ports, comme exemples, comme celui-ci en Verilog utilisant Ikarus Verilog simulator (également logiciel libre de simulation, c’était la référence avnat Verilator), un autre en Verilog pour le Lattice ICE40 FPGA, dont tous les outils pour la programmation sont libres (testé avec une carte en matériel libre/ouvert Olimex) et des outils open source (YoSYS comme synthétiseur), encore en Verilog pour la carte De0-nano (utilisant Altera Cyclone IV FPGA).
*
Implementation de UART/port série/interface RS-232 pour FPGA (versions Verilog et VHDL).
* Consolite, une console de jeu légère en Verilog pour FPGA, programmable en assembleur. Un émulateur est également disponible.
* FloPoCoUnité de caclculs arithmétique à virgule flottante pour FPGA dévelopée par l’INRIA.
* Anglais : Tang nano MIDI Sounder, un synthétiseur MIDI, à la sonorité 8 bits sympa.
* Anglais : Speech256, un synthétiseur vocal.
* Japonais : Processor RISC-V SERV pour Tang Nano
* Japonais : Tetris pour Tang Nano en Verilog
* ZipCPU Un blog pour le CPU ZipCPU avec un des exemples de banc d’ssai Verilator intéressant, utilisant des patrons C++ pour des bancs d’essai génériques.

Pour les hackers amoureux des logiciels libres :
* Apicula project, un projet d’ouvertur d’ouverture des bitstream des FPGA Gow1n.

Art du Pixel 64×64×16 couleurs (Sweetie16) PixelArt avec Pixelorama « β-karoten – Nous savons qui sera mangé »

→ English version here

β-karoten - Nous savons qui sera mangé

J’ai participé à la demoparty LoveByte Battleground qui s’est déroulé ce week-end, en postant la semaine dernière un dessin en pixel art. Malheureusement mais amusent, il y a eu quelques petites erreurs :
* J’ai fait une image de 64×64 pixels au lieu de 128×128 pixels. Une palette de 16 couleurs était imposée, Sweetie16 (la palette par défaut sur le console fantaisie TIC-80 (Code source, FOSS).
* Ma seconde erreur est d’avoir téléchargé une première version de mon image, ainsi que la mise à jour quelques heures après avec la version finale via le navigateur web NetSurf (code source, FOSS), sur Debian, sur l’émulateur RISC-V (Specifications, FOSH, il existes différentes implémentations libres ou non) de Qemu (FOSS, Code source, instance Git). Peut être parce que mon installation de NetSurf ne supporte pas le JavaScript^^. J’ai également participé à un livecoding (256 octets de code maximum en 25 minutes). Le résultat. La vidéo commentée de l’enregistrement de programmation en live.

Ce dessin est est fait avec Pixelorama (FOSS (Code source), lui même fait sur le moteur de jeu Godot (FOSS? code sourcce). J’utilise le système d’exploitation libre Arch Linux. J’ai également fait un paquet ArchLinux AUR pixelorama-git d’après le paquet pixelorama (Je voulais utiliser la version v0.9rc, seul la 0.8 était disponible). Il y a des paquets pixelorama (dernière stable, compilant depuis les sources), et pixelorama-bin (depuis les binaires des développeurs). Pixelorama est un éditeur d’image et animation en Pixel art. Je crois que je l’ai découvert grâce au blog Librearts.org.

Le nom est « β-karoten – « Nous savons qui sera mangé »

Capture d'écran de Pixelorama
Capture d’écran de Pixelorama