Ejemplo n.º 1
0
def main():

    global start

    vcdFile = open("out.vcd", "w")
    vcd = VCDWriter(vcdFile, timescale='1 ns')
    gpio1 = vcd.register_var('BLE', 'timer1', 'wire', size=1, ident='!')
    gpio2 = vcd.register_var('BLE', 'timer2', 'wire', size=1, ident='$')
    start = time.perf_counter_ns()

    # event to signal the consumer to stop consuming
    e = threading.Event()

    timer1 = threading.Timer(10e-3, timer1_func, args=(e, vcd, gpio1))
    #timer2 = threading.Timer(15e-3, timer2_func, args=(e, vcd, gpio2))

    timer1.start()
    time.sleep(10e-3)
    #timer2.start()

    # wait until all timers terminate
    e.wait()

    print("Finish")

    vcd.close()
    vcdFile.close()
Ejemplo n.º 2
0
    async def interact(self, device, args, iface):
        vcd_writer = VCDWriter(args.file, timescale="1 ns", check_values=False)
        signals = []
        for index in range(self._event_sources[0].width):
            signals.append(vcd_writer.register_var(scope="", name="pin[{}]".format(index),
                var_type="wire", size=1, init=0))

        try:
            overrun = False
            timestamp = 0
            while not overrun:
                for cycle, events in await iface.read():
                    timestamp = cycle * 1_000_000_000 // self._sample_freq

                    if events == "overrun":
                        self.logger.error("FIFO overrun, shutting down")
                        for signal in signals:
                            vcd_writer.change(signal, timestamp, "x")
                        overrun = True
                        break

                    if "pin" in events: # could be also "throttle"
                        value = events["pin"]
                        for bit, signal in enumerate(signals):
                            vcd_writer.change(signal, timestamp, (value >> bit) & 1)

        finally:
            vcd_writer.close(timestamp)
Ejemplo n.º 3
0
    async def interact(self, device, args, iface):
        vcd_writer = VCDWriter(args.file, timescale="1 ns", check_values=False)
        tckSignal = vcd_writer.register_var(scope="",
                                            name="tck",
                                            var_type="wire",
                                            size=1,
                                            init=1)
        tmsSignal = vcd_writer.register_var(scope="",
                                            name="tms",
                                            var_type="wire",
                                            size=1,
                                            init=0)
        tdiSignal = vcd_writer.register_var(scope="",
                                            name="tdi",
                                            var_type="wire",
                                            size=1,
                                            init=0)
        tdoSignal = vcd_writer.register_var(scope="",
                                            name="tdo",
                                            var_type="wire",
                                            size=1,
                                            init=0)
        srstSignal = vcd_writer.register_var(scope="",
                                             name="srst",
                                             var_type="wire",
                                             size=1,
                                             init=0)

        cycle = 0
        try:
            tck = tms = tdi = tdo = srst = 0
            while True:
                for byte in await iface.read():
                    tckNext, tmsNext, tdiNext, tdoNext, srstNext = byte
                    if tckNext != tck:
                        tck = tckNext
                        vcd_writer.change(tckSignal, cycle, tck)
                    if tmsNext != tms:
                        tms = tmsNext
                        vcd_writer.change(tmsSignal, cycle, tms)
                    if tdiNext != tdi:
                        tdi = tdiNext
                        vcd_writer.change(tdiSignal, cycle, tdi)
                    if tdoNext != tdo:
                        tdo = tdoNext
                        vcd_writer.change(tdoSignal, cycle, tdo)
                    if srstNext:
                        srst = srstNext
                        vcd_writer.change(srstSignal, cycle, srst)
                    cycle += 1
        finally:
            vcd_writer.close(cycle)
Ejemplo n.º 4
0
class ModelVCD:
    def __init__(self, model, filename):
        self.nodes: Dict[Node, Variable] = {}
        self.model = model
        self._fd = open(filename, "w+")
        self._vcd = VCDWriter(self._fd)
        self._timestamp = 0
        self.__top = "Top"
        # create variables in the model
        for node in model.nodes:
            tile = f"{self.__top}.TILE_X{node.x}Y{node.y}"
            node_name = str(node)
            var = self._vcd.register_var(tile,
                                         node_name,
                                         "integer",
                                         size=node.width,
                                         init=0)
            self.nodes[node] = var
        self.clk = self._vcd.register_var(self.__top,
                                          "clk",
                                          "integer",
                                          size=1,
                                          init=False)
        self.clk_val = 0

    def eval(self, tick=False):
        self._timestamp += 1
        if tick:
            self.clk_val ^= 1
        self._vcd.change(self.clk, self._timestamp, self.clk_val)

        for node, var in self.nodes.items():
            value = self.model.get_value(node)
            self._vcd.change(var, self._timestamp, value)
        if tick:
            self._timestamp += 1
            self.clk_val ^= 1
            self._vcd.change(self.clk, self._timestamp, self.clk_val)

    def close(self):
        self._vcd.close()
        self._fd.close()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()
Ejemplo n.º 5
0
	async def _write_raw_vcd(self, file, iface : JTAGPDIInterface):
		vcd_writer = VCDWriter(file, timescale = "1 ns", check_values = False)
		pdiClk = vcd_writer.register_var(scope = "", name = "pdiClk", var_type = "wire", size = 1, init = 1)
		pdiData = vcd_writer.register_var(scope = "", name = "pdiData", var_type = "wire", size = 8)

		cycle = 1
		try:
			while True:
				for byte in await iface.read():
					vcd_writer.change(pdiClk, cycle, 0)
					vcd_writer.change(pdiData, cycle, byte)
					cycle += 1
					vcd_writer.change(pdiClk, cycle, 1)
					cycle += 1
		finally:
			vcd_writer.close(cycle)
Ejemplo n.º 6
0
class VCDWaveformWriter:
    def __init__(self, simulation, *, vcd_file):
        vcd_file = open(vcd_file, "wt")
        self.sim = simulation

        self.vcd_file = vcd_file
        self.vcd_writer = VCDWriter(self.vcd_file,
                                    timescale="1 ps",
                                    comment="Generated by nmigen-yosim")

        self.vcd_vars = []
        if self.vcd_writer is None:
            return

    def update(self):
        timestamp = self.sim.sim_time
        for vcd_var, signal in self.vcd_vars:
            self.vcd_writer.change(vcd_var, timestamp, signal.value)

    def callback(self, signals):
        self.vcd_vars = []
        for signal in signals:
            hierarchy = signal.name.split('.')
            var_type = "wire"
            var_size = signal.width
            var_init = signal.value
            var_name = hierarchy[-1]
            var_scope = '.'.join(hierarchy[:-1])

            vcd_var = self.vcd_writer.register_var(scope=var_scope,
                                                   name=var_name,
                                                   var_type=var_type,
                                                   size=var_size,
                                                   init=var_init)
            self.vcd_vars.append((vcd_var, signal))

        while True:
            self.update()
            yield

    def __del__(self):
        if self.vcd_writer is not None:
            self.vcd_writer.close(self.sim.sim_time)
        if self.vcd_file is not None:
            self.vcd_file.close()
