Archives par mot-clé : python

Test d’une carte Gowin avec LiteX

Après avoir déballé le kit gowin proposé par trenz micro. Il faut trouver quelques chose à faire de plus avancé qu’un simple clignotement de LED. Pour le clignotement de LED et la prise en main des outils, le lecteur se référera à l’article Hackable 32.

Pourquoi ne pas tenter le Linux des FPGA, j’ai nommé LiteX (Prononcez Lahïtixe ) ? LiteX est un framework HDL basé sur Migen pour construire des systèmes matériel facilement en python.

LiteX inclut un langage de description matériel, mais également tous les outils permettant de faire des simulations, la synthèses et générer les bitstreams pour la plupart des FPGA du marché. Bien évidement, en ce qui concerne la synthèse et les bitstreams, LiteX pilote les outils propriétaires des constructeurs. Ce pilotage ne pose généralement pas trop de problème, car tous les outils constructeurs proposent des interfaces en ligne de commande.

La carte que nous allons tenter de faire fonctionner avec LiteX est donc la TEC0117-1 produite par Trenz electronic et munie d’un FPGA gowin .

La carte TEC0117-1 de trenz micro se pilote intégralement avec le port série.

D’après le wiki, la carte n’est pas encore officiellement supporté par LiteX puisqu’elle ne se trouve pas dans la liste du projet LiteX-Boards

Pas dans le tableau de la documentation du moins, car en fouillant dans le code du projet, il semble qu’il y ait déjà un embryon de quelques chose ici. Voila qui est très engageant pour tester la carte.

Voyons donc voir les étapes nous permettant de construire un système avec LiteX.

Installation de LiteX

Le plus simple pour installer LiteX sur son ordinateur est d’aller suivre le guide officiel d’installation.

$ wget https://raw.githubusercontent.com/enjoy-digital/litex/master/litex_setup.py
$ chmod +x litex_setup.py
$ ./litex_setup.py init install --user (--user to install to user directory)

Attention, si comme moi vous avez un pc qui commence à prendre sérieusement de l’âge, sachez que le script litex_setup.py va descendre beaucoup de projets annexes de LiteX. Ça va prendre quelques minutes.

On aura besoin également de gcc compilé pour RISC-V :

$ ./litex_setup.py gcc

Il faudra bien penser à l’exporter à chaque fois qu’on en aura besoin :

$ export PATH=$PATH:$(echo $PWD/riscv64-*/bin/)

Construire le système pour TEC0117

Pour construire un système pour la carte il faut ensuite se rendre dans le répertoire contenant la carte puis lancer le script python correspondant:

$ export PATH=$PATH:$(echo $PWD/riscv64-*/bin/)
$ cd litex-boards/litex_boards/targets
$ python3 trenz_tec0117.py

Pour synthétiser et générer le bitstream il faut d’abord ajouter le lien vers l’ide (1.9.7 minimum) de gowin :

$ export PATH=$PATH:/opt/gowin/1_9_7/IDE/bin/

Puis lancer le build:

$ python litex-boards/litex_boards/targets/trenz_tec0117.py --build
INFO:SoC:        __   _ __      _  __  
INFO:SoC:       / /  (_) /____ | |/_/  
INFO:SoC:      / /__/ / __/ -_)>  <    
INFO:SoC:     /____/_/\__/\__/_/|_|  
INFO:SoC:  Build your hardware, easily!
INFO:SoC:--------------------------------------------------------------------------------

...

Running timing analysis......
[95%] Timing analysis completed
Bitstream generation in progress......
Bitstream generation completed
Running power analysis......
[100%] Power analysis completed
Generate file "/home/fabienm/myapp/litex/build/trenz_tec0117/gateware/impl/pnr/project.power.html" completed
Generate file "/home/fabienm/myapp/litex/build/trenz_tec0117/gateware/impl/pnr/project.pin.html" completed
Generate file "/home/fabienm/myapp/litex/build/trenz_tec0117/gateware/impl/pnr/project.rpt.html" completed
Generate file "/home/fabienm/myapp/litex/build/trenz_tec0117/gateware/impl/pnr/project.rpt.txt" completed
Generate file "/home/fabienm/myapp/litex/build/trenz_tec0117/gateware/impl/pnr/project.tr.html" completed
Fri Jul  9 13:18:07 2021

Le bitstream généré au format *.fs se trouve ensuite dans le répertoire

./build/trenz_tec0117/gateware/impl/pnr/project.fs

On pourra configurer le FPGA directement avec openFPGALoader :

