Veriloggenとcocotbで、PythonのみでVerilogのデザイン・テストを行う。

##はじめに
Verilog書かずにPythonのみでVerilogのデザイン・テストをやってみました。**やってみた系です。** 
テストは[以前書いた](http://qiita.com/tethys_seesaa/items/99504dede7657f1619ce)cocotb、デザインには[Veriloggen](http://shtaxxx.hatenablog.com/entry/2015/06/22/010559)を使用しました。
環境はCentOS6.6になります。

##環境構築
Veriloggenを使用するには、作者さんの[ブログ](http://shtaxxx.hatenablog.com/entry/2015/06/22/010559)の通り、`Pyverilog`, `Jinja2`のインストールが必要になります。
処理系がPython3.3で書かれているため、cocotbとの相性がOS環境も含めて今ひとつよくありません。
よって、ウチの環境では`virtualenv`を使わずに直接インストールしました。

```sh
git clone https://github.com/shtaxxx/Pyverilog
git clone https://github.com/shtaxxx/veriloggen
sudo pip install jinja2
cd Pyverilog
sudo python3 setup.py install
cd ../veriloggen
sudo python3 setup.py install
cd ../
```

cocotbは、[以前](http://qiita.com/tethys_seesaa/items/99504dede7657f1619ce)と同じです。

今回も前回と同様、`example`ディレクトリに新たにワークディレクトリを作成し、そこにrtlおよびtestsディレクトリを作成し、それぞれDUT、テストを保存しました。
違いは、**rtlディレクトリにVerilogファイルではなくPythonファイルにしたことです。**

```shell-session
├── model
│   └── tests.py
├── rtl
│   └── dff.py
└── tests
    ├── Makefile
    └── run.sh
```

##DUT
DUTは前回の8bit順序回路になります。リセットが正論理かつ同期になったのが前回との違いです。

```python:dff.py
import sys, os
from veriloggen import *

def mkdff():
    m = Module('dff')
    width = m.Parameter('WIDTH', 8)
    clk   = m.Input('CLK')
    rst   = m.Input('RST')
    d     = m.Input('D', width)
    q     = m.OutputReg('Q', width)

    m.Always(Posedge(clk))(
        If(rst)(
            q(0)
        ).Else(
            q(d)
        ))

    return m


if __name__ == '__main__':
    dff = mkdff()
    verilog = dff.to_verilog()
    print(verilog)
```

##テスト
リセット極性を前回から変えてみました。

```python:tests.py
import cocotb
from cocotb.triggers import Timer, RisingEdge
from cocotb.result import TestFailure
from cocotb.clock import Clock

import random

class DffTB(object):
    def __init__(self, dut, dubug=True):
        self.dut = dut

    @cocotb.coroutine
    def reset(self, duration=10000):
        self.dut.log.info("Resetting DUT")
        self.dut.RST <= 1
        self.dut.D <= 0
        yield Timer(duration)
        yield RisingEdge(self.dut.CLK)
        self.dut.RST <= 0
        self.dut.log.info("Out of reset")

    @cocotb.coroutine
    def gen_and_check(self):
        D = random.randint(0, 255)
        self.dut.D = D;
        yield RisingEdge(self.dut.CLK)
        yield Timer(1)
        if int(self.dut.Q) != D :
            raise TestFailure(
                "[NG] Compre error. D==%s Q==%s" %  (D, int(self.dut.Q)))
        else :
            self.dut.log.info("[OK]")


@cocotb.coroutine
def clock_gen(signal):
    while True:
        signal <= 0
        yield Timer(5000)
        signal <= 1
        yield Timer(5000)

@cocotb.test()
def basic_test(dut):
    """basic_test"""
    tb = DffTB(dut)
    cocotb.fork(clock_gen(dut.CLK))
    yield RisingEdge(dut.CLK)
    yield tb.reset()

    for i in range(30):
        yield tb.gen_and_check()
```

##実行スクリプト
つなぎ合わせるのが面倒だったのでシェルスクリプトにしました。

```sh:run.sh
#!/bin/bash

RTLPATH=../rtl
if [ -e ${RTLPATH}/dff.v ]; then
  rm ${RTLPATH}/dff.v
fi

python3 ${RTLPATH}/dff.py > ${RTLPATH}/dff.v

make
```

##結果
Veriloggenでは以下のようなVerilogコードが生成されました。

```verilog:dff.v
module dff #
 (
  parameter WIDTH = 8

 )
 (
  input [0:0] CLK, 
input [0:0] RST, 
input [(WIDTH - 1):0] D, 
output reg [(WIDTH - 1):0] Q

 );
  always @(posedge CLK)
  begin        
  if(RST) begin        
  Q <= 0;
end  
else begin        
  Q <= D;
end 
end 
endmodule
```

cocotbによるテスト結果も無事にオッケーです。

```sh
        TESTCASE= TOPLEVEL=dff \
        vvp -M /tmp/cocotb/build/libs/x86_64 -m gpivpi sim_build/sim.vvp   
     -.--ns INFO     cocotb.gpi                                GpiCommon.cpp:47   in gpi_print_registered_impl       VPI registered
     0.00ns INFO     cocotb.gpi                                  gpi_embed.c:229  in embed_sim_init                  Running on Icarus Verilog version 0.9.6 
     0.00ns INFO     cocotb.gpi                                  gpi_embed.c:230  in embed_sim_init                  Python interpreter initialised and cocotb loaded!
     0.00ns INFO     cocotb.gpi                                  __init__.py:103  in _initialise_testbench           Running tests with Cocotb v1.0 from /tmp/cocotb
     0.00ns INFO     cocotb.gpi                                  __init__.py:119  in _initialise_testbench           Seeding Python random module with 1437305945
     0.00ns INFO     cocotb.regression                         regression.py:153  in initialise                      Found test tests.basic_test
     0.00ns INFO     cocotb.regression                         regression.py:254  in execute                         Running test 1/1: basic_test
     0.00ns INFO     ..tb.coroutine.basic_test.0x2353ed0       decorators.py:186  in send                            Starting test: "basic_test"
                                                                                                                               Description: basic_test
     5.00ns INFO     cocotb.dff                                     tests.py:14   in reset                           Resetting DUT
    15.00ns INFO     cocotb.dff                                     tests.py:20   in reset                           Out of reset
    25.00ns INFO     cocotb.dff                                     tests.py:32   in gen_and_check                   [OK]
    35.00ns INFO     cocotb.dff                                     tests.py:32   in gen_and_check                   [OK]

(中略)

   295.00ns INFO     cocotb.dff                                     tests.py:32   in gen_and_check                   [OK]
   305.00ns INFO     cocotb.dff                                     tests.py:32   in gen_and_check                   [OK]
   315.00ns INFO     cocotb.dff                                     tests.py:32   in gen_and_check                   [OK]
   315.00ns INFO     cocotb.regression                         regression.py:201  in handle_result                   Test Passed: basic_test
   315.00ns INFO     cocotb.regression                         regression.py:162  in tear_down                       Passed 1 tests (0 skipped)
   315.00ns INFO     cocotb.regression                         regression.py:168  in tear_down                       Shutting down...
```

##おわりに
今度はAvalonなどのバスを絡めてやってみましょうかねえ…。