Ejemplo n.º 7
0
class VCDTracer(Tracer):

    name = 'vcd'

    def open(self):
        dump_filename = self.env.config.setdefault('sim.vcd.dump_file',
                                                   'sim.vcd')
        if 'sim.vcd.timescale' in self.env.config:
            vcd_ts_str = self.env.config.setdefault(
                'sim.vcd.timescale', self.env.config['sim.timescale'])
            vcd_timescale = parse_time(vcd_ts_str)
        else:
            vcd_timescale = self.env.timescale
        self.scale_factor = scale_time(self.env.timescale, vcd_timescale)
        check_values = self.env.config.setdefault('sim.vcd.check_values', True)
        self.dump_file = open(dump_filename, 'w')
        self.vcd = VCDWriter(self.dump_file,
                             timescale=vcd_timescale,
                             check_values=check_values)
        self.save_filename = self.env.config.setdefault(
            'sim.gtkw.file', 'sim.gtkw')
        if self.env.config.setdefault('sim.gtkw.live'):
            from vcd.gtkw import spawn_gtkwave_interactive
            quiet = self.env.config.setdefault('sim.gtkw.quiet', True)
            spawn_gtkwave_interactive(dump_filename,
                                      self.save_filename,
                                      quiet=quiet)

        start_time = self.env.config.setdefault('sim.vcd.start_time', '')
        stop_time = self.env.config.setdefault('sim.vcd.stop_time', '')
        t_start = (scale_time(parse_time(start_time), self.env.timescale)
                   if start_time else None)
        t_stop = (scale_time(parse_time(stop_time), self.env.timescale)
                  if stop_time else None)
        self.env.process(self._start_stop(t_start, t_stop))

    def vcd_now(self):
        return self.env.now * self.scale_factor

    def flush(self):
        self.dump_file.flush()

    def _close(self):
        self.vcd.close(self.vcd_now())
        self.dump_file.close()

    def remove_files(self):
        if os.path.isfile(self.dump_file.name):
            os.remove(self.dump_file.name)
        if os.path.isfile(self.save_filename):
            os.remove(self.save_filename)

    def activate_probe(self, scope, target, **hints):
        assert self.enabled
        var_type = hints.get('var_type')
        if var_type is None:
            if isinstance(target, simpy.Container):
                if isinstance(target.level, float):
                    var_type = 'real'
                else:
                    var_type = 'integer'
            elif isinstance(target, (simpy.Resource, simpy.Store, Queue)):
                var_type = 'integer'
            else:
                raise ValueError(
                    'Could not infer VCD var_type for {}'.format(scope))

        kwargs = {k: hints[k] for k in ['size', 'init', 'ident'] if k in hints}

        if 'init' not in kwargs:
            if isinstance(target, simpy.Container):
                kwargs['init'] = target.level
            elif isinstance(target, simpy.Resource):
                kwargs['init'] = len(target.users) if target.users else 'z'
            elif isinstance(target, (simpy.Store, Queue)):
                kwargs['init'] = len(target.items)

        parent_scope, name = scope.rsplit('.', 1)
        var = self.vcd.register_var(parent_scope, name, var_type, **kwargs)

        def probe_callback(value):
            self.vcd.change(var, self.vcd_now(), value)

        return probe_callback

    def activate_trace(self, scope, **hints):
        assert self.enabled
        var_type = hints['var_type']
        kwargs = {k: hints[k] for k in ['size', 'init', 'ident'] if k in hints}

        parent_scope, name = scope.rsplit('.', 1)
        var = self.vcd.register_var(parent_scope, name, var_type, **kwargs)

        if isinstance(var.size, tuple):

            def trace_callback(*value):
                self.vcd.change(var, self.vcd_now(), value)
        else:

            def trace_callback(value):
                self.vcd.change(var, self.vcd_now(), value)

        return trace_callback

    def _start_stop(self, t_start, t_stop):
        # Wait for simulation to start to ensure all variable registration is
        # complete before doing and dump_on()/dump_off() calls.
        yield self.env.timeout(0)

        if t_start is None and t_stop is None:
            # |vvvvvvvvvvvvvv|
            pass
        elif t_start is None:
            # |vvvvvv--------|
            yield self.env.timeout(t_stop)
            self.vcd.dump_off(self.vcd_now())
        elif t_stop is None:
            # |--------vvvvvv|
            self.vcd.dump_off(self.vcd_now())
            yield self.env.timeout(t_start)
            self.vcd.dump_on(self.vcd_now())
        elif t_start <= t_stop:
            # |---vvvvvv-----|
            self.vcd.dump_off(self.vcd_now())
            yield self.env.timeout(t_start)
            self.vcd.dump_on(self.vcd_now())
            yield self.env.timeout(t_stop - t_start)
            self.vcd.dump_off(self.vcd_now())
        else:
            # |vvv-------vvvv|
            yield self.env.timeout(t_stop)
            self.vcd.dump_off(self.vcd_now())
            yield self.env.timeout(t_start - t_stop)
            self.vcd.dump_on(self.vcd_now())
