Example #1
0
def test_gtkw_savefile_none():
    sio = six.StringIO()
    sio.name = '/some/path'
    gtkw = GTKWSave(sio)
    gtkw.savefile()
    assert sio.getvalue() == '[savefile] "{}"\n'.format(
        os.path.abspath('/some/path'))
Example #2
0
def test_gtkw_savefile_none():
    sio = six.StringIO()
    sio.name = '/some/path'
    gtkw = GTKWSave(sio)
    gtkw.savefile()
    assert sio.getvalue() == '[savefile] "{}"\n'.format(
        os.path.abspath('/some/path'))
Example #3
0
 def __enter__(self):
     # pyvcd: https://pyvcd.readthedocs.io/en/latest/vcd.gtkw.html
     from vcd.gtkw import GTKWSave
     self.file = open(self.savefile, "w")
     self.gtkw = GTKWSave(self.file)
     self.gtkw.dumpfile(self.dumpfile)
     modules = self.prefix.rstrip(".").split(".")
     for i in range(len(modules)):
         self.gtkw.treeopen(".".join(modules[:i + 1]))
     self.gtkw.sst_expanded(True)
     return self
Example #4
0
def _add_sig(g: gtkw.GTKWSave, root_group, signals):
    i = 0
    while i < len(signals):
        sig = signals[i]
        if sig[:len(root_group)] == root_group:  # in this group
            if len(sig) - len(root_group) > 1:
                new_group = sig[len(root_group)]
                with g.group(new_group, closed=True):
                    i += _add_sig(g, root_group + [new_group], signals[i:])
            else:
                g.trace(".".join(sig))
                i += 1
        else:
            break
    return i
Example #5
0
    def after_run(self, sim):
        if self.draw_graph:
            g = graph(outdir=reg['results-dir'],
                      node_filter=lambda g: not g.child)
        else:
            g = None

        blocking_gears = set()
        cosim_name = None
        for sim_gear in sim.sim_gears:
            if isinstance(sim_gear, SimSocket):
                cosim_name = sim_gear.gear.name
                break
        if cosim_name and self.cosim_check:
            self.cosim_activity(g, cosim_name)
        self.sim_gears_activity(g, sim, blocking_gears)

        if self.draw_graph:
            outdir = reg['results-dir']
            g.graph.write_svg(os.path.join(outdir, 'proba.svg'))

        try:
            vcd_writer = reg['VCD']
        except KeyError:
            return

        with open(os.path.join(outdir, 'issue.gtkw'), 'w') as f:
            gtkw = GTKWSave(f)
            for module in blocking_gears:
                module_sav(gtkw, module, vcd_writer.vcd_vars)
Example #6
0
def tcl2gtkw(tcl_wave, tcl_init_files: List[str], gtkw,
             ghw: Path) -> List[str]:
    hierarchy = ghw_parse.parse(ghw)
    with open(gtkw, 'wt') as f:
        gtkw = GTKWSave(f)
        gtkw.zoom_markers(-27.0)
        c = TclFuncs(gtkw, hierarchy)
        c.tcl.eval(
            'set SILENT_SANITY "false"')  # TODO: propagate from YML config
        c.tcl.createcommand('vunit_help', lambda: None)
        for tcl in tcl_init_files:
            c.source(tcl)
        c.tcl.createcommand('run_simulation', lambda: None)
        c.source(tcl_wave)
        c.finalize()
    used_signals = sorted(c.used_signals)
    return used_signals
Example #7
0
    def __init__(self, signal_names, *, vcd_file, gtkw_file=None, traces=()):
        if isinstance(vcd_file, str):
            vcd_file = open(vcd_file, "wt")
        if isinstance(gtkw_file, str):
            gtkw_file = open(gtkw_file, "wt")

        self.vcd_vars = SignalDict()
        self.vcd_file = vcd_file
        self.vcd_writer = vcd_file and VCDWriter(self.vcd_file,
            timescale="100 ps", comment="Generated by nMigen")

        self.gtkw_names = SignalDict()
        self.gtkw_file = gtkw_file
        self.gtkw_save = gtkw_file and GTKWSave(self.gtkw_file)

        self.traces = []

        trace_names = SignalDict()
        for trace in traces:
            if trace not in signal_names:
                trace_names[trace] = trace.name
            self.traces.append(trace)

        if self.vcd_writer is None:
            return

        for signal, names in itertools.chain(signal_names.items(), trace_names.items()):
            if signal.decoder:
                var_type = "string"
                var_size = 1
                var_init = self.decode_to_vcd(signal, signal.reset)
            else:
                var_type = "wire"
                var_size = signal.width
                var_init = signal.reset

            for (*var_scope, var_name) in names:
                suffix = None
                while True:
                    try:
                        if suffix is None:
                            var_name_suffix = var_name
                        else:
                            var_name_suffix = "{}${}".format(var_name, suffix)
                        vcd_var = self.vcd_writer.register_var(
                            scope=var_scope, name=var_name_suffix,
                            var_type=var_type, size=var_size, init=var_init)
                        break
                    except KeyError:
                        suffix = (suffix or 0) + 1

                if signal not in self.vcd_vars:
                    self.vcd_vars[signal] = set()
                self.vcd_vars[signal].add(vcd_var)

                if signal not in self.gtkw_names:
                    self.gtkw_names[signal] = (*var_scope, var_name_suffix)
