Beispiel #1
0
def ipgen_render(template_name: str, topname: str, params: Dict,
                 out_path: Path):
    """ Render an IP template for a specific toplevel using ipgen.

    The generated IP block is placed in the 'ip_autogen' directory of the
    toplevel.

    Aborts the program execution in case of an error.
    """
    instance_name = f'top_{topname}_{template_name}'
    ip_template = IpTemplate.from_template_path(
        SRCTREE_TOP / 'hw/ip_templates' / template_name)

    try:
        ip_config = IpConfig(ip_template.params, instance_name, params)
    except ValueError as e:
        log.error(f"Unable to render IP template {template_name!r}: {str(e)}")
        sys.exit(1)

    try:
        renderer = IpBlockRenderer(ip_template, ip_config)
        renderer.render(out_path / 'ip_autogen' / template_name,
                        overwrite_output_dir=True)
    except TemplateRenderError as e:
        log.error(e.verbose_str())
        sys.exit(1)
Beispiel #2
0
def test_render_simpleparam(rendertest_dirs) -> None:
    """ Test the IpBlockRenderer when it gets passed simple parameters. """

    (template_dir, out_dir) = rendertest_dirs

    (template_dir / 'test.txt.tpl').write_text('param1=${param1}')

    # Declare the template parameters.
    params = TemplateParams()
    params.add(BaseParam(name='param1', desc=None, param_type='string'))

    ip_template = IpTemplate('rendertest', params, template_dir)

    param_values = {
        'param1': 'somevalue',
    }

    ip_config = IpConfig(ip_template.params, 'inst_rendertest', param_values)

    # Render into the output directory.
    renderer = IpBlockRenderer(ip_template, ip_config)
    renderer.render(out_dir, overwrite_output_dir=False)

    # Check that the template parameters are rendered correctly.
    assert (out_dir / 'test.txt').is_file()
    assert (out_dir / 'test.txt').read_text() == 'param1=somevalue'
Beispiel #3
0
def test_render_objectparam(rendertest_dirs) -> None:
    """ Test the IpBlockRenderer when it gets passed object parameters. """

    (template_dir, out_dir) = rendertest_dirs

    template = """${obj['super']}
${obj['mega']}
% for item in obj['list']:
${item}
% endfor
"""
    (template_dir / 'test.txt.tpl').write_text(template)

    # Declare the template parameters.
    params = TemplateParams()
    params.add(BaseParam(name='obj', desc=None, param_type='object'))

    ip_template = IpTemplate('rendertest', params, template_dir)

    param_values = {
        'obj': {
            'super': 'duper',
            'mega': 1e6,
            'list': ['of', 'things'],
        },
    }

    ip_config = IpConfig(ip_template.params, 'inst_rendertest', param_values)

    # Render into the output directory.
    renderer = IpBlockRenderer(ip_template, ip_config)
    renderer.render(out_dir, overwrite_output_dir=False)

    # Check that the template parameters are rendered correctly.
    assert (out_dir / 'test.txt').is_file()
    assert (out_dir / 'test.txt').read_text() == 'duper\n1000000\nof\nthings\n'
Beispiel #4
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
Beispiel #5
0
def main() -> int:
    parser = argparse.ArgumentParser()

    # Shared arguments across all actions
    parent_parser = argparse.ArgumentParser(add_help=False)
    parent_parser.add_argument(
        "--verbose",
        help="More info messages",
        action="store_true",
    )
    parent_parser.add_argument(
        '-C',
        '--template-dir',
        type=Path,
        required=True,
        help='IP template directory',
    )

    subparsers = parser.add_subparsers(
        metavar='ACTION',
        title="actions",
        description=("Use 'ipgen.py ACTION --help' to learn more about the "
                     "individual actions."))
    subparsers.required = True

    # 'describe' subparser
    parser_generate = subparsers.add_parser(
        "describe",
        description="Show all information available for the IP template.",
        help="Show details about an IP template",
        parents=[parent_parser],
    )
    parser_generate.set_defaults(func=action_describe)

    # 'generate' subparser
    parser_generate = subparsers.add_parser(
        "generate",
        description="Generate an IP block from an IP template",
        help="Generate an IP block from an IP template",
        parents=[parent_parser],
    )
    parser_generate.add_argument(
        "-o",
        "--outdir",
        required=True,
        type=Path,
        help="output directory for the resulting IP block",
    )
    parser_generate.add_argument(
        "--force",
        "-f",
        required=False,
        default=False,
        action="store_true",
        help="overwrite the output directory, if it exists",
    )
    parser_generate.add_argument(
        "--config-file",
        "-c",
        required=False,
        type=argparse.FileType('r'),
        help="path to a configuration file",
    )
    parser_generate.set_defaults(func=action_generate)

    # Parse command line arguments, parse IP template, and invoke subparsers
    args = parser.parse_args()
    init_logging(args.verbose)

    try:
        ip_template = IpTemplate.from_template_path(args.template_dir)
        args.func(ip_template, args)
    except (TemplateParseError, TemplateRenderError) as e:
        if args.verbose:
            # Show the full backtrace if operating in verbose mode.
            logging.exception(e)
        else:
            # Otherwise just log the problem itself in a more user-friendly way.
            logging.error(str(e))
        return 1

    return 0