Ejemplo n.º 8
0
class Simulator:
    def __init__(self, fragment, vcd_file=None, gtkw_file=None, traces=()):
        self._fragment        = Fragment.get(fragment, platform=None)

        self._signal_slots    = SignalDict()  # Signal -> int/slot
        self._slot_signals    = list()        # int/slot -> Signal

        self._domains         = list()        # [ClockDomain]
        self._clk_edges       = dict()        # ClockDomain -> int/edge
        self._domain_triggers = list()        # int/slot -> ClockDomain

        self._signals         = SignalSet()   # {Signal}
        self._comb_signals    = bitarray()    # {Signal}
        self._sync_signals    = bitarray()    # {Signal}
        self._user_signals    = bitarray()    # {Signal}
        self._domain_signals  = dict()        # ClockDomain -> {Signal}

        self._started         = False
        self._timestamp       = 0.
        self._delta           = 0.
        self._epsilon         = 1e-10
        self._fastest_clock   = self._epsilon
        self._all_clocks      = set()         # {str/domain}
        self._state           = _State()

        self._processes       = set()         # {process}
        self._process_loc     = dict()        # process -> str/loc
        self._passive         = set()         # {process}
        self._suspended       = set()         # {process}
        self._wait_deadline   = dict()        # process -> float/timestamp
        self._wait_tick       = dict()        # process -> str/domain

        self._funclets        = list()        # int/slot -> set(lambda)

        self._vcd_file        = vcd_file
        self._vcd_writer      = None
        self._vcd_signals     = list()        # int/slot -> set(vcd_signal)
        self._vcd_names       = list()        # int/slot -> str/name
        self._gtkw_file       = gtkw_file
        self._traces          = traces

        self._run_called      = False

    @staticmethod
    def _check_process(process):
        if inspect.isgeneratorfunction(process):
            process = process()
        if not (inspect.isgenerator(process) or inspect.iscoroutine(process)):
            raise TypeError("Cannot add a process '{!r}' because it is not a generator or "
                            "a generator function"
                            .format(process))
        return process

    def _name_process(self, process):
        if process in self._process_loc:
            return self._process_loc[process]
        else:
            if inspect.isgenerator(process):
                frame = process.gi_frame
            if inspect.iscoroutine(process):
                frame = process.cr_frame
            return "{}:{}".format(inspect.getfile(frame), inspect.getlineno(frame))

    def add_process(self, process):
        process = self._check_process(process)
        self._processes.add(process)

    def add_sync_process(self, process, domain="sync"):
        process = self._check_process(process)
        def sync_process():
            try:
                cmd = None
                while True:
                    if cmd is None:
                        cmd = Tick(domain)
                    result = yield cmd
                    self._process_loc[sync_process] = self._name_process(process)
                    cmd = process.send(result)
            except StopIteration:
                pass
        sync_process = sync_process()
        self.add_process(sync_process)

    def add_clock(self, period, *, phase=None, domain="sync", if_exists=False):
        if self._fastest_clock == self._epsilon or period < self._fastest_clock:
            self._fastest_clock = period
        if domain in self._all_clocks:
            raise ValueError("Domain '{}' already has a clock driving it"
                             .format(domain))

        half_period = period / 2
        if phase is None:
            phase = half_period
        for domain_obj in self._domains:
            if not domain_obj.local and domain_obj.name == domain:
                clk = domain_obj.clk
                break
        else:
            if if_exists:
                return
            else:
                raise ValueError("Domain '{}' is not present in simulation"
                                 .format(domain))
        def clk_process():
            yield Passive()
            yield Delay(phase)
            while True:
                yield clk.eq(1)
                yield Delay(half_period)
                yield clk.eq(0)
                yield Delay(half_period)
        self.add_process(clk_process)
        self._all_clocks.add(domain)

    def __enter__(self):
        if self._vcd_file:
            self._vcd_writer = VCDWriter(self._vcd_file, timescale="100 ps",
                                         comment="Generated by nMigen")

        root_fragment = self._fragment.prepare()

        hierarchy = {}
        domains = set()
        def add_fragment(fragment, scope=()):
            hierarchy[fragment] = scope
            domains.update(fragment.domains.values())
            for index, (subfragment, name) in enumerate(fragment.subfragments):
                if name is None:
                    add_fragment(subfragment, (*scope, "U{}".format(index)))
                else:
                    add_fragment(subfragment, (*scope, name))
        add_fragment(root_fragment, scope=("top",))
        self._domains = list(domains)
        self._clk_edges = {domain: 1 if domain.clk_edge == "pos" else 0 for domain in domains}

        def add_signal(signal):
            if signal not in self._signals:
                self._signals.add(signal)

                signal_slot = self._state.add(normalize(signal.reset, signal.shape()))
                self._signal_slots[signal] = signal_slot
                self._slot_signals.append(signal)

                self._comb_signals.append(False)
                self._sync_signals.append(False)
                self._user_signals.append(False)
                for domain in self._domains:
                    if domain not in self._domain_signals:
                        self._domain_signals[domain] = bitarray()
                    self._domain_signals[domain].append(False)

                self._funclets.append(set())

                self._domain_triggers.append(None)
                if self._vcd_writer:
                    self._vcd_signals.append(set())
                    self._vcd_names.append(None)

            return self._signal_slots[signal]

        def add_domain_signal(signal, domain):
            signal_slot = add_signal(signal)
            self._domain_triggers[signal_slot] = domain

        for fragment, fragment_scope in hierarchy.items():
            for signal in fragment.iter_signals():
                add_signal(signal)

            for domain_name, domain in fragment.domains.items():
                add_domain_signal(domain.clk, domain)
                if domain.rst is not None:
                    add_domain_signal(domain.rst, domain)

        for fragment, fragment_scope in hierarchy.items():
            for signal in fragment.iter_signals():
                if not self._vcd_writer:
                    continue

                signal_slot = self._signal_slots[signal]

                for i, (subfragment, name) in enumerate(fragment.subfragments):
                    if signal in subfragment.ports:
                        var_name = "{}_{}".format(name or "U{}".format(i), signal.name)
                        break
                else:
                    var_name = signal.name

                if signal.decoder:
                    var_type = "string"
                    var_size = 1
                    var_init = signal.decoder(signal.reset).expandtabs().replace(" ", "_")
                else:
                    var_type = "wire"
                    var_size = signal.nbits
                    var_init = signal.reset

                suffix = None
                while True:
                    try:
                        if suffix is None:
                            var_name_suffix = var_name
                        else:
                            var_name_suffix = "{}${}".format(var_name, suffix)
                        self._vcd_signals[signal_slot].add(self._vcd_writer.register_var(
                            scope=".".join(fragment_scope), name=var_name_suffix,
                            var_type=var_type, size=var_size, init=var_init))
                        if self._vcd_names[signal_slot] is None:
                            self._vcd_names[signal_slot] = \
                                ".".join(fragment_scope + (var_name_suffix,))
                        break
                    except KeyError:
                        suffix = (suffix or 0) + 1

            for domain_name, signals in fragment.drivers.items():
                signals_bits = bitarray(len(self._signals))
                signals_bits.setall(False)
                for signal in signals:
                    signals_bits[self._signal_slots[signal]] = True

                if domain_name is None:
                    self._comb_signals |= signals_bits
                else:
                    self._sync_signals |= signals_bits
                    self._domain_signals[fragment.domains[domain_name]] |= signals_bits

            statements = []
            for domain_name, signals in fragment.drivers.items():
                reset_stmts = []
                hold_stmts  = []
                for signal in signals:
                    reset_stmts.append(signal.eq(signal.reset))
                    hold_stmts .append(signal.eq(signal))

                if domain_name is None:
                    statements += reset_stmts
                else:
                    if fragment.domains[domain_name].async_reset:
                        statements.append(Switch(fragment.domains[domain_name].rst,
                            {0: hold_stmts, 1: reset_stmts}))
                    else:
                        statements += hold_stmts
            statements += fragment.statements

            compiler = _StatementCompiler(self._signal_slots)
            funclet = compiler(statements)

            def add_funclet(signal, funclet):
                if signal in self._signal_slots:
                    self._funclets[self._signal_slots[signal]].add(funclet)

            for signal in compiler.sensitivity:
                add_funclet(signal, funclet)
            for domain in fragment.domains.values():
                add_funclet(domain.clk, funclet)
                if domain.rst is not None:
                    add_funclet(domain.rst, funclet)

        self._user_signals = bitarray(len(self._signals))
        self._user_signals.setall(True)
        self._user_signals &= ~self._comb_signals
        self._user_signals &= ~self._sync_signals

        return self

    def _update_dirty_signals(self):
        """Perform the statement part of IR processes (aka RTLIL case)."""
        # First, for all dirty signals, use sensitivity lists to determine the set of fragments
        # that need their statements to be reevaluated because the signals changed at the previous
        # delta cycle.
        funclets = set()
        for signal_slot in self._state.flush_curr_dirty():
            funclets.update(self._funclets[signal_slot])

        # Second, compute the values of all signals at the start of the next delta cycle, by
        # running precompiled statements.
        for funclet in funclets:
            funclet(self._state)

    def _commit_signal(self, signal_slot, domains):
        """Perform the driver part of IR processes (aka RTLIL sync), for individual signals."""
        # Take the computed value (at the start of this delta cycle) of a signal (that could have
        # come from an IR process that ran earlier, or modified by a simulator process) and update
        # the value for this delta cycle.
        old, new = self._state.commit(signal_slot)
        if old == new:
            return

        # If the signal is a clock that triggers synchronous logic, record that fact.
        if (self._domain_triggers[signal_slot] is not None and
                self._clk_edges[self._domain_triggers[signal_slot]] == new):
            domains.add(self._domain_triggers[signal_slot])

        if self._vcd_writer:
            # Finally, dump the new value to the VCD file.
            for vcd_signal in self._vcd_signals[signal_slot]:
                signal = self._slot_signals[signal_slot]
                if signal.decoder:
                    var_value = signal.decoder(new).expandtabs().replace(" ", "_")
                else:
                    var_value = new
                vcd_timestamp = (self._timestamp + self._delta) / self._epsilon
                self._vcd_writer.change(vcd_signal, vcd_timestamp, var_value)

    def _commit_comb_signals(self, domains):
        """Perform the comb part of IR processes (aka RTLIL always)."""
        # Take the computed value (at the start of this delta cycle) of every comb signal and
        # update the value for this delta cycle.
        for signal_slot in self._state.iter_next_dirty():
            if self._comb_signals[signal_slot]:
                self._commit_signal(signal_slot, domains)

    def _commit_sync_signals(self, domains):
        """Perform the sync part of IR processes (aka RTLIL posedge)."""
        # At entry, `domains` contains a set of every simultaneously triggered sync update.
        while domains:
            # Advance the timeline a bit (purely for observational purposes) and commit all of them
            # at the same timestamp.
            self._delta += self._epsilon
            curr_domains, domains = domains, set()

            while curr_domains:
                domain = curr_domains.pop()

                # Wake up any simulator processes that wait for a domain tick.
                for process, wait_domain_name in list(self._wait_tick.items()):
                    if domain.name == wait_domain_name:
                        del self._wait_tick[process]
                        self._suspended.remove(process)

                        # Immediately run the process. It is important that this happens here,
                        # and not on the next step, when all the processes will run anyway,
                        # because Tick() simulates an edge triggered process. Like DFFs that latch
                        # a value from the previous clock cycle, simulator processes observe signal
                        # values from the previous clock cycle on a tick, too.
                        self._run_process(process)

                # Take the computed value (at the start of this delta cycle) of every sync signal
                # in this domain and update the value for this delta cycle. This can trigger more
                # synchronous logic, so record that.
                for signal_slot in self._state.iter_next_dirty():
                    if self._domain_signals[domain][signal_slot]:
                        self._commit_signal(signal_slot, domains)

            # Unless handling synchronous logic above has triggered more synchronous logic (which
            # can happen e.g. if a domain is clocked off a clock divisor in fabric), we're done.
            # Otherwise, do one more round of updates.

    def _run_process(self, process):
        try:
            cmd = process.send(None)
            while True:
                if type(cmd) is Delay:
                    if cmd.interval is None:
                        interval = self._epsilon
                    else:
                        interval = cmd.interval
                    self._wait_deadline[process] = self._timestamp + interval
                    self._suspended.add(process)
                    break

                elif type(cmd) is Tick:
                    self._wait_tick[process] = cmd.domain
                    self._suspended.add(process)
                    break

                elif type(cmd) is Passive:
                    self._passive.add(process)

                elif type(cmd) is Assign:
                    lhs_signals = cmd.lhs._lhs_signals()
                    for signal in lhs_signals:
                        if not signal in self._signals:
                            raise ValueError("Process '{}' sent a request to set signal '{!r}', "
                                             "which is not a part of simulation"
                                             .format(self._name_process(process), signal))
                        signal_slot = self._signal_slots[signal]
                        if self._comb_signals[signal_slot]:
                            raise ValueError("Process '{}' sent a request to set signal '{!r}', "
                                             "which is a part of combinatorial assignment in "
                                             "simulation"
                                             .format(self._name_process(process), signal))

                    if type(cmd.lhs) is Signal and type(cmd.rhs) is Const:
                        # Fast path.
                        self._state.set(self._signal_slots[cmd.lhs],
                                        normalize(cmd.rhs.value, cmd.lhs.shape()))
                    else:
                        compiler = _StatementCompiler(self._signal_slots)
                        funclet = compiler(cmd)
                        funclet(self._state)

                    domains = set()
                    for signal in lhs_signals:
                        self._commit_signal(self._signal_slots[signal], domains)
                    self._commit_sync_signals(domains)

                elif type(cmd) is Signal:
                    # Fast path.
                    cmd = process.send(self._state.curr[self._signal_slots[cmd]])
                    continue

                elif isinstance(cmd, Value):
                    compiler = _RHSValueCompiler(self._signal_slots)
                    funclet = compiler(cmd)
                    cmd = process.send(funclet(self._state))
                    continue

                else:
                    raise TypeError("Received unsupported command '{!r}' from process '{}'"
                                    .format(cmd, self._name_process(process)))

                cmd = process.send(None)

        except StopIteration:
            self._processes.remove(process)
            self._passive.discard(process)

        except Exception as e:
            process.throw(e)

    def step(self, run_passive=False):
        # Are there any delta cycles we should run?
        if self._state.curr_dirty.any():
            # We might run some delta cycles, and we have simulator processes waiting on
            # a deadline. Take care to not exceed the closest deadline.
            if self._wait_deadline and \
                    (self._timestamp + self._delta) >= min(self._wait_deadline.values()):
                # Oops, we blew the deadline. We *could* run the processes now, but this is
                # virtually certainly a logic loop and a design bug, so bail out instead.d
                raise DeadlineError("Delta cycles exceeded process deadline; combinatorial loop?")

            domains = set()
            while self._state.curr_dirty.any():
                self._update_dirty_signals()
                self._commit_comb_signals(domains)
            self._commit_sync_signals(domains)
            return True

        # Are there any processes that haven't had a chance to run yet?
        if len(self._processes) > len(self._suspended):
            # Schedule an arbitrary one.
            process = (self._processes - set(self._suspended)).pop()
            self._run_process(process)
            return True

        # All processes are suspended. Are any of them active?
        if len(self._processes) > len(self._passive) or run_passive:
            # Are any of them suspended before a deadline?
            if self._wait_deadline:
                # Schedule the one with the lowest deadline.
                process, deadline = min(self._wait_deadline.items(), key=lambda x: x[1])
                del self._wait_deadline[process]
                self._suspended.remove(process)
                self._timestamp = deadline
                self._delta = 0.
                self._run_process(process)
                return True

        # No processes, or all processes are passive. Nothing to do!
        return False

    def run(self):
        self._run_called = True

        while self.step():
            pass

    def run_until(self, deadline, run_passive=False):
        self._run_called = True

        while self._timestamp < deadline:
            if not self.step(run_passive):
                return False

        return True

    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()
