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++.

fSystem 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.
* 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

Telegram, autocollants animés Lottie, Python et Glaxnimate

→ English version here

Sommaire

* Introduction
* Un bon flux de travail
* Publication sur le web (SVG)
* Bot Telegram

Introduction


Je suis un grand fan de l’interface de l’application de chat Telegram. Si le serveur est à sources fermées, l’application bureau et Android sont à sources ouvertes, l’interface est bien pensée, intuitive, relativement légère et plein de fonctionnalités intéressantes par rapport aux autres applications de ce type. Il serait d’ailleurs sans doute intéressant de la patcher pour une compatibilité Jabber/XMPP. Il a été le premier à ma connaissance, à utiliser Lottie, dès juillet 2019 (et l’API pour bot qui va avec) un format d’animation vectoriel dont je rêvait depuis des décennies, devenu un standard de fait et ouvert. Les animations de cette page ont été crées avec Glaxnimate, un outil pour réaliser des animations au format Lottie, que l’on peut également exporter au format SVG animation. Les auteurs de Lottie fournissent des JavaScript pour le web, la bibliothèque libre, rlottie de Samsung (en C++, avec version WASM) permet de les jouer et comporte des adaptations pour différents langage de programmation. Qt intègre sa lecture par défaut et il existe différents outils pour en créer.


Le logiciel libre multiplate-forme d’animation Glaxnimate permet d’en réaliser avec une interface graphique d’un type assez classique pour les logiciels d’animation. Son auteur à également fait une bibliothèque python, python-lottie (AUR : python-lottie-git, pip : Lottie), permettant d’en générer algorithmiquement et les 2 permettent de convertir différents formats (export SVG animé, Lottie JSON, page HTML toute prête, Telegram, mpeg4, PNG, wepb, gif, et je dois en oublier), et en entrée de vectoriser des gif animés, etc… Le logiciel d’animation vectoriel 2D Synfig à également un exportateur, ainsi que Blender3D (voir aussi Blender3D.fr en français), un plugin/greffon (distribué avec python lottie) fait également par l’auteur de Glaxnimate. Les choix ne manquent donc pas pour en créer.


Glaxnimate permet de faire facilement des animations au format Lottie, dont le format .TGS, une version aux spécifications volontairement plus limitées et compressée en gzip de Telegram. Le format de ce dernier est plus compact mais comportes quelques contraintes, cela n’a pas empêché d’avoir quelques merveilles depuis plus de 2 ans :
* format 512×512 pixels
* 3 secondes
* 64 Kio par autocollant animé
* Le tout peut être mis dans un paquet d’animations (sticker pack) regroupant différentes expressions (comme les émotiĉones/emoji).

Pourquoi 512×512 pixels alors que c’est un format vectoriel ? L’application cliente Telegram rendra dans cette dimension l’animation et la compressera en local, pendant la première boucle, en animation WebP de 512×512 pixels pour la conservation en cache. Cette dimension est déjà grande pour les certains écrans mobiles. Cela permet d’économiser la bande passante (animation vectorielle compacte transférée), d’avoir la haute qualité du vectoriel (généralement calculé par GPU, des accélérateurs SVG étaient déjà présent sur les téléphones Symbian des années 2000), et d’avoir des boucles d’animation qui ne prennent pas trop de CPU/GPU après la première boucle, ni trop de mémoire (compression WebP). WeChat/微信 a choisit des GIF animés il y a une bonne décennie, les GIF sont probablement convertit en MPEG aujourd’hui ? La messagerie instantanée Discord, populaire en Extrême-Occident, utilise aussi le format Lottie depuis juillet 2021.


