From: Luke Kenneth Casson Leighton Date: Wed, 14 Apr 2021 11:36:39 +0000 (+0100) Subject: add test boundary scan hard-coded test X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=354e964f3337f94366703f48b923adf3c882be56;p=soc-cocotb-sim.git add test boundary scan hard-coded test --- diff --git a/ls180/post_pnr/cocotb/Makefile b/ls180/post_pnr/cocotb/Makefile index ea51d19..a53c81b 100644 --- a/ls180/post_pnr/cocotb/Makefile +++ b/ls180/post_pnr/cocotb/Makefile @@ -20,7 +20,7 @@ VHDL_SOURCES = \ $(wildcard $(NIOLIBDIR)/*.vhd) TOPLEVEL=chip_r TOPLEVEL_LANG=vhdl -MODULE=test +MODULE ?= test SIM=ghdl GPI_IMPL=vhpi GHDL_ARGS=--std=08 diff --git a/ls180/post_pnr/cocotb/run_ghdl_test_boundary.sh b/ls180/post_pnr/cocotb/run_ghdl_test_boundary.sh new file mode 100755 index 0000000..cf82e2a --- /dev/null +++ b/ls180/post_pnr/cocotb/run_ghdl_test_boundary.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# Only run test in reset state as running CPU takes too much time to simulate +make \ + SIM=ghdl \ + COCOTB_RESULTS_FILE=results_iverilog.xml \ + COCOTB_HDL_TIMEUNIT=100ps \ + MODULE=test_add_boundary \ + TESTCASE="boundary_scan_reset" \ + SIM_BUILD=sim_build_ghdl + + diff --git a/ls180/post_pnr/cocotb/test_add_boundary.py b/ls180/post_pnr/cocotb/test_add_boundary.py new file mode 100644 index 0000000..b518224 --- /dev/null +++ b/ls180/post_pnr/cocotb/test_add_boundary.py @@ -0,0 +1,238 @@ +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 itertools import chain + +from c4m.nmigen.jtag.tap import IOType + +# +# Helper functions +# + +class DUTWrapper: + def __init__(self, dut): + self.dut = dut + ti = dut.instance_corona.core.subckt_22_jtag + ti._discover_all() + self.ti = ti + self.sys_clk = dut.sys_clk + self.sys_rst = dut.sys_rst + self.jtag_tck = dut.jtag_tck + self.jtag_tms = dut.jtag_tms + self.jtag_tdi = dut.jtag_tdi + self.jtag_tdo = dut.jtag_tdo + self.vdd = dut.vdd + self.vss = dut.vss + self.iovdd = dut.iovdd + self.iovss = dut.iovss + + def info(self, *args, **kwargs): + return self.dut._log.info(*args, **kwargs) + + +def setup_sim(dut, *, info, clk_period, run): + """Initialize CPU and setup clock""" + + wrap = DUTWrapper(dut) + wrap.info(info) + + clk_steps = get_sim_steps(clk_period, "ns") + cocotb.fork(Clock(wrap.sys_clk, clk_steps).start()) + + wrap.vdd <= 1 + wrap.vss <= 0 + wrap.iovdd <= 1 + wrap.iovss <= 0 + wrap.sys_rst <= 1 + wrap.sys_clk <= 0 + # adder test (ignore this) + dut.a <= 3 + dut.b <= 2 + + if run: + yield Timer(int(10.5*clk_steps)) + wrap.sys_rst <= 0 + yield Timer(int(5*clk_steps)) + + return wrap + +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) + + +# 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', , 'eint_2', 125) + print (pin) + + +# +# Helper functions +# + +class JTAGPin: + def __init__(self, pin): + self.pin = pin + self.type_ = pin[2] + self.name = pin[3] + + 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 + """ + pins = [] + for pname in ["a_0", "a_1", "a_2", "a_3", + "b_0", "b_1", "b_2", "b_3", + "f_0", "f_1", "f_2", "f_3"]: + if pname.startswith('f'): + ptype = IOType.Out + else: + ptype = IOType.In + pin = (None, None, ptype, pname) + pins.append(JTAGPin(pin)) + return pins + + +# +# 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") + +