Ejemplo n.º 9
0
class VCDTracer(Tracer):

    name = 'vcd'

    def open(self):
        dump_filename = self.env.config.setdefault('sim.vcd.dump_file',
                                                   'sim.vcd')
        if 'sim.vcd.timescale' in self.env.config:
            vcd_ts_str = self.env.config.setdefault(
                'sim.vcd.timescale',
                self.env.config['sim.timescale'])
            vcd_timescale = parse_time(vcd_ts_str)
        else:
            vcd_timescale = self.env.timescale
        self.scale_factor = scale_time(self.env.timescale, vcd_timescale)
        check_values = self.env.config.setdefault('sim.vcd.check_values', True)
        self.dump_file = open(dump_filename, 'w')
        self.vcd = VCDWriter(self.dump_file,
                             timescale=vcd_timescale,
                             check_values=check_values)
        self.save_filename = self.env.config.setdefault('sim.gtkw.file',
                                                        'sim.gtkw')
        if self.env.config.setdefault('sim.gtkw.live'):
            from vcd.gtkw import spawn_gtkwave_interactive
            quiet = self.env.config.setdefault('sim.gtkw.quiet', True)
            spawn_gtkwave_interactive(dump_filename, self.save_filename,
                                      quiet=quiet)

        start_time = self.env.config.setdefault('sim.vcd.start_time', '')
        stop_time = self.env.config.setdefault('sim.vcd.stop_time', '')
        t_start = (scale_time(parse_time(start_time), self.env.timescale)
                   if start_time else None)
        t_stop = (scale_time(parse_time(stop_time), self.env.timescale)
                  if stop_time else None)
        self.env.process(self._start_stop(t_start, t_stop))

    def vcd_now(self):
        return self.env.now * self.scale_factor

    def flush(self):
        self.dump_file.flush()

    def _close(self):
        self.vcd.close(self.vcd_now())
        self.dump_file.close()

    def remove_files(self):
        if os.path.isfile(self.dump_file.name):
            os.remove(self.dump_file.name)
        if os.path.isfile(self.save_filename):
            os.remove(self.save_filename)

    def activate_probe(self, scope, target, **hints):
        assert self.enabled
        var_type = hints.get('var_type')
        if var_type is None:
            if isinstance(target, (simpy.Container, Pool)):
                if isinstance(target.level, float):
                    var_type = 'real'
                else:
                    var_type = 'integer'
            elif isinstance(target, (simpy.Resource, simpy.Store, Queue)):
                var_type = 'integer'
            else:
                raise ValueError(
                    'Could not infer VCD var_type for {}'.format(scope))

        kwargs = {k: hints[k]
                  for k in ['size', 'init', 'ident']
                  if k in hints}

        if 'init' not in kwargs:
            if isinstance(target, (simpy.Container, Pool)):
                kwargs['init'] = target.level
            elif isinstance(target, simpy.Resource):
                kwargs['init'] = len(target.users) if target.users else 'z'
            elif isinstance(target, (simpy.Store, Queue)):
                kwargs['init'] = len(target.items)

        parent_scope, name = scope.rsplit('.', 1)
        var = self.vcd.register_var(parent_scope, name, var_type, **kwargs)

        def probe_callback(value):
            self.vcd.change(var, self.vcd_now(), value)

        return probe_callback

    def activate_trace(self, scope, **hints):
        assert self.enabled
        var_type = hints['var_type']
        kwargs = {k: hints[k]
                  for k in ['size', 'init', 'ident']
                  if k in hints}

        parent_scope, name = scope.rsplit('.', 1)
        var = self.vcd.register_var(parent_scope, name, var_type, **kwargs)

        if isinstance(var.size, tuple):
            def trace_callback(*value):
                self.vcd.change(var, self.vcd_now(), value)
        else:
            def trace_callback(value):
                self.vcd.change(var, self.vcd_now(), value)

        return trace_callback

    def _start_stop(self, t_start, t_stop):
        # Wait for simulation to start to ensure all variable registration is
        # complete before doing and dump_on()/dump_off() calls.
        yield self.env.timeout(0)

        if t_start is None and t_stop is None:
            # |vvvvvvvvvvvvvv|
            pass
        elif t_start is None:
            # |vvvvvv--------|
            yield self.env.timeout(t_stop)
            self.vcd.dump_off(self.vcd_now())
        elif t_stop is None:
            # |--------vvvvvv|
            self.vcd.dump_off(self.vcd_now())
            yield self.env.timeout(t_start)
            self.vcd.dump_on(self.vcd_now())
        elif t_start <= t_stop:
            # |---vvvvvv-----|
            self.vcd.dump_off(self.vcd_now())
            yield self.env.timeout(t_start)
            self.vcd.dump_on(self.vcd_now())
            yield self.env.timeout(t_stop - t_start)
            self.vcd.dump_off(self.vcd_now())
        else:
            # |vvv-------vvvv|
            yield self.env.timeout(t_stop)
            self.vcd.dump_off(self.vcd_now())
            yield self.env.timeout(t_start - t_stop)
            self.vcd.dump_on(self.vcd_now())