Example #8
0
 def pre_init(cls, env):
     analog_kwargs = {
         'datafmt': 'dec',
         'color': 'cycle',
         'extraflags': ['analog_step']
     }
     with open(env.config['sim.gtkw.file'], 'w') as gtkw_file:
         gtkw = GTKWSave(gtkw_file)
         gtkw.dumpfile(env.config['sim.vcd.dump_file'], abspath=False)
         gtkw.trace('customers.active', **analog_kwargs)
         for i in range(env.config['grocery.num_lanes']):
             gtkw.trace('grocery.lane{}.queue'.format(i), **analog_kwargs)
Example #9
0
    def __exit__(self, *args):
        if not self._run_called:
            warnings.warn("Simulation created, but not run", UserWarning)

        if self._vcd_writer:
            vcd_timestamp = (self._timestamp + self._delta) / self._epsilon
            self._vcd_writer.close(vcd_timestamp)

        if self._vcd_file and self._gtkw_file:
            gtkw_save = GTKWSave(self._gtkw_file)
            if hasattr(self._vcd_file, "name"):
                gtkw_save.dumpfile(self._vcd_file.name)
            if hasattr(self._vcd_file, "tell"):
                gtkw_save.dumpfile_size(self._vcd_file.tell())

            gtkw_save.treeopen("top")
            gtkw_save.zoom_markers(math.log(self._epsilon / self._fastest_clock) - 14)

            def add_trace(signal, **kwargs):
                signal_slot = self._signal_slots[signal]
                if self._vcd_names[signal_slot] is not None:
                    if len(signal) > 1:
                        suffix = "[{}:0]".format(len(signal) - 1)
                    else:
                        suffix = ""
                    gtkw_save.trace(self._vcd_names[signal_slot] + suffix, **kwargs)

            for domain, cd in self._domains.items():
                with gtkw_save.group("d.{}".format(domain)):
                    if cd.rst is not None:
                        add_trace(cd.rst)
                    add_trace(cd.clk)

            for signal in self._traces:
                add_trace(signal)

        if self._vcd_file:
            self._vcd_file.close()
        if self._gtkw_file:
            self._gtkw_file.close()
Example #10
0
def _regex_map(sig: SigTrace,
               patterns: Sequence[Regex],
               on_match: Callable[[SigTrace, Regex], Optional[SigTrace]],
               on_no_match: Callable[[SigTrace], Optional[SigTrace]],
               remove_bits: bool = True) -> Optional[SigTrace]:
    # Given `patterns` return `on_match(sig, pattern)` if any pattern matches or else `on_no_match(sig)`
    alias = sig.alias
    if remove_bits:  # get rid of signal bits (e.g. wb_adr[29:0])
        alias = GTKWSave._strip_bits(alias)
    for pattern in patterns:
        if pattern.search(alias):
            return on_match(sig, pattern)
    return on_no_match(sig)
Example #11
0
File: ila.py Project: zyp/luna
    def _emit_gtkw(self, filename, dump_filename, *, add_clock=True):
        """ Emits a GTKWave save file to accompany a generated VCD.

        Parameters:
            filename      -- The filename to write the GTKW save to.
            dump_filename -- The filename of the VCD that should be opened with this save.
            add_clock     -- True iff a clock signal should be added to the GTKW save.
        """

        with open(filename, 'w') as f:
            gtkw = GTKWSave(f)

            # Comments / context.
            gtkw.comment("Generated by the LUNA ILA.")

            # Add a reference to the dumpfile we're working with.
            gtkw.dumpfile(dump_filename)

            # If we're adding a clock, add it to the top of the view.
            gtkw.trace('ila.ila_clock')

            # Add each of our signals to the file.
            for signal in self.ila.signals:
                gtkw.trace(f"ila.{signal.name}")
