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)
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)
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()
class Tracer(object): """Write trace to a VCD file""" def __init__(self, f): from vcd import VCDWriter self.writer = VCDWriter(f, timescale = '1 us', date = 'today') self.t0 = None def add(self, dut, name, *args, **kwargs): return TracerVar(self, dut, name, *args, **kwargs) def register_var(self, dut, name, *args, **kwargs): return self.writer.register_var(dut, name, *args, **kwargs) def change(self, var, timestamp, value): if not timestamp: return timestamp = int(timestamp * 1E6) if self.t0 is None: self.t0 = timestamp self.writer.change(var, timestamp - self.t0, value)
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()
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)
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())
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()
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())
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
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()
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()
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)
_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()
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
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()