Ejemplo n.º 10
0
def log_to_vcd(cfg):
    """
    Cyclically poll heating and log results to VCD file.
    """
    global SIGINT

    # set up VCD logging
    now_utc = datetime.datetime.now(datetime.timezone.utc)
    vcdwriter = VCDWriter(open('vitopy1.vcd', 'w'),
                          timescale='1s',
                          init_timestamp=now_utc.timestamp())
    vcdvars = {}
    for v in config['datapoints']:
        if not 'addr' in v:
            continue
        if not 'id' in v:
            v['id'] = 'NOID'
        if type(v['addr']) is str:
            v['addr'] = int(v['addr'], 0)  # parse non-decimal numbers
        name = '%s_%s' % (v['id'], v['addr'])
        if 'uint8' == v['type']:
            logging.info('Adding variable %s' % v['addr'])
            vcdvars.update({
                v['addr']:
                vcdwriter.register_var('vitopy', name, 'integer', size=8)
            })
        if 'int8' == v['type']:
            logging.info('Adding variable %s' % v['addr'])
            vcdvars.update({
                v['addr']:
                vcdwriter.register_var('vitopy', name, 'integer', size=8)
            })
        if 'uint16' == v['type']:
            logging.info('Adding variable %s' % v['addr'])
            vcdvars.update({
                v['addr']:
                vcdwriter.register_var('vitopy', name, 'integer', size=16)
            })
        if 'uint32' == v['type']:
            logging.info('Adding variable %s' % v['addr'])
            vcdvars.update({
                v['addr']:
                vcdwriter.register_var('vitopy', name, 'integer', size=32)
            })

    # poll heating
    o = optolink.OptoLink()
    o.open_port(args.port)
    o.init()
    while True:
        logging.info('-- start of query --')
        now_utc = datetime.datetime.now(datetime.timezone.utc)
        allreadings = retrieve_all_readings_as_per_configuration(o, config)
        print(allreadings)

        for k in allreadings.keys():
            r = allreadings[k]
            if k in vcdvars:
                vcdwriter.change(vcdvars[k], now_utc.timestamp(), r)
                if args.publishmqtt:
                    mqttc.publish(str(k), r)

        vcdwriter.flush()

        for i in range(10):
            time.sleep(1)
            if SIGINT:
                break
        if SIGINT:
            break
        logging.info('-- end of query --')

    o.deinit()
    o.close_port()
    vcdwriter.close()

    return
