URL:
https://linuxfr.org/news/simplifier-la-visualisation-de-chronogrammes
Title: Simplifier la visualisation de chronogrammes
Authors: Christophe Clienti
Davy Defaud, palm123 et gUI
Date: 2019-11-09T12:10:16+01:00
License: CC by-sa
Tags: fpga, verilog et hdl
Score: 4
Le développement avec des langages de description matériel, le (System)Verilog par exemple, nécessite très souvent de visualiser les chronogrammes afin de vérifier le comportement du composant en développement. Ces chronogrammes sont générés par un simulateur tel qu’[Icarus](
http://iverilog.icarus.com/) et [GHDL](
https://github.com/ghdl/ghdl), pour les versions libres, ou bien encore par [ModelSim](
https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=2ahUKEwj7rvvkgeDlAhUPaBoKHZ4MBsMQFjAAegQIABAB&url=https%3A%2F%2Ffr.wikipedia.org%2Fwiki%2FModelSim&usg=AOvVaw3kRJJ0P8geA9YIMlm9lkKs), [VCS](
https://www.synopsys.com/verification/simulation/vcs.html) et consorts, pour les versions propriétaires.
[GTKWave](
https://en.wikipedia.org/wiki/Waveform_viewer) est la référence dans le monde du logiciel libre pour afficher les chronogrammes, mais il existe également autant d’afficheurs que de simulateurs propriétaires. De très nombreux développeurs ont sans doute été confrontés au côté rébarbatif de l’insertion à la souris des signaux à observer. GTKWave, ainsi que les simulateurs propriétaires, embarquent un interpréteur de langage [Tcl](
https://fr.wikipedia.org/wiki/Tool_Command_Language "Tool Command Language") afin de faciliter l’édition des signaux à observer avec, bien sûr, une syntaxe différente à chaque fois…
J’ai donc décidé de créer le module Python [Wavedisp](
https://github.com/cclienti/wavedisp) permettant de décrire hiérarchiquement les signaux à observer, ainsi que de procéder à la génération de scripts d’affichage pour différents outils de visualisation, dont GTKWave.
----
[Page GitHub du projet Wavedisp](
https://github.com/cclienti/wavedisp)
[Page Web de Wavedisp](
https://wavecruncher.net/wavedisp)
[Page Wikipédia anglaise de GTKWave](
https://en.wikipedia.org/wiki/Waveform_viewer)
[Wavedisp sur PyPI](
https://pypi.org/project/wavedisp/)
----
Introduction
============
[Wavedisp](
https://github.com/cclienti/wavedisp) est un ensemble de classes Python pour vous aider à générer les scripts d’affichage pour différents outils de visualisation de chronogrammes. Au sein d’un fichier _Wave_, il vous permet de sélectionner facilement les signaux de vos composants à afficher.
En plus de déclarer les signaux, il permet également d’ajouter des groupes, de spécifier la hiérarchie des composants, d’ajouter des barres de division et aussi d’inclure d’autres fichiers _Wave_. Il prend en charge la génération des fichiers Tcl pour GTKWave, ModelSim (Mentor) et RivieraPro (Aldec).
Installation
============
Le module Wavedisp est disponible dans le [dépôt PyPI](
https://pypi.org/project/wavedisp/) :
```bash
python3 -m venv wavedisp-venv
pip install wavedisp
```
Description d’un fichier Wave
=============================
Généralement, les projets de description de matériel reposent sur une hiérarchie de modules et de tests. Supposons que nous avons réalisé une file d’attente ([FIFO](
https://fr.wikipedia.org/wiki/File_(structure_de_donn%C3%A9es) "File d’attente")) qui utilise deux modules pour coder et décoder le [code de Gray](
https://fr.wikipedia.org/wiki/Code_de_Gray) depuis et vers le code binaire.
La hiérarchie du projet est la suivante :
```
dclkfifolut_tb (test)
├── dclkfifolut (composant principal)
├── gray2bin (sous‑composant)
├── bin2gray (sous‑composant)
```
Un premier test
---------------
Les sous‐composants _gray2bin_ et _bin2gray_ disposent eux aussi de leurs propres fichiers de test et nous proposons de décrire dans un premier temps les fichiers _Wave_ pour un de ces sous‑modules.
Le code ci-dessous montre la structure du fichier _Wave_ (`gray2bin.wave.py`) pour le composant _gray2bin_ :
```python
from wavedisp.ast import *
def generator():
blk = Block()
blk.add(Disp('gray', radix='hexadecimal'))
blk.add(Disp('bin', radix='hexadecimal'))
return blk
```
On remarque dans le code ci‑dessus qu’il faut en premier lieu importer les classes permettant d’instancier notre arbre de syntaxe (_AST_). Il faut également déclarer une fonction _generator_ afin de retourner le point d’entrée de notre _AST_.
Le nœud _Disp_ permet d’afficher un signal avec différentes propriétés :
- _radix_ (_hexadecimal_, _decimal_, _octal_ ou _binary_) ;
- _color_ ;
- _height_.
Le nœud _Block_ ne représentera rien dans le code généré mais permet de définir un ensemble de propriétés par défaut pour tous ses enfants. Charge à ces derniers de les redéfinir ou non. Tous les nœuds ayant des enfants rattachés ont le même comportement que _Block_.
Procédons maintenant à l’écriture du fichier _Wave_ (`gray2bin_tb.wave.py`) pour le test de notre module _gray2bin_ :
```python
from wavedisp.ast import *
def generator():
hier = Hierarchy('/gray2bin_tb')
inst = hier.add(Hierarchy('gray2bin_inst'))
inst.include('gray2bin_wave.py')
return hier
```
Dans ce dernier fichier nous avons ajouté un nœud déclarant la hiérarchie de notre test ainsi qu’une inclusion vers le fichier _Wave_ `gray2bin_wave.py` déclarant les signaux du module testé.
L’inclusion de fichiers _Wave_ permet de séparer la déclaration des chronogrammes pour un module de test de ceux pour un module synthétisable. Cela permet donc de hiérarchiser nos fichiers _Wave_ avec la même structure que celle du projet.
Nous pouvons maintenant produire un fichier _dot_ représentant notre _AST_ :
```bash
wavedisp -t dot -o wave.dot gray2bin_tb.wave.py
```

La commande suivante génère le script Tcl pour note visualiseur GTKWave :
```bash
wavedisp -t gtkwave -o wave.tcl gray2bin_tb.wave.py
```
Le contenu du fichier `wave.tcl` est le suivant :
```tcl
# Wavedisp generated gtkwave file
gtkwave::/Edit/Set_Trace_Max_Hier 0
gtkwave::addSignalsFromList [list {gray2bin_tb.gray2bin_inst.gray}]
gtkwave::/Edit/Data_Format/Hex {gray2bin_tb.gray2bin_inst.gray}
gtkwave::addSignalsFromList [list {gray2bin_tb.gray2bin_inst.bin}]
gtkwave::/Edit/Data_Format/Hex {gray2bin_tb.gray2bin_inst.bin}
gtkwave::/Edit/Set_Trace_Max_Hier 1
```
Un cas plus ambitieux
---------------------
Nous pouvons maintenant décrire le fichier _Wave_ pour notre projet complet, à savoir la file d’attente ([FIFO](
https://fr.wikipedia.org/wiki/File_(structure_de_donn%C3%A9es) "First in, first out")) qui instancie plusieurs sous‑composants :
```python
from wavedisp.ast import *
def generator():
block = Block()
block.add(Disp('rclk', radix='binary'))
block.add(Disp('rsrst', radix='binary'))
block.add(Disp('ren', radix='binary'))
block.add(Disp('rdata', radix='hexadecimal'))
block.add(Disp('rlevel', radix='hexadecimal'))
block.add(Disp('rempty', radix='binary'))
block.add(Disp('wclk', radix='binary'))
block.add(Disp('wsrst', radix='binary'))
block.add(Disp('wen', radix='binary'))
block.add(Disp('wdata', radix='hexadecimal'))
block.add(Disp('wlevel', radix='hexadecimal'))
block.add(Disp('wfull', radix='binary'))
read_ptr_gray = block.add(Hierarchy('read_ptr_gray')).add(Group('read_ptr_gray'))
read_ptr_gray.include('../../bin2gray/project/bin2gray_wave.py')
write_ptr_gray = block.add(Hierarchy('write_ptr_gray')).add(Group('write_ptr_gray'))
write_ptr_gray.include('../../bin2gray/project/bin2gray_wave.py')
read_ptr_bin = block.add(Hierarchy('read_ptr_bin')).add(Group('read_ptr_bin'))
read_ptr_bin.include('../../gray2bin/project/gray2bin_wave.py')
write_ptr_bin = block.add(Hierarchy('write_ptr_bin')).add(Group('write_ptr_bin'))
write_ptr_bin.include('../../gray2bin/project/gray2bin_wave.py')
return block
```
Le fichier _Wave_ pour le test est séparé dans un fichier à part pour les raisons énoncées dans la section précédente :
```python
from wavedisp.ast import *
def generator():
hier = Hierarchy('/dclkfifolut_tb', color='blue')
inst = hier.add(Hierarchy('DUT'))
inst.include('dclkfifolut_wave.py')
return hier
```
La commande suivante génère le script Tcl pour le simulateur ModelSim :
```bash
$ wavedisp -t modelsim -o wave.tcl dclkfifolut_tb_wave.py
```
Le contenu du fichier généré est le suivant :
```tcl
# Wavedisp generated modelsim file
onerror {resume}
add wave -radix binary -color #0000ff {/dclkfifolut_tb/DUT/rclk}
add wave -radix binary -color #0000ff {/dclkfifolut_tb/DUT/rsrst}
add wave -radix binary -color #0000ff {/dclkfifolut_tb/DUT/ren}
add wave -radix hex -color #0000ff {/dclkfifolut_tb/DUT/rdata}
add wave -radix hex -color #0000ff {/dclkfifolut_tb/DUT/rlevel}
add wave -radix binary -color #0000ff {/dclkfifolut_tb/DUT/rempty}
add wave -radix binary -color #0000ff {/dclkfifolut_tb/DUT/wclk}
add wave -radix binary -color #0000ff {/dclkfifolut_tb/DUT/wsrst}
add wave -radix binary -color #0000ff {/dclkfifolut_tb/DUT/wen}
add wave -radix hex -color #0000ff {/dclkfifolut_tb/DUT/wdata}
add wave -radix hex -color #0000ff {/dclkfifolut_tb/DUT/wlevel}
add wave -radix binary -color #0000ff {/dclkfifolut_tb/DUT/wfull}
add wave -radix hex -color #0000ff -group {read_ptr_gray} {/dclkfifolut_tb/DUT/read_ptr_gray/bin}
add wave -radix hex -color #0000ff -group {read_ptr_gray} {/dclkfifolut_tb/DUT/read_ptr_gray/gray}
add wave -radix hex -color #0000ff -group {write_ptr_gray} {/dclkfifolut_tb/DUT/write_ptr_gray/bin}
add wave -radix hex -color #0000ff -group {write_ptr_gray} {/dclkfifolut_tb/DUT/write_ptr_gray/gray}
add wave -radix hex -color #0000ff -group {read_ptr_bin} {/dclkfifolut_tb/DUT/read_ptr_bin/gray}
add wave -radix hex -color #0000ff -group {read_ptr_bin} {/dclkfifolut_tb/DUT/read_ptr_bin/bin}
add wave -radix hex -color #0000ff -group {write_ptr_bin} {/dclkfifolut_tb/DUT/write_ptr_bin/gray}
add wave -radix hex -color #0000ff -group {write_ptr_bin} {/dclkfifolut_tb/DUT/write_ptr_bin/bin}
update
```
Conclusion
==========
Le module Python [Wavedisp](
https://github.com/cclienti/wavedisp) permet donc simplement de gérer l’édition de vos chronogrammes dans des environnements complexes et variés, à la fois en termes de hiérarchie de composants, mais aussi en termes d’outils de simulation HDL.
Ce module est encore jeune et des retours sont les bienvenus, soit via des commentaires ou sur GitHub.