Added test bench for nmigen TAP with cocotb.
authorStaf Verhaegen <staf@stafverhaegen.be>
Mon, 16 Dec 2019 10:00:04 +0000 (11:00 +0100)
committerStaf Verhaegen <staf@stafverhaegen.be>
Mon, 16 Dec 2019 15:41:49 +0000 (16:41 +0100)
Based on vhdl controller test bench.

.gitignore
test/nmigen/cocotb/controller/.gitignore [new file with mode: 0644]
test/nmigen/cocotb/controller/Makefile [new file with mode: 0644]
test/nmigen/cocotb/controller/generate.py [new file with mode: 0755]
test/nmigen/cocotb/controller/test.py [new file with mode: 0644]

index 7d37e8a8693e91ff0b6347c4fa396eb7b629109d..0cc5bdd54203665ce429aaeb0b899ac9a62d693f 100644 (file)
@@ -6,4 +6,5 @@ results.xml
 *.ghw
 sim/ghdl/bench_idcode
 build
+sim_build
 *.egg-info
\ No newline at end of file
diff --git a/test/nmigen/cocotb/controller/.gitignore b/test/nmigen/cocotb/controller/.gitignore
new file mode 100644 (file)
index 0000000..edb2279
--- /dev/null
@@ -0,0 +1,2 @@
+code
+top.vcd
diff --git a/test/nmigen/cocotb/controller/Makefile b/test/nmigen/cocotb/controller/Makefile
new file mode 100644 (file)
index 0000000..1257cb2
--- /dev/null
@@ -0,0 +1,80 @@
+CURDIR=$(realpath .)
+TOPDIR=$(realpath ../../../..)
+
+ifeq ($(PYTHONPATH),)
+  PYTHONPATH := $(TOPDIR)
+else
+  PYTHONPATH := $(TOPDIR):$(PYTHONPATH)
+endif
+export PYTHONPATH
+
+TOPLEVEL := top
+
+CODEDIR := $(CURDIR)/code
+TOPFILE := $(CODEDIR)/$(TOPLEVEL).v
+TOPVCD := $(CURDIR)/$(TOPLEVEL).vcd
+
+#
+# COCOTB
+#
+VHDL_SOURCES = \
+  $(CODEDIR)/jtag/c4m_jtag_pkg.vhdl \
+  $(CODEDIR)/jtag/c4m_jtag_tap_fsm.vhdl \
+  $(CODEDIR)/jtag/c4m_jtag_irblock.vhdl \
+  $(CODEDIR)/jtag/c4m_jtag_iocell.vhdl \
+  $(CODEDIR)/jtag/c4m_jtag_ioblock.vhdl \
+  $(CODEDIR)/jtag/c4m_jtag_idblock.vhdl \
+  $(CODEDIR)/jtag/c4m_jtag_tap_controller.vhdl \
+  $(CODEDIR)/jtag/jtag_controller_i0.vhdl \
+#VHDL_SOURCES end
+VERILOG_SOURCES = \
+  $(TOPFILE) \
+#VERILOG_SOURCES end
+ALL_SOURCES := $(VHDL_SOURCES) $(VERILOG_SOURCES)
+TOPLEVEL_LANG := verilog
+MODULE := test
+SIM := modelsim
+ARCH := i686
+VCOM_ARGS := -2008
+WAVES := 1
+
+COCOTBMAKEFILESDIR=$(shell cocotb-config --makefiles)
+
+# Add top target to convert output to vcd
+top: $(TOPVCD)
+
+include $(COCOTBMAKEFILESDIR)/Makefile.inc
+include $(COCOTBMAKEFILESDIR)/Makefile.sim
+
+
+#
+# Code generation
+#
+.PHONY: rtl
+rtl: $(ALL_SOURCES)
+$(ALL_SOURCES): generate_once
+
+GENERATE := ./generate.py
+TOPDEPS := \
+  $(TOPDIR)/c4m/nmigen/jtag/tap.py \
+#TOPDEPS end
+
+.INTERMEDIATE: generate_once
+generate_once: $(GENERATE) $(TOPDEPS) | $(CODEDIR)/jtag
+       @echo "Generating RTL"
+       @$(GENERATE)
+
+$(CODEDIR)/jtag:
+       @mkdir -p $@
+
+
+#
+# Convert waveform
+#
+$(TOPVCD): sim
+       wlf2vcd -o $@ sim_build/vsim.wlf
+
+
+.PHONY: clean
+clean::
+       @rm -fr code $(TOPVCD)
diff --git a/test/nmigen/cocotb/controller/generate.py b/test/nmigen/cocotb/controller/generate.py
new file mode 100755 (executable)
index 0000000..fee62fe
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/env python3
+import os
+
+from nmigen import *
+from nmigen.back.verilog import convert
+from nmigen.build import Platform
+
+from c4m.nmigen.jtag import TAP
+
+class DummyPlatform(Platform):
+    resources = []
+    connectors = []
+    required_tools = ["yosys"]
+
+    def toolchain_prepare(self, fragment, name, **kwargs):
+        raise NotImplementedError
+
+class Top(Elaboratable):
+    def __init__(self, io_count):
+        self.tap = TAP(io_count)
+        self.core_i = Signal(io_count, name="top_corei")
+        self.core_o = Signal(io_count, name="top_coreo")
+        self.core_oe = Signal(io_count, name="top_coreoe")
+        self.pad_i = Signal(io_count, name="top_padi")
+        self.pad_o = Signal(io_count, name="top_pado")
+        self.pad_oe = Signal(io_count, name="top_padoe")
+
+    def elaborate(self, platform):
+        m = Module()
+
+        m.submodules.tap = self.tap
+
+        m.d.comb += [
+            self.core_i.eq(Cat(io.i for io in self.tap.core)),
+            Cat(io.o for io in self.tap.core).eq(self.core_o),
+            Cat(io.oe for io in self.tap.core).eq(self.core_oe),
+            Cat(io.i for io in self.tap.pad).eq(self.pad_i),
+            self.pad_o.eq(Cat(io.o for io in self.tap.pad)),
+            self.pad_oe.eq(Cat(io.oe for io in self.tap.pad)),
+        ]
+
+        return m
+
+top = Top(2)
+
+p = DummyPlatform()
+
+ports = [top.tap.bus.tck, top.tap.bus.tms, top.tap.bus.tdi, top.tap.bus.tdo,
+         top.core_i, top.core_o, top.core_oe, top.pad_i, top.pad_o, top.pad_oe]
+# for io in tap.core:
+#     ports += [io.i, io.o, io.oe]
+# for io in tap.pad:
+#     ports += [io.i, io.o, io.oe]
+top_code = convert(top, ports=ports, platform=p)
+with open("code/top.v", "w") as f:
+    f.write(top_code)
+
+for filename, code in p.extra_files.items():
+    with open("code"+ os.path.sep + filename, "w") as f:
+        f.write(code)
+
+    
diff --git a/test/nmigen/cocotb/controller/test.py b/test/nmigen/cocotb/controller/test.py
new file mode 100644 (file)
index 0000000..7b465fb
--- /dev/null
@@ -0,0 +1,94 @@
+import cocotb
+from cocotb.utils import get_sim_steps
+from cocotb.binary import BinaryValue
+
+from c4m.cocotb.jtag.c4m_jtag import JTAG_Master
+
+@cocotb.test()
+def test01_idcode(dut):
+    """
+    Test the IDCODE command
+    """
+
+    # Run @ 1MHz
+    clk_period = get_sim_steps(1, "us")
+    master = JTAG_Master(dut.tap_bus__tck, dut.tap_bus__tms, dut.tap_bus__tdi, dut.tap_bus__tdo, clk_period=clk_period)
+
+    dut._log.info("Trying to get IDCODE...")
+
+    yield master.idcode()
+    result1 = master.result
+    dut._log.info("IDCODE1: {}".format(result1))
+
+    yield master.idcode()
+    result2 = master.result
+    dut._log.info("IDCODE2: {}".format(result2))
+
+    assert(result1 == result2)
+    
+@cocotb.test()
+def test02_bypass(dut):
+    """
+    Test of BYPASS mode
+    """
+
+    # Run @ 1MHz
+    clk_period = get_sim_steps(1, "us")
+    master = JTAG_Master(dut.tap_bus__tck, dut.tap_bus__tms, dut.tap_bus__tdi, dut.tap_bus__tdo, clk_period=clk_period)
+
+    dut._log.info("Loading BYPASS command")
+    yield master.load_ir(master.BYPASS)
+
+    dut._log.info("Sending data")
+
+    data_in = BinaryValue()
+    data_in.binstr = "01001101"
+    yield master.shift_data(data_in)
+
+    dut._log.info("bypass out: {}".format(master.result.binstr))
+    assert(master.result.binstr[:-1] == data_in.binstr[1:])
+
+@cocotb.test()
+def test03_sample(dut):
+    """
+    Test of SAMPLEPRELOAD and EXTEST
+    """
+    data_in = BinaryValue()
+
+    # Run @ 1MHz
+    clk_period = get_sim_steps(1, "us")
+    master = JTAG_Master(dut.tap_bus__tck, dut.tap_bus__tms, dut.tap_bus__tdi, dut.tap_bus__tdo, clk_period=clk_period)
+
+
+    dut._log.info("Load SAMPLEPRELOAD command")
+    yield master.load_ir(master.SAMPLEPRELOAD)
+
+    data_in.binstr = "011000"
+    dut._log.info("  preloading data {}".format(data_in.binstr))
+
+    # Set the ios pins
+    dut.top_coreo = 2
+    dut.top_coreoe = 0
+    dut.top_padi = 1
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert(master.result.binstr == "100010")
+
+
+    dut._log.info("Load EXTEST command")
+    yield master.load_ir(master.EXTEST)
+
+    data_in.binstr = "100111"
+    dut._log.info("  input data {}".format(data_in.binstr))
+    
+    # Set the ios pins
+    dut.top_coreo = 1
+    dut.top_coreoe = 3
+    dut.top_padi = 2
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert(master.result.binstr == "011101")
+
+    dut._log.info("Do a capture of the last loaded data")
+    yield master.shift_data([])
+