$ openFPGALoader ./build/trenz_tec0117/gateware/impl/pnr/project.fs
Parse ./build/trenz_tec0117/gateware/impl/pnr/project.fs: 
checksum 0x15d3
Done
erase SRAM Done
Flash SRAM: [==================================================] 100.000000%
Done
SRAM Flash: Success

Ou avec la bonne option litex (qui fait appel à openFPGALoader de toute manière):

$ python3 ../litex-boards/litex_boards/targets/trenz_tec0117.py --load
INFO:SoC:        __   _ __      _  __  
INFO:SoC:       / /  (_) /____ | |/_/  
INFO:SoC:      / /__/ / __/ -_)>  <    
INFO:SoC:     /____/_/\__/\__/_/|_|  
INFO:SoC:  Build your hardware, easily!
INFO:SoC:--------------------------------------------------------------------------------
INFO:SoC:Creating SoC... (2021-07-12 19:05:10)
INFO:SoC:--------------------------------------------------------------------------------
INFO:SoC:FPGA device : GW1NR-LV9QN88C6/I5.
INFO:SoC:System clock: 25.00MHz.
INFO:SoCBusHandler:Creating Bus Handler...

...

INFO:SoC:Initializing ROM rom with contents (Size: 0x51cc).
INFO:SoC:Auto-Resizing ROM rom from 0x6000 to 0x51cc.
Parse /home/user/myapp/litex/myapp/build/trenz_tec0117/gateware/impl/pnr/project.fs: 
checksum 0xa3a3
Done
erase SRAM Done
Flash SRAM: [==================================================] 100.000000%
Done
SRAM Flash: Success

La confirmation de la bonne configuration est donnée par le message de la console, mais également par le chenillard de LED rouge.

Console litex>

Le convertisseur USB-uart de la carte possède deux interfaces ttyUSB, la première vient d’être utilisée par openFPGALoader pour charger le bitstream, la seconde permet de se connecter à la console litex :

$ screen /dev/ttyUSB1 115200
litex> reboot


        __   _ __      _  __
       / /  (_) /____ | |/_/
      / /__/ / __/ -_)>  <
     /____/_/\__/\__/_/|_|
   Build your hardware, easily!

 (c) Copyright 2012-2021 Enjoy-Digital
 (c) Copyright 2007-2015 M-Labs

 BIOS built on Jul  9 2021 13:17:06
 BIOS CRC passed (855636f6)

 Migen git sha1: 3ffd64c
 LiteX git sha1: 2b49430f

--=============== SoC ==================--
CPU:            VexRiscv_Lite @ 25MHz
BUS:            WISHBONE 32-bit @ 4GiB
CSR:            32-bit data
ROM:            24KiB
SRAM:           4KiB
L2:             0KiB
SDRAM:          8192KiB 16-bit @ 25MT/s (CL-2 CWL-2)

--========== Initialization ============--
Initializing SDRAM @0x40000000...
Switching SDRAM to software control.
Switching SDRAM to hardware control.
Memtest at 0x40000000 (2.0MiB)...
  Write: 0x40000000-0x40200000 2.0MiB     
   Read: 0x40000000-0x40200000 2.0MiB     
Memtest OK
Memspeed at 0x40000000 (2.0MiB)...
  Write speed: 5.6MiB/s
   Read speed: 6.2MiB/s

--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
             Timeout
No boot medium found

--============= Console ================--

litex> 

La commande help nous donnes les commandes disponibles :

litex> help


LiteX BIOS, available commands:

leds                     - Set Leds value
flush_l2_cache           - Flush L2 cache
flush_cpu_dcache         - Flush CPU data cache
crc                      - Compute CRC32 of a part of the address space
ident                    - Identifier of the system
help                     - Print this help

serialboot               - Boot from Serial (SFL)
reboot                   - Reboot
boot                     - Boot from Memory

mem_speed                - Test memory speed
mem_test                 - Test memory access
mem_copy                 - Copy address space
mem_write                - Write address space
mem_read                 - Read address space
mem_list                 - List available memory regions

sdram_test               - Test SDRAM

Photo traditionnelle

Avec LiteX, une tradition «twitter» veux que l’on fasse une photo du kit démarrant LiteX avec la console démarrée.

Photo trophée de la tec0117 avec LiteX

Et voila \o/

L’entrée en matière est incroyablement facile et fait honneur au slogan «Build your hardware, easily!».

Nous regarderons dans de futur articles ce que l’on peut faire avec.

CocoTB 1.4.0, la maturité

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

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

Sommaire

Mais qu’est‑ce que c’est ?

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

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

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

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

Les changements dans le code

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

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

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

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

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

