예제 #1
0
파일: topgen.py 프로젝트: mcy/opentitan-1
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',
        'flash_ctrl_region_cfg.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 = [
        module for module in topcfg['module'] if module['type'] == 'flash_ctrl'
    ]
    if len(flash_mems) > 1:
        log.error("This design does not currently support multiple flashes")
        return

    cfg = flash_mems[0]['memory']['mem']['config']

    # 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]
    gen_rtl.gen_rtl(IpBlock.from_path(str(hjson_path), []), str(rtl_path))
예제 #2
0
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.")

    if n_rstreqs < 1:
        n_rstreqs = 1
        log.warning(
            "The design has no reset request sources. "
            "Reset requests are 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
    gen_rtl.gen_rtl(IpBlock.from_path(str(hjson_path), []), str(rtl_path))
예제 #3
0
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
        gen_rtl.gen_rtl(IpBlock.from_path(str(hjson_path), []), str(genrtl_dir))
예제 #4
0
def generate_hardware_blocks():
    for hardware in config["hardware_definitions"]:
        regs = IpBlock.from_path(str(SRCTREE_TOP.joinpath(hardware)), [])

        hw_path = config["outdir-generated"].joinpath(hardware)
        dst_path = hw_path.parent
        dst_path.mkdir(parents=True, exist_ok=True)

        regs_path = dst_path.joinpath(hw_path.name + '.registers')
        with open(regs_path, 'w') as regs_file:
            gen_html.gen_html(regs, regs_file)

        hwcfg_path = dst_path.joinpath(hw_path.name + '.hwcfg')
        with open(hwcfg_path, 'w') as hwcfg_file:
            gen_cfg_html.gen_cfg_html(regs, hwcfg_file)
예제 #5
0
def generate_regfile_from_path(hjson_path: Path,
                               generated_rtl_path: Path,
                               original_rtl_path: Path = None):
    '''Generate RTL register file from path and check countermeasure labels'''
    obj = IpBlock.from_path(str(hjson_path), [])

    # If this block has countermeasures, we grep for RTL annotations in
    # all .sv implementation files and check whether they match up
    # with what is defined inside the Hjson.
    sv_files = generated_rtl_path.glob('*.sv')
    if original_rtl_path is not None:
        sv_files = chain(sv_files, original_rtl_path.glob('*.sv'))
    rtl_names = CounterMeasure.search_rtl_files(sv_files)
    obj.check_cm_annotations(rtl_names, str(hjson_path))
    gen_rtl.gen_rtl(obj, str(generated_rtl_path))
예제 #6
0
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']

    clocks = top['clocks']
    assert isinstance(clocks, Clocks)

    typed_clocks = clocks.typed_clocks()
    hint_names = typed_clocks.hint_names()

    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,
                                 clocks=clocks,
                                 typed_clocks=typed_clocks,
                                 hint_names=hint_names)
            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
    gen_rtl.gen_rtl(IpBlock.from_path(str(hjson_out), []), str(rtl_path))
예제 #7
0
파일: topgen.py 프로젝트: mcy/opentitan-1
def generate_top_only(top_only_dict, out_path, topname, alt_hjson_path):
    log.info("Generating top only modules")

    for ip, reggen_only in top_only_dict.items():

        if reggen_only and alt_hjson_path is not None:
            hjson_dir = Path(alt_hjson_path)
        else:
            hjson_dir = Path(__file__).resolve(
            ).parent / f"../hw/top_{topname}/ip/{ip}/data/"

        hjson_path = hjson_dir / f"{ip}.hjson"

        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
        gen_rtl.gen_rtl(IpBlock.from_path(str(hjson_path), []),
                        str(genrtl_dir))