Example #12
0
 def pre_init(cls, env):
     # Compose a GTKWave save file that lays-out the various VCD signals in
     # a meaningful manner. This must be done at pre-init time to allow
     # sim.gtkw.live to work.
     analog_kwargs = {
         'datafmt': 'dec',
         'color': 'cycle',
         'extraflags': ['analog_step'],
     }
     with open(env.config['sim.gtkw.file'], 'w') as gtkw_file:
         gtkw = GTKWSave(gtkw_file)
         gtkw.dumpfile(env.config['sim.vcd.dump_file'], abspath=False)
         gtkw.treeopen('grocery')
         gtkw.signals_width(300)
         gtkw.trace('customers.active', **analog_kwargs)
         for i in range(env.config['grocery.num_lanes']):
             with gtkw.group(f'Lane{i}'):
                 scope = f'grocery.lane{i}'
                 gtkw.trace(f'{scope}.customer_queue', **analog_kwargs)
                 gtkw.trace(f'{scope}.feed_belt', **analog_kwargs)
                 gtkw.trace(f'{scope}.bag_area', **analog_kwargs)
                 gtkw.trace(f'{scope}.baggers', **analog_kwargs)
Example #13
0
    def __exit__(self, *args):
        if not self._run_called:
            warnings.warn("Simulation created, but not run", UserWarning)

        if self._vcd_writer:
            vcd_timestamp = (self._timestamp + self._delta) / self._epsilon
            self._vcd_writer.close(vcd_timestamp)

        if self._vcd_file and self._gtkw_file:
            gtkw_save = GTKWSave(self._gtkw_file)
            if hasattr(self._vcd_file, "name"):
                gtkw_save.dumpfile(self._vcd_file.name)
            if hasattr(self._vcd_file, "tell"):
                gtkw_save.dumpfile_size(self._vcd_file.tell())

            gtkw_save.treeopen("top")
            gtkw_save.zoom_markers(math.log(self._epsilon / self._fastest_clock) - 14)

            def add_trace(signal, **kwargs):
                signal_slot = self._signal_slots[signal]
                if self._vcd_names[signal_slot] is not None:
                    if len(signal) > 1 and not signal.decoder:
                        suffix = "[{}:0]".format(len(signal) - 1)
                    else:
                        suffix = ""
                    gtkw_save.trace(self._vcd_names[signal_slot] + suffix, **kwargs)

            for domain in self._domains:
                with gtkw_save.group("d.{}".format(domain.name)):
                    if domain.rst is not None:
                        add_trace(domain.rst)
                    add_trace(domain.clk)

            for signal in self._traces:
                add_trace(signal)

        if self._vcd_file:
            self._vcd_file.close()
        if self._gtkw_file:
            self._gtkw_file.close()
