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 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()
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)
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
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()
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 emit_vcd(self, filename, *, gtkw_filename=None, add_clock=True): """ Emits a VCD file containing the ILA samples. Parameters: filename -- The filename to write to, or '-' to write to stdout. gtkw_filename -- If provided, a gtkwave save file will be generated that automatically displays all of the relevant signals in the order provided to the ILA. add_clock -- If true or not provided, adds a replica of the ILA's sample clock to make change points easier to see. """ # Select the file-like object we're working with. if filename == "-": stream = sys.stdout close_after = False else: stream = open(filename, 'w') close_after = True # Create our basic VCD. with VCDWriter(stream, timescale=f"1 ns", date='today') as writer: first_timestamp = math.inf last_timestamp = 0 signals = {} # If we're adding a clock... if add_clock: clock_value = 0 clock_signal = writer.register_var('ila', 'ila_clock', 'integer', size=1, init=clock_value ^ 1) # Create named values for each of our signals. for signal in self.ila.signals: signals[signal.name] = writer.register_var('ila', signal.name, 'integer', size=len(signal)) # Dump the each of our samples into the VCD. clock_time = 0 for timestamp, sample in self.enumerate_samples(): for signal_name, signal_value in sample.items(): # If we're adding a clock signal, add any changes necessary since # the last value-change. if add_clock: while clock_time < timestamp: writer.change(clock_signal, clock_time / 1e-9, clock_value) clock_value ^= 1 clock_time += (self.ila.sample_period / 2) # Register the signal change. writer.change(signals[signal_name], timestamp / 1e-9, signal_value.to_int()) # If we're generating a GTKW, delegate that to our helper function. if gtkw_filename: assert(filename != '-') self._emit_gtkw(gtkw_filename, filename, add_clock=add_clock)
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()
def __init__(self, signal_names, *, vcd_file, gtkw_file=None, traces=()): if isinstance(vcd_file, str): vcd_file = open(vcd_file, "wt") if isinstance(gtkw_file, str): gtkw_file = open(gtkw_file, "wt") self.vcd_vars = SignalDict() self.vcd_file = vcd_file self.vcd_writer = vcd_file and VCDWriter(self.vcd_file, timescale="100 ps", comment="Generated by nMigen") self.gtkw_names = SignalDict() self.gtkw_file = gtkw_file self.gtkw_save = gtkw_file and GTKWSave(self.gtkw_file) self.traces = [] trace_names = SignalDict() for trace in traces: if trace not in signal_names: trace_names[trace] = trace.name self.traces.append(trace) if self.vcd_writer is None: return for signal, names in itertools.chain(signal_names.items(), trace_names.items()): if signal.decoder: var_type = "string" var_size = 1 var_init = self.decode_to_vcd(signal, signal.reset) else: var_type = "wire" var_size = signal.width var_init = signal.reset for (*var_scope, var_name) in names: suffix = None while True: try: if suffix is None: var_name_suffix = var_name else: var_name_suffix = "{}${}".format(var_name, suffix) vcd_var = self.vcd_writer.register_var( scope=var_scope, name=var_name_suffix, var_type=var_type, size=var_size, init=var_init) break except KeyError: suffix = (suffix or 0) + 1 if signal not in self.vcd_vars: self.vcd_vars[signal] = set() self.vcd_vars[signal].add(vcd_var) if signal not in self.gtkw_names: self.gtkw_names[signal] = (*var_scope, var_name_suffix)
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)
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)
def open(self) -> None: dump_filename: str = self.env.config.setdefault( 'sim.vcd.dump_file', 'sim.vcd') if 'sim.vcd.timescale' in self.env.config: vcd_ts_str: str = self.env.config.setdefault( 'sim.vcd.timescale', self.env.config['sim.timescale']) mag, unit = parse_time(vcd_ts_str) else: mag, unit = self.env.timescale mag_int = int(mag) if mag_int != mag: raise ValueError( f'sim.timescale magnitude must be an integer, got {mag}') vcd_timescale = mag_int, unit self.scale_factor = scale_time(self.env.timescale, vcd_timescale) check_values: bool = 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: str = 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: bool = self.env.config.setdefault('sim.gtkw.quiet', True) spawn_gtkwave_interactive(dump_filename, self.save_filename, quiet=quiet) start_time: str = self.env.config.setdefault('sim.vcd.start_time', '') stop_time: str = 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 __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 run_timed(self,testBench, MaxTime, FileName): self.CurrentTime = 0 objName = getNameOf(testBench) with open(FileName,"w") as f: self.writer = VCDWriter(f, timescale='1 ns', date='today') testBench.set_simulation_param("", objName, self) p_list = self.get_timed_process_list() while (self.CurrentTime < MaxTime): self.run_processor_timed(p_list) while (len(self.updateList)): self.run_symbol_change() self.run_processors() self.writer= None self.CurrentTime =0
def write_vcd(self, path="/tmp/ila.vcd"): from pathlib import Path path = Path(path) print(f"writing vcd to {path.absolute()}") from vcd import VCDWriter with open(path, "w") as f: with VCDWriter(f) as writer: vcd_vars = [(writer.register_var( 'ila_signals', name, 'reg' if decoder is None else 'string', size=size), decoder) for name, (size, decoder) in self.probes] clk = writer.register_var('ila_signals', 'clk', 'reg', size=1) for timestamp, values in enumerate(self.get_values()): writer.change(clk, timestamp * 2, 1) for (var, decoder), value in zip(vcd_vars, values): writer.change( var, timestamp * 2, value if decoder is None else decoder.get( value, str(value))) writer.change(clk, timestamp * 2 + 1, 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)
def __init__(self, str_cfg, result_type_raw, result_path_raw, result_path, float_type=True, emu_time_scaled=True, debug=False, dt_scale=1e-15): """ :param str_cfg: structure config object used in current project. :param result_type_raw: filetype of result file to be converted :param result_path_raw: path to raw result file :param result_path: path to converted result file :param float_type: flag to indicate if real signal's data type is fixed-point or floating point :param emu_time_scaled: flag to indicate, if signals shall be displayed over cycle count or time :param debug: if debug flag is set to true, all signals from result file will be kept, even if they are not a specified probe; keep in mind, that for those signals no fixed to float conversion can be done """ # defaults self.result_path_raw = result_path_raw scfg = str_cfg self.signal_lookup = {} # store data from FPGA in a dictionary probe_data = {} real_signals = set() reg_widths = {} if result_type_raw == ResultFileTypes.CSV: # read CSV file with open(self.result_path_raw, 'r') as f: first_line = f.readline() # split up the first line into comma-delimited names signals = first_line.split(',') # account for whitespace signals = [signal.strip() for signal in signals] # strip off the signal indices and add to a lookup table for k, signal in enumerate(signals): if '[' in signal: signal = signal[:signal.index('[')] self.signal_lookup[signal] = k # print keys print(f'Signals in result file: {[key for key in self.signal_lookup.keys()]}') for analog_signal in scfg.analog_probes: name = 'trace_port_gen_i/' + analog_signal.name if (name) in self.signal_lookup: # add to set of probes with "real" data type real_signals.add(name) # get unscaled data and apply scaling factor probe_data[name] = (2 ** int(analog_signal.exponent)) * self.get_csv_col(name) # convert data to native Python float type (rather than numpy float) # this is required for PyVCD try: probe_data[name] = [float(x) for x in probe_data[name]] except: probe_data[name] = [float(probe_data[name])] for digital_signal in scfg.digital_probes + [scfg.dec_cmp] + [scfg.time_probe]: name = 'trace_port_gen_i/' + digital_signal.name if name in self.signal_lookup: # define width for this probe reg_widths[name] = int(digital_signal.width) # get unscaled data probe_data[name] = self.get_csv_col(name) # convert data to native Python int type (rather than numpy int) # this is required for PyVCD try: probe_data[name] = [int(x) for x in probe_data[name]] except ValueError: print(f'ValueError encountered when converting probe_data[{name}].') print(f'Contents of probe_data[{name}]:') print(f'{probe_data}') print(f'Contents of CSV file {self.result_path_raw}:') print(open(self.result_path_raw, 'r').read()) raise # Write data to VCD file with open(result_path, 'w') as vcd: timescale = self.get_pyvcd_timescale(dt_scale) with VCDWriter(vcd, timescale=timescale, date=str(datetime.datetime.today())) as writer: # register all of the signals that will be written to VCD reg = {} for sig, scaled_data in probe_data.items(): # determine signal scope and name signal_split = sig.split('/') vcd_scope = '.'.join(signal_split[:-1]) vcd_name = signal_split[-1] # determine signal type and size if sig in real_signals: vcd_var_type = 'real' vcd_size = None elif sig in reg_widths: vcd_var_type = 'reg' vcd_size = reg_widths[sig] else: raise Exception('Unknown signal type.') # register the signal reg[sig] = writer.register_var(scope=vcd_scope, name=vcd_name, var_type=vcd_var_type, size=vcd_size) # iterate over all timesteps prev_timestep = float('-inf') for k, timestamp in enumerate(probe_data['trace_port_gen_i/' + scfg.time_probe.name]): # break if the current timestep is less than the previous one, since that means # wrapping has occurred. (only if emu_time_scaled is True, meaning that # we're using emu_time on the x axis, not cycle number) if emu_time_scaled and (timestamp < prev_timestep): break prev_timestep = timestamp # Set the x-axis value according to emu_time_scaled: True means use # emu_time, and False means use the cycle number if emu_time_scaled: chg_val = timestamp else: chg_val = k # iterate over all signals and log their change at this timestamp for sig, scaled_data in probe_data.items(): writer.change(reg[sig], chg_val, scaled_data[k]) elif result_type_raw == ResultFileTypes.VCD: vcd_file_name = result_path_raw vcd_handle = ParseVCD(vcd_file_name) signal_dict = vcd_handle.parse_vcd(update_data=False) # print signal names signal_names = [(signal_dict[key]["nets"][0]["hier"] + '.' + signal_dict[key]["nets"][0]["name"], key) for key in signal_dict.keys()] print(f'Signals in result file: {[sig_name[0] for sig_name in signal_names]}') for analog_signal in scfg.analog_probes: analog_signal_path = 'top.trace_port_gen_i' + '.' + analog_signal.name if analog_signal_path in [sig[0] for sig in signal_names]: # add to set of probes with "real" data type real_signals.add(analog_signal_path) probe_data[analog_signal_path] = {} probe_data[analog_signal_path]['index'] = 0 # get the signal identifier from analog_signal used in VCD file signal_identifier = signal_names[[y[0] for y in signal_names].index(analog_signal_path)][1] data = [] for c,v in signal_dict[signal_identifier]['cv']: if not isinstance(v, float): for k in range(analog_signal.width - len(v)): # extend binary to full width, some simulators don't do that by default and remove leading zeros in VCD file v = '0' + v if v[0] == '1': # check if number is negative and if so calculate the two's complement v = -1 * (int((''.join('1' if x == '0' else '0' for x in v)), 2) + 1) else: v = int(v, 2) if not float_type: v = 2 ** int(analog_signal.exponent) * v data.append((c, v)) probe_data[analog_signal_path]['data'] = np.asarray(data) # convert data to native Python float type (rather than numpy float) this is required for PyVCD probe_data[analog_signal_path]['data'] = [(int(c), float(v)) for c, v in probe_data[analog_signal_path]['data']] for digital_signal in scfg.digital_probes + [scfg.dec_cmp] + [scfg.time_probe]: digital_signal_path = 'top.trace_port_gen_i' + '.' + digital_signal.name if digital_signal_path in [sig[0] for sig in signal_names]: # define width for this probe reg_widths[digital_signal_path] = int(digital_signal.width) probe_data[digital_signal_path] = {} probe_data[digital_signal_path]['index'] = 0 # get unscaled data probe_data[digital_signal_path]['data'] = np.asarray([(c, v) for c,v in signal_dict[signal_names[[y[0] for y in signal_names].index(digital_signal_path)][1]]['cv']]) # convert data to native Python int type (rather than numpy int) this is required for PyVCD data = [] for c, v in probe_data[digital_signal_path]['data']: try: data.append((int(c), int(v, 2))) except: # In case of an x or z value, a 0 will be added; this is necessary for PYVCD data.append((int(c), int(0))) probe_data[digital_signal_path]['data'] = data # Write data to VCD file with open(result_path, 'w') as vcd: timescale = self.get_pyvcd_timescale(dt_scale) with VCDWriter(vcd, timescale=timescale, date=str(datetime.datetime.today())) as writer: # register all of the signals that will be written to VCD reg = {} for sig, scaled_data in probe_data.items(): # determine signal scope and name signal_split = sig.split('.') vcd_scope = '.'.join(signal_split[:-1]) vcd_name = signal_split[-1] # determine signal type and size if sig in real_signals: vcd_var_type = 'real' vcd_size = None elif sig in reg_widths: vcd_var_type = 'reg' vcd_size = reg_widths[sig] else: raise Exception('Unknown signal type.') # register the signal reg[sig] = writer.register_var(scope=vcd_scope, name=vcd_name, var_type=vcd_var_type, size=vcd_size) prev_timestep = float('-inf') # Add all other signals in case debug flag is set if debug: for signal in signal_names: if not signal[0] in probe_data.keys(): # signal was not listed yet var_type = signal_dict[signal[1]]['nets'][0]['type'] if var_type != 'parameter': name = signal_dict[signal[1]]['nets'][0]['name'] scope = signal_dict[signal[1]]['nets'][0]['hier'] path = scope + '.' + name size = signal_dict[signal[1]]['nets'][0]['size'] # register the signal reg[path] = writer.register_var(scope=scope, name=name, var_type=var_type, size=int(size)) # time probe path time_path = 'top.trace_port_gen_i' + '.' + scfg.time_probe.name # calculate emu_time offset offset = 0 for idx, (cycle_count, timestamp) in enumerate(probe_data[time_path]['data']): # break if the current timestep is less than the previous one, since that means # wrapping has occurred if timestamp < prev_timestep: break prev_timestep = timestamp if timestamp > 0: break offset = cycle_count ############################# # Represent signals over time ############################# if emu_time_scaled: # Add an index to all signals in signal dict, in case debug option was selected: if debug: for sig in signal_dict.keys(): signal_dict[sig]['index'] = 0 for idx, (cycle_count, timestamp) in enumerate(probe_data[time_path]['data']): # break if timestamp is less than zero since it means that wrapping has occurred if timestamp < 0: break # store all available signals in list, any signal, that is no longer in the list will be skipped # signals, where the cycle_count is nop longer between current and next cycle count of the time # signal will be removed from the list sigs_in_interval = [] for sig in probe_data.keys(): sigs_in_interval.append((sig, None)) # Add all other signals in case debug flag is set if debug: for signal in signal_names: if not signal[0] in probe_data.keys() and not signal_dict[signal[1]]['nets'][0]['type'] == 'parameter': # signal was not listed yet sigs_in_interval.append((signal[0], signal[1])) # list of all activities within time interval timestep_events = [] # As soon as all signals are no longer in the interval, timestep will advance while sigs_in_interval: for sig in sigs_in_interval: if sig[0] in probe_data.keys(): sig_tuple = probe_data[sig[0]]['data'][probe_data[sig[0]]['index']] else: # debug signals sig_tuple = signal_dict[sig[1]]['cv'][signal_dict[sig[1]]['index']] if cycle_count == sig_tuple[0]: timestep_events.append([sig, timestamp, sig_tuple[1]]) if sig[0] in probe_data.keys(): probe_data[sig[0]]['index'] += 1 else: signal_dict[sig[1]]['index'] += 1 # This signal no longer needs to be observed sigs_in_interval.remove(sig) else: try: # Check if time signal is already at the end next_timestamp = probe_data[time_path]['data'][idx + 1][0] except: # Time signal has finished the end, finish conversion sigs_in_interval = [] break if round(next_timestamp - offset) > sig_tuple[0]: # Note: There is always an offset between cycle count and timestamp -> substract offset # The cycle count from data signal does not have a match with emu_time signal's cycle count # -> we need to apply interpolation in order to assign the timestamp properly cycles_in_dt = probe_data[time_path]['data'][idx+1][0] - cycle_count dt = probe_data[time_path]['data'][idx+1][1] - timestamp interp_timestamp = int(dt/cycles_in_dt * (sig_tuple[0] - cycle_count + offset) + timestamp) timestep_events.append([sig, interp_timestamp, sig_tuple[1]]) if sig[0] in probe_data.keys(): probe_data[sig[0]]['index'] += 1 else: signal_dict[sig[1]]['index'] += 1 else: # This signal no longer needs to be observed sigs_in_interval.remove(sig) # Register events in time interval in chronological order timestep_events = sorted(timestep_events, key=self.sort_timestamp) for [name, timestamp, value] in timestep_events: writer.change(reg[name[0]], timestamp, value) #################################### # Represent signals over cycle count #################################### else: time_events = [] # store all events from result file for signal in probe_data.keys(): for sig_tuple in probe_data[signal]['data']: time_events.append([signal, sig_tuple[0], sig_tuple[1]]) # Add all other signals in case debug flag is set if debug: for signal in signal_names: if not signal[0] in probe_data.keys() and not signal_dict[signal[1]]['nets'][0]['type'] == 'parameter': # signal was not listed yet name = signal_dict[signal[1]]['nets'][0]['name'] hier = signal_dict[signal[1]]['nets'][0]['hier'] path = hier + '.' + name for sig_tuple in signal_dict[signal[1]]['cv']: time_events.append([path, sig_tuple[0], sig_tuple[1]]) # Register events in chronological order time_events = sorted(time_events, key=self.sort_timestamp) for [sig_name, timestamp, value] in time_events: writer.change(reg[sig_name], timestamp, value) else: raise Exception(f'ERROR: No supported Result file format selected:{result_type_raw}')
def __init__(self, f): from vcd import VCDWriter self.writer = VCDWriter(f, timescale = '1 us', date = 'today') self.t0 = None
t_timescale = next(list_tokens[0]) t_module = next(list_tokens[0]) max = 0 oldmax = 0 get_header = 0 prev_max = 0 index = 0 list1 = [] list2 = [] list3 = [] list4 = [] with VCDWriter(w, timescale=t_timescale.data, version=t_version.data, date=t_date.data) as writer: for lt in list_tokens: token = next(lt) print("file : " + str(num_ct_index)) while 1: if (token.kind is TokenKind.DATE or token.kind is TokenKind.VERSION or token.kind is TokenKind.TIMESCALE or token.kind is TokenKind.SCOPE): try: token = next(lt) continue except StopIteration: index = 0 list3.clear() break
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)
async def _main(): args = get_argparser().parse_args() create_logger(args) device = None try: # TODO(py3.7): use importlib.resources firmware_filename = os.path.join(os.path.dirname(__file__), "glasgow.ihex") if args.action in ("build", "test", "tool"): pass elif args.action == "factory": device = GlasgowHardwareDevice(args.serial, firmware_filename, _factory_rev=args.factory_rev) else: device = GlasgowHardwareDevice(args.serial, firmware_filename) if args.action == "voltage": if args.voltage is not None: await device.reset_alert(args.ports) await device.poll_alert() # clear any remaining alerts try: await device.set_voltage(args.ports, args.voltage) except: await device.set_voltage(args.ports, 0.0) raise if args.set_alert and args.voltage != 0.0: await asyncio.sleep( 0.050) # let the output capacitor discharge a bit await device.set_alert_tolerance(args.ports, args.voltage, args.tolerance / 100) print("Port\tVio\tVlimit\tVsense\tMonitor") alerts = await device.poll_alert() for port in args.ports: vio = await device.get_voltage(port) vlimit = await device.get_voltage_limit(port) vsense = await device.measure_voltage(port) alert = await device.get_alert(port) notice = "" if port in alerts: notice += " (ALERT)" print("{}\t{:.2}\t{:.2}\t{:.3}\t{:.2}-{:.2}\t{}".format( port, vio, vlimit, vsense, alert[0], alert[1], notice)) if args.action == "safe": await device.reset_alert("AB") await device.set_voltage("AB", 0.0) await device.poll_alert() # clear any remaining alerts logger.info("all ports safe") if args.action == "voltage-limit": if args.voltage is not None: await device.set_voltage_limit(args.ports, args.voltage) print("Port\tVio\tVlimit") for port in args.ports: vio = await device.get_voltage(port) vlimit = await device.get_voltage_limit(port) print("{}\t{:.2}\t{:.2}".format(port, vio, vlimit)) if args.action in ("run", "run-repl", "run-prebuilt"): target, applet = _applet(device.revision, args) device.demultiplexer = DirectDemultiplexer( device, target.multiplexer.pipe_count) plan = target.build_plan() if args.action in ("run", "run-repl"): await device.download_target(plan, rebuild=args.rebuild) if args.action == "run-prebuilt": bitstream_file = args.bitstream or open( "{}.bin".format(args.applet), "rb") with bitstream_file: logger.warn("downloading prebuilt bitstream from %r", bitstream_file.name) await device.download_bitstream(bitstream_file.read()) do_trace = hasattr(args, "trace") and args.trace if do_trace: logger.info("starting applet analyzer") await device.write_register(target.analyzer.addr_done, 0) analyzer_iface = await device.demultiplexer.claim_interface( target.analyzer, target.analyzer.mux_interface, args=None) trace_decoder = TraceDecoder(target.analyzer.event_sources) vcd_writer = VCDWriter( args.trace, timescale="1 ns", check_values=False, comment='Generated by Glasgow for bitstream ID %s' % plan.bitstream_id.hex()) async def run_analyzer(): signals = {} strobes = set() for field_name, field_trigger, field_width in trace_decoder.events( ): if field_trigger == "throttle": var_type = "wire" var_init = 0 elif field_trigger == "change": var_type = "wire" var_init = "x" elif field_trigger == "strobe": if field_width > 0: var_type = "tri" var_init = "z" else: var_type = "event" var_init = "" else: assert False signals[field_name] = vcd_writer.register_var( scope="", name=field_name, var_type=var_type, size=field_width, init=var_init) if field_trigger == "strobe": strobes.add(field_name) init = True while not trace_decoder.is_done(): trace_decoder.process(await analyzer_iface.read()) for cycle, events in trace_decoder.flush(): if events == "overrun": target.analyzer.logger.error( "FIFO overrun, shutting down") for name in signals: vcd_writer.change(signals[name], next_timestamp, "x") timestamp += 1e3 # 1us break event_repr = " ".join("{}={}".format(n, v) for n, v in events.items()) target.analyzer.logger.trace("cycle %d: %s", cycle, event_repr) timestamp = 1e9 * (cycle + 0) // target.sys_clk_freq next_timestamp = 1e9 * (cycle + 1) // target.sys_clk_freq if init: init = False vcd_writer._timestamp = timestamp for name, value in events.items(): vcd_writer.change(signals[name], timestamp, value) for name, _value in events.items(): if name in strobes: vcd_writer.change(signals[name], next_timestamp, "z") vcd_writer.flush() vcd_writer.close(timestamp) async def run_applet(): logger.info("running handler for applet %r", args.applet) if applet.preview: logger.warn( "applet %r is PREVIEW QUALITY and may CORRUPT DATA", args.applet) try: iface = await applet.run(device, args) if args.action in ("run", "run-prebuilt"): await applet.interact(device, args, iface) if args.action == "run-repl": if applet.has_custom_repl: logger.warn( "applet provides customized REPL(s); consider using `run " "{} ...-repl` subcommands".format(applet.name)) logger.info( "dropping to REPL; use 'help(iface)' to see available APIs" ) await AsyncInteractiveConsole(locals={ "iface": iface }).interact() except GlasgowAppletError as e: applet.logger.error(str(e)) except asyncio.CancelledError: pass # terminate gracefully finally: await device.demultiplexer.flush() async def wait_for_sigint(): await wait_for_signal(signal.SIGINT) logger.debug("Ctrl+C pressed, terminating") if do_trace: analyzer_task = asyncio.ensure_future(run_analyzer()) applet_task = asyncio.ensure_future(run_applet()) sigint_task = asyncio.ensure_future(wait_for_sigint()) tasks = [applet_task, sigint_task] done, pending = await asyncio.wait( tasks, return_when=asyncio.FIRST_COMPLETED) for task in pending: task.cancel() for task in tasks: try: await task except asyncio.CancelledError: pass if do_trace: await device.write_register(target.analyzer.addr_done, 1) await analyzer_task await device.demultiplexer.cancel() if args.action == "tool": tool = GlasgowApplet.all_applets[args.applet].tool_cls() try: await tool.run(args) except GlasgowAppletError as e: tool.logger.error(e) raise SystemExit() if args.action == "flash": logger.info("reading device configuration") header = await device.read_eeprom("fx2", 0, 8 + 4 + GlasgowConfig.size) header[0] = 0xC2 # see below fx2_config = FX2Config.decode(header, partial=True) if (len(fx2_config.firmware) != 1 or fx2_config.firmware[0][0] != 0x4000 - GlasgowConfig.size or len(fx2_config.firmware[0][1]) != GlasgowConfig.size): raise SystemExit( "Unrecognized or corrupted configuration block") glasgow_config = GlasgowConfig.decode(fx2_config.firmware[0][1]) logger.info("device has serial %s-%s", glasgow_config.revision, glasgow_config.serial) if fx2_config.disconnect: logger.info("device has flashed firmware") else: logger.info("device does not have flashed firmware") if glasgow_config.bitstream_size: logger.info("device has flashed bitstream ID %s", glasgow_config.bitstream_id.hex()) else: logger.info("device does not have flashed bitstream") new_bitstream = b"" if args.remove_bitstream: logger.info("removing bitstream") glasgow_config.bitstream_size = 0 glasgow_config.bitstream_id = b"\x00" * 16 elif args.bitstream: logger.info("using bitstream from %s", args.bitstream.name) with args.bitstream as f: new_bitstream = f.read() glasgow_config.bitstream_size = len(new_bitstream) glasgow_config.bitstream_id = b"\xff" * 16 elif args.applet: logger.info("building bitstream for applet %s", args.applet) target, applet = _applet(device.revision, args) plan = target.build_plan() new_bitstream_id = plan.bitstream_id new_bitstream = plan.execute() # We always build and reflash the bitstream in case the one currently # in EEPROM is corrupted. If we only compared the ID, there would be # no easy way to recover from that case. There's also no point in # storing the bitstream hash (as opposed to Verilog hash) in the ID, # as building the bitstream takes much longer than flashing it. logger.info("built bitstream ID %s", new_bitstream_id.hex()) glasgow_config.bitstream_size = len(new_bitstream) glasgow_config.bitstream_id = new_bitstream_id fx2_config.firmware[0] = (0x4000 - GlasgowConfig.size, glasgow_config.encode()) if args.remove_firmware: logger.info("removing firmware") fx2_config.disconnect = False new_image = fx2_config.encode() new_image[0] = 0xC0 # see below else: logger.info( "using firmware from %r", args.firmware.name if args.firmware else firmware_filename) with (args.firmware or open(firmware_filename, "rb")) as f: for (addr, chunk) in input_data(f, fmt="ihex"): fx2_config.append(addr, chunk) fx2_config.disconnect = True new_image = fx2_config.encode() if new_bitstream: logger.info("programming bitstream") old_bitstream = await device.read_eeprom( "ice", 0, len(new_bitstream)) if old_bitstream != new_bitstream: for (addr, chunk) in diff_data(old_bitstream, new_bitstream): await device.write_eeprom("ice", addr, chunk) logger.info("verifying bitstream") if await device.read_eeprom( "ice", 0, len(new_bitstream)) != new_bitstream: logger.critical("bitstream programming failed") return 1 else: logger.info("bitstream identical") logger.info("programming configuration and firmware") old_image = await device.read_eeprom("fx2", 0, len(new_image)) if old_image != new_image: for (addr, chunk) in diff_data(old_image, new_image): await device.write_eeprom("fx2", addr, chunk) logger.info("verifying configuration and firmware") if await device.read_eeprom("fx2", 0, len(new_image)) != new_image: logger.critical( "configuration/firmware programming failed") return 1 else: logger.info("configuration and firmware identical") if args.action == "build": target, applet = _applet(args.rev, args) plan = target.build_plan() if args.type in ("il", "rtlil"): logger.info("building RTLIL for applet %r", args.applet) with open(args.filename or args.applet + ".il", "wt") as f: f.write(plan.rtlil) if args.type in ("bin", "bitstream"): logger.info("building bitstream for applet %r", args.applet) with open(args.filename or args.applet + ".bin", "wb") as f: f.write(plan.execute()) if args.type in ("zip", "archive"): logger.info("building archive for applet %r", args.applet) plan.archive(args.filename or args.applet + ".zip") if args.action == "test": logger.info("testing applet %r", args.applet) applet = GlasgowApplet.all_applets[args.applet]() loader = unittest.TestLoader() stream = unittest.runner._WritelnDecorator(sys.stderr) result = unittest.TextTestResult(stream=stream, descriptions=True, verbosity=2) result.failfast = True def startTest(test): unittest.TextTestResult.startTest(result, test) result.stream.write("\n") result.startTest = startTest if args.tests == []: suite = loader.loadTestsFromTestCase(applet.test_cls) suite.run(result) else: for test in args.tests: suite = loader.loadTestsFromName(test, module=applet.test_cls) suite.run(result) if not result.wasSuccessful(): for _, traceback in result.errors + result.failures: print(traceback, end="", file=sys.stderr) return 1 if args.action == "factory": logger.info("reading device configuration") header = await device.read_eeprom("fx2", 0, 8 + 4 + GlasgowConfig.size) if not re.match(rb"^\xff+$", header): if args.force: logger.warning( "device already factory-programmed, proceeding anyway") else: logger.error("device already factory-programmed") return 1 fx2_config = FX2Config(vendor_id=VID_QIHW, product_id=PID_GLASGOW, device_id=GlasgowConfig.encode_revision( args.rev), i2c_400khz=True) glasgow_config = GlasgowConfig(args.factory_rev, args.factory_serial) fx2_config.append(0x4000 - GlasgowConfig.size, glasgow_config.encode()) image = fx2_config.encode() # Let FX2 hardware enumerate. This won't load the configuration block # into memory automatically, but the firmware has code that does that # if it detects a C0 load. image[0] = 0xC0 logger.info("programming device configuration") await device.write_eeprom("fx2", 0, image) logger.info("verifying device configuration") if await device.read_eeprom("fx2", 0, len(image)) != image: logger.critical("factory programming failed") return 1 except GlasgowDeviceError as e: logger.error(e) return 1 except GatewareBuildError as e: applet.logger.error(e) return 1 finally: if device is not None: device.close() return 0
async def _main(): args = get_argparser().parse_args() create_logger(args) try: firmware_file = os.path.join(os.path.dirname(__file__), "glasgow.ihex") if args.action in ("build", "test", "tool"): pass elif args.action == "factory": device = GlasgowHardwareDevice(firmware_file, VID_CYPRESS, PID_FX2) else: device = GlasgowHardwareDevice(firmware_file) if args.action == "voltage": if args.voltage is not None: await device.reset_alert(args.ports) await device.poll_alert() # clear any remaining alerts try: await device.set_voltage(args.ports, args.voltage) except: await device.set_voltage(args.ports, 0.0) raise if args.set_alert and args.voltage != 0.0: await asyncio.sleep( 0.050) # let the output capacitor discharge a bit await device.set_alert_tolerance(args.ports, args.voltage, args.tolerance / 100) print("Port\tVio\tVlimit\tVsense\tMonitor") alerts = await device.poll_alert() for port in args.ports: vio = await device.get_voltage(port) vlimit = await device.get_voltage_limit(port) vsense = await device.measure_voltage(port) alert = await device.get_alert(port) notice = "" if port in alerts: notice += " (ALERT)" print("{}\t{:.2}\t{:.2}\t{:.3}\t{:.2}-{:.2}\t{}".format( port, vio, vlimit, vsense, alert[0], alert[1], notice)) if args.action == "voltage-limit": if args.voltage is not None: await device.set_voltage_limit(args.ports, args.voltage) print("Port\tVio\tVlimit") for port in args.ports: vio = await device.get_voltage(port) vlimit = await device.get_voltage_limit(port) print("{}\t{:.2}\t{:.2}".format(port, vio, vlimit)) if args.action == "run": if args.applet: target, applet = _applet(device.revision, args) device.demultiplexer = DirectDemultiplexer(device) await device.download_target( target, rebuild=args.rebuild, toolchain_opts=_toolchain_opts(args)) if args.trace: logger.info("starting applet analyzer") await device.write_register(target.analyzer.addr_done, 0) analyzer_iface = await device.demultiplexer.claim_interface( target.analyzer, target.analyzer.mux_interface, args=None) trace_decoder = TraceDecoder(target.analyzer.event_sources) vcd_writer = VCDWriter( args.trace, timescale="1 ns", check_values=False, comment='Generated by Glasgow for bitstream ID %s' % target.get_bitstream_id().hex()) async def run_analyzer(): if not args.trace: return signals = {} strobes = set() for field_name, field_trigger, field_width in trace_decoder.events( ): if field_trigger == "throttle": var_type = "wire" var_init = 0 elif field_trigger == "change": var_type = "wire" var_init = "x" elif field_trigger == "strobe": if field_width > 0: var_type = "tri" var_init = "z" else: var_type = "event" var_init = "" else: assert False signals[field_name] = vcd_writer.register_var( scope="", name=field_name, var_type=var_type, size=field_width, init=var_init) if field_trigger == "strobe": strobes.add(field_name) init = True while not trace_decoder.is_done(): trace_decoder.process(await analyzer_iface.read()) for cycle, events in trace_decoder.flush(): if events == "overrun": target.analyzer.logger.error( "FIFO overrun, shutting down") for name in signals: vcd_writer.change(signals[name], next_timestamp, "x") timestamp += 1e3 # 1us break event_repr = " ".join("{}={}".format(n, v) for n, v in events.items()) target.analyzer.logger.trace( "cycle %d: %s", cycle, event_repr) timestamp = 1e9 * (cycle + 0) // target.sys_clk_freq next_timestamp = 1e9 * (cycle + 1) // target.sys_clk_freq if init: init = False vcd_writer._timestamp = timestamp for name, value in events.items(): vcd_writer.change(signals[name], timestamp, value) for name, _value in events.items(): if name in strobes: vcd_writer.change(signals[name], next_timestamp, "z") vcd_writer.flush() vcd_writer.close(timestamp) async def run_applet(): logger.info("running handler for applet %r", args.applet) try: iface = await applet.run(device, args) await applet.interact(device, args, iface) except GlasgowAppletError as e: applet.logger.error(str(e)) finally: if args.trace: await device.write_register( target.analyzer.addr_done, 1) done, pending = await asyncio.wait( [run_analyzer(), run_applet()], return_when=asyncio.FIRST_EXCEPTION) for task in done: await task # Work around bugs in python-libusb1 that cause segfaults on interpreter shutdown. await device.demultiplexer.flush() else: with args.bitstream as f: logger.info("downloading bitstream from %r", f.name) await device.download_bitstream(f.read()) if args.action == "tool": tool = GlasgowApplet.all_applets[args.applet].tool_cls() await tool.run(args) if args.action == "flash": logger.info("reading device configuration") header = await device.read_eeprom("fx2", 0, 8 + 4 + GlasgowConfig.size) header[0] = 0xC2 # see below fx2_config = FX2Config.decode(header, partial=True) if (len(fx2_config.firmware) != 1 or fx2_config.firmware[0][0] != 0x4000 - GlasgowConfig.size or len(fx2_config.firmware[0][1]) != GlasgowConfig.size): raise SystemExit( "Unrecognized or corrupted configuration block") glasgow_config = GlasgowConfig.decode(fx2_config.firmware[0][1]) logger.info("device has serial %s-%s", glasgow_config.revision, glasgow_config.serial) if fx2_config.disconnect: logger.info("device has flashed firmware") else: logger.info("device does not have flashed firmware") if glasgow_config.bitstream_size: logger.info("device has flashed bitstream ID %s", glasgow_config.bitstream_id.hex()) else: logger.info("device does not have flashed bitstream") new_bitstream = b"" if args.remove_bitstream: logger.info("removing bitstream") glasgow_config.bitstream_size = 0 glasgow_config.bitstream_id = b"\x00" * 16 elif args.bitstream: logger.info("using bitstream from %s", args.bitstream.name) with args.bitstream as f: new_bitstream = f.read() glasgow_config.bitstream_size = len(new_bitstream) glasgow_config.bitstream_id = b"\xff" * 16 elif args.applet: logger.info("building bitstream for applet %s", args.applet) target, applet = _applet(device.revision, args) new_bitstream_id = target.get_bitstream_id() new_bitstream = target.get_bitstream(**_toolchain_opts(args)) # We always build and reflash the bitstream in case the one currently # in EEPROM is corrupted. If we only compared the ID, there would be # no easy way to recover from that case. There's also no point in # storing the bitstream hash (as opposed to Verilog hash) in the ID, # as building the bitstream takes much longer than flashing it. logger.info("built bitstream ID %s", new_bitstream_id.hex()) glasgow_config.bitstream_size = len(new_bitstream) glasgow_config.bitstream_id = new_bitstream_id fx2_config.firmware[0] = (0x4000 - GlasgowConfig.size, glasgow_config.encode()) if args.remove_firmware: logger.info("removing firmware") fx2_config.disconnect = False new_image = fx2_config.encode() new_image[0] = 0xC0 # see below else: logger.info( "using firmware from %r", args.firmware.name if args.firmware else firmware_file) with (args.firmware or open(firmware_file, "rb")) as f: for (addr, chunk) in input_data(f, fmt="ihex"): fx2_config.append(addr, chunk) fx2_config.disconnect = True new_image = fx2_config.encode() if new_bitstream: logger.info("programming bitstream") old_bitstream = await device.read_eeprom( "ice", 0, len(new_bitstream)) if old_bitstream != new_bitstream: for (addr, chunk) in diff_data(old_bitstream, new_bitstream): await device.write_eeprom("ice", addr, chunk) logger.info("verifying bitstream") if await device.read_eeprom( "ice", 0, len(new_bitstream)) != new_bitstream: logger.critical("bitstream programming failed") return 1 else: logger.info("bitstream identical") logger.info("programming configuration and firmware") old_image = await device.read_eeprom("fx2", 0, len(new_image)) if old_image != new_image: for (addr, chunk) in diff_data(old_image, new_image): await device.write_eeprom("fx2", addr, chunk) logger.info("verifying configuration and firmware") if await device.read_eeprom("fx2", 0, len(new_image)) != new_image: logger.critical( "configuration/firmware programming failed") return 1 else: logger.info("configuration and firmware identical") if args.action == "build": target, applet = _applet(args.rev, args) if args.type in ("v", "verilog"): logger.info("building Verilog for applet %r", args.applet) target.get_verilog().write(args.filename or args.applet + ".v") if args.type in ("bin", "bitstream"): logger.info("building bitstream for applet %r", args.applet) with open(args.filename or args.applet + ".bin", "wb") as f: f.write(target.get_bitstream(**_toolchain_opts(args))) if args.type in ("zip", "archive"): logger.info("building archive for applet %r", args.applet) with target.get_build_tree() as tree: if args.filename: basename, = os.path.splitext(args.filename) else: basename = args.applet shutil.make_archive(basename, format="zip", root_dir=tree, logger=logger) if args.action == "test": logger.info("testing applet %r", args.applet) applet = GlasgowApplet.all_applets[args.applet]() loader = unittest.TestLoader() stream = unittest.runner._WritelnDecorator(sys.stderr) result = unittest.TextTestResult(stream=stream, descriptions=True, verbosity=2) result.failfast = True def startTest(test): unittest.TextTestResult.startTest(result, test) result.stream.write("\n") result.startTest = startTest if args.tests == []: suite = loader.loadTestsFromTestCase(applet.test_cls) suite.run(result) else: for test in args.tests: suite = loader.loadTestsFromName(test, module=applet.test_cls) suite.run(result) if not result.wasSuccessful(): for _, traceback in result.errors + result.failures: print(traceback, end="", file=sys.stderr) return 1 if args.action == "factory": logger.info("reading device configuration") header = await device.read_eeprom("fx2", 0, 8 + 4 + GlasgowConfig.size) if not re.match(rb"^\xff+$", header): if args.force: logger.warning( "device already factory-programmed, proceeding anyway") else: logger.error("device already factory-programmed") return 1 fx2_config = FX2Config(vendor_id=VID_QIHW, product_id=PID_GLASGOW, device_id=1 + ord(args.rev) - ord('A'), i2c_400khz=True) glasgow_config = GlasgowConfig(args.rev, args.serial) fx2_config.append(0x4000 - GlasgowConfig.size, glasgow_config.encode()) image = fx2_config.encode() # Let FX2 hardware enumerate. This won't load the configuration block # into memory automatically, but the firmware has code that does that # if it detects a C0 load. image[0] = 0xC0 logger.info("programming device configuration") await device.write_eeprom("fx2", 0, image) logger.info("verifying device configuration") if await device.read_eeprom("fx2", 0, len(image)) != image: logger.critical("factory programming failed") return 1 except GlasgowDeviceError as e: logger.error(e) return 1 except GatewareBuildError as e: applet.logger.error(e) return 1 return 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())
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())
for i in range(0, len(D1_timestamp)): # print(D_timestamp.index(D1_timestamp[i])) D_data[D_timestamp.index(D1_timestamp[i])][2] = D1_Value[i] # 把D2_timestamp对应的value填入D_data中 for i in range(0, len(D2_timestamp)): # print(D_timestamp.index(D2_timestamp[i])) D_data[D_timestamp.index(D2_timestamp[i])][3] = D2_Value[i] '''D_data = [ [3,1,None], # len(D0_data)算的是最外层[]的长度 [6,0,1], [9,1,0], [18,0,1]]''' with VCDWriter( sys.stdout, timescale='1 ns', date='today', comment='Acquisition with 8/8 channels at 20 kHz', version='libsigrok 0.5.1') as writer: # timescale='1 ns'这里单位不对有问题 counter_var0 = writer.register_var('libsigrok', 'D0', 'wire', size=1, init=D0_init, ident='#') # init是这个var的初始值 counter_var1 = writer.register_var('libsigrok', 'D1', 'wire', size=1, init=D1_init, ident='!') # init是这个var的初始值 counter_var2 = writer.register_var('libsigrok',
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()
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
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
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 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 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()
#machine_name = os.path.split(os.path.dirname(dpath))[1] machine_name = os.path.split(dpath)[1] # this card supports upto two 'machines' pods can be assigned between them machine_name = "HP16555A." + machine_name #print machine_name file_list = [] # full relative path of ascii files var_list = [] signal_list = [] file_handle_list = [] prev_line = [] try: with VCDWriter(sys.stdout, timescale='1 ns', date='today', init_timestamp=0) as writer: # figure out what signals ther are # each signal has its own file for filename in glob.glob(os.path.join(dpath, '*.txt')): if os.path.basename(filename) != "1st_line.txt": file_list.append(os.path.basename(filename)) # re-order list so line number and absolute time at start #find line number old_index = file_list.index("line_num.txt") #then move it: file_list.insert(0, file_list.pop(old_index)) #find time old_index = file_list.index("time_abs.txt") #then move it: file_list.insert(1, file_list.pop(old_index))
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
field_toks = line.split(",") if len(field_toks) != len(field_names): break for field_i in range(0,len(field_names)): field_tok = field_toks[field_i] field_value_lists[field_i].append(field_tok) for field_i in range(0,len(field_names)): field_name = field_names[field_i] #print("Field:",field_name) #print(field_value_lists[field_i]) import tempfile fp = tempfile.NamedTemporaryFile(mode='w') #fp = open("out.vcd",'w') from vcd import VCDWriter with VCDWriter(fp, timescale='1 ns', date='today') as writer: field_types = [] field_vars = [] for field_i in range(0,len(field_names)): # Setup name and type field_name = field_names[field_i] try: int_val = int(field_value_lists[field_i][0]) field_type = 'integer' #'real' field_size = 64 # hardcode assume big enough for now field_var = writer.register_var('debug_probes', field_name, field_type, size=field_size) #init=1.23 except: field_type = 'string' field_var = writer.register_var('debug_probes', field_name, field_type) field_types.append(field_type) field_vars.append(field_var)
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()