예제 #8
0
    def render(self, output_dir: Path, overwrite_output_dir: bool) -> None:
        """ Render the IP template into output_dir. """

        # Ensure that we operate on an absolute path for output_dir.
        output_dir = output_dir.resolve()

        if not overwrite_output_dir and output_dir.exists():
            raise TemplateRenderError(
                "Output directory '{}' exists and should not be overwritten.".
                format(output_dir))

        # Prepare the IP directory in a staging area to later atomically move it
        # to the final destination.
        output_dir_staging = output_dir.parent / f".~{output_dir.stem}.staging"
        if output_dir_staging.is_dir():
            raise TemplateRenderError(
                "Output staging directory '{}' already exists. Remove it and "
                "try again.".format(output_dir_staging))

        template_path = self.ip_template.template_path

        try:
            # Copy everything but the templates and the template description.
            ignore = shutil.ignore_patterns('*.tpl', '*.tpldesc.hjson')
            shutil.copytree(template_path, output_dir_staging, ignore=ignore)

            # Render templates.
            for template_filepath in template_path.glob('**/*.tpl'):
                template_filepath_rel = template_filepath.relative_to(
                    template_path)

                # Put the output file into the same relative directory as the
                # template. The output file will also have the same name as the
                # template, just without the '.tpl' suffix.
                outdir_path = output_dir_staging / template_filepath_rel.parent

                self._render_mako_template_to_file(template_filepath,
                                                   outdir_path)

            # Generate register interface through reggen.
            hjson_path = (output_dir_staging / 'data' /
                          (self.ip_template.name + '.hjson'))
            if not hjson_path.exists():
                raise TemplateRenderError(
                    "Invalid template: The IP description file "
                    f"{str(hjson_path)!r} does not exist.")
            rtl_path = output_dir_staging / 'rtl'
            rtl_path.mkdir(exist_ok=True)

            obj = IpBlock.from_path(str(hjson_path), [])

            # If this block has countermeasures, we grep for RTL annotations in
            # all .sv implementation files and check whether they match up
            # with what is defined inside the Hjson.
            sv_files = rtl_path.glob('*.sv')
            rtl_names = CounterMeasure.search_rtl_files(sv_files)
            obj.check_cm_annotations(rtl_names, str(hjson_path))

            # TODO: Pass on template parameters to reggen? Or enable the user
            # to set a different set of parameters in the renderer?
            reggen.gen_rtl.gen_rtl(obj, str(rtl_path))

            # Write IP configuration (to reproduce the generation process).
            # TODO: Should the ipconfig file be written to the instance name,
            # or the template name?
            self.ip_config.to_file(
                output_dir_staging /
                'data/{}.ipconfig.hjson'.format(self.ip_config.instance_name),
                header=_HJSON_LICENSE_HEADER)

            # Safely overwrite the existing directory if necessary:
            #
            # - First move the existing directory out of the way.
            # - Then move the staging directory with the new content in place.
            # - Finally remove the old directory.
            #
            # If anything goes wrong in the meantime we are left with either
            # the old or the new directory, and potentially some backups of
            # outdated files.
            do_overwrite = overwrite_output_dir and output_dir.exists()
            output_dir_existing_bak = output_dir.with_suffix(
                '.bak~' + str(int(time.time())))
            if do_overwrite:
                os.rename(output_dir, output_dir_existing_bak)

            # Move the staging directory to the final destination.
            os.rename(output_dir_staging, output_dir)

            # Remove the old/"overwritten" data.
            if do_overwrite:
                try:
                    shutil.rmtree(output_dir_existing_bak)
                except Exception as e:
                    msg = (
                        'Unable to delete the backup directory '
                        f'{output_dir_existing_bak} of the overwritten data. '
                        'Please remove it manually.')
                    raise TemplateRenderError(msg).with_traceback(
                        e.__traceback__)

        finally:
            # Ensure that the staging directory is removed at the end. Ignore
            # errors as the directory should not exist at this point actually.
            shutil.rmtree(output_dir_staging, ignore_errors=True)