Cette écriture restant cependant valable.

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

Installation

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

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

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

cocotb-config --version
1.4.0

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

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

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

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

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

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

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

   $ gtkwave button_deb.vcd
Vue de traces VCD avec GTKWave

Une organisation qui tourne

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

Les statistiques de modification de cette version sont les suivantes :

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

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

Test your Chisel design in python with CocoTB

Chisel is a hardware description language embedded in Scala language. Compared to VHDL/Verilog Chisel it’s a high-level language. With Chisel it’s easier to parametrize and to abstract your hardware. It’s the language used for all SiFive RISC-V cores and for Google Edge TPU.

What’s great with chisel that it generate Verilog sources for synthesis. And we can use this Verilog generated design for simulation or formal prove.

Simulation can be done in Scala with chisel.testers. But this tester is mostly under development project for the moment. And there is no test library for common busses and devices like SPI, Wishbone, AXI, PWM, …

CocoTB is a cosimulation testbench framework written in Python. Main advantage of CocoTB is that you write your testbench stimulis in python language. Python is really comfortable programming language. The other advantage of using CocoTB is that there is a growing library of modules available to test devices like SPI, Wishbone, USB, uart, … And its easier to use a library than to reinvent the wheel.

Then, let’s write Chisel testbench with CocoTB !

As an example we will use the ChisNesPad project (yes, same as formal prove article).

The directory structure is following :

/
|-- build.sbt  <- scala build configuration
|-- src/   <- all chisel sources
|   |-- main/
|       |-- scala/
|           |-- chisnespad.scala <- Chisel module we will test
|           |-- snespadled.scala <- "top" module to test with
|                                    tang nano (gowin)
|-- formal/   <- formal directory 
|-- platform/ <- some usefull files for synthesis with 
|                final platform (gowin).
|-- cocotb/   <- python cocotb tests
    |-- chisnespad/ <- test for chisnespad core
    |   |-- Makefile    <- makefile to compile and launch simulation
    |   |-- test_chisnespad.py <- cocotb stimulis
    |-- snespadled/ <- test for «top» project that toggle leds
        |              when push buttons
        |-- Makefile
        |-- test_snespadled.py

To launch tests we needs following dependencies :

  • Icarus Verilog : for simulation
  • Python 3: This example use python 3.7. It can be compiled and used with virtualenv
  • sbt: The scala build system
  • Chisel : The official website explain how to use it
  • CocoTB: and of course it need CocoTB that can be installed with python (python -m pip install cocotb)
  • cocotbify: python package included in repository chisverilogutil. Its used to inject some Verilog code in top module to generate VCD traces.
  • fpgamacro: it’s a Chisel library used to instantiate some fpga-specific RawModule. In our case it’s for ResetGen block.
  • gtkwave: VCD waveforms viewer.

Once all dependencies installed we can clone the chisNesPad project :

$ git clone https://github.com/Martoni/chisNesPad.git

Then launch simulation :


$ cd chisNesPad/cocotb/chisnespad/
$ make 
make[1]: Entering directory '/home/fabien/myapp/chisNesPad/cocotb/chisnespad'

[...] lots of compilation lines [...]

/myapp/chisNesPad/cocotb/chisnespad/build/libs/x86_64:/usr/local/lib:/usr/local/lib:/usr/local/lib:/usr/local/lib:/usr/local/lib MODULE=test_chisnespad \
        TESTCASE= TOPLEVEL=ChisNesPad TOPLEVEL_LANG=verilog COCOTB_SIM=1 \
        /usr/local/bin/vvp -M /home/fabien/myapp/chisNesPad/cocotb/chisnespad/build/libs/x86_64 -m gpivpi sim_build/sim.vvp   
     -.--ns INFO     cocotb.gpi                                  gpi_embed.c:103  in embed_init_python               Using virtualenv at /home/fabien/pyenv/pyenv37/bin/python.
     -.--ns INFO     cocotb.gpi                                GpiCommon.cpp:91   in gpi_print_registered_impl       VPI registered
     0.00ns INFO     Running tests with Cocotb v1.2.0 from /home/fabien/pyenv/pyenv37/lib/python3.7/site-packages
     0.00ns INFO     Seeding Python random module with 1583180134
     0.00ns INFO     Found test test_chisnespad.always_ready
     0.00ns INFO     Found test test_chisnespad.double_test
     0.00ns INFO     Found test test_chisnespad.simple_test
     0.00ns INFO     Running test 1/3: always_ready
     0.00ns INFO     Starting test: "always_ready"
                     Description: None
