示例#1
0
文件: pysim.py 项目: Amalinda/nmigen
    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, 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()
示例#2
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)
示例#3
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()
示例#4
0
文件: gtkwave.py 项目: hplp/litex
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)