Example #14
0
class GTKWSave:
    """Generator of pretty GTKWave savefiles from SoC signals

    Usage example:
    ```
    builder = Builder(soc, **builder_kwargs)
    vns = builder.build(run=False, **build_kwargs)

    with GTKWSave(vns, savefile=savefile, dumpfile=dumpfile) as gtkw:
        gtkw.clocks()
        gtkw.fsm_states(soc)
        gtkw.add(soc.bus.slaves["main_ram"])
    ```
    """
    def __init__(self,
                 vns: Namespace,
                 savefile: str,
                 dumpfile: str,
                 filtersdir: str = None,
                 treeopen: bool = True,
                 prefix: str = "TOP.sim."):
        """Crate savefile generator for the namespace.

        `prefix` is prepended to all signal names and defaults to the one used by Litex simulator.
        """
        self.vns = vns  # Namespace output of Builder.build, required to resolve signal names
        self.prefix = prefix
        self.savefile = savefile
        self.dumpfile = dumpfile
        self.filtersdir = filtersdir
        self.treeopen = treeopen
        if self.filtersdir is None:
            self.filtersdir = os.path.dirname(self.savefile)

    def __enter__(self):
        # pyvcd: https://pyvcd.readthedocs.io/en/latest/vcd.gtkw.html
        from vcd.gtkw import GTKWSave
        self.file = open(self.savefile, "w")
        self.gtkw = GTKWSave(self.file)
        self.gtkw.dumpfile(self.dumpfile)
        if self.treeopen:
            modules = self.prefix.rstrip(".").split(".")
            for i in range(len(modules)):
                if modules[i]:
                    self.gtkw.treeopen(".".join(modules[:i + 1]))
        self.gtkw.sst_expanded(True)
        return self

    def __exit__(self, type, value, traceback):
        self.file.close()
        print("\nGenerated GTKWave save file at: {}\n".format(self.savefile))

    def name(self, sig: Signal) -> str:
        bits = ""
        if len(sig) > 1:
            bits = "[{}:0]".format(len(sig) - 1)
        return self.vns.get_name(sig) + bits

    def signal(self, signal: Signal):
        self.gtkw.trace(self.prefix + self.name(signal))

    def common_prefix(self, names: Sequence[str]) -> str:
        prefix = os.path.commonprefix(names)
        last_underscore = prefix.rfind("_")
        return prefix[:last_underscore + 1]

    def group(self,
              signals: Sequence[Signal],
              group_name: str = None,
              alias: bool = True,
              closed: bool = True,
              mappers: Optional[Sequence[SigMapper]] = None,
              translation_files: Optional[Sequence[str]] = None,
              **kwargs):
        mappers = mappers or []
        translation_files = translation_files or {}
        if len(signals) == 1:
            return self.signal(signals[0])

        names = [self.name(s) for s in signals]
        common = self.common_prefix(names)

        make_alias = (lambda n: n[len(common):]) if alias else (lambda n: n)
        sigs = [
            SigTrace(name=n, signal=s, alias=make_alias(n))
            for i, (s, n) in enumerate(zip(signals, names))
        ]

        for sig, file in zip(sigs, translation_files):
            sig.filter_file = file

        for mapper in mappers:
            sigs = list(mapper(sigs))

        with self.gtkw.group(group_name or common.strip("_"), closed=closed):
            for s in sigs:
                self.gtkw.trace(self.prefix + s.name,
                                alias=s.alias,
                                color=s.color,
                                translate_filter_file=s.filter_file,
                                **kwargs)

    def by_regex(self, regex: Regex, **kwargs):
        pattern = re.compile(regex)
        signals = list(
            filter(lambda sig: pattern.search(self.vns.pnd[sig]),
                   self.vns.pnd.keys()))
        assert len(signals) > 0, "No match found for {}".format(regex)
        return self.group(signals, **kwargs)

    def clocks(self, **kwargs):
        clks = [cd.clk for cd in self.vns.clock_domains]
        self.group(clks,
                   group_name="clocks",
                   alias=False,
                   closed=False,
                   **kwargs)

    def add(self, obj: Any, no_defaults=False, **kwargs):
        # TODO: add automatic default handlers for Litex types (e.g. WishBone, AXI, ...)

        def default_mappers(types, mappers):
            if not no_defaults and isinstance(obj, types):
                kwargs["mappers"] = DEFAULT_ENDPOINT_MAPPERS + kwargs.get(
                    "mappers", [])

        if isinstance(obj, Record):
            # automatic settings for supported Record types
            default_mappers(stream.Endpoint, DEFAULT_ENDPOINT_MAPPERS)
            self.group([s for s, _ in obj.iter_flat()], **kwargs)
        elif isinstance(obj, Signal):
            self.signal(obj)
        elif self._is_module_with_attrs(obj, ["sink", "source"],
                                        types=stream.Endpoint,
                                        required=any):
            self._add_groupped_attrs(
                obj, ["sink", "source"],
                **kwargs)  # recurse to Record->Endpoint handler
        else:
            raise NotImplementedError(type(obj), obj)

    def _add_groupped_attrs(self, obj, attrs, **kwargs):
        # add given attributes of an object in an encapsulating group, with attribute names as subgroup names
        with self.gtkw.group(kwargs["group_name"],
                             closed=kwargs.get("closed", True)):
            for attr in attrs:
                if hasattr(obj, attr):
                    new_kwargs = kwargs.copy()
                    new_kwargs["group_name"] = attr
                    self.add(getattr(obj, attr), **new_kwargs)

    def make_fsm_state_translation(self, fsm: FSM) -> str:
        # generate filter file
        from vcd.gtkw import make_translation_filter
        translations = list(fsm.decoding.items())
        filename = "filter__{}.txt".format(
            self._strip_bits(self.name(fsm.state)))
        filepath = os.path.join(self.filtersdir, filename)
        with open(filepath, 'w') as f:
            f.write(make_translation_filter(translations, size=len(fsm.state)))
        return filepath

    def iter_submodules(self, parent: Module) -> Generator[Module, None, None]:
        for name, module in getattr(parent, "_submodules", []):
            yield module
            yield from self.iter_submodules(module)

    def make_fsm_state_alias(self, state: Signal):
        # Try to improve state name, as the defaults are usually hard to decipher.
        # This will make sure to include the name of the module that has the FSM,
        # but still there are some issues, e.g. we always add number to all names.
        alias = ""
        for name, num in reversed(state.backtrace):
            if alias.startswith(name):
                continue
            if name == "subfragments":
                break
            alias = "{}{}_{}".format(name, num, alias)
        return alias.strip("_")

    def fsm_states(self, soc: Module, alias: bool = True, **kwargs):
        fsms = list(
            filter(lambda module: isinstance(module, FSM),
                   self.iter_submodules(soc)))
        states = [fsm.state for fsm in fsms]
        files = [self.make_fsm_state_translation(fsm) for fsm in fsms]

        if alias:
            aliases = {
                state: self.make_fsm_state_alias(state)
                for state in states
            }

            def add_alias(sig):
                sig.alias = aliases.get(sig.signal, None)
                return sig

            kwargs["mappers"] = [lambda sigs: map(add_alias, sigs)
                                 ] + kwargs.get("mappers", [])

        self.group(states,
                   group_name="FSM states",
                   translation_files=files,
                   **kwargs)

    @staticmethod
    def _strip_bits(name: str) -> str:
        if name.endswith("]") and "[" in name:
            name = name[:name.rfind("[")]
        return name

    @staticmethod
    def _is_module_with_attrs(obj, attrs, types, required) -> bool:
        if not isinstance(obj, Module):
            return False
        available = map(
            lambda a: hasattr(obj, a) and isinstance(getattr(obj, a), types),
            attrs)
        return required(available)