Ejemplo n.º 11
0
class Simulator:
    def __init__(self, fragment, vcd_file=None, gtkw_file=None, traces=()):
        self._fragment        = Fragment.get(fragment, platform=None)

        self._signal_slots    = SignalDict()  # Signal -> int/slot
        self._slot_signals    = list()        # int/slot -> Signal

        self._domains         = dict()        # str/domain -> ClockDomain
        self._domain_triggers = list()        # int/slot -> str/domain

        self._signals         = SignalSet()   # {Signal}
        self._comb_signals    = bitarray()    # {Signal}
        self._sync_signals    = bitarray()    # {Signal}
        self._user_signals    = bitarray()    # {Signal}
        self._domain_signals  = dict()        # str/domain -> {Signal}

        self._started         = False
        self._timestamp       = 0.
        self._delta           = 0.
        self._epsilon         = 1e-10
        self._fastest_clock   = self._epsilon
        self._state           = _State()

        self._processes       = set()         # {process}
        self._process_loc     = dict()        # process -> str/loc
        self._passive         = set()         # {process}
        self._suspended       = set()         # {process}
        self._wait_deadline   = dict()        # process -> float/timestamp
        self._wait_tick       = dict()        # process -> str/domain

        self._funclets        = list()        # int/slot -> set(lambda)

        self._vcd_file        = vcd_file
        self._vcd_writer      = None
        self._vcd_signals     = list()        # int/slot -> set(vcd_signal)
        self._vcd_names       = list()        # int/slot -> str/name
        self._gtkw_file       = gtkw_file
        self._traces          = traces

        self._run_called      = False

    @staticmethod
    def _check_process(process):
        if inspect.isgeneratorfunction(process):
            process = process()
        if not inspect.isgenerator(process):
            raise TypeError("Cannot add a process '{!r}' because it is not a generator or "
                            "a generator function"
                            .format(process))
        return process

    def _name_process(self, process):
        if process in self._process_loc:
            return self._process_loc[process]
        else:
            frame = process.gi_frame
            return "{}:{}".format(inspect.getfile(frame), inspect.getlineno(frame))

    def add_process(self, process):
        process = self._check_process(process)
        self._processes.add(process)

    def add_sync_process(self, process, domain="sync"):
        process = self._check_process(process)
        def sync_process():
            try:
                cmd = None
                while True:
                    if cmd is None:
                        cmd = Tick(domain)
                    result = yield cmd
                    self._process_loc[sync_process] = self._name_process(process)
                    cmd = process.send(result)
            except StopIteration:
                pass
        sync_process = sync_process()
        self.add_process(sync_process)

    def add_clock(self, period, phase=None, domain="sync"):
        if self._fastest_clock == self._epsilon or period < self._fastest_clock:
            self._fastest_clock = period

        half_period = period / 2
        if phase is None:
            phase = half_period
        clk = self._domains[domain].clk
        def clk_process():
            yield Passive()
            yield Delay(phase)
            while True:
                yield clk.eq(1)
                yield Delay(half_period)
                yield clk.eq(0)
                yield Delay(half_period)
        self.add_process(clk_process)

    def __enter__(self):
        if self._vcd_file:
            self._vcd_writer = VCDWriter(self._vcd_file, timescale="100 ps",
                                         comment="Generated by nMigen")

        root_fragment = self._fragment.prepare()
        self._domains = root_fragment.domains

        hierarchy = {}
        def add_fragment(fragment, scope=()):
            hierarchy[fragment] = scope
            for index, (subfragment, name) in enumerate(fragment.subfragments):
                if name is None:
                    add_fragment(subfragment, (*scope, "U{}".format(index)))
                else:
                    add_fragment(subfragment, (*scope, name))
        add_fragment(root_fragment, scope=("top",))

        def add_signal(signal):
            if signal not in self._signals:
                self._signals.add(signal)

                signal_slot = self._state.add(normalize(signal.reset, signal.shape()))
                self._signal_slots[signal] = signal_slot
                self._slot_signals.append(signal)

                self._comb_signals.append(False)
                self._sync_signals.append(False)
                self._user_signals.append(False)
                for domain in self._domains:
                    if domain not in self._domain_signals:
                        self._domain_signals[domain] = bitarray()
                    self._domain_signals[domain].append(False)

                self._funclets.append(set())

                self._domain_triggers.append(None)
                if self._vcd_writer:
                    self._vcd_signals.append(set())
                    self._vcd_names.append(None)

            return self._signal_slots[signal]

        def add_domain_signal(signal, domain):
            signal_slot = add_signal(signal)
            self._domain_triggers[signal_slot] = domain

        for fragment, fragment_scope in hierarchy.items():
            for signal in fragment.iter_signals():
                add_signal(signal)

            for domain, cd in fragment.domains.items():
                add_domain_signal(cd.clk, domain)
                if cd.rst is not None:
                    add_domain_signal(cd.rst, domain)

        for fragment, fragment_scope in hierarchy.items():
            for signal in fragment.iter_signals():
                if not self._vcd_writer:
                    continue

                signal_slot = self._signal_slots[signal]

                for i, (subfragment, name) in enumerate(fragment.subfragments):
                    if signal in subfragment.ports:
                        var_name = "{}_{}".format(name or "U{}".format(i), signal.name)
                        break
                else:
                    var_name = signal.name

                if signal.decoder:
                    var_type = "string"
                    var_size = 1
                    var_init = signal.decoder(signal.reset).replace(" ", "_")
                else:
                    var_type = "wire"
                    var_size = signal.nbits
                    var_init = signal.reset

                suffix = None
                while True:
                    try:
                        if suffix is None:
                            var_name_suffix = var_name
                        else:
                            var_name_suffix = "{}${}".format(var_name, suffix)
                        self._vcd_signals[signal_slot].add(self._vcd_writer.register_var(
                            scope=".".join(fragment_scope), name=var_name_suffix,
                            var_type=var_type, size=var_size, init=var_init))
                        if self._vcd_names[signal_slot] is None:
                            self._vcd_names[signal_slot] = \
                                ".".join(fragment_scope + (var_name_suffix,))
                        break
                    except KeyError:
                        suffix = (suffix or 0) + 1

            for domain, signals in fragment.drivers.items():
                signals_bits = bitarray(len(self._signals))
                signals_bits.setall(False)
                for signal in signals:
                    signals_bits[self._signal_slots[signal]] = True

                if domain is None:
                    self._comb_signals |= signals_bits
                else:
                    self._sync_signals |= signals_bits
                    self._domain_signals[domain] |= signals_bits

            statements = []
            for domain, signals in fragment.drivers.items():
                reset_stmts = []
                hold_stmts  = []
                for signal in signals:
                    reset_stmts.append(signal.eq(signal.reset))
                    hold_stmts .append(signal.eq(signal))

                if domain is None:
                    statements += reset_stmts
                else:
                    if self._domains[domain].async_reset:
                        statements.append(Switch(self._domains[domain].rst,
                            {0: hold_stmts, 1: reset_stmts}))
                    else:
                        statements += hold_stmts
            statements += fragment.statements

            compiler = _StatementCompiler(self._signal_slots)
            funclet = compiler(statements)

            def add_funclet(signal, funclet):
                if signal in self._signal_slots:
                    self._funclets[self._signal_slots[signal]].add(funclet)

            for signal in compiler.sensitivity:
                add_funclet(signal, funclet)
            for domain, cd in fragment.domains.items():
                add_funclet(cd.clk, funclet)
                if cd.rst is not None:
                    add_funclet(cd.rst, funclet)

        self._user_signals = bitarray(len(self._signals))
        self._user_signals.setall(True)
        self._user_signals &= ~self._comb_signals
        self._user_signals &= ~self._sync_signals

        return self

    def _update_dirty_signals(self):
        """Perform the statement part of IR processes (aka RTLIL case)."""
        # First, for all dirty signals, use sensitivity lists to determine the set of fragments
        # that need their statements to be reevaluated because the signals changed at the previous
        # delta cycle.
        funclets = set()
        for signal_slot in self._state.flush_curr_dirty():
            funclets.update(self._funclets[signal_slot])

        # Second, compute the values of all signals at the start of the next delta cycle, by
        # running precompiled statements.
        for funclet in funclets:
            funclet(self._state)

    def _commit_signal(self, signal_slot, domains):
        """Perform the driver part of IR processes (aka RTLIL sync), for individual signals."""
        # Take the computed value (at the start of this delta cycle) of a signal (that could have
        # come from an IR process that ran earlier, or modified by a simulator process) and update
        # the value for this delta cycle.
        old, new = self._state.commit(signal_slot)
        if old == new:
            return

        # If the signal is a clock that triggers synchronous logic, record that fact.
        if new == 1 and self._domain_triggers[signal_slot] is not None:
            domains.add(self._domain_triggers[signal_slot])

        if self._vcd_writer:
            # Finally, dump the new value to the VCD file.
            for vcd_signal in self._vcd_signals[signal_slot]:
                signal = self._slot_signals[signal_slot]
                if signal.decoder:
                    var_value = signal.decoder(new).replace(" ", "_")
                else:
                    var_value = new
                vcd_timestamp = (self._timestamp + self._delta) / self._epsilon
                self._vcd_writer.change(vcd_signal, vcd_timestamp, var_value)

    def _commit_comb_signals(self, domains):
        """Perform the comb part of IR processes (aka RTLIL always)."""
        # Take the computed value (at the start of this delta cycle) of every comb signal and
        # update the value for this delta cycle.
        for signal_slot in self._state.iter_next_dirty():
            if self._comb_signals[signal_slot]:
                self._commit_signal(signal_slot, domains)

    def _commit_sync_signals(self, domains):
        """Perform the sync part of IR processes (aka RTLIL posedge)."""
        # At entry, `domains` contains a list of every simultaneously triggered sync update.
        while domains:
            # Advance the timeline a bit (purely for observational purposes) and commit all of them
            # at the same timestamp.
            self._delta += self._epsilon
            curr_domains, domains = domains, set()

            while curr_domains:
                domain = curr_domains.pop()

                # Wake up any simulator processes that wait for a domain tick.
                for process, wait_domain in list(self._wait_tick.items()):
                    if domain == wait_domain:
                        del self._wait_tick[process]
                        self._suspended.remove(process)

                        # Immediately run the process. It is important that this happens here,
                        # and not on the next step, when all the processes will run anyway,
                        # because Tick() simulates an edge triggered process. Like DFFs that latch
                        # a value from the previous clock cycle, simulator processes observe signal
                        # values from the previous clock cycle on a tick, too.
                        self._run_process(process)

                # Take the computed value (at the start of this delta cycle) of every sync signal
                # in this domain and update the value for this delta cycle. This can trigger more
                # synchronous logic, so record that.
                for signal_slot in self._state.iter_next_dirty():
                    if self._domain_signals[domain][signal_slot]:
                        self._commit_signal(signal_slot, domains)

            # Unless handling synchronous logic above has triggered more synchronous logic (which
            # can happen e.g. if a domain is clocked off a clock divisor in fabric), we're done.
            # Otherwise, do one more round of updates.

    def _run_process(self, process):
        try:
            cmd = process.send(None)
            while True:
                if type(cmd) is Delay:
                    if cmd.interval is None:
                        interval = self._epsilon
                    else:
                        interval = cmd.interval
                    self._wait_deadline[process] = self._timestamp + interval
                    self._suspended.add(process)
                    break

                elif type(cmd) is Tick:
                    self._wait_tick[process] = cmd.domain
                    self._suspended.add(process)
                    break

                elif type(cmd) is Passive:
                    self._passive.add(process)

                elif type(cmd) is Assign:
                    lhs_signals = cmd.lhs._lhs_signals()
                    for signal in lhs_signals:
                        if not signal in self._signals:
                            raise ValueError("Process '{}' sent a request to set signal '{!r}', "
                                             "which is not a part of simulation"
                                             .format(self._name_process(process), signal))
                        signal_slot = self._signal_slots[signal]
                        if self._comb_signals[signal_slot]:
                            raise ValueError("Process '{}' sent a request to set signal '{!r}', "
                                             "which is a part of combinatorial assignment in "
                                             "simulation"
                                             .format(self._name_process(process), signal))

                    if type(cmd.lhs) is Signal and type(cmd.rhs) is Const:
                        # Fast path.
                        self._state.set(self._signal_slots[cmd.lhs],
                                        normalize(cmd.rhs.value, cmd.lhs.shape()))
                    else:
                        compiler = _StatementCompiler(self._signal_slots)
                        funclet = compiler(cmd)
                        funclet(self._state)

                    domains = set()
                    for signal in lhs_signals:
                        self._commit_signal(self._signal_slots[signal], domains)
                    self._commit_sync_signals(domains)

                elif type(cmd) is Signal:
                    # Fast path.
                    cmd = process.send(self._state.curr[self._signal_slots[cmd]])
                    continue

                elif isinstance(cmd, Value):
                    compiler = _RHSValueCompiler(self._signal_slots)
                    funclet = compiler(cmd)
                    cmd = process.send(funclet(self._state))
                    continue

                else:
                    raise TypeError("Received unsupported command '{!r}' from process '{}'"
                                    .format(cmd, self._name_process(process)))

                cmd = process.send(None)

        except StopIteration:
            self._processes.remove(process)
            self._passive.discard(process)

        except Exception as e:
            process.throw(e)

    def step(self, run_passive=False):
        # Are there any delta cycles we should run?
        if self._state.curr_dirty.any():
            # We might run some delta cycles, and we have simulator processes waiting on
            # a deadline. Take care to not exceed the closest deadline.
            if self._wait_deadline and \
                    (self._timestamp + self._delta) >= min(self._wait_deadline.values()):
                # Oops, we blew the deadline. We *could* run the processes now, but this is
                # virtually certainly a logic loop and a design bug, so bail out instead.d
                raise DeadlineError("Delta cycles exceeded process deadline; combinatorial loop?")

            domains = set()
            while self._state.curr_dirty.any():
                self._update_dirty_signals()
                self._commit_comb_signals(domains)
            self._commit_sync_signals(domains)
            return True

        # Are there any processes that haven't had a chance to run yet?
        if len(self._processes) > len(self._suspended):
            # Schedule an arbitrary one.
            process = (self._processes - set(self._suspended)).pop()
            self._run_process(process)
            return True

        # All processes are suspended. Are any of them active?
        if len(self._processes) > len(self._passive) or run_passive:
            # Are any of them suspended before a deadline?
            if self._wait_deadline:
                # Schedule the one with the lowest deadline.
                process, deadline = min(self._wait_deadline.items(), key=lambda x: x[1])
                del self._wait_deadline[process]
                self._suspended.remove(process)
                self._timestamp = deadline
                self._delta = 0.
                self._run_process(process)
                return True

        # No processes, or all processes are passive. Nothing to do!
        return False

    def run(self):
        self._run_called = True

        while self.step():
            pass

    def run_until(self, deadline, run_passive=False):
        self._run_called = True

        while self._timestamp < deadline:
            if not self.step(run_passive):
                return False

        return True

    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()
