def test_intf_name_inference(): reg['gear/infer_signal_names'] = True @gear def fsub1(din1, din2) -> Tuple['din1', 'din2']: pass @gear def fsub2(din) -> b'din': pass @gear def fgear(din1, din2): var1 = fsub1(din1, din2) var2 = fsub2(var1) return var2 fgear(Intf(Uint[1]), Intf(Uint[2])) fsub1_inst = find('/fgear/fsub1') fsub2_inst = find('/fgear/fsub2') assert fsub1_inst.outputs[0].var_name == 'var1' assert fsub2_inst.outputs[0].var_name == 'var2'
def run_file(path): print(f'Running example {path}') example = os.path.splitext(os.path.basename(path))[0] cfg_fn = os.path.splitext(path)[0] + '_cfg.py' clear() runpy.run_path(path) if os.path.exists(cfg_fn): runpy.run_path(cfg_fn) else: gear = get_example_gear(example).basename for inp in find(f'/{gear}').in_ports: reg['debug/trace'].append(inp.name) for outp in find(f'/{gear}').out_ports: reg['debug/trace'].append(outp.name) # reg['results-dir'] = '/tools/home/tmp' reg['wavejson/trace_fn'] = os.path.join(examples_dir, f'{example}.json') reg['sim/extens'].append(WaveJSON) # reg['trace/level'] = 0 sim()
def sim_vcd_to_json(self): graph = dump_json_graph('/') if self.multiprocess: self.finish_event.set() json_vcds = [qin.recv() for qin in self.qin] for p in self.p: p.join() else: json_vcds = [] for top, trace_fn in self.cosim_modules: json_vcds.append(vcd_to_json(top, follow(trace_fn))) json_vcds.append(vcd_to_json(find('/'), follow(self.vcd_fn))) visited_channels = set() changes = [] for json_vcd in json_vcds: if json_vcd is None: continue for p_name in json_vcd: p = find(p_name) intf_name = p_name port_name = None if isinstance(p, Intf) else p.producer.name if ((isinstance(p, InPort) or (isinstance(p, OutPort) and node_hierarchical(p.gear))) and len(p.producer.consumers) > 1): for i in range(len(p.producer.consumers)): if p.producer.consumers[i] is p: break if isinstance(p, InPort) and node_hierarchical(p.gear): intf_name = p_name bc_name = f'{p.producer.parent.name}/{p.producer.basename}_bc' port_name = f'{bc_name}.dout{i}' if isinstance(p, InPort) and (p.consumer is None or any( isinstance(c, HDLConsumer) for c in p.consumer.consumers)): intf_name = None for channel_name in [intf_name, port_name]: if channel_name is not None and channel_name not in visited_channels: changes.append({ 'channelName': channel_name, 'changes': json_vcd[p_name] }) visited_channels.add(channel_name) return { 'graphInfo': graph, 'simulationChanges': { 'startCycle': 0, 'endCycle': timestep(), 'channelChanges': changes } }
def sim_hdl_pid(Kp, Ki, Kd): plant = tf([1], [1, 10, 20]) config['sim/clk_freq'] = 1000 seq = [0.] * 2 + [1.] * config['sim/clk_freq'] * 2 set_point = drv(t=Float, seq=seq) plant_out = set_point \ | hdl_pid_sys(Kp=Kp, Ki=Ki, Kd=Kd, plant=plant) find('/hdl_pid_sys/plant.x').producer | scope(title="PID Output") plant_out | scope(title="Plant Output") sim('/tools/home/tmp', timeout=len(seq))
def sim_pid_lti_comb(Kp, Ki, Kd, Nfilt): plant = tf([1], [1, 10, 20]) config['sim/clk_freq'] = 1000 seq = [0.] * 2 + [1.] * config['sim/clk_freq'] set_point = drv(t=Float, seq=seq) plant_out = set_point \ | pid_lti_comb(Kp=Kp, Ki=Ki, Kd=Kd, Nfilt=Nfilt, plant=plant) find('/pid_lti_comb/lti.x').producer | scope(title="PID Input") plant_out | scope(title="Plant Output") sim(timeout=len(seq))
def __init__(self, trace_fn=Inject('wavejson/trace_fn'), include=Inject('debug/trace'), sim=Inject('sim/simulator'), outdir=Inject('results-dir'), sim_map=Inject('sim/map')): super().__init__() self.sim = sim self.finished = False self.outdir = outdir self.trace_fn = trace_fn if not os.path.isabs(self.trace_fn): self.trace_fn = os.path.abspath(os.path.join( self.outdir, trace_fn)) atexit.register(self.finish) self.writer = WaveJSONWriter() reg['WaveJSONWriter'] = self.writer self.handhake = set() v = WaveJSONHierVisitor(self.writer, include) v.visit(find('/')) self.vcd_vars = v.vcd_vars for intf in self.vcd_vars: intf.events['put'].append(self.intf_put) intf.events['ack'].append(self.intf_ack)
def ipinst(top, resdir=None): if isinstance(top, str): top = find(top) if resdir is None: resdir = os.path.join(CACHE_DIR, 'vivado', 'ipinst') prjdir = tempfile.mkdtemp() os.makedirs(resdir, exist_ok=True) params = {k: v for k, v in top.explicit_params.items() if k[0] != '_'} ipname = top.definition.__name__ hsh_name = hashlib.sha1( (ipname + json.dumps(params, sort_keys=True)).encode()).hexdigest()[-8:] ipinst = f'{ipname}_{hsh_name}' ipdir = os.path.join(resdir, ipinst) if glob.glob(os.path.join(ipdir, 'synth', f'{ipinst}.*')): return ipdir, ipinst tclpath = os.path.join(prjdir, 'ippack.tcl') with open(tclpath, 'w') as f: f.write(load_jenv().snippets.ip_inst_prj(ipinst, ipname, resdir, prjdir, params)) ret = run(tclpath) if ret: shutil.rmtree(ipdir) raise Exception(f"Vivado build failed with code: {ret}") return ipdir, ipinst
def cosim(top, sim, *args, **kwds): if top is None: top = reg['gear/root'] elif isinstance(top, str): top_name = top top = find(top) if top is None: raise Exception(f'No gear found on path: "{top_name}"') if isinstance(sim, str): if sim in ['cadence', 'xsim', 'questa']: from .modules import SimSocket sim_cls = SimSocket kwds['sim'] = sim elif sim == 'verilator': from .modules import SimVerilated from .modules.verilator import build kwds['outdir'] = kwds.get('outdir', reg['results-dir']) kwds['rebuild'] = kwds.get('rebuild', True) sim_cls = SimVerilated build(top, **kwds) kwds['rebuild'] = False else: raise Exception(f"Unsupported simulator: {sim}") else: sim_cls = sim if args or kwds: top.params['sim_cls'] = partial(sim_cls, *args, **kwds) else: top.params['sim_cls'] = sim_cls
def list_hdl_files(top, outdir, rtl_only=False, wrapper=False, filt=None): if isinstance(top, str): top = find(top) orig_fns = set() hdlmods = {} for fn in enum_hdl_files(top, outdir, rtl_only=rtl_only, wrapper=wrapper, filt=filt): modname, lang = os.path.splitext(os.path.basename(fn)) hdlmods[(modname, lang[1:])] = fn hdltop = reg['hdlgen/map'][top] disambig = reg['hdlgen/disambig'] fns = [] for (modname, lang), fn in hdlmods.items(): if lang == hdltop.lang or (modname, hdltop.lang) not in hdlmods: fns.append(fn) continue fn_dis = os.path.join(outdir, f'{modname}_{lang}.{lang}') disambig[(modname, lang)] = (fn, fn_dis) fns.append(fn_dis) return fns
def before_run(self, sim): vcd_visitor = VCDHierVisitor(self.include, False) vcd_visitor.visit(find('/')) if not vcd_visitor.vcd_vars: self.deactivate('before_run') return True self.vcd_vars = { p: register_traces_for_intf(p.dtype, scope, self.writer, self.expand_data) for p, scope in vcd_visitor.vcd_vars.items() } self.end_consumers = vcd_visitor.end_consumers self.writer.flush() for intf in self.end_consumers: intf.events['put'].append(self.intf_put) intf.events['ack'].append(self.intf_ack) vcd_intf_vars = {} for p, v in self.vcd_vars.items(): intf.events['put'].append(self.intf_put) intf.events['ack'].append(self.intf_ack) vcd_intf_vars[p] = v self.vcd_vars = vcd_intf_vars self.extend_intfs()
def test_uint(cosim_cls): res = drv(t=Uint[4], seq=[0xa, 0xb, 0xc, 0xd]) << 4 assert res.dtype == Uint[8] res | check(ref=[0xa0, 0xb0, 0xc0, 0xd0]) find('/shl').params['sim_cls'] = cosim_cls sim()
def test_int(sim_cls): res = drv(t=Int[5], seq=[-0xa, -0xb, -0xc, -0xd]) << 4 assert res.dtype == Int[9] res | check(ref=[-0xa0, -0xb0, -0xc0, -0xd0]) find('/shl').params['sim_cls'] = sim_cls sim()
def test_int_logical(sim_cls): inp = drv(t=Int[9], seq=[-0xa1, -0xa2, -0xa3, -0xa4]) res = (inp >> Uint) >> 4 res | check(ref=[(-0xa1 & 0x1ff) >> 4] * 4) find('/shr').params['sim_cls'] = sim_cls sim()
def sim_inst(top=None): if top is None: top = find('/') v = SimInstVisitor() v.visit(top) return top
def find_target_intf(gear_name, intf_name): gear_mod = find(gear_name) rtl_node = reg['rtl/gear_node_map'][gear_mod].node intf_name = intf_name[1:] # spy name always starts with _ for i in rtl_node.local_interfaces(): if reg['hdlgen/map'][i].basename == intf_name: return i
def test_int(sim_cls): res = drv(t=Int[9], seq=[-0xa1, -0xa2, -0xa3, -0xa4]) >> 4 assert res.dtype == Int[5] res | check(ref=[-0xb] * 4) find('/shr').params['sim_cls'] = sim_cls sim()
def test_uint(sim_cls): res = drv(t=Uint[8], seq=[0xa1, 0xa2, 0xa3, 0xa4]) >> 4 assert res.dtype == Uint[4] res | check(ref=[0xa] * 4) find('/shr').params['sim_cls'] = sim_cls sim()
def get_example_gear(example): for c in find('/').child: if not c.basename.startswith(example.split('_')[0]): continue return c for c in find('/').child: if c.definition.func.__module__ == 'pygears.lib.verif': continue if (c.basename == 'delay_gen'): continue if (c.basename == 'ccat') and (not example.startswith('ccat')): continue return c
def test_intf(enable_coverage=True): t_din = Queue[Uint[4]] t_size = Uint[16] din_cp = [ CoverPoint('val', dtype=t_din[0], bind_dtype=True), CoverPoint('qlen_cp', bins=[CoverBin('all')], bind_dtype=True, dtype=t_din) ] din_cg = CoverGroup('din_cg', t_din, cover_points=din_cp) size_cp = [ CoverPoint('size_cp', bins=[ CoverBin('two', enablement=lambda x: x == 2), CoverBin('three', enablement=lambda x: x == 3), CoverBin('four', enablement=lambda x: x == 4) ]) ] size_cg = CoverGroup('size_cg', t_size, cover_points=size_cp) directed(drv(t=t_din, seq=[list(range(9)), list(range(3))]), drv(t=t_size, seq=[2, 3]), f=chop, ref=[[0, 1], [2, 3], [4, 5], [6, 7], [8], [0, 1, 2]]) cover_intf(find('/chop.size').consumer, cg=size_cg) cover_intf(find('/chop.din').consumer, cg=din_cg) sim() # print(size_cg.report()) # print(din_cg.report()) # size assert size_cg.cover_points[0].bins[0].cover_cnt == 1 assert size_cg.cover_points[0].bins[1].cover_cnt == 1 assert size_cg.cover_points[0].bins[2].cover_cnt == 0 # din assert din_cg.cover_points[0].cover_cnt == 12 assert din_cg.cover_points[1].bins[0].cover_cnt == 2
def test_basic(): @gear def qrange_wrp(din): return qrange(din) directed(drv(t=Uint[4], seq=[4]), f=qrange_wrp, ref=[list(range(4))]) find('/qrange_wrp/qrange').meta_kwds['hdl']['lang'] = 'v' cosim('/qrange_wrp', 'verilator', lang='sv') sim()
def test_clk_channeling(): @gear def dut(din): return din \ | gear_clk2 Intf(Uint[16]) | dut mod_dut = find('/dut') assert InSig('clk2', 1) in mod_dut.meta_kwds['signals']
def synth_check_fixt(tmpdir, lang, request): skip_ifndef('SYNTH_TEST') # lang = 'sv' util_ref = request.param[0] params = request.param[1] tool = request.param[2] top = request.param[3] if tool == 'vivado': skip_ifndef('SYNTH_TEST') if tool == 'vivado': if not shutil.which('vivado'): raise unittest.SkipTest(f"Skipping test, vivado not found") params['util'] = True params['timing'] = True elif tool == 'yosys' and lang == 'v': if lang != 'v': raise unittest.SkipTest( f"Skipping test, unsupported lang for yosys") if not shutil.which('yosys'): raise unittest.SkipTest(f"Skipping test, yosys not found") params['synthcmd'] = 'synth_xilinx' # from pygears.hdl.yosys import synth # report = synth(tmpdir, top=top, synth_cmd='synth_xilinx', optimize=True) else: raise unittest.SkipTest( f"Skipping test, not appropriate tool not found") yield if top is None: top = find('/').child[0] util = synth(tool, outdir=tmpdir, top=top, lang=lang, **params) if tool == 'vivado': util = util['util'] print(util) for param, value in util_ref.items(): if callable(value): assert value(util[param]) else: assert util[param] == value
def cosim(top, sim, *args, **kwds): if top is None: top = reg['gear/root'] elif isinstance(top, str): top_name = top top = find(top) if top is None: raise Exception(f'No gear found on path: "{top_name}"') kwds['outdir'] = expand(kwds.get('outdir', reg['results-dir'])) kwds['rebuild'] = kwds.get('rebuild', True) reg['sim/hook/cosim_build_before'](top, args, kwds) if not kwds['rebuild']: import sys if sys.flags.hash_randomization: raise Exception( f"Reuse of previous cosim build turned on for module '{top}'.\n" " In order to use this feature you need to turn off Python hash randomization.\n" " Before running Python, you need to set environment variable 'PYTHONHASHSEED=0'") if isinstance(sim, str): if sim in ['cadence', 'xsim', 'questa']: from .modules import SimSocket sim_cls = SimSocket kwds['sim'] = sim elif sim == 'verilator': from .modules import SimVerilated from .modules.verilator import build timeout = kwds.pop('timeout', 100) sim_cls = SimVerilated build(top, **kwds) kwds['timeout'] = timeout else: raise Exception(f"Unsupported simulator: {sim}") kwds['rebuild'] = False else: sim_cls = sim # TODO: Should we invoke build here even when simulation class is sent # directly reg['sim/hook/cosim_build_after'](top, args, kwds) if args or kwds: top.params['sim_cls'] = partial(sim_cls, *args, **kwds) else: top.params['sim_cls'] = sim_cls
def enum_hdl_files(top, outdir, rtl_only=False, wrapper=False, filt=None): if isinstance(top, str): top = find(top) vgen_map = reg[f'hdlgen/map'] dirs = {} for lang in ['v', 'sv', 'vhd']: dirs[lang] = reg[f'{lang}gen/include'] dti_yielded = False if reg['hdl/toplang'] == 'sv': dti_yielded = True yield os.path.join(LIB_SVLIB_DIR, 'dti.sv') for node in NodeYielder(filt).visit(top): if node not in vgen_map: continue vinst = vgen_map[node] if vinst.lang == 'sv' and not dti_yielded: dti_yielded = True yield os.path.join(LIB_SVLIB_DIR, 'dti.sv') # TODO: What? if ((node is top) and wrapper and not rtl_only): yield os.path.join(outdir, f'wrap_{vinst.file_basename}') if isinstance(node, Gear): for f in vinst.files: path = find_in_dirs(f, dirs[vinst.lang]) if path is not None: yield path else: yield os.path.join(outdir, f) if hasattr(vinst, 'file_basename'): file_name = getattr(vinst, 'impl_path', None) if file_name: yield file_name # elif rtl_only is False: # for f in vinst.files: # yield os.path.join(outdir, f) elif vinst.is_broadcast: if vinst.lang == 'v': yield os.path.join(LIB_VLIB_DIR, 'bc.v') elif vinst.lang == 'sv': yield os.path.join(LIB_SVLIB_DIR, 'bc.sv')
def __init__(self, outdir='timelapse', dpi=60): super().__init__() self.outdir = os.path.abspath( os.path.expandvars(os.path.expanduser(outdir))) os.makedirs(self.outdir, exist_ok=True) self.g = graphviz.graph(find('/'), edge_fmt='{prod_gear} -> {cons_gear}') self.g.set_dpi(dpi) self.img_cnt = 0 for out_port in self.g.prod_edge_map: out_port.producer.events['put'].append(self.intf_put) for in_port in self.g.cons_edge_map: in_port.consumer.events['ack'].append(self.intf_ack) in_port.consumer.events['finish'].append(self.intf_finish)
def test_update_after_in_loop(): @gear(hdl={'compile': True}) async def test(din: Queue[Uint]) -> b'din': acc = din.dtype.data(0) async for d, eot in din: acc = d + acc if eot: yield acc, eot test(Intf(Queue[Uint[8]])) ctx, res = translate_gear(find('/test')) assert ctx.scope['acc'].reg
def comp_pid_pg_comb_lti_comb(Kp, Ki, Kd, Nfilt): plant = tf([1], [1, 10, 20]) config['sim/clk_freq'] = 1000 seq = [0.] * 2 + [1.] * config['sim/clk_freq'] set_point = drv(t=Float, seq=seq) plant_out = set_point \ | pid_pg_comb(Kp=Kp, Ki=Ki, Kd=Kd, Nfilt=Nfilt, plant=plant) find('/pid_pg_comb/pid.x').producer | scope(title="PID Input") find('/pid_pg_comb/pid.dout').consumer | scope(title="PID Output") plant_out | scope(title="Plant Output") ref_out = set_point \ | pid_lti_comb(Kp=Kp, Ki=Ki, Kd=Kd, Nfilt=Nfilt, plant=plant) ref_out | scope(title="Continuous Reference") report = [] scoreboard(plant_out, ref_out, report=report, tolerance=2e-2) sim(timeout=len(seq))
def test_optional_loop_assign(): @gear async def test(din: Queue[Bool]) -> b'din': flag = False async for d, eot in din: if d: flag = True yield flag test(Intf(Queue[Bool])) ctx, res = translate_gear(find('/test')) assert ctx.scope['flag'].reg
def ipdir(top, resdir=None, **kwds): if isinstance(top, str): top = find(top) if resdir is None: resdir = os.path.join(reg['results-dir'], 'ip') params = top.explicit_params ipname = top.definition.__name__ hsh_name = hashlib.sha1( (ipname + json.dumps(params, sort_keys=True)).encode()).hexdigest()[-8:] ipinst = f'{ipname}_{hsh_name}' return os.path.join(resdir, ipinst)
def test_fn_clash(): @gear def test_v(din): return din[0] + din[1] @gear def test_clash(din): return din[0], test_v(din) directed(drv(t=Tuple[Uint[4], Uint[4]], seq=[(4, 4)]), f=test_clash, ref=[[4], [8]]) find('/test_clash/test_v').meta_kwds['hdl']['lang'] = 'v' cosim('/test_clash', 'verilator', lang='sv', rebuild=True) sim()