From: Luke Kenneth Casson Leighton Date: Tue, 6 Apr 2021 19:10:58 +0000 (+0100) Subject: add test wishbone (separate from test.py) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=df5067a44b4212fec1e4eb550b867aeb99143622;p=soc-cocotb-sim.git add test wishbone (separate from test.py) --- diff --git a/ls180/pre_pnr/testwb.py b/ls180/pre_pnr/testwb.py new file mode 100644 index 0000000..9a20ae9 --- /dev/null +++ b/ls180/pre_pnr/testwb.py @@ -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', , 'eint_2', 125) + print (pin)