def dfi_group(name, suffixes): save.add(soc.sdrphy.dfi, group_name=name, mappers=[ gtkw.regex_filter(gtkw.suffixes2re(suffixes)), gtkw.dfi_sorter(), gtkw.dfi_per_phase_colorer(), ])
def generate_gtkw_savefile(builder, vns, trace_fst): from litex.build.sim import gtkwave as gtkw dumpfile = os.path.join(builder.gateware_dir, "sim.{}".format("fst" if trace_fst else "vcd")) savefile = os.path.join(builder.gateware_dir, "sim.gtkw") soc = builder.soc wrphase = soc.sdram.controller.settings.phy.wrphase.reset.value with gtkw.GTKWSave(vns, savefile=savefile, dumpfile=dumpfile) as save: save.clocks() save.add(soc.bus.slaves["main_ram"], mappers=[gtkw.wishbone_sorter(), gtkw.wishbone_colorer()]) save.fsm_states(soc) # all dfi signals save.add(soc.ddrphy.dfi, mappers=[gtkw.dfi_sorter(), gtkw.dfi_in_phase_colorer()]) # each phase in separate group with save.gtkw.group("dfi phaseX", closed=True): for i, phase in enumerate(soc.ddrphy.dfi.phases): save.add(phase, group_name="dfi p{}".format(i), mappers=[ gtkw.dfi_sorter(phases=False), gtkw.dfi_in_phase_colorer(), ]) # only dfi command signals save.add(soc.ddrphy.dfi, group_name="dfi commands", mappers=[ gtkw.regex_filter( gtkw.suffixes2re(["cas_n", "ras_n", "we_n"])), gtkw.dfi_sorter(), gtkw.dfi_per_phase_colorer(), ]) # only dfi data signals save.add(soc.ddrphy.dfi, group_name="dfi wrdata", mappers=[ gtkw.regex_filter( ["wrdata$", "p{}.*wrdata_en$".format(wrphase)]), gtkw.dfi_sorter(), gtkw.dfi_per_phase_colorer(), ]) save.add(soc.ddrphy.dfi, group_name="dfi wrdata_mask", mappers=[ gtkw.regex_filter(gtkw.suffixes2re(["wrdata_mask"])), gtkw.dfi_sorter(), gtkw.dfi_per_phase_colorer(), ]) save.add(soc.ddrphy.dfi, group_name="dfi rddata", mappers=[ gtkw.regex_filter( gtkw.suffixes2re(["rddata", "p0.*rddata_valid"])), gtkw.dfi_sorter(), gtkw.dfi_per_phase_colorer(), ]) # serialization with save.gtkw.group("serialization", closed=True): if isinstance(soc.ddrphy, DoubleRateLPDDR4SimPHY): ser_groups = [("out 1x", soc.ddrphy._out), ("out 2x", soc.ddrphy.out)] else: ser_groups = [("out", soc.ddrphy.out)] for name, out in ser_groups: save.group([ out.cs, out.dqs_o[0], out.dqs_oe, out.dmi_o[0], out.dmi_oe ], group_name=name, mappers=[ gtkw.regex_colorer({ "yellow": gtkw.suffixes2re(["cs"]), "orange": ["_o[^e]"], "red": gtkw.suffixes2re(["oe"]), }) ]) with save.gtkw.group("deserialization", closed=True): if isinstance(soc.ddrphy, DoubleRateLPDDR4SimPHY): ser_groups = [("in 1x", soc.ddrphy._out), ("in 2x", soc.ddrphy.out)] else: ser_groups = [("in", soc.ddrphy.out)] for name, out in ser_groups: save.group([out.dq_i[0], out.dq_oe, out.dqs_i[0], out.dqs_oe], group_name=name, mappers=[ gtkw.regex_colorer({ "yellow": ["dqs"], "orange": ["dq[^s]"], }) ]) # dram pads save.group( [ s for s in vars(soc.ddrphy.pads).values() if isinstance(s, Signal) ], group_name="pads", mappers=[ gtkw.regex_filter(["clk_n$", "_[io]$"], negate=True), gtkw.regex_sorter( gtkw.suffixes2re([ "cke", "odt", "reset_n", "clk_p", "cs", "ca", "dq", "dqs", "dmi", "oe" ])), gtkw.regex_colorer({ "yellow": gtkw.suffixes2re(["cs", "ca"]), "orange": gtkw.suffixes2re(["dq", "dqs", "dmi"]), "red": gtkw.suffixes2re(["oe"]), }), ], )
def run_test(self, dut, dfi_sys, dfi_expected, dfi_input=None, **kwargs): assert callable( dut), "dut must be a callable that returns new Dut instance" dfi = DFISequencer(dfi_sys) # Add GTKWave savefile for debugging if we are creating a VCD dumpfile (requires pyvcd installed) if kwargs.get("vcd_name", False): dumpfile = kwargs["vcd_name"] savefile = os.path.splitext(dumpfile)[0] + ".gtkw" # Create a separate dut just for the purpose of generaing a savefile tmp_dut = dut() vns = verilog.convert(tmp_dut).ns with gtkw.GTKWSave(vns, savefile=savefile, dumpfile=dumpfile, prefix="") as save: save.clocks() for grp_dfi, grp_name in [(tmp_dut.dfi, "dfi new"), (tmp_dut.dfi_old, "dfi old")]: with save.gtkw.group(grp_name): # each phase in separate group with save.gtkw.group("dfi phaseX", closed=True): for i, phase in enumerate(grp_dfi.phases): save.add(phase, group_name="dfi p{}".format(i), mappers=[ gtkw.dfi_sorter(phases=False), gtkw.dfi_in_phase_colorer(), ]) # only dfi command signals save.add(grp_dfi, group_name="dfi commands", mappers=[ gtkw.regex_filter( gtkw.suffixes2re( ["cas_n", "ras_n", "we_n"])), gtkw.dfi_sorter(), gtkw.dfi_per_phase_colorer(), ]) # only dfi data signals save.add(grp_dfi, group_name="dfi wrdata", mappers=[ gtkw.regex_filter(["wrdata$"]), gtkw.dfi_sorter(), gtkw.dfi_per_phase_colorer(), ]) save.add(grp_dfi, group_name="dfi wrdata_mask", mappers=[ gtkw.regex_filter( gtkw.suffixes2re(["wrdata_mask"])), gtkw.dfi_sorter(), gtkw.dfi_per_phase_colorer(), ]) save.add(grp_dfi, group_name="dfi rddata", mappers=[ gtkw.regex_filter( gtkw.suffixes2re( ["rddata", "p0.*rddata_valid"])), gtkw.dfi_sorter(), gtkw.dfi_per_phase_colorer(), ]) def checker(dfi): fail = False history = defaultdict(list) reference = defaultdict(list) # first cycle has undefined data until DFISequencer drives the signals yield for i, dfi_phases in enumerate(dfi_expected): for phase in range(len(dfi.phases)): values = dfi_reset_values() values.update(dfi_phases.get(phase, {})) for name, ref in values.items(): # if name in ["rddata", "rddata_valid"]: # continue val = (yield getattr(dfi.phases[phase], name)) msg = f"Cycle {i}, dfi.p{phase}.{name} = {val} != {ref}" history[name].append(val) reference[name].append(ref) if not fail and val != ref: fail = (val, ref, msg) yield def split_cycles(hist): s = "" for i, val in enumerate(hist): s += str(val) if (i + 1) % len(dfi.phases) == 0: s += " " return s if fail: print() for sig in history: if len(getattr(dfi.phases[0], sig)) == 1: print(f"{sig:12}: {split_cycles(history[sig])}") print(" " * 14 + f"{split_cycles(reference[sig])}") self.assertEqual(*fail) dut = dut() run_simulation(dut.ratio, dut, generators={ "sys": [dfi.generator(dut.dfi), dfi.reader(dut.dfi)], f"sys{dut.ratio}x": [ checker(dut.dfi_old), DFISequencer.input_generator( dut.dfi_old, dfi_input or []) ], }, **kwargs) dfi.assert_ok(self)