Beispiel #1
0
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)
Beispiel #2
0
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)))
Beispiel #3
0
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