def generate_pinmux(top, out_path): # MIO Pads num_mio = top["pinmux"]["num_mio"] if num_mio <= 0: log.warning( "No PINMUX is generated. The top %s has no multiplexed IO ports." % top["name"]) return # Total inputs/outputs num_inputs = sum( [x["width"] if "width" in x else 1 for x in top["pinmux"]["inputs"]]) num_outputs = sum( [x["width"] if "width" in x else 1 for x in top["pinmux"]["outputs"]]) num_inouts = sum( [x["width"] if "width" in x else 1 for x in top["pinmux"]["inouts"]]) n_periph_in = num_inouts + num_inputs n_periph_out = num_inouts + num_outputs # Target path # rtl: pinmux_reg_pkg.sv & pinmux_reg_top.sv # data: pinmux.hjson rtl_path = out_path / 'ip/pinmux/rtl/autogen' rtl_path.mkdir(parents=True, exist_ok=True) data_path = out_path / 'ip/pinmux/data/autogen' data_path.mkdir(parents=True, exist_ok=True) # Template path tpl_path = out_path / '../ip/pinmux/data/pinmux.hjson.tpl' # Generate register package and RTLs gencmd = ("// util/topgen.py -t hw/top_earlgrey/data/top_earlgrey.hjson " "-o hw/top_earlgrey/\n\n") hjson_gen_path = data_path / "pinmux.hjson" out = StringIO() with tpl_path.open(mode='r', encoding='UTF-8') as fin: hjson_tpl = Template(fin.read()) try: out = hjson_tpl.render(n_periph_in=n_periph_in, n_periph_out=n_periph_out, n_mio_pads=num_mio) except: log.error(exceptions.text_error_template().render()) log.info("PINMUX HJSON: %s" % out) if out == "": log.error("Cannot generate pinmux HJSON") return with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + gencmd + out) hjson_obj = hjson.loads(out, use_decimal=True, object_pairs_hook=validate.checking_dict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(rtl_path))
def generate_flash(topcfg, out_path): log.info("Generating flash") # Define target path rtl_path = out_path / 'ip/flash_ctrl/rtl/autogen' rtl_path.mkdir(parents=True, exist_ok=True) doc_path = out_path / 'ip/flash_ctrl/data/autogen' doc_path.mkdir(parents=True, exist_ok=True) tpl_path = Path(__file__).resolve().parent / '../hw/ip/flash_ctrl/data' # Read template files from ip directory. tpls = [] outputs = [] names = ['flash_ctrl.hjson', 'flash_ctrl.sv', 'flash_ctrl_pkg.sv'] for x in names: tpls.append(tpl_path / Path(x + ".tpl")) if "hjson" in x: outputs.append(doc_path / Path(x)) else: outputs.append(rtl_path / Path(x)) # Parameters needed for generation flash_mems = [mem for mem in topcfg['memory'] if mem['type'] == 'eflash'] if len(flash_mems) > 1: log.error("This design does not currently support multiple flashes") return cfg = flash_mems[0] # Generate templated files for idx, t in enumerate(tpls): out = StringIO() with t.open(mode='r', encoding='UTF-8') as fin: tpl = Template(fin.read()) try: out = tpl.render(cfg=cfg) except: # noqa: E722 log.error(exceptions.text_error_template().render()) if out == "": log.error("Cannot generate {}".format(names[idx])) return with outputs[idx].open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + out) # Generate reg files hjson_path = outputs[0] with open(str(hjson_path), 'r') as out: hjson_obj = hjson.load(out, use_decimal=True, object_pairs_hook=OrderedDict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(rtl_path))
def generate_pwrmgr(top, out_path): log.info("Generating pwrmgr") # Count number of wakeups n_wkups = len(top["wakeups"]) log.info("Found {} wakeup signals".format(n_wkups)) # Count number of reset requests n_rstreqs = len(top["reset_requests"]) log.info("Found {} reset request signals".format(n_rstreqs)) if n_wkups < 1: n_wkups = 1 log.warning( "The design has no wakeup sources. Low power not supported") # Define target path rtl_path = out_path / 'ip/pwrmgr/rtl/autogen' rtl_path.mkdir(parents=True, exist_ok=True) doc_path = out_path / 'ip/pwrmgr/data/autogen' doc_path.mkdir(parents=True, exist_ok=True) # So, read template files from ip directory. tpl_path = Path(__file__).resolve().parent / '../hw/ip/pwrmgr/data' hjson_tpl_path = tpl_path / 'pwrmgr.hjson.tpl' # Render and write out hjson out = StringIO() with hjson_tpl_path.open(mode='r', encoding='UTF-8') as fin: hjson_tpl = Template(fin.read()) try: out = hjson_tpl.render(NumWkups=n_wkups, Wkups=top["wakeups"], NumRstReqs=n_rstreqs) except: # noqa: E722 log.error(exceptions.text_error_template().render()) log.info("pwrmgr hjson: %s" % out) if out == "": log.error("Cannot generate pwrmgr config file") return hjson_path = doc_path / "pwrmgr.hjson" with hjson_path.open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + out) # Generate reg files with open(str(hjson_path), 'r') as out: hjson_obj = hjson.load(out, use_decimal=True, object_pairs_hook=OrderedDict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(rtl_path))
def load_registers(path: str) -> Tuple[int, List[_HjsonDict]]: '''Load hjson file at path with reggen Return its register width and list of registers. ''' try: with open(path, 'r') as handle: obj = hjson.loads(handle.read(), use_decimal=True, object_pairs_hook=checking_dict) except ValueError as err: raise RuntimeError('Failed to parse {!r}: {}'.format(path, err)) # Unconditionally run second validation pass num_errs = validate(obj) if num_errs: raise RuntimeError('Reggen second validation pass failed for {!r} ' '({} errors).' .format(path, num_errs)) reg_bit_width = int(obj.get('regwidth', 32)) assert isinstance(reg_bit_width, int) and reg_bit_width >= 0 reg_byte_width = reg_bit_width // 8 # obj should be an OrderedDict and should contain a registers entry # (checked by validate). This is a list of registers which we'll return. # The validation code would also have exploded if it wasn't a list of # dictionaries, so we can assert the type safely. registers = obj['registers'] assert isinstance(registers, list) return (reg_byte_width, registers)
def generate_top_only(top_only_list, out_path): log.info("Generating top only modules") for ip in top_only_list: rtl_path = out_path / "ip/{}/rtl".format(ip) hjson_path = out_path / "ip/{}/data/{}.hjson".format(ip, ip) log.info("Generating top modules {}, hjson: {}, output: {}".format( ip, hjson_path, rtl_path)) # Generate reg files with open(str(hjson_path), 'r') as out: hjson_obj = hjson.load(out, use_decimal=True, object_pairs_hook=OrderedDict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(rtl_path))
def generate_hardware_blocks(): for hardware in config["hardware_definitions"]: hardware_file = open(str(SRCTREE_TOP.joinpath(hardware))) regs = hjson.load(hardware_file, use_decimal=True, object_pairs_hook=validate.checking_dict) if validate.validate(regs) == 0: logging.info("Parsed %s" % (hardware)) else: logging.fatal("Failed to parse %s" % (hardware)) base_path = config["outdir-generated"].joinpath(hardware) base_path.parent.mkdir(parents=True, exist_ok=True) regs_html = open(str( base_path.parent.joinpath(base_path.name + '.registers')), mode='w') gen_html.gen_html(regs, regs_html) regs_html.close() hwcfg_html = open(str( base_path.parent.joinpath(base_path.name + '.hwcfg')), mode='w') gen_cfg_html.gen_cfg_html(regs, hwcfg_html) hwcfg_html.close()
def generate_top_only(top_only_list, out_path, topname): log.info("Generating top only modules") for ip in top_only_list: hjson_path = Path(__file__).resolve( ).parent / "../hw/top_{}/ip/{}/data/{}.hjson".format(topname, ip, ip) genrtl_dir = out_path / "ip/{}/rtl".format(ip) genrtl_dir.mkdir(parents=True, exist_ok=True) log.info("Generating top modules {}, hjson: {}, output: {}".format( ip, hjson_path, genrtl_dir)) # Generate reg files with open(str(hjson_path), 'r') as out: hjson_obj = hjson.load(out, use_decimal=True, object_pairs_hook=OrderedDict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(genrtl_dir))
def load_registers() -> Tuple[int, List[object]]: '''Load otbn.hjson with reggen Returns (width, regs) where width is the register width and regs is a list of Register, MultiRegister or Window objects. Memoized. ''' global _LR_RETVAL if _LR_RETVAL is not None: return _LR_RETVAL path = os.path.join(os.path.dirname(__file__), '..', '..', 'data', 'otbn.hjson') try: with open(path, 'r') as handle: obj = hjson.loads(handle.read(), use_decimal=True, object_pairs_hook=checking_dict) except ValueError as err: raise RuntimeError('Failed to parse {!r}: {}'.format(path, err)) # Unconditionally run second validation pass num_errs = validate(obj) if num_errs: raise RuntimeError('Reggen second validation pass failed for {!r} ' '({} errors).'.format(path, num_errs)) reg_bit_width = int(obj.get('regwidth', 32)) assert isinstance(reg_bit_width, int) and reg_bit_width >= 0 reg_byte_width = reg_bit_width // 8 # obj should be an OrderedDict and should contain a registers entry # (checked by validate). This is a list of registers which we'll return. # The validation code would also have exploded if it wasn't a list of # dictionaries, so we can assert the type safely. registers = obj['registers'] assert isinstance(registers, list) _LR_RETVAL = (reg_byte_width, registers) return _LR_RETVAL
def main(): parser = argparse.ArgumentParser(prog="topgen") parser.add_argument('--topcfg', '-t', required=True, help="`top_{name}.hjson` file.") parser.add_argument( '--tpl', '-c', help= "The directory having top_{name}_core.sv.tpl and top_{name}.tpl.sv.") parser.add_argument( '--outdir', '-o', help='''Target TOP directory. Module is created under rtl/. (default: dir(topcfg)/..) ''') # yapf: disable parser.add_argument('--verbose', '-v', action='store_true', help="Verbose") # Generator options: 'no' series. cannot combined with 'only' series parser.add_argument( '--no-top', action='store_true', help="If defined, topgen doesn't generate top_{name} RTLs.") parser.add_argument( '--no-xbar', action='store_true', help="If defined, topgen doesn't generate crossbar RTLs.") parser.add_argument( '--no-plic', action='store_true', help="If defined, topgen doesn't generate the interrup controller RTLs." ) # Generator options: 'only' series. cannot combined with 'no' series parser.add_argument( '--top-only', action='store_true', help="If defined, the tool generates top RTL only") # yapf:disable parser.add_argument( '--xbar-only', action='store_true', help="If defined, the tool generates crossbar RTLs only") parser.add_argument( '--plic-only', action='store_true', help="If defined, the tool generates RV_PLIC RTL and Hjson only") parser.add_argument( '--alert-handler-only', action='store_true', help="If defined, the tool generates alert handler hjson only") # Generator options: generate dv ral model parser.add_argument( '--top_ral', '-r', default=False, action='store_true', help="If set, the tool generates top level RAL model for DV") args = parser.parse_args() # check combinations if args.top_ral: args.no_top = True if (args.no_top or args.no_xbar or args.no_plic) and (args.top_only or args.xbar_only or args.plic_only or args.alert_handler_only): log.error( "'no' series options cannot be used with 'only' series options") raise SystemExit(sys.exc_info()[1]) if not (args.top_ral or args.plic_only or args.alert_handler_only or args.tpl): log.error( "Template file can be omitted only if '--hjson-only' is true") raise SystemExit(sys.exc_info()[1]) if args.verbose: log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) else: log.basicConfig(format="%(levelname)s: %(message)s") if not args.outdir: outdir = Path(args.topcfg).parent / ".." log.info("TOP directory not given. Use %s", (outdir)) elif not Path(args.outdir).is_dir(): log.error("'--outdir' should point to writable directory") raise SystemExit(sys.exc_info()[1]) else: outdir = Path(args.outdir) out_path = Path(outdir) cfg_path = Path(args.topcfg).parents[1] try: with open(args.topcfg, 'r') as ftop: topcfg = hjson.load(ftop, use_decimal=True, object_pairs_hook=OrderedDict) except ValueError: raise SystemExit(sys.exc_info()[1]) # Create generated list # These modules are generated through topgen generated_list = [ module['type'] for module in topcfg['module'] if 'generated' in module and module['generated'] == 'true' ] log.info("Filtered list is {}".format(generated_list)) # These modules are NOT generated but belong to a specific top # and therefore not part of "hw/ip" top_only_list = [ module['type'] for module in topcfg['module'] if 'top_only' in module and module['top_only'] == 'true' ] log.info("Filtered list is {}".format(top_only_list)) topname = topcfg["name"] # Sweep the IP directory and gather the config files ip_dir = Path(__file__).parents[1] / 'hw/ip' ips = search_ips(ip_dir) # exclude filtered IPs (to use top_${topname} one) and exclude_list = generated_list + top_only_list ips = [x for x in ips if not x.parents[1].name in exclude_list] # Hack alert # Generate clkmgr.hjson here so that it can be included below # Unlike other generated hjsons, clkmgr thankfully does not require # ip.hjson information. All the information is embedded within # the top hjson file amend_clocks(topcfg) generate_clkmgr(topcfg, cfg_path, out_path) # It may require two passes to check if the module is needed. # TODO: first run of topgen will fail due to the absent of rv_plic. # It needs to run up to amend_interrupt in merge_top function # then creates rv_plic.hjson then run xbar generation. hjson_dir = Path(args.topcfg).parent for ip in generated_list: log.info("Appending {}".format(ip)) ip_hjson = hjson_dir.parent / "ip/{}/data/autogen/{}.hjson".format( ip, ip) ips.append(ip_hjson) for ip in top_only_list: log.info("Appending {}".format(ip)) ip_hjson = hjson_dir.parent / "ip/{}/data/{}.hjson".format(ip, ip) ips.append(ip_hjson) # load Hjson and pass validate from reggen try: ip_objs = [] for x in ips: # Skip if it is not in the module list if x.stem not in [ip["type"] for ip in topcfg["module"]]: log.info("Skip module %s as it isn't in the top module list" % x.stem) continue obj = hjson.load(x.open('r'), use_decimal=True, object_pairs_hook=OrderedDict) if validate.validate(obj) != 0: log.info("Parsing IP %s configuration failed. Skip" % x) continue ip_objs.append(obj) except ValueError: raise SystemExit(sys.exc_info()[1]) # Read the crossbars under the top directory xbar_objs = get_hjsonobj_xbars(hjson_dir) log.info("Detected crossbars: %s" % (", ".join([x["name"] for x in xbar_objs]))) topcfg, error = validate_top(topcfg, ip_objs, xbar_objs) if error != 0: raise SystemExit("Error occured while validating top.hjson") completecfg = merge_top(topcfg, ip_objs, xbar_objs) if args.top_ral: generate_top_ral(completecfg, ip_objs, out_path) sys.exit() # Generate PLIC if not args.no_plic and \ not args.alert_handler_only and \ not args.xbar_only: generate_plic(completecfg, out_path) if args.plic_only: sys.exit() # Generate Alert Handler if not args.xbar_only: generate_alert_handler(completecfg, out_path) if args.alert_handler_only: sys.exit() # Generate Pinmux generate_pinmux_and_padctrl(completecfg, out_path) # Generate Pwrmgr generate_pwrmgr(completecfg, out_path) # Generate rstmgr generate_rstmgr(completecfg, out_path) # Generate top only modules # These modules are not templated, but are not in hw/ip generate_top_only(top_only_list, out_path) # Generate xbars if not args.no_xbar or args.xbar_only: generate_xbars(completecfg, out_path) # All IPs are generated. Connect phase now # Find {memory, module} <-> {xbar} connections first. im.autoconnect(completecfg) # Generic Inter-module connection im.elab_intermodule(completecfg) top_name = completecfg["name"] # Generate top.gen.hjson right before rendering hjson_dir = Path(args.topcfg).parent genhjson_path = hjson_dir / ("autogen/top_%s.gen.hjson" % completecfg["name"]) gencmd = ( "// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson --hjson-only " "-o hw/top_{topname}/\n".format(topname=topname)) genhjson_path.write_text(genhdr + gencmd + hjson.dumps(completecfg, for_json=True)) if not args.no_top or args.top_only: tpl_path = Path(args.tpl) def render_template(out_name_tpl, out_dir, **other_info): top_tplpath = tpl_path / ((out_name_tpl + '.tpl') % (top_name)) template_contents = generate_top(completecfg, str(top_tplpath), **other_info) rendered_dir = out_path / out_dir rendered_dir.mkdir(parents=True, exist_ok=True) rendered_path = rendered_dir / (out_name_tpl % (top_name)) with rendered_path.open(mode='w', encoding='UTF-8') as fout: fout.write(template_contents) return rendered_path # SystemVerilog Top: # 'top_earlgrey.sv.tpl' -> 'rtl/autogen/top_earlgrey.sv' render_template('top_%s.sv', 'rtl/autogen') # 'top_earlgrey_pkg.sv.tpl' -> 'rtl/autogen/top_earlgrey_pkg.sv' render_template('top_%s_pkg.sv', 'rtl/autogen') # C Header + C File + Clang-format file # The C file needs some complex information, so we initialize this # object to store it. c_helper = TopGenC(completecfg) # 'clang-format' -> 'sw/autogen/.clang-format' cformat_tplpath = tpl_path / 'clang-format' cformat_dir = out_path / 'sw/autogen' cformat_dir.mkdir(parents=True, exist_ok=True) cformat_path = cformat_dir / '.clang-format' cformat_path.write_text(cformat_tplpath.read_text()) # 'top_earlgrey.h.tpl' -> 'sw/autogen/top_earlgrey.h' cheader_path = render_template('top_%s.h', 'sw/autogen', helper=c_helper).resolve() # Save the relative header path into `c_gen_info` rel_header_path = cheader_path.relative_to(SRCTREE_TOP) c_helper.header_path = str(rel_header_path) # 'top_earlgrey.c.tpl' -> 'sw/autogen/top_earlgrey.c' render_template('top_%s.c', 'sw/autogen', helper=c_helper) # 'top_earlgrey_memory.ld.tpl' -> 'sw/autogen/top_earlgrey_memory.ld' render_template('top_%s_memory.ld', 'sw/autogen') # 'top_earlgrey_memory.h.tpl' -> 'sw/autogen/top_earlgrey_memory.h' memory_cheader_path = render_template('top_%s_memory.h', 'sw/autogen') # Fix the C header guards, which will have the wrong name subprocess.run(["util/fix_include_guard.py", str(cheader_path), str(memory_cheader_path)], universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, cwd=str(SRCTREE_TOP)) # yapf: disable # generate chip level xbar TB tb_files = ["xbar_env_pkg__params.sv", "tb__xbar_connect.sv"] for fname in tb_files: tpl_fname = "%s.tpl" % (fname) xbar_chip_data_path = tpl_path / tpl_fname template_contents = generate_top(completecfg, str(xbar_chip_data_path)) rendered_dir = tpl_path / '../dv/autogen' rendered_dir.mkdir(parents=True, exist_ok=True) rendered_path = rendered_dir / fname with rendered_path.open(mode='w', encoding='UTF-8') as fout: fout.write(template_contents)
def generate_rstmgr(topcfg, out_path): log.info("Generating rstmgr") # Define target path rtl_path = out_path / 'ip/rstmgr/rtl/autogen' rtl_path.mkdir(parents=True, exist_ok=True) doc_path = out_path / 'ip/rstmgr/data/autogen' doc_path.mkdir(parents=True, exist_ok=True) tpl_path = out_path / '../ip/rstmgr/data' # Read template files from ip directory. tpls = [] outputs = [] names = ['rstmgr.hjson', 'rstmgr.sv', 'rstmgr_pkg.sv'] for x in names: tpls.append(tpl_path / Path(x + ".tpl")) if "hjson" in x: outputs.append(doc_path / Path(x)) else: outputs.append(rtl_path / Path(x)) # Parameters needed for generation clks = [] output_rsts = OrderedDict() sw_rsts = OrderedDict() leaf_rsts = OrderedDict() # unique clocks for rst in topcfg["resets"]["nodes"]: if rst['type'] != "ext" and rst['clk'] not in clks: clks.append(rst['clk']) # resets sent to reset struct output_rsts = [ rst for rst in topcfg["resets"]["nodes"] if rst['type'] == "top" ] # sw controlled resets sw_rsts = [ rst for rst in topcfg["resets"]["nodes"] if 'sw' in rst and rst['sw'] == 1 ] # leaf resets leaf_rsts = [rst for rst in topcfg["resets"]["nodes"] if rst['gen']] log.info("output resets {}".format(output_rsts)) log.info("software resets {}".format(sw_rsts)) log.info("leaf resets {}".format(leaf_rsts)) # Generate templated files for idx, t in enumerate(tpls): out = StringIO() with t.open(mode='r', encoding='UTF-8') as fin: tpl = Template(fin.read()) try: out = tpl.render(clks=clks, sw_rsts=sw_rsts, output_rsts=output_rsts, leaf_rsts=leaf_rsts) except: # noqa: E722 log.error(exceptions.text_error_template().render()) if out == "": log.error("Cannot generate {}".format(names[idx])) return with outputs[idx].open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + out) # Generate reg files hjson_path = outputs[0] with open(str(hjson_path), 'r') as out: hjson_obj = hjson.load(out, use_decimal=True, object_pairs_hook=OrderedDict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(rtl_path))
def render_lowrisc_escape(self, token): # plan eventually to allow lowrisc-doc-hdr=doctype if token.type[:15] == "lowrisc-doc-hdr": return html_data.lowrisc_title_head + token.text + \ html_data.lowrisc_title_tail if token.type == "toc": return html_data.toc_mark_head + token.text + \ html_data.toc_mark_tail if token.type == "regfile": regfile = open(path.join(self.basedir, token.text), 'r', encoding='UTF-8') with regfile: try: obj = hjson.load(regfile, use_decimal=True, object_pairs_hook=validate.checking_dict) except ValueError: raise SystemExit(sys.exc_info()[1]) if validate.validate(obj) == 0: log.info("Generated register object\n") self.regs = obj else: log.warn("Register import failed\n") self.regs = None return "" if token.type == "registers": if self.regs == None: return "<B>Errors parsing registers prevents insertion.</B>" outbuf = io.StringIO() # note for CSS need to escape the mdown class on the div outbuf.write("</div>" + html_data.register_header) gen_html.gen_html(self.regs, outbuf, toclist=self.toc, toclevel=3) outbuf.write(html_data.register_trailer + '<div class="mdown">') generated = outbuf.getvalue() outbuf.close() return generated if token.type == "cfgfile": log.error("Deprecated lowRISC token cfgfile ignored. Config is now"\ " in a single file with the registers!") return "" if token.type == "hwcfg": if self.regs == None: return "<B>Errors parsing configuration prevents insertion.</B>" outbuf = io.StringIO() # note for CSS need to escape the mdown class on the div outbuf.write("</div>" + html_data.hwcfg_header) gen_cfg_html.gen_cfg_html(self.regs, outbuf) outbuf.write(html_data.hwcfg_trailer + '<div class="mdown">') generated = outbuf.getvalue() outbuf.close() return generated if token.type == "section1": # TODO should token.text get parsed to allow markdown in it? id = self.id_from_inner(token.text) self.toc.append((2, token.text, id)) return html_data.section_template.format(cls="section_heading", id=id, inner=token.text) if token.type == "section2": # TODO should token.text get parsed to allow markdown in it? id = self.id_from_inner(token.text) self.toc.append((3, token.text, id)) return html_data.section_template.format(cls="subsection_heading", id=id, inner=token.text) if token.type == "doctree": md_paths = [] return_string = '' subdirs = [path.join(self.basedir, s) for s in token.text.split()] for subdir in sorted(subdirs): md_paths.extend(sorted(Path(subdir).rglob('*.md'))) for md_path in md_paths: rel_md_path = md_path.relative_to(self.basedir) return_string += html_data.doctree_template.format( link=rel_md_path.with_suffix('.html'), text=rel_md_path.with_suffix('')) return html_data.doctree_head + return_string + html_data.doctree_tail bad_tag = '{{% ' + token.type + ' ' + token.text + ' }}' log.warn("Unknown lowRISC tag " + bad_tag) return bad_tag
def generate_pinmux_and_padctrl(top, out_path): topname = top["name"] # MIO Pads n_mio_pads = top["pinmux"]["num_mio"] if n_mio_pads <= 0: # TODO: add support for no MIO case log.error("Topgen does currently not support generation of a top " + "without a pinmux.") return if "padctrl" not in top: # TODO: add support for no MIO case log.error("Topgen does currently not support generation of a top " + "without a padctrl instance.") return # Get number of wakeup detectors if "num_wkup_detect" in top["pinmux"]: num_wkup_detect = top["pinmux"]["num_wkup_detect"] else: num_wkup_detect = 1 if num_wkup_detect <= 0: # TODO: add support for no wakeup counter case log.error("Topgen does currently not support generation of a top " + "without DIOs.") return if "wkup_cnt_width" in top["pinmux"]: wkup_cnt_width = top["pinmux"]["wkup_cnt_width"] else: wkup_cnt_width = 8 if wkup_cnt_width <= 1: log.error("Wakeup counter width must be greater equal 2.") return # Total inputs/outputs # Validation ensures that the width field is present. num_mio_inputs = sum([x["width"] for x in top["pinmux"]["inputs"]]) num_mio_outputs = sum([x["width"] for x in top["pinmux"]["outputs"]]) num_mio_inouts = sum([x["width"] for x in top["pinmux"]["inouts"]]) num_dio_inputs = sum([ x["width"] if x["type"] == "input" else 0 for x in top["pinmux"]["dio"] ]) num_dio_outputs = sum([ x["width"] if x["type"] == "output" else 0 for x in top["pinmux"]["dio"] ]) num_dio_inouts = sum([ x["width"] if x["type"] == "inout" else 0 for x in top["pinmux"]["dio"] ]) n_mio_periph_in = num_mio_inouts + num_mio_inputs n_mio_periph_out = num_mio_inouts + num_mio_outputs n_dio_periph_in = num_dio_inouts + num_dio_inputs n_dio_periph_out = num_dio_inouts + num_dio_outputs n_dio_pads = num_dio_inouts + num_dio_inputs + num_dio_outputs if n_dio_pads <= 0: # TODO: add support for no DIO case log.error("Topgen does currently not support generation of a top " + "without DIOs.") return log.info("Generating pinmux with following info from hjson:") log.info("num_mio_inputs: %d" % num_mio_inputs) log.info("num_mio_outputs: %d" % num_mio_outputs) log.info("num_mio_inouts: %d" % num_mio_inouts) log.info("num_dio_inputs: %d" % num_dio_inputs) log.info("num_dio_outputs: %d" % num_dio_outputs) log.info("num_dio_inouts: %d" % num_dio_inouts) log.info("num_wkup_detect: %d" % num_wkup_detect) log.info("wkup_cnt_width: %d" % wkup_cnt_width) log.info("This translates to:") log.info("n_mio_periph_in: %d" % n_mio_periph_in) log.info("n_mio_periph_out: %d" % n_mio_periph_out) log.info("n_dio_periph_in: %d" % n_dio_periph_in) log.info("n_dio_periph_out: %d" % n_dio_periph_out) log.info("n_dio_pads: %d" % n_dio_pads) # Target path # rtl: pinmux_reg_pkg.sv & pinmux_reg_top.sv # data: pinmux.hjson rtl_path = out_path / 'ip/pinmux/rtl/autogen' rtl_path.mkdir(parents=True, exist_ok=True) data_path = out_path / 'ip/pinmux/data/autogen' data_path.mkdir(parents=True, exist_ok=True) # Template path tpl_path = out_path / '../ip/pinmux/data/pinmux.hjson.tpl' # Generate register package and RTLs gencmd = ("// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson " "-o hw/top_{topname}/\n\n".format(topname=topname)) hjson_gen_path = data_path / "pinmux.hjson" out = StringIO() with tpl_path.open(mode='r', encoding='UTF-8') as fin: hjson_tpl = Template(fin.read()) try: # TODO: pass in information about always-on peripherals # TODO: pass in information on which DIOs can be selected # as wakeup signals # TODO: pass in signal names such that we can introduce # named enums for select signals out = hjson_tpl.render( n_mio_periph_in=n_mio_periph_in, n_mio_periph_out=n_mio_periph_out, n_mio_pads=n_mio_pads, # each DIO has in, out and oe wires # some of these have to be tied off in the # top, depending on the type. n_dio_periph_in=n_dio_pads, n_dio_periph_out=n_dio_pads, n_dio_pads=n_dio_pads, n_wkup_detect=num_wkup_detect, wkup_cnt_width=wkup_cnt_width) except: # noqa: E722 log.error(exceptions.text_error_template().render()) log.info("PINMUX HJSON: %s" % out) if out == "": log.error("Cannot generate pinmux HJSON") return with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + gencmd + out) hjson_obj = hjson.loads(out, use_decimal=True, object_pairs_hook=validate.checking_dict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(rtl_path)) # Target path # rtl: padctrl_reg_pkg.sv & padctrl_reg_top.sv # data: padctrl.hjson rtl_path = out_path / 'ip/padctrl/rtl/autogen' rtl_path.mkdir(parents=True, exist_ok=True) data_path = out_path / 'ip/padctrl/data/autogen' data_path.mkdir(parents=True, exist_ok=True) # Template path tpl_path = out_path / '../ip/padctrl/data/padctrl.hjson.tpl' # Generate register package and RTLs gencmd = ("// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson " "-o hw/top_{topname}/\n\n".format(topname=topname)) hjson_gen_path = data_path / "padctrl.hjson" out = StringIO() with tpl_path.open(mode='r', encoding='UTF-8') as fin: hjson_tpl = Template(fin.read()) try: out = hjson_tpl.render(n_mio_pads=n_mio_pads, n_dio_pads=n_dio_pads, attr_dw=10) except: # noqa: E722 log.error(exceptions.text_error_template().render()) log.info("PADCTRL HJSON: %s" % out) if out == "": log.error("Cannot generate padctrl HJSON") return with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + gencmd + out) hjson_obj = hjson.loads(out, use_decimal=True, object_pairs_hook=validate.checking_dict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(rtl_path))
def generate_clkmgr(top, cfg_path, out_path): # Target paths rtl_path = out_path / 'ip/clkmgr/rtl/autogen' rtl_path.mkdir(parents=True, exist_ok=True) data_path = out_path / 'ip/clkmgr/data/autogen' data_path.mkdir(parents=True, exist_ok=True) # Template paths hjson_tpl = cfg_path / '../ip/clkmgr/data/clkmgr.hjson.tpl' rtl_tpl = cfg_path / '../ip/clkmgr/data/clkmgr.sv.tpl' pkg_tpl = cfg_path / '../ip/clkmgr/data/clkmgr_pkg.sv.tpl' hjson_out = data_path / 'clkmgr.hjson' rtl_out = rtl_path / 'clkmgr.sv' pkg_out = rtl_path / 'clkmgr_pkg.sv' tpls = [hjson_tpl, rtl_tpl, pkg_tpl] outputs = [hjson_out, rtl_out, pkg_out] names = ['clkmgr.hjson', 'clkmgr.sv', 'clkmgr_pkg.sv'] # clock classification grps = top['clocks']['groups'] src_aon_attr = OrderedDict() ft_clks = OrderedDict() rg_clks = OrderedDict() sw_clks = OrderedDict() hint_clks = OrderedDict() # construct a dictionary of the aon attribute for easier lookup # ie, src_name_A: True, src_name_B: False for src in top['clocks']['srcs'] + top['clocks']['derived_srcs']: if src['aon'] == 'yes': src_aon_attr[src['name']] = True else: src_aon_attr[src['name']] = False rg_srcs = [src for (src, attr) in src_aon_attr.items() if not attr] # clocks fed through clkmgr but are not disturbed in any way # This maintains the clocking structure consistency # This includes two groups of clocks # Clocks fed from the always-on source # Clocks fed to the powerup group ft_clks = OrderedDict([(clk, src) for grp in grps for (clk, src) in grp['clocks'].items() if src_aon_attr[src] or grp['name'] == 'powerup']) # root-gate clocks rg_clks = OrderedDict([(clk, src) for grp in grps for (clk, src) in grp['clocks'].items() if grp['name'] != 'powerup' and grp['sw_cg'] == 'no' and not src_aon_attr[src]]) # direct sw control clocks sw_clks = OrderedDict([(clk, src) for grp in grps for (clk, src) in grp['clocks'].items() if grp['sw_cg'] == 'yes' and not src_aon_attr[src]]) # sw hint clocks hint_clks = OrderedDict([ (clk, src) for grp in grps for (clk, src) in grp['clocks'].items() if grp['sw_cg'] == 'hint' and not src_aon_attr[src] ]) for idx, tpl in enumerate(tpls): out = "" with tpl.open(mode='r', encoding='UTF-8') as fin: tpl = Template(fin.read()) try: out = tpl.render(cfg=top, div_srcs=top['clocks']['derived_srcs'], rg_srcs=rg_srcs, ft_clks=ft_clks, rg_clks=rg_clks, sw_clks=sw_clks, hint_clks=hint_clks) except: # noqa: E722 log.error(exceptions.text_error_template().render()) if out == "": log.error("Cannot generate {}".format(names[idx])) return with outputs[idx].open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + out) # Generate reg files with open(str(hjson_out), 'r') as out: hjson_obj = hjson.load(out, use_decimal=True, object_pairs_hook=OrderedDict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(rtl_path))
def generate_alert_handler(top, out_path): # default values esc_cnt_dw = 32 accu_cnt_dw = 16 lfsr_seed = 2**31 - 1 async_on = "'0" # leave this constant n_classes = 4 topname = top["name"] # check if there are any params to be passed through reggen and placed into # the generated package ip_list_in_top = [x["name"].lower() for x in top["module"]] ah_idx = ip_list_in_top.index("alert_handler") if 'localparam' in top['module'][ah_idx]: if 'EscCntDw' in top['module'][ah_idx]['localparam']: esc_cnt_dw = int(top['module'][ah_idx]['localparam']['EscCntDw']) if 'AccuCntDw' in top['module'][ah_idx]['localparam']: accu_cnt_dw = int(top['module'][ah_idx]['localparam']['AccuCntDw']) if 'LfsrSeed' in top['module'][ah_idx]['localparam']: lfsr_seed = int(top['module'][ah_idx]['localparam']['LfsrSeed'], 0) if esc_cnt_dw < 1: log.error("EscCntDw must be larger than 0") if accu_cnt_dw < 1: log.error("AccuCntDw must be larger than 0") if (lfsr_seed & 0xFFFFFFFF) == 0 or lfsr_seed > 2**32: log.error("LFSR seed out of range or zero") # Count number of interrupts n_alerts = sum([x["width"] if "width" in x else 1 for x in top["alert"]]) if n_alerts < 1: # set number of alerts to 1 such that the config is still valid # that input will be tied off n_alerts = 1 log.warning("no alerts are defined in the system") else: async_on = "" for alert in top['alert']: for k in range(alert['width']): async_on = str(alert['async']) + async_on async_on = ("%d'b" % n_alerts) + async_on log.info("alert handler parameterization:") log.info("NAlerts = %d" % n_alerts) log.info("EscCntDw = %d" % esc_cnt_dw) log.info("AccuCntDw = %d" % accu_cnt_dw) log.info("LfsrSeed = %d" % lfsr_seed) log.info("AsyncOn = %s" % async_on) # Define target path rtl_path = out_path / 'ip/alert_handler/rtl/autogen' rtl_path.mkdir(parents=True, exist_ok=True) doc_path = out_path / 'ip/alert_handler/data/autogen' doc_path.mkdir(parents=True, exist_ok=True) dv_path = out_path / 'ip/alert_handler/dv' dv_path.mkdir(parents=True, exist_ok=True) # Generating IP top module script is not generalized yet. # So, topgen reads template files from alert_handler directory directly. tpl_path = out_path / '../ip/alert_handler/data' hjson_tpl_path = tpl_path / 'alert_handler.hjson.tpl' dv_tpl_path = tpl_path / 'alert_handler_env_pkg__params.sv.tpl' # Generate Register Package and RTLs out = StringIO() with hjson_tpl_path.open(mode='r', encoding='UTF-8') as fin: hjson_tpl = Template(fin.read()) try: out = hjson_tpl.render(n_alerts=n_alerts, esc_cnt_dw=esc_cnt_dw, accu_cnt_dw=accu_cnt_dw, lfsr_seed=lfsr_seed, async_on=async_on, n_classes=n_classes) except: # noqa: E722 log.error(exceptions.text_error_template().render()) log.info("alert_handler hjson: %s" % out) if out == "": log.error("Cannot generate alert_handler config file") return hjson_gen_path = doc_path / "alert_handler.hjson" gencmd = ( "// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson --alert-handler-only " "-o hw/top_{topname}/\n\n".format(topname=topname)) with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + gencmd + out) # Generate register RTLs (currently using shell execute) # TODO: More secure way to gneerate RTL hjson_obj = hjson.loads(out, use_decimal=True, object_pairs_hook=validate.checking_dict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(rtl_path)) # generate testbench for alert_handler with dv_tpl_path.open(mode='r', encoding='UTF-8') as fin: dv_tpl = Template(fin.read()) try: out = dv_tpl.render(n_alerts=n_alerts, async_on=async_on) except: # noqa : E722 log.error(exceptions.text_error_template().render()) log.info("ALERT_HANDLER DV: %s" % out) if out == "": log.error("Cannot generate dv alert_handler parameter file") return dv_gen_path = dv_path / 'alert_handler_env_pkg__params.sv' with dv_gen_path.open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + gencmd + out)
def generate_plic(top, out_path): topname = top["name"] # Count number of interrupts # Interrupt source 0 is tied to 0 to conform RISC-V PLIC spec. # So, total number of interrupts are the number of entries in the list + 1 src = sum([x["width"] if "width" in x else 1 for x in top["interrupt"]]) + 1 # Target and priority: Currently fixed target = int(top["num_cores"], 0) if "num_cores" in top else 1 prio = 3 # Define target path # rtl: rv_plic.sv & rv_plic_reg_pkg.sv & rv_plic_reg_top.sv # data: rv_plic.hjson rtl_path = out_path / 'ip/rv_plic/rtl/autogen' rtl_path.mkdir(parents=True, exist_ok=True) doc_path = out_path / 'ip/rv_plic/data/autogen' doc_path.mkdir(parents=True, exist_ok=True) hjson_path = out_path / 'ip/rv_plic/data/autogen' hjson_path.mkdir(parents=True, exist_ok=True) # Generating IP top module script is not generalized yet. # So, topgen reads template files from rv_plic directory directly. # Next, if the ip top gen tool is placed in util/ we can import the library. tpl_path = out_path / '../ip/rv_plic/data' hjson_tpl_path = tpl_path / 'rv_plic.hjson.tpl' rtl_tpl_path = tpl_path / 'rv_plic.sv.tpl' # Generate Register Package and RTLs out = StringIO() with hjson_tpl_path.open(mode='r', encoding='UTF-8') as fin: hjson_tpl = Template(fin.read()) try: out = hjson_tpl.render(src=src, target=target, prio=prio) except: # noqa: E722 log.error(exceptions.text_error_template().render()) log.info("RV_PLIC hjson: %s" % out) if out == "": log.error("Cannot generate interrupt controller config file") return hjson_gen_path = hjson_path / "rv_plic.hjson" gencmd = ( "// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson --plic-only " "-o hw/top_{topname}/\n\n".format(topname=topname)) with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + gencmd + out) # Generate register RTLs (currently using shell execute) # TODO: More secure way to generate RTL hjson_obj = hjson.loads(out, use_decimal=True, object_pairs_hook=OrderedDict) validate.validate(hjson_obj) gen_rtl.gen_rtl(hjson_obj, str(rtl_path)) # Generate RV_PLIC Top Module with rtl_tpl_path.open(mode='r', encoding='UTF-8') as fin: rtl_tpl = Template(fin.read()) try: out = rtl_tpl.render(src=src, target=target, prio=prio) except: # noqa: E722 log.error(exceptions.text_error_template().render()) log.info("RV_PLIC RTL: %s" % out) if out == "": log.error("Cannot generate interrupt controller RTL") return rtl_gen_path = rtl_path / "rv_plic.sv" with rtl_gen_path.open(mode='w', encoding='UTF-8') as fout: fout.write(genhdr + gencmd + out)
def _process_top(topcfg, args, cfg_path, out_path, pass_idx): # Create generated list # These modules are generated through topgen generated_list = [ module['type'] for module in topcfg['module'] if 'generated' in module and module['generated'] == 'true' ] log.info("Filtered list is {}".format(generated_list)) # These modules are NOT generated but belong to a specific top # and therefore not part of "hw/ip" top_only_list = [ module['type'] for module in topcfg['module'] if 'top_only' in module and module['top_only'] == 'true' ] log.info("Filtered list is {}".format(top_only_list)) topname = topcfg["name"] # Sweep the IP directory and gather the config files ip_dir = Path(__file__).parents[1] / 'hw/ip' ips = search_ips(ip_dir) # exclude filtered IPs (to use top_${topname} one) and exclude_list = generated_list + top_only_list ips = [x for x in ips if not x.parents[1].name in exclude_list] # Hack alert # Generate clkmgr.hjson here so that it can be included below # Unlike other generated hjsons, clkmgr thankfully does not require # ip.hjson information. All the information is embedded within # the top hjson file amend_clocks(topcfg) generate_clkmgr(topcfg, cfg_path, out_path) # It may require two passes to check if the module is needed. # TODO: first run of topgen will fail due to the absent of rv_plic. # It needs to run up to amend_interrupt in merge_top function # then creates rv_plic.hjson then run xbar generation. hjson_dir = Path(args.topcfg).parent for ip in generated_list: # For modules that are generated prior to gathering, we need to take it from # the output path. For modules not generated before, it may exist in a # pre-defined area already. log.info("Appending {}".format(ip)) if ip == 'clkmgr' or (pass_idx > 0): ip_hjson = Path(out_path) / "ip/{}/data/autogen/{}.hjson".format( ip, ip) else: ip_hjson = hjson_dir.parent / "ip/{}/data/autogen/{}.hjson".format( ip, ip) ips.append(ip_hjson) for ip in top_only_list: log.info("Appending {}".format(ip)) ip_hjson = hjson_dir.parent / "ip/{}/data/{}.hjson".format(ip, ip) ips.append(ip_hjson) # load Hjson and pass validate from reggen try: ip_objs = [] for x in ips: # Skip if it is not in the module list if x.stem not in [ip["type"] for ip in topcfg["module"]]: log.info("Skip module %s as it isn't in the top module list" % x.stem) continue # The auto-generated hjson might not yet exist. It will be created # later, see generate_{ip_name}() calls below. For the initial # validation, use the template in hw/ip/{ip_name}/data . if x.stem in generated_list and not x.is_file(): hjson_file = ip_dir / "{}/data/{}.hjson".format(x.stem, x.stem) log.info( "Auto-generated hjson %s does not yet exist. " % str(x) + "Falling back to template %s for initial validation." % str(hjson_file)) else: hjson_file = x obj = hjson.load(hjson_file.open('r'), use_decimal=True, object_pairs_hook=OrderedDict) if validate.validate(obj) != 0: log.info("Parsing IP %s configuration failed. Skip" % x) continue ip_objs.append(obj) except ValueError: raise SystemExit(sys.exc_info()[1]) # Read the crossbars under the top directory xbar_objs = get_hjsonobj_xbars(hjson_dir) log.info("Detected crossbars: %s" % (", ".join([x["name"] for x in xbar_objs]))) # If specified, override the seed for random netlist constant computation. if args.rnd_cnst_seed: log.warning('Commandline override of rnd_cnst_seed with {}.'.format( args.rnd_cnst_seed)) topcfg['rnd_cnst_seed'] = args.rnd_cnst_seed # Otherwise, we either take it from the top_{topname}.hjson if present, or # randomly generate a new seed if not. else: random.seed() new_seed = random.getrandbits(64) if topcfg.setdefault('rnd_cnst_seed', new_seed) == new_seed: log.warning( 'No rnd_cnst_seed specified, setting to {}.'.format(new_seed)) topcfg, error = validate_top(topcfg, ip_objs, xbar_objs) if error != 0: raise SystemExit("Error occured while validating top.hjson") completecfg = merge_top(topcfg, ip_objs, xbar_objs) # Generate flash controller and flash memory generate_flash(topcfg, out_path) # Generate PLIC if not args.no_plic and \ not args.alert_handler_only and \ not args.xbar_only: generate_plic(completecfg, out_path) if args.plic_only: sys.exit() # Generate Alert Handler if not args.xbar_only: generate_alert_handler(completecfg, out_path) if args.alert_handler_only: sys.exit() # Generate Pinmux generate_pinmux(completecfg, out_path) # Generate Pwrmgr generate_pwrmgr(completecfg, out_path) # Generate rstmgr generate_rstmgr(completecfg, out_path) # Generate top only modules # These modules are not templated, but are not in hw/ip generate_top_only(top_only_list, out_path, topname) if pass_idx > 0 and args.top_ral: generate_top_ral(completecfg, ip_objs, args.dv_base_prefix, out_path) sys.exit() return completecfg
def main(): parser = argparse.ArgumentParser(prog="topgen") parser.add_argument('--topcfg', '-t', required=True, help="`top_{name}.hjson` file.") parser.add_argument( '--tpl', '-c', help= "The directory having top_{name}_core.sv.tpl and top_{name}.tpl.sv.") parser.add_argument( '--outdir', '-o', help='''Target TOP directory. Module is created under rtl/. (default: dir(topcfg)/..) ''') # yapf: disable parser.add_argument('--verbose', '-v', action='store_true', help="Verbose") # Generator options: 'no' series. cannot combined with 'only' series parser.add_argument( '--no-top', action='store_true', help="If defined, topgen doesn't generate top_{name} RTLs.") parser.add_argument( '--no-xbar', action='store_true', help="If defined, topgen doesn't generate crossbar RTLs.") parser.add_argument( '--no-plic', action='store_true', help="If defined, topgen doesn't generate the interrup controller RTLs." ) # Generator options: 'only' series. cannot combined with 'no' series parser.add_argument( '--top-only', action='store_true', help="If defined, the tool generates top RTL only") # yapf:disable parser.add_argument( '--xbar-only', action='store_true', help="If defined, the tool generates crossbar RTLs only") parser.add_argument( '--plic-only', action='store_true', help="If defined, the tool generates RV_PLIC RTL and Hjson only") parser.add_argument( '--alert-handler-only', action='store_true', help="If defined, the tool generates alert handler hjson only") # Generator options: generate dv ral model parser.add_argument( '--top_ral', '-r', default=False, action='store_true', help="If set, the tool generates top level RAL model for DV") parser.add_argument('--dv-base-prefix', default='dv_base', help='Prefix for the DV register classes from which ' 'the register models are derived.') # Generator options for compile time random netlist constants parser.add_argument( '--rnd_cnst_seed', type=int, metavar='<seed>', help='Custom seed for RNG to compute netlist constants.') args = parser.parse_args() # check combinations if args.top_ral: args.no_top = True if (args.no_top or args.no_xbar or args.no_plic) and (args.top_only or args.xbar_only or args.plic_only or args.alert_handler_only): log.error( "'no' series options cannot be used with 'only' series options") raise SystemExit(sys.exc_info()[1]) if not (args.top_ral or args.plic_only or args.alert_handler_only or args.tpl): log.error( "Template file can be omitted only if '--hjson-only' is true") raise SystemExit(sys.exc_info()[1]) if args.verbose: log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) else: log.basicConfig(format="%(levelname)s: %(message)s") if not args.outdir: outdir = Path(args.topcfg).parent / ".." log.info("TOP directory not given. Use %s", (outdir)) elif not Path(args.outdir).is_dir(): log.error("'--outdir' should point to writable directory") raise SystemExit(sys.exc_info()[1]) else: outdir = Path(args.outdir) out_path = Path(outdir) cfg_path = Path(args.topcfg).parents[1] try: with open(args.topcfg, 'r') as ftop: topcfg = hjson.load(ftop, use_decimal=True, object_pairs_hook=OrderedDict) except ValueError: raise SystemExit(sys.exc_info()[1]) # Create generated list # These modules are generated through topgen generated_list = [ module['type'] for module in topcfg['module'] if 'generated' in module and module['generated'] == 'true' ] log.info("Filtered list is {}".format(generated_list)) # These modules are NOT generated but belong to a specific top # and therefore not part of "hw/ip" top_only_list = [ module['type'] for module in topcfg['module'] if 'top_only' in module and module['top_only'] == 'true' ] log.info("Filtered list is {}".format(top_only_list)) topname = topcfg["name"] # Sweep the IP directory and gather the config files ip_dir = Path(__file__).parents[1] / 'hw/ip' ips = search_ips(ip_dir) # exclude filtered IPs (to use top_${topname} one) and exclude_list = generated_list + top_only_list ips = [x for x in ips if not x.parents[1].name in exclude_list] # Hack alert # Generate clkmgr.hjson here so that it can be included below # Unlike other generated hjsons, clkmgr thankfully does not require # ip.hjson information. All the information is embedded within # the top hjson file amend_clocks(topcfg) generate_clkmgr(topcfg, cfg_path, out_path) # It may require two passes to check if the module is needed. # TODO: first run of topgen will fail due to the absent of rv_plic. # It needs to run up to amend_interrupt in merge_top function # then creates rv_plic.hjson then run xbar generation. hjson_dir = Path(args.topcfg).parent for ip in generated_list: log.info("Appending {}".format(ip)) if ip == 'clkmgr': ip_hjson = Path(out_path) / "ip/{}/data/autogen/{}.hjson".format( ip, ip) else: ip_hjson = hjson_dir.parent / "ip/{}/data/autogen/{}.hjson".format( ip, ip) ips.append(ip_hjson) for ip in top_only_list: log.info("Appending {}".format(ip)) ip_hjson = hjson_dir.parent / "ip/{}/data/{}.hjson".format(ip, ip) ips.append(ip_hjson) # load Hjson and pass validate from reggen try: ip_objs = [] for x in ips: # Skip if it is not in the module list if x.stem not in [ip["type"] for ip in topcfg["module"]]: log.info("Skip module %s as it isn't in the top module list" % x.stem) continue # The auto-generated hjson might not yet exist. It will be created # later, see generate_{ip_name}() calls below. For the initial # validation, use the template in hw/ip/{ip_name}/data . if x.stem in generated_list and not x.is_file(): hjson_file = ip_dir / "{}/data/{}.hjson".format(x.stem, x.stem) log.info( "Auto-generated hjson %s does not yet exist. " % str(x) + "Falling back to template %s for initial validation." % str(hjson_file)) else: hjson_file = x obj = hjson.load(hjson_file.open('r'), use_decimal=True, object_pairs_hook=OrderedDict) if validate.validate(obj) != 0: log.info("Parsing IP %s configuration failed. Skip" % x) continue ip_objs.append(obj) except ValueError: raise SystemExit(sys.exc_info()[1]) # Read the crossbars under the top directory xbar_objs = get_hjsonobj_xbars(hjson_dir) log.info("Detected crossbars: %s" % (", ".join([x["name"] for x in xbar_objs]))) # If specified, override the seed for random netlist constant computation. if args.rnd_cnst_seed: log.warning('Commandline override of rnd_cnst_seed with {}.'.format( args.rnd_cnst_seed)) topcfg['rnd_cnst_seed'] = args.rnd_cnst_seed # Otherwise, we either take it from the top_{topname}.hjson if present, or # randomly generate a new seed if not. else: random.seed() new_seed = random.getrandbits(64) if topcfg.setdefault('rnd_cnst_seed', new_seed) == new_seed: log.warning( 'No rnd_cnst_seed specified, setting to {}.'.format(new_seed)) topcfg, error = validate_top(topcfg, ip_objs, xbar_objs) if error != 0: raise SystemExit("Error occured while validating top.hjson") completecfg = merge_top(topcfg, ip_objs, xbar_objs) if args.top_ral: generate_top_ral(completecfg, ip_objs, args.dv_base_prefix, out_path) sys.exit() # Generate PLIC if not args.no_plic and \ not args.alert_handler_only and \ not args.xbar_only: generate_plic(completecfg, out_path) if args.plic_only: sys.exit() # Generate Alert Handler if not args.xbar_only: generate_alert_handler(completecfg, out_path) if args.alert_handler_only: sys.exit() # Generate Pinmux generate_pinmux_and_padctrl(completecfg, out_path) # Generate Pwrmgr generate_pwrmgr(completecfg, out_path) # Generate rstmgr generate_rstmgr(completecfg, out_path) # Generate flash generate_flash(completecfg, out_path) # Generate top only modules # These modules are not templated, but are not in hw/ip generate_top_only(top_only_list, out_path, topname) # Generate xbars if not args.no_xbar or args.xbar_only: generate_xbars(completecfg, out_path) # All IPs are generated. Connect phase now # Find {memory, module} <-> {xbar} connections first. im.autoconnect(completecfg) # Generic Inter-module connection im.elab_intermodule(completecfg) top_name = completecfg["name"] # Generate top.gen.hjson right before rendering genhjson_dir = Path(out_path) / "data/autogen" genhjson_dir.mkdir(parents=True, exist_ok=True) genhjson_path = genhjson_dir / ("top_%s.gen.hjson" % completecfg["name"]) # Header for HJSON gencmd = '''// // util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson \\ // -o hw/top_{topname}/ \\ // --hjson-only \\ // --rnd_cnst_seed {seed} '''.format(topname=topname, seed=completecfg['rnd_cnst_seed']) genhjson_path.write_text(genhdr + gencmd + hjson.dumps(completecfg, for_json=True)) if not args.no_top or args.top_only: tpl_path = Path(args.tpl) def render_template(out_name_tpl, out_dir, **other_info): top_tplpath = tpl_path / ((out_name_tpl + '.tpl') % (top_name)) template_contents = generate_top(completecfg, str(top_tplpath), **other_info) rendered_dir = out_path / out_dir rendered_dir.mkdir(parents=True, exist_ok=True) rendered_path = rendered_dir / (out_name_tpl % (top_name)) with rendered_path.open(mode='w', encoding='UTF-8') as fout: fout.write(template_contents) return rendered_path.resolve() # Header for SV files gencmd = warnhdr + '''// // util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson \\ // --tpl hw/top_{topname}/data/ \\ // -o hw/top_{topname}/ \\ // --rnd_cnst_seed {seed} '''.format(topname=topname, seed=topcfg['rnd_cnst_seed']) # SystemVerilog Top: render_template('top_%s.sv', 'rtl/autogen', gencmd=gencmd) # 'top_{topname}.sv.tpl' -> 'rtl/autogen/top_{topname}.sv' # The C / SV file needs some complex information, so we initialize this # object to store it. c_helper = TopGenC(completecfg) # 'top_{topname}_pkg.sv.tpl' -> 'rtl/autogen/top_{topname}_pkg.sv' render_template('top_%s_pkg.sv', 'rtl/autogen', helper=c_helper, gencmd=gencmd) # compile-time random netlist constants render_template('top_%s_rnd_cnst_pkg.sv', 'rtl/autogen', gencmd=gencmd) # C Header + C File + Clang-format file # Since SW does not use FuseSoC and instead expects those files always # to be in hw/top_{topname}/sw/autogen, we currently create these files # twice: # - Once under out_path/sw/autogen # - Once under hw/top_{topname}/sw/autogen for path in [ Path(out_path).resolve(), (SRCTREE_TOP / 'hw/top_{}/'.format(topname)).resolve() ]: # 'clang-format' -> 'sw/autogen/.clang-format' cformat_tplpath = tpl_path / 'clang-format' cformat_dir = path / 'sw/autogen' cformat_dir.mkdir(parents=True, exist_ok=True) cformat_path = cformat_dir / '.clang-format' cformat_path.write_text(cformat_tplpath.read_text()) # 'top_{topname}.h.tpl' -> 'sw/autogen/top_{topname}.h' cheader_path = render_template('top_%s.h', cformat_dir, helper=c_helper) # Save the relative header path into `c_gen_info` rel_header_path = cheader_path.relative_to(path.parents[1]) c_helper.header_path = str(rel_header_path) # 'top_{topname}.c.tpl' -> 'sw/autogen/top_{topname}.c' render_template('top_%s.c', cformat_dir, helper=c_helper) # 'top_{topname}_memory.ld.tpl' -> 'sw/autogen/top_{topname}_memory.ld' render_template('top_%s_memory.ld', cformat_dir) # 'top_{topname}_memory.h.tpl' -> 'sw/autogen/top_{topname}_memory.h' memory_cheader_path = render_template('top_%s_memory.h', cformat_dir) try: cheader_path.relative_to(SRCTREE_TOP) except ValueError: log.error("cheader_path %s is not within SRCTREE_TOP %s", cheader_path, SRCTREE_TOP) log.error("Thus skipping util/fix_include_guard.py") continue # Fix the C header guards, which will have the wrong name subprocess.run(["util/fix_include_guard.py", str(cheader_path), str(memory_cheader_path)], universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True, cwd=str(SRCTREE_TOP)) # yapf: disable # generate chip level xbar and alert_handler TB tb_files = [ "xbar_env_pkg__params.sv", "tb__xbar_connect.sv", "tb__alert_handler_connect.sv" ] for fname in tb_files: tpl_fname = "%s.tpl" % (fname) xbar_chip_data_path = tpl_path / tpl_fname template_contents = generate_top(completecfg, str(xbar_chip_data_path)) rendered_dir = Path(out_path) / 'dv/autogen' rendered_dir.mkdir(parents=True, exist_ok=True) rendered_path = rendered_dir / fname with rendered_path.open(mode='w', encoding='UTF-8') as fout: fout.write(template_contents) # generate chip level alert_handler pkg tpl_fname = 'alert_handler_env_pkg__params.sv.tpl' alert_handler_chip_data_path = tpl_path / tpl_fname template_contents = generate_top(completecfg, str(alert_handler_chip_data_path)) rendered_dir = Path(out_path) / 'dv/env/autogen' rendered_dir.mkdir(parents=True, exist_ok=True) rendered_path = rendered_dir / 'alert_handler_env_pkg__params.sv' with rendered_path.open(mode='w', encoding='UTF-8') as fout: fout.write(template_contents)
def main(): format = 'hjson' verbose = 0 parser = argparse.ArgumentParser( prog="regtool", formatter_class=argparse.RawDescriptionHelpFormatter, usage=USAGE, description=DESC) parser.add_argument('input', nargs='?', metavar='file', type=argparse.FileType('r'), default=sys.stdin, help='input file in Hjson type') parser.add_argument('-d', action='store_true', help='Output register documentation (html)') parser.add_argument('--cdefines', '-D', action='store_true', help='Output C defines header') parser.add_argument('--ctdefines', '-T', action='store_true', help='Output C defines header (Titan style)') parser.add_argument('--doc', action='store_true', help='Output source file documentation (gfm)') parser.add_argument('-j', action='store_true', help='Output as formatted JSON') parser.add_argument('-c', action='store_true', help='Output as JSON') parser.add_argument('-r', action='store_true', help='Output as SystemVerilog RTL') parser.add_argument('-s', action='store_true', help='Output as UVM Register class') parser.add_argument('--outdir', '-t', help='Target directory for generated RTL, '\ 'tool uses ../rtl if blank.') parser.add_argument('--outfile', '-o', type=argparse.FileType('w'), default=sys.stdout, help='Target filename for json, html, gfm.') parser.add_argument('--verbose', '-v', action='store_true', help='Verbose and run validate twice') parser.add_argument('--param', '-p', type=str, default="", help='''Change the Parameter values. Only integer value is supported. You can add multiple param arguments. Format: ParamA=ValA;ParamB=ValB ''') parser.add_argument('--version', '-V', action='store_true', help='Show version') parser.add_argument('--novalidate', action='store_true', help='Skip validate, just output json') args = parser.parse_args() if args.version: version.show_and_exit(__file__, ["Hjson", "Mako"]) verbose = args.verbose if args.j: format = 'json' elif args.c: format = 'compact' elif args.d: format = 'html' elif args.doc: format = 'doc' elif args.r: format = 'rtl' elif args.s: format = 'dv' elif args.cdefines: format = 'cdh' elif args.ctdefines: format = 'cth' if (verbose): log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) else: log.basicConfig(format="%(levelname)s: %(message)s") outfile = args.outfile infile = args.input params = args.param.split(';') if format == 'rtl': if args.outdir: outdir = args.outdir elif infile != sys.stdin: outdir = str(PurePath(infile.name).parents[1].joinpath("rtl")) else: # Using sys.stdin. not possible to generate RTL log.error("-r option cannot be used with pipe or stdin") elif format == 'dv': if args.outdir: outdir = args.outdir elif infile != sys.stdin: outdir = str(PurePath(infile.name).parents[1].joinpath("dv")) else: # Using sys.stdin. not possible to generate RTL log.error("-s option cannot be used with pipe or stdin") else: # Ignore outdir = "." if format == 'doc': with outfile: gen_selfdoc.document(outfile) exit(0) with infile: try: srcfull = infile.read() obj = hjson.loads(srcfull, use_decimal=True, object_pairs_hook=validate.checking_dict) except ValueError: raise SystemExit(sys.exc_info()[1]) if args.novalidate: with outfile: gen_json.gen_json(obj, outfile, format) outfile.write('\n') elif (validate.validate(obj, params=params) == 0): if (verbose): log.info("Second validate pass (should show added optional keys)") validate.validate(obj, params=params) if format == 'rtl': gen_rtl.gen_rtl(obj, outdir) return 0 if format == 'dv': gen_dv.gen_dv(obj, outdir) return 0 src_lic = None src_copy = '' found_spdx = None found_lunder = None copy = re.compile(r'.*(copyright.*)|(.*\(c\).*)', re.IGNORECASE) spdx = re.compile(r'.*(SPDX-License-Identifier:.+)') lunder = re.compile(r'.*(Licensed under.+)', re.IGNORECASE) for line in srcfull.splitlines(): mat = copy.match(line) if mat != None: src_copy += mat.group(1) mat = spdx.match(line) if mat != None: found_spdx = mat.group(1) mat = lunder.match(line) if mat != None: found_lunder = mat.group(1) if found_lunder: src_lic = found_lunder if found_spdx: src_lic += '\n' + found_spdx with outfile: if format == 'html': gen_html.gen_html(obj, outfile) elif format == 'cdh': gen_cheader.gen_cdefines(obj, outfile, src_lic, src_copy) elif format == 'cth': gen_ctheader.gen_cdefines(obj, outfile, src_lic, src_copy) else: gen_json.gen_json(obj, outfile, format) outfile.write('\n')
def main(): verbose = 0 parser = argparse.ArgumentParser( prog="regtool", formatter_class=argparse.RawDescriptionHelpFormatter, usage=USAGE, description=DESC) parser.add_argument('input', nargs='?', metavar='file', type=argparse.FileType('r'), default=sys.stdin, help='input file in Hjson type') parser.add_argument('-d', action='store_true', help='Output register documentation (html)') parser.add_argument('--cdefines', '-D', action='store_true', help='Output C defines header') parser.add_argument('--ctdefines', '-T', action='store_true', help='Output C defines header (Titan style)') parser.add_argument('--doc', action='store_true', help='Output source file documentation (gfm)') parser.add_argument('-j', action='store_true', help='Output as formatted JSON') parser.add_argument('-c', action='store_true', help='Output as JSON') parser.add_argument('-r', action='store_true', help='Output as SystemVerilog RTL') parser.add_argument('-s', action='store_true', help='Output as UVM Register class') parser.add_argument('-f', action='store_true', help='Output as FPV CSR rw assertion module') parser.add_argument('--outdir', '-t', help='Target directory for generated RTL; ' 'tool uses ../rtl if blank.') parser.add_argument('--dv-base-prefix', default='dv_base', help='Prefix for the DV register classes from which ' 'the register models are derived.') parser.add_argument('--outfile', '-o', type=argparse.FileType('w'), default=sys.stdout, help='Target filename for json, html, gfm.') parser.add_argument('--verbose', '-v', action='store_true', help='Verbose and run validate twice') parser.add_argument('--param', '-p', type=str, default="", help='''Change the Parameter values. Only integer value is supported. You can add multiple param arguments. Format: ParamA=ValA;ParamB=ValB ''') parser.add_argument('--version', '-V', action='store_true', help='Show version') parser.add_argument('--novalidate', action='store_true', help='Skip validate, just output json') args = parser.parse_args() if args.version: version.show_and_exit(__file__, ["Hjson", "Mako"]) verbose = args.verbose if (verbose): log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) else: log.basicConfig(format="%(levelname)s: %(message)s") # Entries are triples of the form (arg, (format, dirspec)). # # arg is the name of the argument that selects the format. format is the # name of the format. dirspec is None if the output is a single file; if # the output needs a directory, it is a default path relative to the source # file (used when --outdir is not given). arg_to_format = [('j', ('json', None)), ('c', ('compact', None)), ('d', ('html', None)), ('doc', ('doc', None)), ('r', ('rtl', 'rtl')), ('s', ('dv', 'dv')), ('f', ('fpv', 'fpv/vip')), ('cdefines', ('cdh', None)), ('ctdefines', ('cth', None))] format = None dirspec = None for arg_name, spec in arg_to_format: if getattr(args, arg_name): if format is not None: log.error('Multiple output formats specified on ' 'command line ({} and {}).'.format(format, spec[0])) sys.exit(1) format, dirspec = spec if format is None: format = 'hjson' infile = args.input params = args.param.split(';') # Define either outfile or outdir (but not both), depending on the output # format. outfile = None outdir = None if dirspec is None: if args.outdir is not None: log.error('The {} format expects an output file, ' 'not an output directory.'.format(format)) sys.exit(1) outfile = args.outfile else: if args.outfile is not sys.stdout: log.error('The {} format expects an output directory, ' 'not an output file.'.format(format)) sys.exit(1) if args.outdir is not None: outdir = args.outdir elif infile is not sys.stdin: outdir = str(PurePath(infile.name).parents[1].joinpath(dirspec)) else: # We're using sys.stdin, so can't infer an output directory name log.error( 'The {} format writes to an output directory, which ' 'cannot be inferred automatically if the input comes ' 'from stdin. Use --outdir to specify it manually.'.format( format)) sys.exit(1) if format == 'doc': with outfile: gen_selfdoc.document(outfile) exit(0) try: srcfull = infile.read() obj = hjson.loads(srcfull, use_decimal=True, object_pairs_hook=validate.checking_dict) except ValueError: raise SystemExit(sys.exc_info()[1]) if args.novalidate: with outfile: gen_json.gen_json(obj, outfile, format) outfile.write('\n') elif (validate.validate(obj, params=params) == 0): if (verbose): log.info("Second validate pass (should show added optional keys)") validate.validate(obj, params=params) if format == 'rtl': gen_rtl.gen_rtl(obj, outdir) return 0 if format == 'dv': gen_dv.gen_dv(obj, args.dv_base_prefix, outdir) return 0 if format == 'fpv': gen_fpv.gen_fpv(obj, outdir) return 0 src_lic = None src_copy = '' found_spdx = None found_lunder = None copy = re.compile(r'.*(copyright.*)|(.*\(c\).*)', re.IGNORECASE) spdx = re.compile(r'.*(SPDX-License-Identifier:.+)') lunder = re.compile(r'.*(Licensed under.+)', re.IGNORECASE) for line in srcfull.splitlines(): mat = copy.match(line) if mat is not None: src_copy += mat.group(1) mat = spdx.match(line) if mat is not None: found_spdx = mat.group(1) mat = lunder.match(line) if mat is not None: found_lunder = mat.group(1) if found_lunder: src_lic = found_lunder if found_spdx: src_lic += '\n' + found_spdx with outfile: if format == 'html': gen_html.gen_html(obj, outfile) elif format == 'cdh': gen_cheader.gen_cdefines(obj, outfile, src_lic, src_copy) elif format == 'cth': gen_ctheader.gen_cdefines(obj, outfile, src_lic, src_copy) else: gen_json.gen_json(obj, outfile, format) outfile.write('\n')
def main(): parser = argparse.ArgumentParser(prog="topgen") parser.add_argument('--topcfg', '-t', required=True, help="`top_{name}.hjson` file.") parser.add_argument( '--tpl', '-c', help= "The directory having top_{name}_core.sv.tpl and top_{name}.tpl.sv.") parser.add_argument( '--outdir', '-o', help='''Target TOP directory. Module is created under rtl/. (default: dir(topcfg)/..) ''') # yapf: disable parser.add_argument('--verbose', '-v', action='store_true', help="Verbose") # Generator options: 'no' series. cannot combined with 'only' series parser.add_argument( '--no-top', action='store_true', help="If defined, topgen doesn't generate top_{name} RTLs.") parser.add_argument( '--no-xbar', action='store_true', help="If defined, topgen doesn't generate crossbar RTLs.") parser.add_argument( '--no-plic', action='store_true', help="If defined, topgen doesn't generate the interrup controller RTLs." ) parser.add_argument( '--no-gen-hjson', action='store_true', help='''If defined, the tool assumes topcfg as a generated hjson. So it bypasses the validation step and doesn't read ip and xbar configurations ''') # Generator options: 'only' series. cannot combined with 'no' series parser.add_argument( '--top-only', action='store_true', help="If defined, the tool generates top RTL only") # yapf:disable parser.add_argument( '--xbar-only', action='store_true', help="If defined, the tool generates crossbar RTLs only") parser.add_argument( '--plic-only', action='store_true', help="If defined, the tool generates RV_PLIC RTL and Hjson only") parser.add_argument( '--alert-handler-only', action='store_true', help="If defined, the tool generates alert handler hjson only") parser.add_argument( '--hjson-only', action='store_true', help="If defined, the tool generates complete Hjson only") # Generator options: generate dv ral model parser.add_argument( '--top_ral', '-r', default=False, action='store_true', help="If set, the tool generates top level RAL model for DV") args = parser.parse_args() # check combinations if args.top_ral: args.hjson_only = True args.no_top = True if args.hjson_only: args.no_gen_hjson = False if (args.no_top or args.no_xbar or args.no_plic) and (args.top_only or args.xbar_only or args.plic_only or args.alert_handler_only): log.error( "'no' series options cannot be used with 'only' series options") raise SystemExit(sys.exc_info()[1]) if not (args.hjson_only or args.plic_only or args.alert_handler_only or args.tpl): log.error( "Template file can be omitted only if '--hjson-only' is true") raise SystemExit(sys.exc_info()[1]) if args.verbose: log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) else: log.basicConfig(format="%(levelname)s: %(message)s") if not args.outdir: outdir = Path(args.topcfg).parent / ".." log.info("TOP directory not given. Use %s", (outdir)) elif not Path(args.outdir).is_dir(): log.error("'--outdir' should point to writable directory") raise SystemExit(sys.exc_info()[1]) else: outdir = Path(args.outdir) out_path = Path(outdir) if not args.no_gen_hjson or args.hjson_only: # load top configuration try: with open(args.topcfg, 'r') as ftop: topcfg = hjson.load(ftop, use_decimal=True, object_pairs_hook=OrderedDict) except ValueError: raise SystemExit(sys.exc_info()[1]) # Sweep the IP directory and gather the config files ip_dir = Path(__file__).parents[1] / 'hw/ip' ips = search_ips(ip_dir) # exclude rv_plic (to use top_earlgrey one) and ips = [x for x in ips if not x.parents[1].name in filter_list] # It may require two passes to check if the module is needed. # TODO: first run of topgen will fail due to the absent of rv_plic. # It needs to run up to amend_interrupt in merge_top function # then creates rv_plic.hjson then run xbar generation. hjson_dir = Path(args.topcfg).parent rv_plic_hjson = hjson_dir.parent / 'ip/rv_plic/data/autogen/rv_plic.hjson' ips.append(rv_plic_hjson) pinmux_hjson = hjson_dir.parent / 'ip/pinmux/data/autogen/pinmux.hjson' ips.append(pinmux_hjson) alert_handler_hjson = hjson_dir.parent / 'ip/alert_handler/data/autogen/alert_handler.hjson' ips.append(alert_handler_hjson) # load Hjson and pass validate from reggen try: ip_objs = [] for x in ips: # Skip if it is not in the module list if not x.stem in [ip["type"] for ip in topcfg["module"]]: log.info( "Skip module %s as it isn't in the top module list" % x.stem) continue obj = hjson.load(x.open('r'), use_decimal=True, object_pairs_hook=OrderedDict) if validate.validate(obj) != 0: log.info("Parsing IP %s configuration failed. Skip" % x) continue ip_objs.append(obj) except ValueError: raise SystemExit(sys.exc_info()[1]) # Read the crossbars under the top directory xbar_objs = get_hjsonobj_xbars(hjson_dir) log.info("Detected crossbars: %s" % (", ".join([x["name"] for x in xbar_objs]))) topcfg, error = validate_top(topcfg, ip_objs, xbar_objs) if error != 0: raise SystemExit("Error occured while validating top.hjson") completecfg = merge_top(topcfg, ip_objs, xbar_objs) genhjson_path = hjson_dir / ("autogen/top_%s.gen.hjson" % completecfg["name"]) gencmd = ( "// util/topgen.py -t hw/top_earlgrey/data/top_earlgrey.hjson --hjson-only " "-o hw/top_earlgrey/\n") if args.top_ral: generate_top_ral(completecfg, ip_objs, out_path) else: genhjson_path.write_text(genhdr + gencmd + hjson.dumps(completecfg, for_json=True)) if args.hjson_only: log.info("hjson is generated. Exiting...") sys.exit() if args.no_gen_hjson: # load top.complete configuration try: with open(args.topcfg, 'r') as ftop: completecfg = hjson.load(ftop, use_decimal=True, object_pairs_hook=OrderedDict) except ValueError: raise SystemExit(sys.exc_info()[1]) # Generate PLIC if not args.no_plic and \ not args.alert_handler_only and \ not args.xbar_only: generate_plic(completecfg, out_path) if args.plic_only: sys.exit() # Generate Alert Handler if not args.xbar_only: generate_alert_handler(completecfg, out_path) if args.alert_handler_only: sys.exit() generate_pinmux(completecfg, out_path) # Generate xbars if not args.no_xbar or args.xbar_only: generate_xbars(completecfg, out_path) top_name = completecfg["name"] if not args.no_top or args.top_only: tpl_path = Path(args.tpl) top_tplpath = tpl_path / ("top_%s.sv.tpl" % (top_name)) out_top = generate_top(completecfg, str(top_tplpath)) rtl_path = out_path / 'rtl/autogen' rtl_path.mkdir(parents=True, exist_ok=True) top_path = rtl_path / ("top_%s.sv" % top_name) with top_path.open(mode='w', encoding='UTF-8') as fout: fout.write(out_top) # C header top_tplpath = tpl_path / ("top_%s.h.tpl" % top_name) out_cheader = generate_top(completecfg, str(top_tplpath)) sw_path = out_path / "sw/autogen" sw_path.mkdir(parents=True, exist_ok=True) cheader_path = sw_path / ("top_%s.h" % top_name) with cheader_path.open(mode='w', encoding='UTF-8') as fout: fout.write(out_cheader)