VCD info: dumpfile ChisNesPad.vcd opened for output.
401300.00ns INFO     Test Passed: always_ready
401300.00ns INFO     Running test 2/3: double_test
401300.00ns INFO     Starting test: "double_test"
                     Description: None
436000.00ns INFO     Value read CAFE
470420.00ns INFO     Value read DECA
471440.00ns INFO     Test Passed: double_test
471440.00ns INFO     Running test 3/3: simple_test
471440.00ns INFO     Starting test: "simple_test"
                     Description: None
506140.00ns INFO     Value read CAFE
507160.00ns INFO     Test Passed: simple_test
507160.00ns INFO     Passed 3 tests (0 skipped)
507160.00ns INFO     *************************************************************************
                     ** TEST                          PASS/FAIL  SIM TIME(NS)  REAL TIME(S)  RATIO(NS/S) **
                     *************************************************************************
                     ** test_chisnespad.always_ready    PASS       401300.00          2.78    144519.92  **
                     ** test_chisnespad.double_test     PASS        70140.00          0.49    143736.56  **
                     ** test_chisnespad.simple_test     PASS        35720.00          0.25    144120.85  **
                     **************************************************************************************
                     
507160.00ns INFO     *************************************************************************************
                     **                                 ERRORS : 0                                      **
                     *************************************************************************************
                     **                               SIM TIME : 507160.00 NS                           **
                     **                              REAL TIME : 3.52 S                                 **
                     **                        SIM / REAL TIME : 144276.59 NS/S                         **
                     *************************************************************************************
                     
507160.00ns INFO     Shutting down...
make[1]: Leaving directory '/home/fabien/myapp/chisNesPad/cocotb/chisnespad'

