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]) # TODO, long term, the levels of dependency should be automatically determined instead # of hardcoded. The following are a few examples: # Example 1: pinmux depends on amending all modules before calculating the correct number of # pins. # This would be 1 level of dependency and require 2 passes. # Example 2: pinmux depends on amending all modules, and pwrmgr depends on pinmux generation to # know correct number of wakeups. This would be 2 levels of dependency and require 3 # passes. # # How does mulit-pass work? # In example 1, the first pass gathers all modules and merges them. However, the merge process # uses a stale pinmux. The correct pinmux is then generated using the merged configuration. The # second pass now merges all the correct modules (including the generated pinmux) and creates # the final merged config. # # In example 2, the first pass gathers all modules and merges them. However, the merge process # uses a stale pinmux and pwrmgr. The correct pinmux is then generated using the merged # configuration. However, since pwrmgr is dependent on this new pinmux, it is still generated # incorrectly. The second pass merge now has an updated pinmux but stale pwrmgr. The correct # pwrmgr can now be generated. The final pass then merges all the correct modules and creates # the final configuration. # # This fix is related to #2083 process_dependencies = 1 for pass_idx in range(process_dependencies + 1): log.debug("Generation pass {}".format(pass_idx)) if pass_idx < process_dependencies: cfg_copy = deepcopy(topcfg) _process_top(cfg_copy, args, cfg_path, out_path, pass_idx) else: completecfg = _process_top(topcfg, args, cfg_path, out_path, pass_idx) topname = topcfg["name"] # 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(): 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 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)