add test wishbone (separate from test.py)
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 6 Apr 2021 19:10:58 +0000 (20:10 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 6 Apr 2021 19:13:20 +0000 (20:13 +0100)
ls180/pre_pnr/testwb.py [new file with mode: 0644]

diff --git a/ls180/pre_pnr/testwb.py b/ls180/pre_pnr/testwb.py
new file mode 100644 (file)
index 0000000..9a20ae9
--- /dev/null
@@ -0,0 +1,451 @@
+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
+#
+
+class DUTWrapper:
+    def __init__(self, dut):
+        self.dut = dut
+        try:
+            ls180 = dut.ls180
+        except AttributeError:
+            ls180 = dut
+        ls180._discover_all()
+        ls180.test_issuer._discover_all()
+        ls180.test_issuer.ti._discover_all()
+        ls180.test_issuer.ti.dbg._discover_all()
+        self.ls180 = ls180
+        self.clk = ls180.sys_clk
+        self.rst = ls180.sys_rst
+        self.tck = ls180.jtag_tck
+        self.tms = ls180.jtag_tms
+        self.tdi = ls180.jtag_tdi
+        self.tdo = ls180.jtag_tdo
+
+    def info(self, *args, **kwargs):
+        return self.dut._log.info(*args, **kwargs)
+
+
+class JTAGPin:
+    def __init__(self, pin):
+        self.type_ = pin[2]
+        self.name = pin[3]
+        self._pin = pin
+
+    def __repr__(self):
+        return str(self._pin)
+
+    def log(self, wrap):
+        if self.type_ == IOType.In:
+            core_i = getattr(wrap.ti, f"{self.name}__core__i").value
+            pad_i = getattr(wrap.ti, f"{self.name}__pad__i").value
+            wrap.info(f"{self.name}: core.i={core_i}, pad.i={pad_i}")
+        elif self.type_ == IOType.Out:
+            core_o = getattr(wrap.ti, f"{self.name}__core__o").value
+            pad_o = getattr(wrap.ti, f"{self.name}__pad__o").value
+            wrap.info(f"{self.name}: core.o={core_o}, pad.o={pad_o}")
+        elif self.type_ == IOType.TriOut:
+            core_o = getattr(wrap.ti, f"{self.name}__core__o").value
+            core_oe = getattr(wrap.ti, f"{self.name}__core__oe").value
+            pad_o = getattr(wrap.ti, f"{self.name}__pad__o").value
+            pad_oe = getattr(wrap.ti, f"{self.name}__pad__oe").value
+            wrap.info(f"{self.name}: core.(o={core_o}, oe={core_oe}), " \
+                       "pad.(o={pad_o}, oe={pad_oe})")
+        elif self.type_ == IOType.InTriOut:
+            core_i = getattr(wrap.ti, f"{self.name}__core__i").value
+            core_o = getattr(wrap.ti, f"{self.name}__core__o").value
+            core_oe = getattr(wrap.ti, f"{self.name}__core__oe").value
+            pad_i = getattr(wrap.ti, f"{self.name}__pad__i").value
+            pad_o = getattr(wrap.ti, f"{self.name}__pad__o").value
+            pad_oe = getattr(wrap.ti, f"{self.name}__pad__oe").value
+            wrap.info(f"{self.name}: core.(i={core_i}, o={core_o}, " \
+                       "oe={core_oe}), pad.(i={core_i}, o={pad_o}, " \
+                       "oe={pad_oe})")
+        else:
+            raise ValueError(f"Unsupported pin type {self.type_}")
+
+    def data(self, *, i=None, o=None, oe=None):
+        if self.type_ == IOType.In:
+            assert i is not None
+            return [i]
+        elif self.type_ == IOType.Out:
+            assert o is not None
+            return [o]
+        elif self.type_ == IOType.TriOut:
+            assert (o is not None) and (oe is not None)
+            return [o, oe]
+        elif self.type_ == IOType.InTriOut:
+            assert (i is not None) and(o is not None) and (oe is not None)
+            return [i, o, oe]
+        else:
+            raise ValueError(f"Unsupported pin type {self.type_}")
+
+    def check(self, *, wrap, i=None, o=None, oe=None):
+        if self.type_ in (IOType.In, IOType.InTriOut):
+            sig = f"{self.name}__core__i"
+            val = getattr(wrap.ti, sig).value
+            if val != i:
+                raise ValueError(f"'{sig}' should be {i}, not {val}")
+        if self.type_ in (IOType.Out, IOType.TriOut, IOType.InTriOut):
+            sig = f"{self.name}__pad__o"
+            val = getattr(wrap.ti, sig).value
+            if val != o:
+                raise ValueError(f"'{sig}' should be {o}, not {val}")
+        if self.type_ in (IOType.TriOut, IOType.InTriOut):
+            sig = f"{self.name}__pad__oe"
+            val = getattr(wrap.ti, sig).value
+            if val != oe:
+                raise ValueError(f"'{sig}' should be {oe}, not {val}")
+
+
+def log_pins(wrap, pins):
+    for pin in pins:
+        pin.log(wrap)
+
+
+def get_jtag_boundary():
+    """gets the list of information for jtag boundary scan
+    """
+    # currently only a subset of pins is enabled. nuisance
+    subset = ['uart',
+              'mtwi',
+              'eint', 'gpio', 'mspi0',
+              # 'mspi1', - disabled for now
+              # 'pwm', 'sd0', - disabled for now
+               'sdr']
+    pins = tuple(JTAGPin(pin) for pin in Pins(get_pinspecs(subset=subset)))
+    return pins
+
+
+def setup_sim(dut, *, info, clk_period, run):
+    """Initialize CPU and setup clock"""
+
+    wrap = DUTWrapper(dut)
+    wrap.info(info)
+
+    wrap.clk <= 0
+    wrap.rst <= 1
+
+    clk_steps = get_sim_steps(clk_period, "ns")
+    cocotb.fork(Clock(wrap.clk, clk_steps).start())
+
+    if run:
+        yield Timer(int(1.5*clk_steps))
+        wrap.rst <= 0
+        #yield Timer(int(3*clk_steps))
+
+    return wrap
+
+
+def setup_jtag(wrap, *, tck_period):
+    # TODO: Make this a generator
+    if False:
+        yield Timer(0)
+    return JTAG_Master(wrap.tck, wrap.tms, wrap.tdi, wrap.tdo,
+                       clk_period=tck_period,
+                       ir_width=4)
+
+def execute_svf(wrap, *, jtag, svf_filename):
+    yield jtag.reset()
+
+    jtag_svf = SVF_Executor(jtag)
+    with open(svf_filename, "r") as f:
+        svf_deck = f.read()
+    yield jtag_svf.run(svf_deck, p=wrap.info)
+
+#
+# IDCODE using JTAG_master
+#
+
+def idcode(wrap, *, jtag):
+    yield jtag.idcode()
+    result1 = jtag.result
+    wrap.info("IDCODE1: {}".format(result1))
+    assert(result1 == BinaryValue("00000000000000000001100011111111"))
+
+    yield jtag.idcode()
+    result2 = jtag.result
+    wrap.info("IDCODE2: {}".format(result2))
+
+    assert(result1 == result2)
+
+
+@cocotb.test()
+def idcode_reset(dut):
+    clk_period = 100 # 10MHz
+    tck_period = 300 # 3MHz
+
+    info = "Running IDCODE test; cpu in reset..."
+    wrap = yield from setup_sim(dut, info=info, clk_period=clk_period,
+                                run=False)
+    jtag = yield from setup_jtag(wrap, tck_period = tck_period)
+
+    yield from idcode(wrap, jtag=jtag)
+
+    wrap.info("IDCODE test completed")
+
+
+@cocotb.test()
+def idcode_run(dut):
+    clk_period = 100 # 10MHz
+    tck_period = 300 # 3MHz
+
+    info = "Running IDCODE test; cpu running..."
+    wrap = yield from setup_sim(dut, info=info, clk_period=clk_period,
+                         run=True)
+    jtag = yield from setup_jtag(wrap, tck_period = tck_period)
+
+    yield from idcode(wrap, jtag=jtag)
+
+    wrap.info("IDCODE test completed")
+
+#
+# Read IDCODE from SVF file
+#
+
+@cocotb.test()
+def idcodesvf_reset(dut):
+    clk_period = 100 # 10MHz
+    tck_period = 300 # 3MHz
+
+    info = "Running IDCODE through SVF test; cpu in reset..."
+    wrap = yield from setup_sim(dut, info=info, clk_period=clk_period,
+                                run=False)
+    jtag = yield from setup_jtag(wrap, tck_period = tck_period)
+
+    yield from execute_svf(wrap, jtag=jtag, svf_filename="idcode.svf")
+
+    wrap.info("IDCODE test completed")
+
+
+@cocotb.test()
+def idcodesvf_run(dut):
+    clk_period = 100 # 10MHz
+    tck_period = 300 # 3MHz
+
+    info = "Running IDCODE through SVF test; cpu running..."
+    wrap = yield from setup_sim(dut, info=info, clk_period=clk_period,
+                                run=True)
+    jtag = yield from setup_jtag(wrap, tck_period = tck_period)
+
+    yield from execute_svf(wrap, jtag=jtag, svf_filename="idcode.svf")
+
+    wrap.info("IDCODE test completed")
+
+#
+# Boundary scan
+#
+
+def boundary_scan(wrap, *, jtag):
+    pins = get_jtag_boundary()
+
+    yield jtag.reset()
+
+    wrap.info("")
+    wrap.info("Before scan")
+    log_pins(wrap, pins)
+
+    yield jtag.load_ir([0, 0, 0, 0])
+    pinsdata = tuple(pin.data(i=i%2, o=((i%3)%2), oe=((i%5)%2))
+                     for i, pin in enumerate(pins))
+    yield jtag.shift_data(chain(*pinsdata))
+
+    wrap.info("")
+    wrap.info("After scan")
+    log_pins(wrap, pins)
+    for i, pin in enumerate(pins):
+        pin.check(wrap=wrap, i=i%2, o=((i%3)%2), oe=((i%5)%2))
+
+    yield jtag.reset()
+
+    wrap.info("")
+    wrap.info("After reset")
+    log_pins(wrap, pins)
+
+
+@cocotb.test()
+def boundary_scan_reset(dut):
+    clk_period = 100 # 10MHz
+    tck_period = 300 # 3MHz
+
+    info = "Running boundary scan test; cpu in reset..."
+    wrap = yield from setup_sim(dut, info=info, clk_period=clk_period,
+                                run=False)
+    jtag = yield from setup_jtag(wrap, tck_period = tck_period)
+
+    yield from boundary_scan(wrap, jtag=jtag)
+
+    wrap.info("IDCODE test completed")
+
+
+@cocotb.test()
+def boundary_scan_run(dut):
+    clk_period = 100 # 10MHz
+    tck_period = 300 # 3MHz
+
+    info = "Running boundary scan test; cpu running..."
+    wrap = yield from setup_sim(dut, info=info, clk_period=clk_period,
+                                run=True)
+    jtag = yield from setup_jtag(wrap, tck_period = tck_period)
+
+    yield from boundary_scan(wrap, jtag=jtag)
+
+    wrap.info("IDCODE test completed")
+
+
+@cocotb.test()
+def wishbone_basic(dut):
+    """
+    Test of an added Wishbone interface
+    """
+    clk_period = 100 # 10MHz
+    tck_period = 30000 # 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"
+    wrap = yield from setup_sim(dut, info=info, clk_period=clk_period,
+                                run=False)
+    master = yield from setup_jtag(wrap, tck_period = tck_period)
+
+    wrap.dut.ls180.test_issuer.ti.dbg.core_stopped_i <= 1
+
+    clk_steps = get_sim_steps(clk_period, "ns")
+    yield Timer(int(4.5*clk_steps))
+    wrap.rst <= 0
+
+    #yield master.reset()
+
+    yield Timer(int(10.5*clk_steps))
+
+    return
+
+    #wrap.rst <= 0
+
+    # Load the memory address
+    yield master.load_ir(cmd_MEMADDRESS)
+    dut._log.info("Loading address")
+
+    data_in.binstr = "00000000000000000000000000001"
+    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" * 8
+    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" * 8
+    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 = "00000000000000000000000000001"
+    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 == "00000000000000000000000000000"
+
+    # Do read and write
+    yield master.load_ir(cmd_MEMREADWRITE)
+    dut._log.info("Reading and writing memory")
+
+    data_in.binstr = "10101010" * 8
+    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" * 8
+
+    data_in.binstr = "01010101" * 8
+    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" * 8
+
+    # Load the memory address
+    yield master.load_ir(cmd_MEMADDRESS)
+    dut._log.info("Loading address")
+
+    data_in.binstr = "00000000000000000000000000001"
+    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 == "00000000000000000000000000010"
+
+    # Do read
+    yield master.load_ir(cmd_MEMREAD)
+    dut._log.info("Reading memory")
+    data_in.binstr = "00000000" * 8
+
+    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" * 8
+
+    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" * 8
+
+    # Load the memory address
+    yield master.load_ir(cmd_MEMADDRESS) # MEMADDR
+    dut._log.info("Loading address")
+
+    data_in.binstr = "00000000000000000000000000001"
+    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 == "00000000000000000000000000010"
+
+    # Do read
+    yield master.load_ir(cmd_MEMREAD) # MEMREAD
+    dut._log.info("Reading memory")
+    data_in.binstr = "00000000" * 8
+
+    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" * 8
+
+    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" * 8
+
+    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)