Pour référence, l’animation du haut fait :
* 1109 octets (1,1 Ko) en TGS (format Telegram compressé binaire)
* 4682 octets (4,5 Ko) en json uglifié (plus d’indentations ni re retours chariots)
* 508054 octets (500 Ko) en WebP
* 9710 octets (9,6 Ko) en SVG animé (il y avait un path inutile en trop, je sais pas pourquoi).
* 1804 octets (1,7 Ko) en SVGZ animé (SVG compressé via gzip), il peut être plus intéressant de laisser le serveur compresser via brotli (Nginx, Apache) ou de pré-compresser (.br) Il est bien supporté par les navigateurs
* 6114 octets (6,1 Ko) en SVG, sortie optimisée Inkscape (basée sur Scour), on peut encore gagner, aujourd’hui si l’option de précision du nombre de chiffres après la virgule est inférieure à 3, elle reste à 3. Dans les parties animation non gérées on se retrouve à six 0 (1.000000 ou 0.000000). On peut encore l’optimiser à la main en attendant de l’intégrer (voir les sed plus bas), soit :
* 5827 octets (5,8 Ko) en SVG, retravaillé un peu à la main.
* 5559 octets (5,5 Ko) en supprimant des groupes intermédiaires inutiles (attention à ne pas tout casser, tester groupe par groupe) et en remplaçant 1.0 par 1


Un des principaux problèmes à la sortie optimisée de SVG depuis Glaxnimate est que Lottie est basés sur une forte utilisation des groupes, tandis-que SVG permet d’effectuer de nombreuses opérations équivalentes dur les objets eux-mêmes. Ainsi les matrices de transformation sont situées dans des groupes contenant les objets. Voir l’affichage XML d’Inkscape ci-contre. Cela demande donc aujourd’hui du travail manuel sur les fichiers. Il doit être possible d’automatiser ça.

Il semble que SVGO ai d’autres méthodes encore plus efficaces, mais basé sur du node.js, mais il casse complétement les animations. Je n’ai pas trop confiance, en raison de beaucoup de mauvaises habitudes autour de Node. Et le plugin Inkscape, inkscape-svgo ne déroge pas à ses nouvelles habitudes, si l’on essaie de le compiler avec le Makefile inclus, pas de test d’installation existante, récupération forcée de node 11 (x86_64, perd le multi-architecture), et recompilation de toutes les dépendances. On se retrouve une fois de plus avec une extension énorme de dizaines de méga-octets (75 Mo, contre 145 Mo pour Inkscape et 5 Mo pour la bibliothèque SVGO elle même), alors que ça devrait faire que quelques Ko grand maximum.

Un bon flux de travail

* Créer l’objet sous Inkscape
* L’animer avec Glaxnimate
* Exporter en SVG (n’oubliez pas de faire également une sauvegarde du format Glaxnimate)
* Ouvrir de nouveau avec Inkscape pour nettoyer (sortir des groupes ce qui n’en a pas besoin
* Faire une sortie optimisée
* Retravailler à la main.

Pour la dernière partie, cela oriente vers deux solutions pour l’automatisation :
* Patcher la sortie d’Inkscape optimisée
* Faire un optimiseur de 2e passage

On peut déjà pas mal gagner avec :

sed s/1.00000/1/g <input.svg | sed s/0.00000/0/g | sed 's/0000;/;/g' >output.svg

Un ou 2 chiffre de précision (à la place de 3 ou 4 aujourd’hui de Scour)), devraient également suffire.

SVGcleaner est meilleur que Scour dans différents cas, mais ne supporte pas et n’a pas l’intention de supporter les animations (complexe à gérer, peut facilement tourner à la catastrophe).

Publication sur le web (SVG)

Donc, comme je viens de dire, le SVG animé étant infiniment plus simple à intégrer dans une page web je préfère cette option. Le mieux est de ne pas compresser en SVGZ, mais seulement d’optimiser le fichier SVG puis de le compresser en Brotli (.br) et éventuellement en gzip pour des très vieux navigateurs.

L’intégration dans une page est très simple, elle se fait comme pour toute image :

<img src="/chemin/du/fichier_animation.svg" align="right" width="200" height="250">

C’est ce que j’ai fait sur cette page, le navigateur la prend automatiquement en charge.

Note : Il peut être intéressant de l’intégrer en ligne pour manipuler ses attributs à l’aide de JavaScript. Mais pour de simples illustration animées, comme ici, ou des objets animés (déplacés/zoomés/tournés sur la page) sans toucher à ses attributs internes, l’utilisation d’image est parfaite.

Pour la compression, j’utilise les max voici un moyen simple d’avoir tous les fichiers d’un dossier compressé à côté de la version non compressée :