(Note : I can’t find how to change width of code text in this #*/% wordpress )

And we can see wave form with gtkwave:

$ gtkwave ChisNesPad.vcd
Waveform of CocoTB stimulis for NES Pad controller

Let’s see what happen

All commands are described in the Makefile in directory chisNesPad/cocotb/chisnespad/.

Chisel Module

The Chisel Module we test here is in directory src/main/scala/chisnespad/ and is named chisnespad.scala with following interfaces :

class ChisNesPad (val mainClockFreq: Int = 100,
                  val clockFreq: Int = 1,
                  val regLen: Int = 16) extends Module {
  val io = IO(new Bundle{
    /* SNES Pinout */
    val dclock = Output(Bool())
    val dlatch = Output(Bool())
    val sdata  = Input(Bool())
    /* read/valid output */
    val data = Decoupled(Output(UInt(16.W)))
  })
//...
}

The scala verilog generator driver is given at the end of file :

object ChisNesPad extends App {
  println("Generating Verilog sources for ChisNesPad Module")
  chisel3.Driver.execute(Array[String](), () => new ChisNesPad)
}

This object will be called by SBT following command:

$ sbt "runMain chisnespad.ChisNesPad"

Generated Verilog

This will generate the Verilog file named ChisNesPad.v in root directory. With following interfaces :

module ChisNesPad(
  input         clock,
  input         reset,
  output        io_dclock,
  output        io_dlatch,
  input         io_sdata,
  input         io_data_ready,
  output        io_data_valid,
  output [15:0] io_data_bits
);
//...
endmodule

As we can see, all bundled ports are kept but with little modification : dot ‘.’ are replaced by underscore ‘_’. clock and reset has been added and we can retrieve our decoupled signal io.data.{ready, valid, bits} -> io_data_{ready, valid, bits} .

CocoTB testbench

With these changes in mind, we can read/write our chisel ports signals with CocoTB.

CocoTB tests are described in file test_chisnespad.py. This file describe a class to store all method and data for testing ChisNesPad Module then list cocotb test function :

# main class for all test
class ChisNesPadTest(object):
    """
    """
    LOGLEVEL = logging.INFO
    PERIOD = (20, "ns")
    SUPER_NES_LEN = 16
    NES_LEN = 8

    def __init__(self, dut, reg_init_value=0xcafe, reg_len=16):
        if sys.version_info[0] < 3:
            raise Exception("Must be using Python 3")
        self._dut = dut
#...
# all tests
@cocotb.test()
def simple_test(dut):
    cnpt = ChisNesPadTest(dut)
    yield cnpt.reset()
    yield Timer(1, units="us")
    dut.io_data_ready <= 1
#...

@cocotb.test()#skip=True)
def double_test(dut):
    cnpt = ChisNesPadTest(dut)
    yield cnpt.reset()
#...

@cocotb.test()
def always_ready(dut):
    cnpt = ChisNesPadTest(dut)
    yield cnpt.reset()
#...

Here we see tree tests decorated with @cocotb.test(). The our module ChisNesPad is the Device Under Test (DUT) and is passed in test function arguments : dut.

To access input/output ports we just have to use dot on our dut object.

  • set io.data.ready to logic level ‘1’ :
    dut.io_data_ready <= 1
  • read io.data.bits
    vread = int(dut.io_data_bits)
  • We can also read register under the module or a submodule :
    countvalue = int(dut.countReg)

It’s also possible to write register under the module, but be careful of the race condition when you doing that. It can be re-written by simulation with 0-delay.

Get Waveform

All tests can be done with procedure describe above. But with Icarus as simulator we don’t get the waveforms.

It’s not easy to develop HDL without any waveform. To get waveform we can use another simulator that will generate the traces (mainly in VCD format) but Icarus is mature and free then it’s cheaper to use it.

The solution given in CocoTB documentation is to add following verilog code in top module :

`ifdef COCOTB_SIM
initial begin
  $dumpfile ("ChisNesPad.vcd");
  $dumpvars (0, ChisNesPad);
  #1;
end
`endif

With this $dumpX() function we will records all signals under the file named ChisNesPad.vcd. If we had to add this code by hand each time we re-generate verilog from Chisel module, it would quickly become painful.

That why we use the tool cocotbify, included in package chisverilogutils.

$ cocotbify
Usages:
cocotbify.py [options]
-h, --help             print this help
-v, --verilog          verilog filename to modify (filename is used 
                       as module name)
-o, --output filename  output filename

This tool will take a verilog source as input and generate an output with dumpvars code added for cocotb. In the example makefile the output name will be ChisNesPadCocotb.v. This file will be used by CocoTB and Icarus for simulation. VCD file can then be view with gtkwave:

$ gtkwave ChisNesPad.vcd
Simulation traces

Conclusion

As we can see, it’s perfectly possible to use CocoTB framework for testing Chisel components. CocoTB has more library test modules available than chisel.tester and we can code in Python. Python is used by lots of peoples through the world and is less scary than Scala or SystemVerilog for hardware engineers that develop digital hardware.

Cocotb Tips

Some tips for python HDL test module Cocotb.

Read and Write signal

Write:

clk.value = 1
dut.input_signal <= 12
dut.sub_block.memory.array[4] <= 2

Read:

count = dut.counter.value
print(count.binstr)
print(count.integer)
print(count.n_bits)
print(int(dut.counter))

See it under the official documentation.

Yielding a coroutine in a select list fashion

Question asked on stackoverflow.

Using latest python version with virtualenv

If you compile python yourself, don’t forget to add option --enable-shared at configure time (./configure --enable-shared)

$ virtualenv --python=/usr/local/bin/python3.7 ~/envp37
$ source ~/envp37/bin/activate
$ python -m pip install cocotb

Do not forget to re-source your environnement each time you open a new terminal :

$ source ~/envp37/bin/activate

Logging messages and main test class template

This is a template for declaring a class used for test in function @cocotb.test() :

import logging
from cocotb import SimLog
...

class MyDUTNameTest(object):
    """ Test class for MyDUTName"""
    LOGLEVEL = logging.INFO
    # clock frequency is 50Mhz
    PERIOD = (20, "ns")

    def __init__(self):
        if sys.version_info[0] < 3: # because python 2.7 is obsolete
            raise Exception("Must be using Python 3")
        self._dut = dut
        self.log = SimLog("RmiiDebug.{}".format(self.__class__.__name__))
        self.log.setLevel(self.LOGLEVEL)
        self._dut._log.setLevel(self.LOGLEVEL)
        self.clock = Clock(self._dut.clock, self.PERIOD[0], self.PERIOD[1])
        self._clock_thread = cocotb.fork(self.clock.start())

# ....

@cocotb.test()
def my_test(dut):
    mdutn = MyDUTNameTest()
    mdutn.log.info("Launching my test")

Register size in bits

How to calculate register size in bits N

Chisel3 : log2Ceil()

import chisel3.util.log2Ceil

val Nsize = log2Ceil(N)

Verilog : $clog2()

 parameter NSIZE = $clog2(N);

VHDL : ceil(log2()

use IEEE.math_real."ceil";
use IEEE.math_real."log2";

Nsize := integer(ceil(log2(real(N))));

Python: math.ceil(math.log(N+1, 2))

import math
Nsize = math.ceil(math.log(N, 2))

CλaSH: ?

SystemC/C++: ceil(log2())

#include <math.h>       /* ceil and log */
Nsize = ceil(log2(N));