Ejemplo n.º 12
0
class VCD(SimExtend):
    @inject
    def __init__(self,
                 trace_fn='pygears.vcd',
                 include=Inject('debug/trace'),
                 tlm=False,
                 shmidcat=Inject('sim_extens/vcd/shmidcat'),
                 vcd_fifo=Inject('sim_extens/vcd/vcd_fifo'),
                 sim=Inject('sim/simulator'),
                 outdir=Inject('results-dir')):

        super().__init__()
        self.sim = sim
        self.finished = False
        self.vcd_fifo = vcd_fifo
        self.shmidcat = shmidcat
        self.outdir = outdir
        self.trace_fn = None
        self.shmid_proc = None

        vcd_visitor = VCDHierVisitor(include, tlm)
        vcd_visitor.visit(find('/'))

        if not vcd_visitor.vcd_vars:
            self.deactivate()
            return

        self.trace_fn = os.path.abspath(os.path.join(self.outdir, trace_fn))
        atexit.register(self.finish)

        try:
            subprocess.call(f"rm -f {self.trace_fn}", shell=True)
        except OSError:
            pass

        if self.vcd_fifo:
            subprocess.call(f"mkfifo {self.trace_fn}", shell=True)
        else:
            log.info(f'Main VCD dump to "{self.trace_fn}"')

        if self.shmidcat:
            self.shmid_proc = subprocess.Popen(f'shmidcat {self.trace_fn}',
                                               shell=True,
                                               stdout=subprocess.PIPE)

            # Wait for shmidcat to actually open the pipe, which is necessary
            # to happen prior to init of the verilator. If shmidcat does not
            # open the pipe, verilator will get stuck
            import time
            time.sleep(0.1)

        self.vcd_file = open(self.trace_fn, 'w')

        if self.shmidcat:
            self.shmid = self.shmid_proc.stdout.readline().decode().strip()
            log.info(f'Main VCD dump to shared memory at 0x{self.shmid}')

        self.writer = VCDWriter(self.vcd_file, timescale='1 ns', date='today')

        reg['VCDWriter'] = self.writer
        reg['VCD'] = self

        self.clk_var = self.writer.register_var('', 'clk', 'wire', size=1, init=1)

        self.timestep_var = self.writer.register_var('', 'timestep', 'integer', init=0)

        self.handhake = set()

        self.vcd_vars = {
            p: register_traces_for_intf(p.dtype, scope, self.writer)
            for p, scope in vcd_visitor.vcd_vars.items()
        }

        self.writer.flush()

    def before_run(self, sim):
        vcd_intf_vars = {}
        for p, v in self.vcd_vars.items():
            intf = p.consumer
            intf.events['put'].append(self.intf_put)
            intf.events['ack'].append(self.intf_ack)
            vcd_intf_vars[intf] = v

        self.vcd_vars = vcd_intf_vars

    def intf_put(self, intf, val):
        if intf not in self.vcd_vars:
            return True

        v = self.vcd_vars[intf]

        if typeof(intf.dtype, TLM):
            self.writer.change(v['data'], timestep() * 10, str(val))
        else:
            visitor = VCDValVisitor(v, self.writer, timestep() * 10)
            visitor.visit(intf.dtype, 'data', val=val)

        self.writer.change(v['valid'], timestep() * 10, 1)
        return True

    def intf_ack(self, intf):
        if intf not in self.vcd_vars:
            return True

        v = self.vcd_vars[intf]
        self.writer.change(v['ready'], timestep() * 10, 1)

        self.handhake.add(intf)
        return True

    def before_timestep(self, sim, timestep):
        self.writer.change(self.clk_var, timestep * 10 + 5, 0)
        return True

    def after_timestep(self, sim, timestep):
        timestep += 1
        self.writer.change(self.timestep_var, timestep * 10, timestep)
        self.writer.change(self.clk_var, timestep * 10, 1)
        for intf, v in self.vcd_vars.items():
            if intf in self.handhake:
                self.writer.change(v['ready'], timestep * 10, 0)
                self.writer.change(v['valid'], timestep * 10, 0)
                self.handhake.remove(intf)

        self.writer.flush()

        return True

    def finish(self):
        if not self.finished:
            self.writer.close()
            self.vcd_file.close()
            self.finished = True

            if self.shmid_proc:
                self.shmid_proc.terminate()

    def after_cleanup(self, sim):
        self.finish()
Ejemplo n.º 13
0
	async def _write_pdi_vcd(self, file, iface : JTAGPDIInterface):
		vcd_writer = VCDWriter(file, timescale = "1 ns", check_values = False)
		pdiClk = vcd_writer.register_var(scope = "", name = "pdiClk", var_type = "wire", size = 1, init = 1)
		pdiData = vcd_writer.register_var(scope = "", name = "pdiData", var_type = "wire", size = 1, init = 1)

		cycle = 1
		operation = 0
		count = 4
		pdiInsn = PDIOpcodes.IDLE
		readCount = 0
		writeCount = 0
		repCount = 0
		repBytes = 0

		try:
			while True:
				for byte in await iface.read():
					if operation == 0:
						operation = byte
						continue
					elif operation == Header.IDCode:
						count -= 1
						if count == 0:
							operation = 0
							count = 4
						continue
					elif operation == Header.PDI and (readCount == 0 and writeCount == 0):
						pdiInsn = (byte & 0xE0) >> 5
						readCount, writeCount = self._decode_counts(pdiInsn, byte & 0x0F, repCount)
						if pdiInsn == PDIOpcodes.LD or pdiInsn == PDIOpcodes.ST:
							repCount = 0
					else:
						if writeCount != 0:
							writeCount -= 1
						else:
							readCount -= 1

						if pdiInsn == PDIOpcodes.REPEAT:
							repCount <<= 8
							repCount |= byte
							repBytes += 1
							if writeCount == 0:
								repCount = self._reverse_count(repCount, repBytes)
								repBytes = 0

						if readCount == 0 and writeCount == 0:
							operation = 0

					vcd_writer.change(pdiClk, cycle, 0)
					vcd_writer.change(pdiData, cycle, 0)
					cycle += 1
					vcd_writer.change(pdiClk, cycle, 1)
					cycle += 1
					parity = 0
					for i in range(8):
						bit = (byte >> i) & 1
						parity ^= bit
						vcd_writer.change(pdiClk, cycle, 0)
						vcd_writer.change(pdiData, cycle, bit)
						cycle += 1
						vcd_writer.change(pdiClk, cycle, 1)
						cycle += 1
					vcd_writer.change(pdiClk, cycle, 0)
					vcd_writer.change(pdiData, cycle, parity)
					cycle += 1
					vcd_writer.change(pdiClk, cycle, 1)
					cycle += 1
					vcd_writer.change(pdiClk, cycle, 0)
					vcd_writer.change(pdiData, cycle, 1)
					cycle += 1
					vcd_writer.change(pdiClk, cycle, 1)
					cycle += 1
					vcd_writer.change(pdiClk, cycle, 0)
					cycle += 1
					vcd_writer.change(pdiClk, cycle, 1)
					cycle += 1
		finally:
			vcd_writer.close(cycle)
Ejemplo n.º 14
0
                _name = _bName + "." + _bID

                if _I == 0:
                    # _c = COLS.setdefault(_bName, {})
                    # _v = _c.setdefault(_bID, [0])
                    # _v[0] +=1
                    # dump.writerow(["TS", "BUS", "BUS_DESC", "ID", "ID_DESC", "VAL"])
                    dump.writerow({_k:_v for _k,_v in r.iteritems() if _k in dump.fieldnames})
                    _fo.flush()

                    _l = BUSES.setdefault(_name, [])
                    if not _l:
                        trace.set_scope_type(r['busID'], 'module')
                        _l.append(trace.register_var(r['busID'], _name,'reg', (8, ) * r['dlc'], (0, ) * r['dlc']))

                elif _I == 1:
                    _val = r['data']

                    _dd = [_l[0], _ts, _val[:]]

                    try:
                        trace.change(*_dd)
                    except:
                        print(_dd)
                    trace.flush(_dd[1])
                else:
                    assert False

if trace:
    trace.close()