ls --ignore=*.gz --ignore=*.br | while read file
do
 brotli -q 11 -c <"${file}" >"${file}".br
 gzip -9 -c <"${file}" >"${file}".gz
done

* 5559 murphy_anim0.animoptiv5.svg
* 1303 murphy_anim0.animoptiv5.svg.br
* 1543 murphy_anim0.animoptiv5.svg.gz
* 4682 murphy.json
* 976 murphy.json.br
* 1149 murphy.json.gz

Pour forcer le chargement d’un des 2, si le navigateur supporte (si il supporte les 2, Brotli (.br) sera utilisé, sous Nginx (après avoir compilé le patch Brotli, les règles dans le fichier de configuration sont simples :

brotli_static on;
gzip_static on;

Sous Apache, Brotli est présent par défaut dans les versions 2.4, mais c’est un peu plus compliqué.

Pour tester, le mieux est d’utiliser la commande curl :

curl -I -H 'Accept-Encoding: gzip, deflate, br' https://host.net/fichier.svg

Si le brotli est bien activé, dans la réponse, vous verrez :

content-encoding: br

Bot Telegram

La bibliothèque python-lottie tombe bien, je voulais me mettre à Python, notamment pour micropython dans l’embarqué et pour du travail d’admin sys & réseau, où il est de plus en plus utilisé, avec l’outil de déploiement de parc Ansible, et d’autres outils système de base. Environ une année d’expérience de génération procédurale en Lua me donnait envie d’en faire d’en d’autres langages (je réserve une surprise pour bientôt). Il existe plusieurs bibliothèques de bot pour Telegram en python, dont Python Telegram Bot, qui colle assez bien à l’API officielle de Telegram et Telethon (Documentation) qui permet de communiquer directement en MTproto, le protocole de Telegram, plutôt que de passer par la couche HTTP, cela réduit d’une couche et permet un contrôle plus poussé des échanges. Voir le comparatif Bot API (HTTP vs MT Proto).


On ne trouve pas encore beaucoup d’exemples avec Python Telegram bot avec lequel j’ai commencé, mais en fouillant un peu dans la documentation, on trouve ce qu’on veut. En mixant la bibliothèque et Python-Lottie pour la génération procédurale, Glaxnimate pour la génération affinée à la main et le bot on a de quoi s’amuser. J’ai donc fait un premier bot en Python-Telegram-Bot, suites aux suggestions de Glax, l’auteur de Glaxnimate qui m’a fait connaître le module python, Telethon, qu’il utilise pour son bot similaire, Glaxcomm, générant des Lottie/TGS à la volée. Je vais refaire mon bot et l’emmener vers ce que je voulais faire. Mon but étant aussi de pouvoir l’utiliser avec d’autres protocoles que Telegram, dont ActivityPub (spécifications W3C).

Glax à fait un bon ensemble d’exemples de génération procédurale de base pour comprendre l’utilisation, bonne documentation sur le format Lottie, exemples de scripts de stickers

J’ai fait un bot simple en partant des exemples de la bibliothèque python-telegram-bot, mais il manquait des explications complètes (et je n’ai pas trouvé sur le net) sur la façon d’utiliser le file_id, méthode recommandée par Telegram, plutôt que d’envoyer plusieurs fois le même fichier. En fait ce file_id est récupéré au premier envoie. Donc, l’idéal est de stocker dans une base ou fichier sur disque l’id des fichiers qui ont déjà été envoyés (et reçu) au premier envoie réussi). Cet exemple crée un fichier contenant l’id à côté de l’image, c’est sans doute un peu plus bourrin que de conserver tous les ID dans une base (sql ou TOML) :

if os.path.exists(idfile):
  fid = open(idfile)
  stickid = fid.read()
  fid.close()
  msg = update.message.reply_sticker(stickid)
else:
  msg = update.message.reply_sticker(open(stickfile, 'rb'))
  fid = open(idfile, 'w')
  fid.write(msg['sticker']['file_id'])
  fid.close()

Lua, TIC-80, LÖVE, etc : Introduction aux systèmes de particles et jeux

English version here
Code par défaut de TIC-80 au lancement

Un langage intéressant pour déveloper des jeux, contenus interactifs et de l’art procédural est le langage de script Lua. C’est un langage fonctionnel simple avec quelques fonctionnalités limitées de langage orienté objet. C’est, en raison de sa simplicité et de la compilation en bytecode au démarrage par défaut, un des plus légers et rapides langages de scripts. Différentes intégrations comme le moteur de jeu/médias et (très puissante) API LÖVE, ainsi que TIC-80, qui est un ordinateur imaginaire virtuel inspiré par PICO-8 (qui utilise également Lua). Ils permettent des prototypages rapides, pour un produit fini dans le même langage ou bien porté plus tard dans l’avancement du projet dans un autre langage. Lua est également utilisé comme langage de systèmes de plug-in (greffons), dans de nombreux jeux, outils, dont des applications de bureau (comme Blender), des applications web (comme MediaWiki derrière Wikimedia), ou dans le mondede l’embarqué. Dans ce dernier domaine, les cartes de contrôle de drones populaires et open-source (logiciels libres à source ouvertes) BetaFlight ou le logiciel de commande-radio Open-TX notamment pour les commandes Taranis. Il y a une documentation en ligne complète de Lua (en anglais) sur le site web officiel. Il est possible d’inclure des fonctions ou bibliothèques en C dans les programmes en Lua programs avec libffi. Elle a été (je crois) crée à l’origine pour Python, avec CFFI, et il a également un support FFI for PHP à présent. Il est également possible d’intégrer des scripts Lua dans des programmes en C. J’ai également découvert en écrivant cet article (merci à l’auteur de TIC-80, qu’il existe également PicoC, un simple interpréteur du langage C, permettant donc un contrôle plus bas niveau/din des structures de données. La taille du binaire est similaire à celle de l’interpréteur de Lua.

Une demo interactive du fonctionnement et utilisation s fonctions trigonométriquesDonc, après plusieurs années à regarder de temps en temps ce langage et ces outils, J’ai commencé à jouer un peu plus avec, vers la fin de 2020, et en quelques mois, je peux dire que j’ai bien progressé dans la programmation temps-réel. Que j’ai réussi à faire et même finir des jeux légers. J’ai donc du étudier de nouveau la trigonométrie de base (suivez ce lien pour explication interactive simple), de l’algèbre vectoriel de base and et quelques autres éléments amusants des mathémtiques, que je considère personnellement comme des jeux de puzzles.

Banner of Falacy Gorx, pseudo 3d game, using lot of tables
J’ai également écrit (en anglais) une court article de making-off sur Itch.io en mars, 2021, pendant une compétition de bœuf de jeu (game jam, dans le même sens que les bœufs musicaux), au lieu de coder (malgré le temps limité à 2 semaines ^^). Tout cela m’a motivé à écrire d’avantage d’articles didactiques à propos de la programmation temps-réel/vectorielle et génération procédurale. Je vais tenter d’écrire une série une série d’articles expliquant les méthodes que j’ai utilisé. Je vais tenter de le faire avec autant de simplicité pour tout le monde, mais quelques connaissances de base en programmation générale et en mathématiques pourront beaucoup aider dans ce champs du développement, comme dans la vie en général.

Je commence donc ici une courte introduction avec ce que j’ai utilisé le plus dans ces développements. Des tables d’éléments et du hazard:,z
* Les tables et leur gestion de base commune, relative à une logique d’animation
* Créer une table
* Génération procédurale du contenu d’une table
* Exemple simple pour nettoyer une table
* Variation et nettoyage d’une table en fonction de tests
* Compacter un peu le code
* Génération procédurale du contenu de la table
* Exemple simple de système de particule graphique

Les tables et leur gestion de base commune, relative à une logique d’animation

Quelques usages généraux de tables dans des applications interactives, objets, personnages, particules

La plupart des choses sont gérées, pour la scalabilitées, sous forme de tables, que ce soit les acteurs principaux (comme les personnages des joueurs), les objets, les particules, les agents actifs, etc. Les objets eux-mêmes, comportent également souvent des sous-éléments sous forme de table, pensez par exemple aux parties du corps d’une chenille.

Donc, la plupart des structures ont généralement quatre fonctions logiques vitales pouvant être gérées de différentes façons :
* Nettoyage d’une table, utilisée lorsque les éléments ne le sont plus, mais peut-être pertinent de l’utiliser également pour initialiser un état général, comme passer du menu au jeu lui même.
* Initialisation de la table, incluans généralement le nettoyage si nécessaire et l’ajout d’1 à n éléments selon les besoins.
* Mise à jour de la table, et des état de ses éléments. Cela inclus leurs relations mécaniques et physiques, leur position, leur changement d’état relatif à leur tracé à l’écran, l’interaction aveec les autres objets, et enfin leur suppression ou création d’un nouvel élément si nécessaire. Tout cela dépendant de critères très variés.
* Sortie du contenu de la table, à l’écran pour l’affichage, mais aussi du son qu’il peut produire, etc.

Les exemples donnés ici peuvent être testé avec l’interpréteur en ligne de commande de Lua, soit en tapant lua dans un shell, puis en les copie-collant, ou sans doute plus pratique, en les collant dans un simple fichier texte, par exemple, fichier.lua, puis en l’exécutant via :

lua fichier.lua

Les derniers exemples utilisent TIC-80, qui est disponible gratuitement et peut être utiliser en Web, mais également pour différents systèmes, tels que Linux, Mac, Windows, Android, Rpi, etc. Il y a une version Pro, mais je n’ai jamais utilisé ses fonctionnalités. Les exemples sont également facilement portable sur d’autres environnements.

Créer une table

On va utiliser une table Lua que l’on appelle objs[] (objs comme objets, j’aime bien mettre un s à la table et pas de s un élément unique). En Lua, il y a une particualarité par rapport aux autre langages informatique. Par défaut, les tables commencent à l’élément 1 lorsque vous les remplissez par objs={elt1,elt2}, mais il est toujours possible d’avoir un éléement ayant pour indexe 0 en utilisantpar exemple objs[0]=elt0. Il est possible de (pre-)initialiser une table video avec objs={}. Si elle n’était pas vide et que ses anciens éléments ne sont plus utilisés, le ramasse miette se chargera de les néttoyer. Ayant pas mal codé en bas niveau (C/C++ et assembleur), je ne suis pas un grand fan des ramasses miettes, mais c’est bien pratique pour les prototypes rapides.

Le contenu de la table peut être simplement affiché à l’écran avec une boucle classique for. Pour la tables appellée objs, #objs peut être utilisé pour connaître le nombre d’éléménts qu’elle contient.

objs={"boule","cube","joueur"}
for i=1,#objs do
 print(objs[i])
end

Génération procédurale du contenu d’une table

Les systèmes de particules sont utilisés ici pour les nuages, montagnes, personnages, et pour les éléments du corps du dragonEn génération procédurale, le meilleur ami du chaos, également appelé Nature, est la fonction aléatoire (random en anglais), qui génère des nombres aléatoires. Dans le cœur de Lua, la fonction math.random est dédié à ça. Elle accépte des paramètres d’étendue, limité à des entiers avec des valeurs croissantes uniquement. Nous pouvons par exemple, décider de générer un nombre compris entre 1 et 6 inclus, pour déterminer le nomrbe d’éléments que nous désirons obtgenir. Si vous essayez plusieurs fois de suite cette foncvtion, elle affichera une valeur différente comprise entre 1 et 6 à chaque fois, comme lors du lancé d’un dé à 6 faces.

print(math.random(1,6))

Dans un programme interactif ou animé, on veut généralement crée des objets aléatoires, et les faire varier suivant une certaine gestion et pendant un certain temps. Nous remplissons donc dans une tables des valeurs initiales aléatoires, qui seront ensuite réutilisées et modules. On doit donc en premier (re-)initialiser une table objs vide afin qu’elle puisse ensuite être remplie par la boucle for.

objs={}
for i=1,math.random(1,6) do
 objs[i]={lt=math.random(1,50)}
end

Nous avons donc généré ici 1 à 6 nombres aléatoires compris entre 1 et 50 et avont assigné ces valeurs à un paramètre appelé lt plutôt qu’à la table directement. Ce type d’élément est accessible de deux façons différentes en Lua, objs[i].lt ou objs[i]["lt"]. La seconde méthode peut être utilie daans certaines situations particulières, nous y reviendrons dans un autre tuto.
so now the table is filled, we can print values several times, they will be kept the same:

for i=1,#objs do
 print("objs["..i.."]="..objs[i].lt)
end

Nous utilisons ici le symbole de concaténation de chaînes de caractères (symbole ..) afin d’afficher d’avantage d’informations à propos de l’élément que nous affichons.

Exemple simple pour nettoyer une table

Il est important, lorsqu’on nettoie une table ou qu’on en retire des éléments en général, d’effectuer la boucle du dernier au premier indexe d’éléments, car lorsqu’un élément est supprimé, l’indexe des éléments qui le suivent dansla table est décrémenté, on risque donc de sauter des éléments, et de supprimer des éléments non désirés.

for i=#objs,1,-1 do
 table.remove(objs,i)
end

Dans la fonction Lua standard table.remove() (signifiant table.supprime()) les argumenst sont le nom de la table suivit de l’index de l’élément à supprimer.

Variation et nettoyage d’une table en fonction de tests

Ça peut être une bonne habitude d’utiliser une variable locale de pointeur avec un nom court pointant sur l’élément à traiter, afin de réduire le code à l’intérieur de la boucle, car on risque en général d’avoir à y accéder plusieurs fois. On supprime ici les éléments, lorsque lt (raccourcit pour lifetime, temps de vie) est arrivé à 0. Dans le cas contraire on le décrémente.

for i=#objs,1,-1 do
 local o=objs[i]
 if o.lt<=0 then
  table.remove(objs,i)
 else
  o.lt=o.lt-1
 end
end

La variable o ne peut être passer à la fonction table.remove(), car elle est utilisée comme pointeur vers unélément de la table, et nom pas comme nom de la table. Le second argument, est l’index de la table correspondant à l’éléemnt, ça n’est donc pas non plus un pointeur vers un élément.

Nous avosn à présent la base générale d’un sytème de particules.

Tous les systems de particules fonctionne avec une génération, utilisant généralement un peu de hasard et un temps de vie comme critère de base des particules. Les autres critères change plus ou moins en fonction du type de particule.

Compacter un peu le code

J’ajoute générallement les varibles ocale à la fin de la la ligne for...do afin d’avoir un code plus lisible et compacte. De la même façon je place les changements, lorsqu’ils ne sont pas trop longs derrières les mots-clés if...then ou else, afin d’avoir un code plus compacte mais toujours lisible :

for i=#objs,1,-1 do local o=objs[i]
 if o.lt<=0 then table.remove(objs,i)
 else o.lt=o.lt-1 end
end

Lua permet également d’assigner facilement un pointeur de fonction à une variable, j’utilise donc généralement la méthode suivante pour compacter d’avantage le code, comme j’utilise beaucoup l’amis chaos :

m=math rnd=m.random

Donc, ici, m est assigné à la bibliothèque math puis rnd à m(ath).random, que l’on peut décrire comme la fonction random de la bibliothèque math.

Attention : Une contrainte est de ne pas utiliser la variable m, entre sa déclaration comme équivalent de math et l’assignation à d’autres variable des fonctions m.*. Il est donc mieux de les définir en tout débbut de code pour ne pasavoir de problmes et de pouvoir utiliser librement la variable m.

Exemple simple de système de particule graphique

Dans cet exemple, nous allons simplement ajouter une distance aléatoire x (axe horizontal) et y (axe vertical) autour d’un point central, ici on choisit (120,70)

On défini donc le point central 120,70 et on varie la position de départ au hazard de -15 à +15 pixels dans chaque direction :

objs[i]={lt=rnd(10,30),x=120+rnd(-15,15),y=70+rnd(-15,15)}

Système de particule basiqueOn fera ensuite varier ces points aléatoirement dans le temps. avec un déplacement d’une distance de 0 à 1 pixel, dans les directions gauche/droite et haut/bas. On utilise donc :
Pour l’axe des x :
* -1 = gauche
* 0 = immobile
* +1 = droite
Pour l’axe des y :
* -1 = haut
* 0 = immobile
* +1 = bas

o.x = o.x+rnd(-1,1) o.y = o.y+rnd(-1,1)

Dans Lua, les fonctions sont définies par function nom_de_la_fonction(arg1,arg2,...) et se terminent par end. Dans le cas de TIC-80, par example, qui a l’avantage d’avoir toutes les fonctionnalités embarquées dans un seul binaire executable, function TIC() est une fonction appelée périodiquement, à chaque rafraichissement d’écran (nouvelle image à l’écran), permettant ainsi d’avoir du cotenu lié au temps (donc de l’animation) facilement. La focntion cls(couleur) y est utilisé pour nettoyer l’écran avec une couleur particulière avec l’inedex de couleur, « couleur », comme TIC-80 utilise une pallette de 16 couleurs indexées sur un total de 16 millions (8bits=256 niveaux pour chaque composante, Rouge, Vert et Bleu. 8^3 ~= 16 millions de couleurs).

l’emplacement des particules est tracé ici par la fonction circ() (raccourcit pour cirle, signiant cercle en anglais).

Définition de la fonction :

circ(centre X, centre Y, rayon, couleur)

On place donc le centre du points aux coordonnées de l’objet, le cercle à un rayon de 1 et utilise l’index de couleur 0,; qi est noir par défaut sous TIC-80.

circ((o.x, o.y, 1, 0)

L’actuelle durée de vie restante de cette particule est affichée à l’aide de la fonction print (imprimer), à sa droite, pour la démonstration de cet exemple. Dans le cas de TIC-80 print(), est utilisé pour placer du texte sur l’écran graphique, et trace() sur la console texte.

La partie de arguments de la définition que nous utilisons de print sont sous cette forme :

print(texte, début X, début Y, couleur, fonte de largeur fixe, échelle, petite font)

Nous plaçons donc comme texte le temps de vie restant de la particule actuelle (o.lt), placé au centre de sa position son centre (o.x, o.y), déplacé dhorizontalement de +2 pixel (donc à droite) et verticalement -2 pixel (donc au dessus). Nous utilisons une largeur de fonte fixe (true), à l’échelle 1, et une fonte compacte (true):

print(o.lt, o.x+2, o.y-2, 2, true, 1, true)

En Lua on peut définir plusieurs variables sur une même ligne, notamment en croisant leur noms et leurs assignation. Par exemple, ici x=5 y=4 peut également être écrit x,y=5,4.

m=math rnd=m.random
t=0
objs={}
for i=1,rnd(5,8) do
 objs[i]={lt=rnd(10,30),x=120+rnd(-15,15),y=70+rnd(-15,15)}
end
function TIC()
 cls(12)
 for i=#objs,1,-1 do local o=objs[i]
  if o.lt<=0 then table.remove(objs,i)
  else o.lt,o.x,o.y=o.lt-1,o.x+rnd(-1,1),o.y+rnd(-1,1)
   circ(o.x,o.y,1,0)
   print(o.lt,o.x+2,o.y-2,2,true,1,true)
  end
 end
 t=t+1
end

Dans l’exemple à charger la génération est placée dans une fonction appelée generate(). Celle-ci estg appelée lorsque le contenu de la table est nul (#objs==0) afin de créer une boucle simple de régénération.

function generate()
 for i=1,rnd(5,8) do
  objs[i]={lt=rnd(10,30),x=120+rnd(-15,15),y=70+rnd(-15,15)}
 end
end

function TIC()
 ...
 if #objs==0 then generate()end
 t=t+1
end

Si vous désirez dans l’exemple téléchargeable, voir les particules sans leur nombre, il suffit que vous commentiez la ligne comportant le print. Pour commenter du code en Lua, il suffit simplement de le précéder de deux traits d’union.

   -- print(o.lt,o.x+2,o.y-2,2,true,1,true)

Android without Google and GNU/Linux with APK

Android will move from Linux kernel and open specs APK format to a more closed package format and to their closed source Fuschia kernel going away from Free Open Source software (FOSS) that make Google, Android, ChromeOS grow, but also that are used on most of the internet servers, on most of supercomputer in the world, on most spacecrafts, on most internet boxes, TVboxes, etc…

GNU/Linux on your phone now

At the same time GNU/Linux distributions for mobile grow a lot the 2 recent years, thanks to availability of PinePhone for developers for about 2 years. There are now at least 15 Linux distributions for phone and applications maturated a lot, including since few month MegaPixels hardware accelerted camera application, that use GL and other processor to accelerate rendering/computing of what camera receive.

PostMarketOS is a GNU/Linux distribution available for a bunch of available market phones, but not everything is managed an all devices.

GNU/Linux with Android APK compatibility layer

Just like there is Wine to run Windows application on GNU/Linux, there are also several systems allowing to run Android APK on your phone if needed. Jusr as Wine, beside the possibility of running Android in a qemu VM, you can also run these application inside your the standard GNU system.

AnBox allow to run APK/Android application inside a snap packaged container.

Google goes away from FOSS, afraid by the lost of market share, and they probably loose a lot from this, as Microsoft does by the past. Chinese company, thanks to Trump restrictions, embrace more FOSS.

Huawei the maker of the probably highest quality phones, that was going in first seller position just ahead Samsung, when Trump blocked it, already very active in Linux kernel and community for years, made HarmonyOS for their devices, a system, that continue tu uses Linux kernel for their power devices, and LiteOS for their low end/old devices. LiteOS is a FOSS embedded devices oriented light kernel. They keep the EMUI User Interface they used previously on Android, moving step by step away from it, and at the same time keep a compatibility layer with current APK open-source format.

Another Chinese company, JingOS, made high quality tablets, based on GNU/Linux distribution, that derivates from Ubuntu, with KDE tools and Android APK application compatibility using Anbox. And they are active with KDE and open source communities. Few demos here and here.

Removing closed source spywares from your current Android system as a transition phase

Beside long time F-droid Free software apk app repository (I use it for year as main source of APK).

You can remove Android pre-installed (memory+cpu+bandwidth thieves) applications, including NSA compliant one like Google, Amazon, FaceBook, Microsoft ones using from a computer with adb. This allowed me to have less bloated phone and to gain few more days of on-battery usage.

List packages:

adb shell pm list packages

See which one are related to facebook:

adb shell pm list packages facebook

For easier copy paste and have only the package name without package: prefix

adb shell pm list packages | cut -d: -f 2

The corresponding application, can be checked by using in a web browser, this does not work for some system applications, for example, for com.google.android.videos:

https://play.google.com/store/apps/details?id=com.google.android.videos

Remove never at least first seen, never used application:

# FaceBook spyware

adb shell pm uninstall --user 0 com.facebook.katana
adb shell pm uninstall --user 0 com.facebook.system
adb shell pm uninstall --user 0 com.facebook.appmanager

# Microsoft stuff
adb shell pm uninstall --user 0 com.microsoft.office.excel
adb shell pm uninstall --user 0 com.microsoft.office.onenote
adb shell pm uninstall --user 0 com.microsoft.office.powerpoint
adb shell pm uninstall --user 0 com.microsoft.office.word
adb shell pm uninstall --user 0 com.microsoft.skydrive # data thief

adb shell pm uninstall --user 0 com.google.android.apps.docs # Google Drive (data thief)
adb shell pm uninstall --user 0 com.google.android.apps.photos # Google photos (data thief)
adb shell pm uninstall --user 0 com.google.android.googlequicksearchbox # Google Search box (behavior thief)
adb shell pm uninstall --user 0 com.google.android.gm # GMail
adb shell pm uninstall --user 0 com.google.android.talk # Hangouts
adb shell pm uninstall --user 0 com.google.android.music # Google Play Music
adb shell pm uninstall --user 0 com.google.android.videos # Google Play Film & Series
adb shell pm uninstall --user 0 com.google.android.youtube # Youtube application

To be checked

adb shell pm uninstall --user 0 com.google.android.gms # Services Google Play

adb shell Dumpsys a dumper and debugger

adb shell dumpsys allow to dump application before removing them and reinstalling it after that.

I still didn’t tried that, will update it after my tests

adb shell dumpsys package package.name # dump package state
adb pm enable —user 0 nom_de_paquet