FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

cocotbでAXIインタフェースのチェックパタンを書こう

久し振りにVerilogで回路を書き始めている。ユニットずつ実装をしているのだが、まずはAXI経由でメモリにアクセスする回路を書かなければ。 その際、バッファが溢れないようにAXIインタフェースをちゃんと制御できるか、ちゃんと設計結果を検証することは、なかなかに大変だ。 その際、いろんなパタンを、簡単な言語で記述できるとうれしい。そのための言語としてはやはりSystemVerilogが有名だ。SystemVerilogを使えば、Verilog-HDLよりもより簡単にテストパタンを記述できる。

しかし、今回はもっと別のアプローチを取ってみよう。そのために採用したのは、cocotbを用いたPythonでのテストパタン記述だ。 Pythonをインタフェースとしたことで、VPIを経由したインタフェースでVerilogとの協調検証を行うことができる。

cocotbについては、去年もちょっと試していたが、本格的な導入は未経験だった。

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

1. cocotb + icarus Verilogでテストパタンを実行する

f:id:msyksphinz:20160219011823p:plain

上記のような環境を構築する。実際には、テスト対象に対して、Pythonとテストシナリオを記述していくだけだ。テストシナリオを書くと、テストパタンは自動的に構築される。

import cocotb
from cocotb.triggers import Timer, RisingEdge
from cocotb.result import TestFailure
from cocotb.clock import Clock

import random

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

...

@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 = tb_mag_ifu(dut)
    cocotb.fork(clock_gen(dut.CPU_CLK))
    tb.axi_reset()
    tb.reset_br_signal()
    tb.set_pc_init(0xbfc00000)
    yield RisingEdge(dut.CPU_CLK)
    yield tb.reset()

    for i in range(10):
        yield tb.gen_and_check()

上記のPythonのテストシナリオにより、tb.gen_and_check()を10回呼んでいる。この中に実際のAXIのテストパタンが書かれており、AXIの動作をチェックしているという訳だ。

1.1. AXIテストの本体(書きかけ)

実際のgen_and_check()は、現在は書きかけなのだが、大体こんな感じだ。

    def gen_and_check(self):
        yield RisingEdge(self.dut.CPU_CLK)
        yield RisingEdge(self.dut.CPU_CLK)
        if self.dut.IF_MARVALID == 1:
            self.dut.log.info("Detect MARVALID")
        else:
            self.dut.log.info("Wait MARVALID")
        wait_time = random.randint(0, 10)
        for i in range(0, wait_time):
            yield RisingEdge(self.dut.CPU_CLK)
            if self.dut.IF_MARVALID == 0:
                raise TestFailure("[NG] IF_MARVILD goes down")
        self.dut.IF_MARREADY <= 1

IF_MARVALIDが立ち上がる(テスト対象がアドレスチャネルを有効化する)と、それからランダムなタイミングでIF_MARREADYをアサートする。ここでRandomを使えたり、for文で簡単にテスト動作を記述できるのがcocotbの強みだ。 そこから、さらに別のIDとか、VALIDのタイミングなどがチェックできるし、Pythonのライブラリを使ってFIFOを擬似的に作ったりすることができる。 ただし、そこまではまだ構築できていない。

2. 実行結果

makeによりテストパタンを実行すると、以下のような結果が出力された。

        TESTCASE= TOPLEVEL=mag_ifu TOPLEVEL_LANG=verilog \
        vvp -M /home/vagrant/magnetor-1/hardware/cocotb/build/libs/x86_64 -m gpivpi sim_build/sim.vvp  -I /home/vagrant/magnetor-1/hardware/mag_core/mag_ifu/cocotb/../../../common/include/
     -.--ns INFO     cocotb.gpi                                GpiCommon.cpp:91   in gpi_print_registered_impl       VPI registered
     0.00ns INFO     cocotb.gpi                                  gpi_embed.c:248  in embed_sim_init                  Running on Icarus Verilog version 0.9.7
     0.00ns INFO     cocotb.gpi                                  gpi_embed.c:249  in embed_sim_init                  Python interpreter initialised and cocotb loaded!
     0.00ns INFO     cocotb                                      __init__.py:115  in _initialise_testbench           Running tests with Cocotb v1.0 from /home/vagrant/magnetor-1/hardware/cocotb
     0.00ns INFO     cocotb                                      __init__.py:131  in _initialise_testbench           Seeding Python random module with 1455812561
     0.00ns INFO     cocotb.regression                         regression.py:161  in initialise                      Found test tb_mag_ifu.basic_test
     0.00ns INFO     cocotb.regression                         regression.py:262  in execute                         Running test 1/1: basic_test
     0.00ns INFO     ..routine.basic_test.0x2b690cd41910       decorators.py:189  in send                            Starting test: "basic_test"
                                                                                                                               Description: basic_test
/home/vagrant/magnetor-1/hardware/cocotb/cocotb/handle.py:165: UserWarning: Use of log attribute is deprecated
  warnings.warn("Use of %s attribute is deprecated" % name)
     0.00ns INFO     cocotb.mag_ifu                            tb_mag_ifu.py:13   in axi_reset                       Resetting AXI
VCD info: dumpfile dump.vcd opened for output.
     5.00ns INFO     cocotb.mag_ifu                            tb_mag_ifu.py:31   in reset                           Resetting DUT
    15.00ns INFO     cocotb.mag_ifu                            tb_mag_ifu.py:40   in reset                           Out of reset
    35.00ns INFO     cocotb.mag_ifu                            tb_mag_ifu.py:47   in gen_and_check                   Detect MARVALID
   105.00ns WARNING  ..tine.gen_and_check.0x2b690cd86490       decorators.py:117  in send                            [NG] IF_MARVILD should go down
   105.00ns ERROR    cocotb.regression                         regression.py:246  in handle_result                   Test Failed: basic_test (result was TestFailure)
   105.00ns ERROR    cocotb.regression                         regression.py:167  in tear_down                       Failed 1 out of 1 tests (0 skipped)
   105.00ns INFO     cocotb.regression                         regression.py:176  in tear_down                       Shutting down...

failになってしまった!MARVALIDとMARREADYが立ち上がったあと、一度間隔をあけるためにMARVALIDを落とすような仕様にしているのだが、それがうまく動作していないようだ? まあ、それ以外にも、前回と違うIDを出力してくるはずだとか、チェックのやりようはたくさんあるので、その辺を追加していこう。

ちなみに、

今回はIcarus Verilogを利用したのだが、GTKWaveがおそろしく使いにくいので、もっと使いやすい環境に移行したい。

おまけ. Vivado Simulatorの環境を構築することは可能なのか?

調査してみたが、どうやらシミュレータにVPIの環境が備わっている必要があるようだ。現在、Vivado SimulatorはDPIしか装備しておらず、うまくいかない。。。 この辺は、もしかしたらDPIを記述すれば、自力で対応させることができるかもしれない?