Ejemplo n.º 15
0
class VCDTracer(Tracer):

    name = 'vcd'

    def open(self):
        dump_filename = self.env.config['sim.vcd.dump_file']
        if 'sim.vcd.timescale' in self.env.config:
            vcd_timescale = parse_time(self.env.config['sim.vcd.timescale'])
        else:
            vcd_timescale = self.env.timescale
        self.scale_factor = scale_time(self.env.timescale, vcd_timescale)
        check_values = self.env.config.get('sim.vcd.check_values', True)
        self.dump_file = open(dump_filename, 'w')
        self.vcd = VCDWriter(self.dump_file,
                             timescale=vcd_timescale,
                             check_values=check_values)
        if self.env.config.get('sim.gtkw.live'):
            from vcd.gtkw import spawn_gtkwave_interactive
            save_filename = self.env.config['sim.gtkw.file']
            quiet = self.env.config.get('sim.gtkw.quiet', True)
            spawn_gtkwave_interactive(dump_filename, save_filename,
                                      quiet=quiet)

    def vcd_now(self):
        return self.env.now * self.scale_factor

    def _close(self):
        self.vcd.close(self.vcd_now())
        self.dump_file.close()

    def activate_probe(self, scope, target, **hints):
        assert self.enabled
        var_type = hints.get('var_type')
        if var_type is None:
            if isinstance(target, simpy.Container):
                if isinstance(target.level, float):
                    var_type = 'real'
                else:
                    var_type = 'integer'
            elif isinstance(target, (simpy.Resource, simpy.Store, Queue)):
                var_type = 'integer'
            else:
                raise ValueError(
                    'Could not infer VCD var_type for {}'.format(scope))

        kwargs = {k: hints[k]
                  for k in ['size', 'init', 'ident']
                  if k in hints}

        if 'init' not in kwargs:
            if isinstance(target, simpy.Container):
                kwargs['init'] = target.level
            elif isinstance(target, simpy.Resource):
                kwargs['init'] = len(target.users) if target.users else 'z'
            elif isinstance(target, (simpy.Store, Queue)):
                kwargs['init'] = len(target.items)

        parent_scope, name = scope.rsplit('.', 1)
        var = self.vcd.register_var(parent_scope, name, var_type, **kwargs)

        def probe_callback(value):
            self.vcd.change(var, self.vcd_now(), value)

        return probe_callback

    def activate_trace(self, scope, **hints):
        assert self.enabled
        var_type = hints['var_type']
        kwargs = {k: hints[k]
                  for k in ['size', 'init', 'ident']
                  if k in hints}

        parent_scope, name = scope.rsplit('.', 1)
        var = self.vcd.register_var(parent_scope, name, var_type, **kwargs)

        if isinstance(var.size, tuple):
            def trace_callback(*value):
                self.vcd.change(var, self.vcd_now(), value)
        else:
            def trace_callback(value):
                self.vcd.change(var, self.vcd_now(), value)

        return trace_callback
Ejemplo n.º 16
0
class VCD(SimExtend):
    @inject
    def __init__(
            self,
            trace_fn='pygears.vcd',
            include=Inject('debug/trace'),
            tlm=False,
            shmidcat=Inject('sim_extens/vcd/shmidcat'),
            vcd_fifo=Inject('sim_extens/vcd/vcd_fifo'),
            sim=Inject('sim/simulator'),
            outdir=Inject('results-dir'),
            expand_data=Inject('debug/expand_trace_data'),
    ):
        super().__init__()
        self.sim = sim
        self.expand_data = expand_data
        self.finished = False
        self.vcd_fifo = vcd_fifo
        self.shmidcat = shmidcat
        self.outdir = outdir
        self.trace_fn = None
        self.shmid_proc = None
        self.include = include

        self.trace_fn = os.path.abspath(os.path.join(self.outdir, trace_fn))
        atexit.register(self.finish)

        try:
            subprocess.call(f"rm -f {self.trace_fn}", shell=True)
        except OSError:
            pass

        if self.vcd_fifo:
            subprocess.call(f"mkfifo {self.trace_fn}", shell=True)
        else:
            log.info(f'Main VCD dump to "{self.trace_fn}"')

        if self.shmidcat:
            self.shmid_proc = subprocess.Popen(f'shmidcat {self.trace_fn}',
                                               shell=True,
                                               stdout=subprocess.PIPE)

            # Wait for shmidcat to actually open the pipe, which is necessary
            # to happen prior to init of the verilator. If shmidcat does not
            # open the pipe, verilator will get stuck
            import time
            time.sleep(0.1)

        self.vcd_file = open(self.trace_fn, 'w')

        if self.shmidcat:
            self.shmid = self.shmid_proc.stdout.readline().decode().strip()
            log.info(f'Main VCD dump to shared memory at 0x{self.shmid}')

        self.writer = VCDWriter(self.vcd_file, timescale='1 ns', date='today')

        reg['VCDWriter'] = self.writer
        reg['VCD'] = self

        self.clk_var = self.writer.register_var('', 'clk', 'wire', size=1, init=1)

        self.timestep_var = self.writer.register_var('', 'timestep', 'integer', init=0)

        self.handhake = set()

    def before_run(self, sim):
        vcd_visitor = VCDHierVisitor(self.include, False)
        vcd_visitor.visit(find('/'))

        if not vcd_visitor.vcd_vars:
            self.deactivate('before_run')
            return True

        self.vcd_vars = {
            p: register_traces_for_intf(p.dtype, scope, self.writer, self.expand_data)
            for p, scope in vcd_visitor.vcd_vars.items()
        }

        self.end_consumers = vcd_visitor.end_consumers

        self.writer.flush()

        for intf in self.end_consumers:
            intf.events['put'].append(self.intf_put)
            intf.events['ack'].append(self.intf_ack)

        vcd_intf_vars = {}
        for p, v in self.vcd_vars.items():
            intf.events['put'].append(self.intf_put)
            intf.events['ack'].append(self.intf_ack)
            vcd_intf_vars[p] = v

        self.vcd_vars = vcd_intf_vars
        self.extend_intfs()

    def extend_intfs(self):
        for p, v in self.vcd_vars.items():
            v['srcs'] = [self.end_consumers[pp.consumer] for pp in get_consumer_tree(p.consumer)]
            v['srcs_active'] = [False] * len(v['srcs'])
            v['p'] = p
            for vs in v['srcs']:
                vs['prods'].append(v)

        reg['graph/consumer_tree'] = {}
        reg['graph/end_producer'] = {}

    def var_put(self, v, val):
        cur_timestep = timestep() * 10
        if typeof(v['dtype'], (Any, TLM)):
            self.writer.change(v['data'], cur_timestep, str(val))
        else:
            try:
                if self.expand_data:
                    visitor = VCDValVisitor(v, self.writer, cur_timestep, max_level=10)
                    visitor.visit(v['dtype'], 'data', val=val)
                else:
                    self.writer.change(v['data'], cur_timestep, val.code())
            except AttributeError:
                pass

        self.writer.change(v['valid'], cur_timestep, 1)

    def intf_put(self, intf, val):
        p = intf.producer
        if p in self.vcd_vars:
            v = self.vcd_vars[p]
            self.var_put(v, val)

        if intf in self.end_consumers:
            v = self.end_consumers[intf]
            for vp in v['prods']:
                if not any(vp['srcs_active']):
                    # TODO: Optimization possibility, don't write the data, only ready/valid signals
                    self.var_put(vp, val)

                for i, vv in enumerate(vp['srcs']):
                    if vv is v:
                        vp['srcs_active'][i] = True
                        break

        return True

    def intf_ack(self, intf):
        p = intf.producer
        if p in self.vcd_vars:
            v = self.vcd_vars[p]
            self.writer.change(v['ready'], timestep() * 10, 1)
            self.handhake.add(p)

        if intf in self.end_consumers:
            v = self.end_consumers[intf]
            for vp in v['prods']:

                for i, vv in enumerate(vp['srcs']):
                    if vv is v:
                        vp['srcs_active'][i] = False
                        break

                if not any(vp['srcs_active']):
                    self.writer.change(vp['ready'], timestep() * 10, 1)
                    self.handhake.add(vp['p'])

        return True

    def before_timestep(self, sim, timestep):
        self.writer.change(self.clk_var, timestep * 10 + 5, 0)
        return True

    def after_timestep(self, sim, timestep):
        timestep += 1
        self.writer.change(self.timestep_var, timestep * 10, timestep)
        self.writer.change(self.clk_var, timestep * 10, 1)
        for p, v in self.vcd_vars.items():
            if p in self.handhake:
                self.writer.change(v['ready'], timestep * 10, 0)
                if not any(v['srcs_active']):
                    self.writer.change(v['valid'], timestep * 10, 0)

                self.handhake.remove(p)

        self.writer.flush()

        return True

    def finish(self):
        if not self.finished:
            self.writer.close()
            self.vcd_file.close()
            self.finished = True

            if self.shmid_proc:
                self.shmid_proc.terminate()

    def after_cleanup(self, sim):
        self.finish()