Example #15
0
 def pre_init(cls, env):
     # Compose a GTKWave save file that lays-out the various VCD signals in
     # a meaningful manner. This must be done at pre-init time to allow
     # sim.gtkw.live to work.
     analog_kwargs = {'datafmt': 'dec',
                      'color': 'cycle',
                      'extraflags': ['analog_step']}
     with open(env.config['sim.gtkw.file'], 'w') as gtkw_file:
         gtkw = GTKWSave(gtkw_file)
         gtkw.dumpfile(env.config['sim.vcd.dump_file'], abspath=False)
         gtkw.treeopen('grocery')
         gtkw.signals_width(300)
         gtkw.trace('customers.active', **analog_kwargs)
         for i in range(env.config['grocery.num_lanes']):
             with gtkw.group('Lane{}'.format(i)):
                 scope = 'grocery.lane{}'.format(i)
                 gtkw.trace(scope + '.customer_queue', **analog_kwargs)
                 gtkw.trace(scope + '.feed_belt', **analog_kwargs)
                 gtkw.trace(scope + '.bag_area', **analog_kwargs)
                 gtkw.trace(scope + '.baggers', **analog_kwargs)
Example #16
0
    def __init__(self, fragment, *, vcd_file, gtkw_file=None, traces=()):
        if isinstance(vcd_file, str):
            vcd_file = open(vcd_file, "wt")
        if isinstance(gtkw_file, str):
            gtkw_file = open(gtkw_file, "wt")

        self.vcd_vars = SignalDict()
        self.vcd_file = vcd_file
        self.vcd_writer = vcd_file and VCDWriter(
            self.vcd_file, timescale="100 ps", comment="Generated by nMigen")

        self.gtkw_names = SignalDict()
        self.gtkw_file = gtkw_file
        self.gtkw_save = gtkw_file and GTKWSave(self.gtkw_file)

        self.traces = []

        signal_names = _NameExtractor()(fragment)

        trace_names = SignalDict()
        for trace in traces:
            if trace not in signal_names:
                trace_names[trace] = {("top", trace.name)}
            self.traces.append(trace)

        if self.vcd_writer is None:
            return

        for signal, names in itertools.chain(signal_names.items(),
                                             trace_names.items()):
            if signal.decoder:
                var_type = "string"
                var_size = 1
                var_init = self.decode_to_vcd(signal, signal.reset)
            else:
                var_type = "wire"
                var_size = signal.width
                var_init = signal.reset

            for (*var_scope, var_name) in names:
                if ' ' in var_name:
                    raise ValueError(
                        "Variable '{}' cannot contain a space.".format(
                            var_name))
                suffix = None
                while True:
                    try:
                        if suffix is None:
                            var_name_suffix = var_name
                        else:
                            var_name_suffix = "{}${}".format(var_name, suffix)
                        if signal not in self.vcd_vars:
                            vcd_var = self.vcd_writer.register_var(
                                scope=var_scope,
                                name=var_name_suffix,
                                var_type=var_type,
                                size=var_size,
                                init=var_init)
                            self.vcd_vars[signal] = vcd_var
                        else:
                            self.vcd_writer.register_alias(
                                scope=var_scope,
                                name=var_name_suffix,
                                var=self.vcd_vars[signal])
                        break
                    except KeyError:
                        suffix = (suffix or 0) + 1

                if signal not in self.gtkw_names:
                    self.gtkw_names[signal] = (*var_scope, var_name_suffix)