{"id":1355,"date":"2020-03-01T17:08:00","date_gmt":"2020-03-01T16:08:00","guid":{"rendered":"http:\/\/www.fabienm.eu\/flf\/?p=1355"},"modified":"2020-03-04T17:08:54","modified_gmt":"2020-03-04T16:08:54","slug":"test-your-chisel-design-in-python-with-cocotb","status":"publish","type":"post","link":"https:\/\/www.fabienm.eu\/flf\/test-your-chisel-design-in-python-with-cocotb\/","title":{"rendered":"Test your Chisel design in python with CocoTB"},"content":{"rendered":"\n<p><a href=\"https:\/\/www.chisel-lang.org\/\">Chisel<\/a> is a hardware description language embedded in <a href=\"https:\/\/scala-lang.org\/\">Scala<\/a> language. Compared to VHDL\/Verilog Chisel it&rsquo;s a high-level language. With Chisel it&rsquo;s easier to parametrize and to abstract your hardware. It&rsquo;s the language used for all <a href=\"https:\/\/www.sifive.com\/\">SiFive<\/a> RISC-V cores and for<a href=\"https:\/\/www.youtube.com\/watch?v=x85342Cny8c\"> Google Edge TPU<\/a>.<\/p>\n\n\n\n<p>What&rsquo;s great with chisel that it generate Verilog sources for synthesis. And we can use this Verilog generated design for simulation or <a href=\"http:\/\/www.fabienm.eu\/flf\/prove-chisel-design-with-yosys-smtbmc\/\">formal prove<\/a>. <\/p>\n\n\n\n<p>Simulation can be done in Scala  with <a href=\"https:\/\/www.chisel-lang.org\/chisel-testers\/\">chisel.testers<\/a>. 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, &#8230;<\/p>\n\n\n\n<p><a href=\"https:\/\/cocotb.readthedocs.io\/en\/latest\/introduction.html#what-is-cocotb\">CocoTB<\/a> is a cosimulation testbench framework written in <a href=\"https:\/\/www.python.org\/\">Python<\/a>. 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 <a href=\"http:\/\/www.fabienm.eu\/flf\/cocotb-modules-libraries\/\">growing library<\/a> of modules available to test devices like <a href=\"https:\/\/github.com\/Martoni\/cocotbext-spi\">SPI<\/a>, <a href=\"https:\/\/github.com\/wallento\/cocomod-wishbone\">Wishbone<\/a>, <a href=\"https:\/\/github.com\/antmicro\/usb-test-suite-cocotb-usb\">USB<\/a>, <a href=\"https:\/\/github.com\/wallento\/cocotbext-uart\">uart<\/a>, &#8230; And its easier to use a library than to reinvent the wheel.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Then, let&rsquo;s write Chisel testbench with CocoTB !<\/h4>\n\n\n\n<p>As an example we will use the <a href=\"https:\/\/github.com\/Martoni\/chisNesPad\">ChisNesPad<\/a> project (yes, same as formal prove <a href=\"http:\/\/www.fabienm.eu\/flf\/prove-chisel-design-with-yosys-smtbmc\/\">article<\/a>). <\/p>\n\n\n\n<p>The directory structure is following :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\n|-- build.sbt  &lt;- scala build configuration\n|-- src\/   &lt;- all chisel sources\n|   |-- main\/\n|       |-- scala\/\n|           |-- chisnespad.scala &lt;- Chisel module we will test\n|           |-- snespadled.scala &lt;- \"top\" module to test with\n|                                    tang nano (gowin)\n|-- formal\/   &lt;- formal directory \n|-- platform\/ &lt;- some usefull files for synthesis with \n|                final platform (gowin).\n|-- cocotb\/   &lt;- python cocotb tests\n    |-- chisnespad\/ &lt;- test for chisnespad core\n    |   |-- Makefile    &lt;- makefile to compile and launch simulation\n    |   |-- test_chisnespad.py &lt;- cocotb stimulis\n    |-- snespadled\/ &lt;- test for \u00abtop\u00bb project that toggle leds\n        |              when push buttons\n        |-- Makefile\n        |-- test_snespadled.py<\/code><\/pre>\n\n\n\n<p>To launch tests we needs following dependencies :<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/github.com\/steveicarus\/iverilog\">Icarus Verilog<\/a> : for simulation<\/li><li><a href=\"https:\/\/www.python.org\/\">Python 3<\/a>: This example use python 3.7. It can be compiled and used with <a href=\"https:\/\/www.pythonforbeginners.com\/basics\/how-to-use-python-virtualenv\">virtualenv<\/a><\/li><li><a href=\"https:\/\/www.scala-sbt.org\/\">sbt<\/a>: The scala build system<\/li><li><a href=\"https:\/\/github.com\/freechipsproject\/chisel3\">Chisel<\/a> : The official website explain how to use it<\/li><li><a href=\"https:\/\/cocotb.readthedocs.io\/en\/latest\/introduction.html\">CocoTB<\/a>: and of course it need CocoTB that can be installed with python (<code>python -m pip install cocotb<\/code>)<\/li><li><a href=\"https:\/\/github.com\/Martoni\/chisverilogutils\/tree\/master\/cocotbify\">cocotbify<\/a>: python package included in repository <a href=\"https:\/\/github.com\/Martoni\/chisverilogutils\">chisverilogutil<\/a>. Its used to inject some Verilog code in top module to generate VCD traces.<\/li><li><a href=\"https:\/\/github.com\/Martoni\/fpgamacro\">fpgamacro<\/a>: it&rsquo;s a Chisel library used to instantiate some fpga-specific RawModule. In our case it&rsquo;s for <a href=\"https:\/\/github.com\/Martoni\/fpgamacro\/blob\/master\/src\/main\/scala\/fpgamacro\/generic\/resetgen.scala\">ResetGen<\/a> block.<\/li><li><a href=\"http:\/\/gtkwave.sourceforge.net\/\">gtkwave<\/a>: VCD waveforms viewer.<\/li><\/ul>\n\n\n\n<p>Once all dependencies installed we can clone the <a href=\"https:\/\/github.com\/Martoni\/chisNesPad\">chisNesPad<\/a> project :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ git clone https:\/\/github.com\/Martoni\/chisNesPad.git<\/code><\/pre>\n\n\n\n<p>Then launch simulation :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n$ cd chisNesPad\/cocotb\/chisnespad\/\n$ make \nmake[1]: Entering directory '\/home\/fabien\/myapp\/chisNesPad\/cocotb\/chisnespad'\n\n[...] lots of compilation lines [...]\n\n\/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 \\\n        TESTCASE= TOPLEVEL=ChisNesPad TOPLEVEL_LANG=verilog COCOTB_SIM=1 \\\n        \/usr\/local\/bin\/vvp -M \/home\/fabien\/myapp\/chisNesPad\/cocotb\/chisnespad\/build\/libs\/x86_64 -m gpivpi sim_build\/sim.vvp   \n     -.--ns INFO     cocotb.gpi                                  gpi_embed.c:103  in embed_init_python               Using virtualenv at \/home\/fabien\/pyenv\/pyenv37\/bin\/python.\n     -.--ns INFO     cocotb.gpi                                GpiCommon.cpp:91   in gpi_print_registered_impl       VPI registered\n     0.00ns INFO     Running tests with Cocotb v1.2.0 from \/home\/fabien\/pyenv\/pyenv37\/lib\/python3.7\/site-packages\n     0.00ns INFO     Seeding Python random module with 1583180134\n     0.00ns INFO     Found test test_chisnespad.always_ready\n     0.00ns INFO     Found test test_chisnespad.double_test\n     0.00ns INFO     Found test test_chisnespad.simple_test\n     0.00ns INFO     Running test 1\/3: always_ready\n     0.00ns INFO     Starting test: \"always_ready\"\n                     Description: None\nVCD info: dumpfile ChisNesPad.vcd opened for output.\n401300.00ns INFO     Test Passed: always_ready\n401300.00ns INFO     Running test 2\/3: double_test\n401300.00ns INFO     Starting test: \"double_test\"\n                     Description: None\n436000.00ns INFO     Value read CAFE\n470420.00ns INFO     Value read DECA\n471440.00ns INFO     Test Passed: double_test\n471440.00ns INFO     Running test 3\/3: simple_test\n471440.00ns INFO     Starting test: \"simple_test\"\n                     Description: None\n506140.00ns INFO     Value read CAFE\n507160.00ns INFO     Test Passed: simple_test\n507160.00ns INFO     Passed 3 tests (0 skipped)\n507160.00ns INFO     *************************************************************************\n                     ** TEST                          PASS\/FAIL  SIM TIME(NS)  REAL TIME(S)  RATIO(NS\/S) **\n                     *************************************************************************\n                     ** test_chisnespad.always_ready    PASS       401300.00          2.78    144519.92  **\n                     ** test_chisnespad.double_test     PASS        70140.00          0.49    143736.56  **\n                     ** test_chisnespad.simple_test     PASS        35720.00          0.25    144120.85  **\n                     **************************************************************************************\n                     \n507160.00ns INFO     *************************************************************************************\n                     **                                 ERRORS : 0                                      **\n                     *************************************************************************************\n                     **                               SIM TIME : 507160.00 NS                           **\n                     **                              REAL TIME : 3.52 S                                 **\n                     **                        SIM \/ REAL TIME : 144276.59 NS\/S                         **\n                     *************************************************************************************\n                     \n507160.00ns INFO     Shutting down...\nmake[1]: Leaving directory '\/home\/fabien\/myapp\/chisNesPad\/cocotb\/chisnespad'\n<\/code><\/pre>\n\n\n\n<p>(Note : I can&rsquo;t find how to change width of code text in this #*\/% <a href=\"https:\/\/www.wordpress.com\/\">wordpress<\/a> )<\/p>\n\n\n\n<p>And we can see wave form with <a href=\"http:\/\/gtkwave.sourceforge.net\/\">gtkwave<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ gtkwave ChisNesPad.vcd<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"278\" src=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2020\/03\/Screenshot-from-2020-03-02-21-26-28-1024x278.png\" alt=\"\" class=\"wp-image-1458\" srcset=\"https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2020\/03\/Screenshot-from-2020-03-02-21-26-28-1024x278.png 1024w, https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2020\/03\/Screenshot-from-2020-03-02-21-26-28-300x82.png 300w, https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2020\/03\/Screenshot-from-2020-03-02-21-26-28-768x209.png 768w, https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2020\/03\/Screenshot-from-2020-03-02-21-26-28.png 1527w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Waveform of CocoTB stimulis for NES Pad controller<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Let&rsquo;s see what happen<\/h4>\n\n\n\n<p>All commands are described in the Makefile  in directory<code> chisNesPad\/cocotb\/chisnespad\/<\/code>. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Chisel Module<\/h4>\n\n\n\n<p>The Chisel Module we test here is in directory <code>src\/main\/scala\/chisnespad\/<\/code> and is named <code>chisnespad.scala<\/code> with following interfaces :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class ChisNesPad (val mainClockFreq: Int = 100,\n                  val clockFreq: Int = 1,\n                  val regLen: Int = 16) extends Module {\n  val io = IO(new Bundle{\n    \/* SNES Pinout *\/\n    val dclock = Output(Bool())\n    val dlatch = Output(Bool())\n    val sdata  = Input(Bool())\n    \/* read\/valid output *\/\n    val data = Decoupled(Output(UInt(16.W)))\n  })\n\/\/...\n}<\/code><\/pre>\n\n\n\n<p>The scala verilog generator driver is given at the end of file :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>object ChisNesPad extends App {\n  println(\"Generating Verilog sources for ChisNesPad Module\")\n  chisel3.Driver.execute(Array[String](), () => new ChisNesPad)\n}<\/code><\/pre>\n\n\n\n<p>This object will be called by <a href=\"https:\/\/www.scala-sbt.org\/\">SBT<\/a> following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sbt \"runMain chisnespad.ChisNesPad\"<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Generated Verilog<\/h4>\n\n\n\n<p>This will generate the Verilog file named <code>ChisNesPad.v<\/code> in root directory. With following interfaces :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>module ChisNesPad(\n  input         clock,\n  input         reset,\n  output        io_dclock,\n  output        io_dlatch,\n  input         io_sdata,\n  input         io_data_ready,\n  output        io_data_valid,\n  output [15:0] io_data_bits\n);\n\/\/...\nendmodule<\/code><\/pre>\n\n\n\n<p>As we can see, all bundled ports are kept but with little modification : dot &lsquo;.&rsquo; are replaced by underscore &lsquo;_&rsquo;. clock and reset has been added and we can retrieve our decoupled signal io.data.{ready, valid, bits} -&gt; io_data_{ready, valid, bits} .<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">CocoTB testbench<\/h4>\n\n\n\n<p>With these changes in mind, we can read\/write our chisel ports signals with CocoTB.<\/p>\n\n\n\n<p>CocoTB tests are described in file <code>test_chisnespad.py<\/code>. This file describe a class to store all method and data for testing ChisNesPad Module then list cocotb test function :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># main class for all test\nclass ChisNesPadTest(object):\n    \"\"\"\n    \"\"\"\n    LOGLEVEL = logging.INFO\n    PERIOD = (20, \"ns\")\n    SUPER_NES_LEN = 16\n    NES_LEN = 8\n\n    def __init__(self, dut, reg_init_value=0xcafe, reg_len=16):\n        if sys.version_info[0] &lt; 3:\n            raise Exception(\"Must be using Python 3\")\n        self._dut = dut\n#...\n# all tests\n@cocotb.test()\ndef simple_test(dut):\n    cnpt = ChisNesPadTest(dut)\n    yield cnpt.reset()\n    yield Timer(1, units=\"us\")\n    dut.io_data_ready &lt;= 1\n#...\n\n@cocotb.test()#skip=True)\ndef double_test(dut):\n    cnpt = ChisNesPadTest(dut)\n    yield cnpt.reset()\n#...\n\n@cocotb.test()\ndef always_ready(dut):\n    cnpt = ChisNesPadTest(dut)\n    yield cnpt.reset()\n#...<\/code><\/pre>\n\n\n\n<p>Here we see tree tests decorated with <code>@cocotb.test()<\/code>. The our module ChisNesPad is the Device Under Test (DUT) and is passed in test function arguments : <code>dut<\/code>. <\/p>\n\n\n\n<p>To access input\/output ports we just have to use dot on our dut object.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>set io.data.ready to logic level &lsquo;1&rsquo; :<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>    dut.io_data_ready &lt;= 1<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>read io.data.bits<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>    vread = int(dut.io_data_bits)<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\"><li>We can also read register under the module or a submodule :<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>    countvalue = int(dut.countReg)<\/code><\/pre>\n\n\n\n<p>It&rsquo;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.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Get Waveform<\/h4>\n\n\n\n<p>All tests can be done with procedure describe above. But with Icarus as simulator we don&rsquo;t get the waveforms.<\/p>\n\n\n\n<p>It&rsquo;s not easy to develop <a href=\"https:\/\/en.wikipedia.org\/wiki\/Hardware_description_language\">HDL<\/a> 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&rsquo;s cheaper to use it.<\/p>\n\n\n\n<p>The solution given in <a href=\"https:\/\/cocotb.readthedocs.io\/en\/latest\/simulator_support.html#waveforms\">CocoTB documentation<\/a> is to add following verilog code in top module :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>`ifdef COCOTB_SIM\ninitial begin\n  $dumpfile (\"ChisNesPad.vcd\");\n  $dumpvars (0, ChisNesPad);\n  #1;\nend\n`endif<\/code><\/pre>\n\n\n\n<p>With this $dumpX() function we will records all signals under the file named <code>ChisNesPad.vcd.<\/code> If we had to add this code by hand each time we re-generate verilog from Chisel module, it would quickly become painful.<\/p>\n\n\n\n<p>That why we use the tool <a href=\"https:\/\/github.com\/Martoni\/chisverilogutils\/tree\/master\/cocotbify\">cocotbify<\/a>, included in package <a href=\"https:\/\/github.com\/Martoni\/chisverilogutils\">chisverilogutils<\/a>. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ cocotbify\nUsages:\ncocotbify.py [options]\n-h, --help             print this help\n-v, --verilog          verilog filename to modify (filename is used \n                       as module name)\n-o, --output filename  output filename<\/code><\/pre>\n\n\n\n<p>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<code> ChisNesPad<strong>Cocotb<\/strong>.v<\/code>. This file will be used by CocoTB and Icarus for simulation. VCD file can then be view with gtkwave:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ gtkwave ChisNesPad.vcd<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"278\" src=\"http:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2020\/03\/Screenshot-from-2020-03-02-21-26-28-1024x278.png\" alt=\"\" class=\"wp-image-1458\" srcset=\"https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2020\/03\/Screenshot-from-2020-03-02-21-26-28-1024x278.png 1024w, https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2020\/03\/Screenshot-from-2020-03-02-21-26-28-300x82.png 300w, https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2020\/03\/Screenshot-from-2020-03-02-21-26-28-768x209.png 768w, https:\/\/www.fabienm.eu\/flf\/wp-content\/uploads\/2020\/03\/Screenshot-from-2020-03-02-21-26-28.png 1527w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Simulation traces<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Conclusion<\/h4>\n\n\n\n<p>As we can see, it&rsquo;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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Chisel is a hardware description language embedded in Scala language. Compared to VHDL\/Verilog Chisel it&rsquo;s a high-level language. With Chisel it&rsquo;s easier to parametrize and to abstract your hardware. It&rsquo;s the language used for all SiFive RISC-V cores and for Google Edge TPU. What&rsquo;s great with chisel that it generate Verilog sources for synthesis. And &hellip; <a href=\"https:\/\/www.fabienm.eu\/flf\/test-your-chisel-design-in-python-with-cocotb\/\" class=\"more-link\">Continuer la lecture de <span class=\"screen-reader-text\">Test your Chisel design in python with CocoTB<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","footnotes":""},"categories":[1],"tags":[57,55,56,8],"class_list":["post-1355","post","type-post","status-publish","format-standard","hentry","category-non-classe","tag-chisel","tag-cocotb","tag-python","tag-scala"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"post-thumbnail":false},"uagb_author_info":{"display_name":"Fabien Marteau","author_link":"https:\/\/www.fabienm.eu\/flf\/author\/admin\/"},"uagb_comment_info":0,"uagb_excerpt":"Chisel is a hardware description language embedded in Scala language. Compared to VHDL\/Verilog Chisel it&rsquo;s a high-level language. With Chisel it&rsquo;s easier to parametrize and to abstract your hardware. It&rsquo;s the language used for all SiFive RISC-V cores and for Google Edge TPU. What&rsquo;s great with chisel that it generate Verilog sources for synthesis. And\u2026","_links":{"self":[{"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/1355","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/comments?post=1355"}],"version-history":[{"count":33,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/1355\/revisions"}],"predecessor-version":[{"id":1480,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/posts\/1355\/revisions\/1480"}],"wp:attachment":[{"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/media?parent=1355"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/categories?post=1355"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fabienm.eu\/flf\/wp-json\/wp\/v2\/tags?post=1355"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}