예제 #9
0
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 lib.is_templated(module)
    ]
    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 lib.is_top_reggen(module)
    ]
    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
    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 == '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

            ip_objs.append(IpBlock.from_path(str(hjson_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()

    # 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:
        exit_code = generate_top_ral(completecfg, name_to_block,
                                     args.dv_base_prefix, out_path)
        sys.exit(exit_code)

    return completecfg, name_to_block
예제 #10
0
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 = Path(__file__).resolve().parent / '../hw/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
    reset_obj = topcfg['resets']

    # unique clocks
    clks = reset_obj.get_clocks()

    # resets sent to reset struct
    output_rsts = reset_obj.get_top_resets()

    # sw controlled resets
    sw_rsts = reset_obj.get_sw_resets()

    # leaf resets
    leaf_rsts = reset_obj.get_generated_resets()

    # Number of reset requests
    n_rstreqs = len(topcfg["reset_requests"])

    # 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,
                                 power_domains=topcfg['power']['domains'],
                                 num_rstreqs=n_rstreqs,
                                 sw_rsts=sw_rsts,
                                 output_rsts=output_rsts,
                                 leaf_rsts=leaf_rsts,
                                 export_rsts=topcfg['exported_rsts'],
                                 reset_obj=topcfg['resets'])

            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]
    gen_rtl.gen_rtl(IpBlock.from_path(str(hjson_path), []), str(rtl_path))
예제 #11
0
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']

    # A dictionary of the aon attribute for easier lookup. src_aon_attr[C] is
    # True if clock C is always-on and False otherwise.
    src_aon_attr = {
        src['name']: (src['aon'] == 'yes')
        for src in (top['clocks']['srcs'] + top['clocks']['derived_srcs'])
    }

    # Classify the various clock signals. Here, we build the following
    # dictionaries, each mapping the derived clock name to its source.
    #
    # ft_clks:  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
    #
    # rg_clks: Non-feedthrough clocks that have no software control. These
    #          clocks are root-gated and the root-gated clock is then exposed
    #          directly in clocks_o.
    #
    # sw_clks: Non-feedthrough clocks that have direct software control. These
    #          are root-gated, but (unlike rg_clks) then go through a second
    #          clock gate which is controlled by software.
    #
    # hints: Non-feedthrough clocks that have "hint" software control (with a
    #        feedback mechanism to allow blocks to avoid being suspended when
    #        they are not idle).
    ft_clks = {}
    rg_clks = {}
    sw_clks = {}
    hints = {}

    # We also build rg_srcs_set, which is the set of non-always-on clock sources
    # that are exposed without division. This doesn't include clock sources
    # that are only used to derive divided clocks (we might gate the divided
    # clocks, but don't bother gating the upstream source).
    rg_srcs_set = set()

    for grp in top['clocks']['groups']:
        if grp['name'] == 'powerup':
            # All clocks in the "powerup" group are considered feed-throughs.
            ft_clks.update(grp['clocks'])
            continue

        for clk, src in grp['clocks'].items():
            if src_aon_attr[src]:
                # Any always-on clock is a feedthrough
                ft_clks[clk] = src
                continue

            rg_srcs_set.add(src)

            if grp['sw_cg'] == 'no':
                # A non-feedthrough clock with no software control
                rg_clks[clk] = src
                continue

            if grp['sw_cg'] == 'yes':
                # A non-feedthrough clock with direct software control
                sw_clks[clk] = src
                continue

            # The only other valid value for the sw_cg field is "hint", which
            # means a non-feedthrough clock with "hint" software control.
            assert grp['sw_cg'] == 'hint'
            hints[clk] = src
            continue

    # hint clocks dict.
    #
    # The clock is constructed as clk_{src_name}_{module_name}. So to get the
    # module name we split from the right and pick the last entry
    hint_clks = {
        clk: {
            'name': clk.rsplit('_', 1)[-1],
            'src': src
        }
        for clk, src in hints.items()
    }

    # The names of blocks that use one or more sw hint clocks (clkmgr has an
    # "idle" feedback signal from each), in ascending order.
    hint_blocks = sorted(set([v['name'] for v in hint_clks.values()]))

    # Define a canonical ordering for rg_srcs
    rg_srcs = sorted(rg_srcs_set)

    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,
                                 export_clks=top['exported_clks'],
                                 hint_clks=hint_clks,
                                 hint_blocks=hint_blocks)
            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
    gen_rtl.gen_rtl(IpBlock.from_path(str(hjson_out), []), str(rtl_path))
예제 #12
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
예제 #13
0
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']

    ft_clks = OrderedDict()
    rg_clks = OrderedDict()
    sw_clks = OrderedDict()
    src_aon_attr = 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
    hints = 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]])

    # hint clocks dict
    for clk, src in hints.items():
        # the clock is constructed as clk_{src_name}_{module_name}.
        # so to get the module name we split from the right and pick the last entry
        hint_clks[clk] = OrderedDict()
        hint_clks[clk]['name'] = (clk.rsplit('_', 1)[-1])
        hint_clks[clk]['src'] = 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,
                                 export_clks=top['exported_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
    gen_rtl.gen_rtl(IpBlock.from_path(str(hjson_out), []), str(rtl_path))