add verilator post-pnr cocotb sim
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sat, 17 Apr 2021 12:22:06 +0000 (13:22 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sat, 17 Apr 2021 12:22:06 +0000 (13:22 +0100)
ls180/post_pnr/cocotb_v/Makefile [new file with mode: 0644]
ls180/post_pnr/cocotb_v/idcode.svf [new file with mode: 0644]
ls180/post_pnr/cocotb_v/run_verilator_chip.sh [new file with mode: 0755]
ls180/post_pnr/cocotb_v/test.py [new file with mode: 0644]

diff --git a/ls180/post_pnr/cocotb_v/Makefile b/ls180/post_pnr/cocotb_v/Makefile
new file mode 100644 (file)
index 0000000..b62b2b9
--- /dev/null
@@ -0,0 +1,15 @@
+ifeq ($(SIM),)
+  $(error Use one of the run_*.sh scripts to run cocotb test bench)
+endif
+
+TOPLEVEL_LANG := verilog
+
+# copy chip.v from post_pnr ghdl "make chip"
+VERILOG_SOURCES := \
+  chip.v \
+# END VERILOG_SOURCES
+
+MODULE := test
+
+include $(shell cocotb-config --makefiles)/Makefile.sim
+
diff --git a/ls180/post_pnr/cocotb_v/idcode.svf b/ls180/post_pnr/cocotb_v/idcode.svf
new file mode 100644 (file)
index 0000000..994e9c8
--- /dev/null
@@ -0,0 +1,4 @@
+!Loading device with 'idcode' instruction.
+SIR 4 TDI (1);
+SDR 32 TDI (00000000) TDO (000018FF) ;
+
diff --git a/ls180/post_pnr/cocotb_v/run_verilator_chip.sh b/ls180/post_pnr/cocotb_v/run_verilator_chip.sh
new file mode 100755 (executable)
index 0000000..674cb24
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# create dummy memory files
+yes 0 | head -128 > mem_1.init 
+yes 0 | head -32 > mem_1.init 
+touch mem.init mem_1.init mem_2.init mem_3.init mem_4.init
+
+# Only run test in reset state as running CPU takes too much time to simulate
+make \
+  SIM=verilator \
+  TOPLEVEL=chip \
+  COCOTB_RESULTS_FILE=results_iverilator_chip.xml \
+  COCOTB_HDL_TIMEUNIT=1ns \
+  COCOTB_HDL_TIMEPRECISION=1ps \
+  TESTCASE="idcode_reset,idcodesvf_reset,boundary_scan_reset,idcode_run" \
+  VERILATOR_TRACE="1" \
+  NOTUSEDCOMPILE_ARGS="--hierarchical " \
+  NOTUSEDCOMPILE_ARGS="--unroll-count 256 \
+        --output-split 5000 \
+        --output-split-cfuncs 500 \
+        --output-split-ctrace 500 \
+        --prefix Vtop -o chip" \
+  EXTRA_ARGS="--trace --trace-structs -Wno-fatal \
+        -Wno-BLKANDNBLK \
+        -Wno-TIMESCALEMOD \
+        -Wno-WIDTH" \
+  MODULE="test" \
+  SIM_BUILD=sim_build_iverilator_chip
+
+
+
diff --git a/ls180/post_pnr/cocotb_v/test.py b/ls180/post_pnr/cocotb_v/test.py
new file mode 100644 (file)
index 0000000..a96846c
--- /dev/null
@@ -0,0 +1,269 @@
+import cocotb
+from cocotb.clock import Clock
+from cocotb.triggers import Timer
+from cocotb.utils import get_sim_steps
+from cocotb.binary import BinaryValue
+
+from c4m.cocotb.jtag.c4m_jtag import JTAG_Master
+from c4m.cocotb.jtag.c4m_jtag_svfcocotb import SVF_Executor
+
+from itertools import chain
+
+import cocotb
+from cocotb.clock import Clock
+from cocotb.triggers import Timer
+from cocotb.utils import get_sim_steps
+from cocotb.binary import BinaryValue
+
+from c4m.nmigen.jtag.tap import IOType
+from c4m.cocotb.jtag.c4m_jtag import JTAG_Master
+from c4m.cocotb.jtag.c4m_jtag_svfcocotb import SVF_Executor
+
+from soc.config.pinouts import get_pinspecs
+from soc.debug.jtag import Pins
+
+#
+# Helper functions
+#
+
+def setup_sim(dut, *, clk_period, run):
+    """Initialize CPU and setup clock"""
+
+    clk_steps = get_sim_steps(clk_period, "ns")
+    cocotb.fork(Clock(dut.sys_clk, clk_steps).start())
+
+    dut.vdd <= 1
+    dut.vss <= 0
+    dut.iovdd <= 1
+    dut.iovss <= 0
+    dut.sys_rst <= 1
+    dut.sys_clk <= 0
+    # adder test (ignore this)
+    #dut.a <= 3
+    #dut.b <= 2
+
+    if run:
+        yield Timer(int(10.5*clk_steps))
+        dut.sys_rst <= 0
+        yield Timer(int(5*clk_steps))
+
+def setup_jtag(dut, *, tck_period):
+    # Make this a generator
+    if False:
+        yield Timer(0)
+    clk_steps = get_sim_steps(tck_period, "ns")
+    return JTAG_Master(dut.jtag_tck, dut.jtag_tms,
+                       dut.jtag_tdi, dut.jtag_tdo,
+                       clk_period=clk_steps,
+                       ir_width=4)
+
+def execute_svf(dut, *, jtag, svf_filename):
+    jtag_svf = SVF_Executor(jtag)
+    with open(svf_filename, "r") as f:
+        svf_deck = f.read()
+    yield jtag_svf.run(svf_deck, p=dut._log.info)
+    
+#
+# IDCODE using JTAG_master
+#
+
+def idcode(dut, *, jtag):
+    #jtag.IDCODE = [0, 0, 0, 1]
+    yield jtag.idcode()
+    result1 = jtag.result
+    dut._log.info("IDCODE1: {}".format(result1))
+    assert(result1 == BinaryValue("00000000000000000001100011111111"))
+
+    yield jtag.idcode()
+    result2 = jtag.result
+    dut._log.info("IDCODE2: {}".format(result2))
+
+    assert(result1 == result2)
+
+@cocotb.test()
+def idcode_reset(dut):
+    dut._log.info("Running IDCODE test; cpu in reset...")
+
+    clk_period = 100 # 10MHz
+    tck_period = 300 # 3MHz
+
+    yield from setup_sim(dut, clk_period=clk_period, run=False)
+    jtag = yield from setup_jtag(dut, tck_period = tck_period)
+
+    yield from idcode(dut, jtag=jtag)
+
+    dut._log.info("IDCODE test completed")
+
+@cocotb.test()
+def idcode_run(dut):
+    dut._log.info("Running IDCODE test; cpu running...")
+
+    clk_period = 100 # 10MHz
+    tck_period = 300 # 3MHz
+
+    yield from setup_sim(dut, clk_period=clk_period, run=True)
+    jtag = yield from setup_jtag(dut, tck_period = tck_period)
+
+    yield from idcode(dut, jtag=jtag)
+
+    dut._log.info("IDCODE test completed")
+
+#
+# Read IDCODE from SVF file
+#
+
+@cocotb.test()
+def idcodesvf_reset(dut):
+    dut._log.info("Running IDCODE through SVF test; cpu in reset...")
+
+    clk_period = 100 # 10MHz
+    tck_period = 300 # 3MHz
+
+    yield from setup_sim(dut, clk_period=clk_period, run=False)
+    jtag = yield from setup_jtag(dut, tck_period = tck_period)
+
+    yield from execute_svf(dut, jtag=jtag, svf_filename="idcode.svf")
+
+    dut._log.info("IDCODE test completed")
+
+@cocotb.test()
+def idcode_run_svf(dut):
+    dut._log.info("Running IDCODE through test; cpu running...")
+
+    clk_period = 100 # 10MHz
+    tck_period = 300 # 3MHz
+
+    yield from setup_sim(dut, clk_period=clk_period, run=True)
+    jtag = yield from setup_jtag(dut, tck_period = tck_period)
+
+    yield from execute_svf(dut, jtag=jtag, svf_filename="idcode.svf")
+
+    dut._log.info("IDCODE test completed")
+
+
+@cocotb.test()
+def wishbone_basic(dut):
+    """
+    Test of an added Wishbone interface
+    """
+    clk_period = 100 # 100MHz
+    tck_period = 3000 # 0.3MHz
+
+    data_in = BinaryValue()
+    # these have to match with soc.debug.jtag.JTAG ircodes
+    cmd_MEMADDRESS = BinaryValue("0101")    # 5
+    cmd_MEMREAD = BinaryValue("0110")       # 6
+    cmd_MEMREADWRITE = BinaryValue("0111")  # 7
+
+    info = "Running Wishbone basic test"
+    yield from setup_sim(dut, clk_period=clk_period, run=True)
+    master = yield from setup_jtag(dut, tck_period = tck_period)
+
+    # Load the memory address
+    yield master.load_ir(cmd_MEMADDRESS)
+    dut._log.info("Loading address")
+
+    data_in.binstr = "000000000000000000000000000001"
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+
+    # Do write
+    yield master.load_ir(cmd_MEMREADWRITE)
+    dut._log.info("Writing memory")
+
+    data_in.binstr = "01010101" * 4
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+
+    data_in.binstr = "10101010" * 4
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+
+    # Load the memory address
+    yield master.load_ir(cmd_MEMADDRESS)
+    dut._log.info("Loading address")
+
+    data_in.binstr = "000000000000000000000000000001"
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert master.result.binstr == "000000000000000000000000000011"
+
+    # Do read and write
+    yield master.load_ir(cmd_MEMREADWRITE)
+    dut._log.info("Reading and writing memory")
+
+    data_in.binstr = "10101010" * 4
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert master.result.binstr == "01010101" * 4
+
+    data_in.binstr = "01010101" * 4
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert master.result.binstr == "10101010" * 4
+
+    # Load the memory address
+    yield master.load_ir(cmd_MEMADDRESS)
+    dut._log.info("Loading address")
+
+    data_in.binstr = "000000000000000000000000000001"
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert master.result.binstr == "000000000000000000000000000011"
+
+    # Do read
+    yield master.load_ir(cmd_MEMREAD)
+    dut._log.info("Reading memory")
+    data_in.binstr = "00000000" * 4
+
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert master.result.binstr == "10101010" * 4
+
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert master.result.binstr == "01010101" * 4
+
+    # Load the memory address
+    yield master.load_ir(cmd_MEMADDRESS) # MEMADDR
+    dut._log.info("Loading address")
+
+    data_in.binstr = "000000000000000000000000000001"
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert master.result.binstr == "000000000000000000000000000011"
+
+    # Do read
+    yield master.load_ir(cmd_MEMREAD) # MEMREAD
+    dut._log.info("Reading memory")
+    data_in.binstr = "00000000" * 4
+
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert master.result.binstr == "10101010" * 4
+
+    dut._log.info("  input: {}".format(data_in.binstr))
+    yield master.shift_data(data_in)
+    dut._log.info("  output: {}".format(master.result.binstr))
+    assert master.result.binstr == "01010101" * 4
+
+    #dut._log.info("{!r}".format(wbmem))
+
+
+# demo / debug how to get boundary scan names. run "python3 test.py"
+if __name__ == '__main__':
+    pinouts = get_jtag_boundary()
+    for pin in pinouts:
+        # example: ('eint', '2', <IOType.In: 1>, 'eint_2', 125)
+        print (pin)