def main(): # Parse command line args. parser = argparse.ArgumentParser() parser.add_argument( "--topcfg_path", "-t", type=Path, default=(REPO_TOP / "hw/top_earlgrey/data/top_earlgrey.hjson"), help="path of the top hjson file.", ) args = parser.parse_args() # Parse toplevel Hjson to get IPs that are templated / generated with IPgen. try: topcfg_text = args.topcfg_path.read_text() except FileNotFoundError: logging.error(f"hjson {args.topcfg_path} could not be found.") sys.exit(1) topcfg = hjson.loads(topcfg_text, use_decimal=True) templated_modules = topgen_lib.get_templated_modules(topcfg) ipgen_modules = topgen_lib.get_ipgen_modules(topcfg) # Define autogen DIF directory. autogen_dif_directory = REPO_TOP / "sw/device/lib/dif/autogen" # Create list of IPs to generate shared testutils code for. This is all IPs # that have a DIF library, that the testutils functions can use. Note, the # templates will take care of only generating ISR testutil functions for IPs # that can actually generate interrupts. ips_with_difs = [] for autogen_dif_filename in glob.iglob(str(autogen_dif_directory / "*.h")): # NOTE: the line below takes as input a file path # (/path/to/dif_uart_autogen.c) and returns the IP name in lower # case snake mode (i.e., uart). ip_name_snake = Path(autogen_dif_filename).stem[4:-8] # NOTE: ip.name_long_* not needed for auto-generated files which # are the only files (re-)generated in batch mode. ips_with_difs.append( Ip(ip_name_snake, "AUTOGEN", templated_modules, ipgen_modules)) # Auto-generate testutils files. gen_testutils(ips_with_difs)
def main(): dif_dir = REPO_TOP / "sw/device/lib/dif" autogen_dif_dir = dif_dir / "autogen" parser = argparse.ArgumentParser() parser.add_argument( "--mode", "-m", choices=["new", "regen"], default="new", required=True, help="mode to generate DIF code. Use 'new' if no DIF code exists." "Use 'rege' to regenerate all auto-generated DIFs for all IPs.") parser.add_argument("--topcfg", "-t", help="path of the top hjson file.") parser.add_argument("--ip-name-snake", "-i", help="the short name of the IP, in snake_case.") parser.add_argument("--ip-name-long", "-l", help="the documentation-friendly name of the IP.") parser.add_argument("--only", choices=ALL_PARTS, default=[], action="append", help="only create some files; defaults to all.") args = parser.parse_args() # Parse CMD line args. ips = [] # hjson path topcfg_path = REPO_TOP / "hw/top_earlgrey/data/top_earlgrey.hjson" if args.topcfg: topcfg_path = args.topcfg try: with open(topcfg_path, 'r') as ftop: topcfg = hjson.load(ftop, use_decimal=True) except FileNotFoundError: print(f"hjson {topcfg_path} could not be found") sys.exit(1) templated_modules = lib.get_templated_modules(topcfg) ipgen_modules = lib.get_ipgen_modules(topcfg) print(f"modules {templated_modules} {ipgen_modules}") # Check for regeneration mode (used in CI check: # ci/scripts/check-generated.sh) if args.mode == "regen": if len(args.only) != 1 or args.only[0] != "autogen": raise RuntimeError( "can only regenerate DIF code that is auto-generated.") # Create list of IPs to re-generate DIF code for. for autogen_src_filename in glob.iglob( str(REPO_TOP / "sw/device/lib/dif/autogen/*.c")): # NOTE: the line below takes as input a file path # (/path/to/dif_uart_autogen.c) and returns the IP name in lower # case snake mode (i.e., uart). ip_name_snake = Path(autogen_src_filename).stem[4:-8] # NOTE: ip.name_long_* not needed for auto-generated files which # are the only files (re-)generated in regen mode. ips.append( Ip(ip_name_snake, "AUTOGEN", templated_modules, ipgen_modules)) else: assert args.ip_name_snake and args.ip_name_long, \ "ERROR: pass --ip-name-snake and --ip-name-long when --mode=new." ips.append( Ip(args.ip_name_snake, args.ip_name_long, templated_modules, ipgen_modules)) # Default to generating all parts. if len(args.only) == 0: args.only += ALL_PARTS # Create output directories if needed. if len(args.only) > 0: dif_dir.mkdir(exist_ok=True) autogen_dif_dir.mkdir(exist_ok=True) for ip in ips: if "header" in args.only: header_template_file = ( REPO_TOP / "util/make_new_dif/templates/dif_template.h.tpl") header_out_file = dif_dir / "dif_{}.h".format(ip.name_snake) if header_out_file.is_file(): raise FileExistsError( "DIF header already exists for the IP. To overwrite, " "delete existing header and try again.") header_template = Template(header_template_file.read_text()) header_out_file.write_text(header_template.render(ip=ip)) print("DIF header successfully written to {}.".format( str(header_out_file))) if "autogen" in args.only: # Render all templates for filetype in [".h", ".c", "_unittest.cc"]: # Build input/output file names. template_file = ( REPO_TOP / f"util/make_new_dif/templates/dif_autogen{filetype}.tpl") out_file = (autogen_dif_dir / f"dif_{ip.name_snake}_autogen{filetype}") # Read in template. template = Template(template_file.read_text(), strict_undefined=True) # Generate output file. out_file.write_text( template.render( ip=ip, autogen_banner=get_autogen_banner( "util/make_new_dif.py --mode=regen --only=autogen", "//"))) # Format autogenerated file with clang-format. assert (shutil.which("clang-format") and "ERROR: clang-format must be installed to format " " autogenerated code to pass OpenTitan CI checks.") try: subprocess.check_call(["clang-format", "-i", out_file]) except subprocess.CalledProcessError: logging.error( f"failed to format {out_file} with clang-format.") sys.exit(1) print("Autogenerated DIF successfully written to {}.".format( out_file)) if "checklist" in args.only: checklist_template_file = REPO_TOP / "doc/project/sw_checklist.md.tpl" checklist_out_file = dif_dir / "dif_{}.md".format(ip.name_snake) if checklist_out_file.is_file(): raise FileExistsError( "DIF checklist already exists for the IP. To " "overwrite, delete existing checklist and try again.") markdown_template = Template(checklist_template_file.read_text()) checklist_out_file.write_text(markdown_template.render(ip=ip)) print("DIF Checklist successfully written to {}.".format( str(checklist_out_file)))
def _process_top(topcfg, args, cfg_path, out_path, pass_idx): # Create generated list # These modules are generated through topgen templated_list = lib.get_templated_modules(topcfg) log.info("Templated list is {}".format(templated_list)) ipgen_list = lib.get_ipgen_modules(topcfg) log.info("Ip gen list is {}".format(ipgen_list)) generated_list = templated_list + ipgen_list # These modules are NOT generated but belong to a specific top # and therefore not part of "hw/ip" top_only_dict = { module['type']: lib.is_reggen_only(module) for module in topcfg['module'] if lib.is_top_reggen(module) } log.info("Filtered dict is {}".format(top_only_dict)) 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 + list(top_only_dict.keys()) 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 topcfg['clocks'] = Clocks(topcfg['clocks']) extract_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 in ipgen_list: ip_relpath = 'ip_autogen' desc_file_relpath = 'data' else: ip_relpath = 'ip' desc_file_relpath = 'data/autogen' if ip == 'clkmgr' or (pass_idx > 0): ip_hjson = (Path(out_path) / ip_relpath / ip / desc_file_relpath / f"{ip}.hjson") else: ip_hjson = (hjson_dir.parent / ip_relpath / ip / desc_file_relpath / f"{ip}.hjson") ips.append(ip_hjson) for ip, reggen_only in top_only_dict.items(): log.info("Appending {}".format(ip)) if reggen_only and args.hjson_path: ip_hjson = Path(args.hjson_path) / f"{ip}.hjson" else: ip_hjson = hjson_dir.parent / f"ip/{ip}/data/{ip}.hjson" ips.append(ip_hjson) # load Hjson and pass validate from reggen try: ip_objs = [] for ip_desc_file in ips: ip_name = ip_desc_file.stem # Skip if it is not in the module list if ip_name not in [ip["type"] for ip in topcfg["module"]]: log.info("Skip module %s as it isn't in the top module list" % ip_name) 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 Hjson file with default values. # TODO: All of this is a rather ugly hack that we need to get rid # of as soon as we don't arbitrarily template IP description Hjson # files any more. if ip_name in generated_list and not ip_desc_file.is_file(): if ip_name in ipgen_list: log.info( "To-be-auto-generated Hjson %s does not yet exist. " "Falling back to the default configuration of template " "%s for initial validation." % (ip_desc_file, ip_name)) tpl_path = SRCTREE_TOP / 'hw/ip_templates' / ip_name ip_template = IpTemplate.from_template_path(tpl_path) ip_config = IpConfig(ip_template.params, f'top_{topname}_{ip_name}') try: ip_desc = IpDescriptionOnlyRenderer( ip_template, ip_config).render() except TemplateRenderError as e: log.error(e.verbose_str()) sys.exit(1) s = 'default description of IP template {}'.format(ip_name) ip_objs.append(IpBlock.from_text(ip_desc, [], s)) else: # TODO: Remove this block as soon as all IP templates use # ipgen. template_hjson_file = ip_dir / "{}/data/{}.hjson".format( ip_name, ip_name) log.info( "To-be-auto-generated Hjson %s does not yet exist. " "Falling back to Hjson description file %s shipped " "with the IP template for initial validation." % (ip_desc_file, template_hjson_file)) ip_objs.append( IpBlock.from_path(str(template_hjson_file), [])) else: ip_objs.append(IpBlock.from_path(str(ip_desc_file), [])) except ValueError: raise SystemExit(sys.exc_info()[1]) name_to_block = {} # type: Dict[str, IpBlock] for block in ip_objs: lblock = block.name.lower() assert lblock not in name_to_block name_to_block[lblock] = block connect_clocks(topcfg, name_to_block) # 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, name_to_block, 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() # Create Alert Handler LPGs before # generating the Alert Handler create_alert_lpgs(topcfg, name_to_block) # 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_dict, out_path, topname, args.hjson_path) return completecfg, name_to_block