Ejemplo n.º 1
0
def main():
    verbose = 0

    parser = argparse.ArgumentParser(
        prog="regtool",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        usage=USAGE,
        description=DESC)
    parser.add_argument('input',
                        nargs='?',
                        metavar='file',
                        type=argparse.FileType('r'),
                        default=sys.stdin,
                        help='input file in Hjson type')
    parser.add_argument('-d',
                        action='store_true',
                        help='Output register documentation (html)')
    parser.add_argument('--cdefines',
                        '-D',
                        action='store_true',
                        help='Output C defines header')
    parser.add_argument('--doc',
                        action='store_true',
                        help='Output source file documentation (gfm)')
    parser.add_argument('-j',
                        action='store_true',
                        help='Output as formatted JSON')
    parser.add_argument('-c', action='store_true', help='Output as JSON')
    parser.add_argument('-r',
                        action='store_true',
                        help='Output as SystemVerilog RTL')
    parser.add_argument('-s',
                        action='store_true',
                        help='Output as UVM Register class')
    parser.add_argument('-f',
                        action='store_true',
                        help='Output as FPV CSR rw assertion module')
    parser.add_argument('--outdir',
                        '-t',
                        help='Target directory for generated RTL; '
                        'tool uses ../rtl if blank.')
    parser.add_argument('--dv-base-prefix',
                        default='dv_base',
                        help='Prefix for the DV register classes from which '
                        'the register models are derived.')
    parser.add_argument('--outfile',
                        '-o',
                        type=argparse.FileType('w'),
                        default=sys.stdout,
                        help='Target filename for json, html, gfm.')
    parser.add_argument('--verbose',
                        '-v',
                        action='store_true',
                        help='Verbose and run validate twice')
    parser.add_argument('--param',
                        '-p',
                        type=str,
                        default="",
                        help='''Change the Parameter values.
                                Only integer value is supported.
                                You can add multiple param arguments.

                                  Format: ParamA=ValA;ParamB=ValB
                                  ''')
    parser.add_argument('--version',
                        '-V',
                        action='store_true',
                        help='Show version')
    parser.add_argument('--novalidate',
                        action='store_true',
                        help='Skip validate, just output json')

    args = parser.parse_args()

    if args.version:
        version.show_and_exit(__file__, ["Hjson", "Mako"])

    verbose = args.verbose
    if (verbose):
        log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG)
    else:
        log.basicConfig(format="%(levelname)s: %(message)s")

    # Entries are triples of the form (arg, (format, dirspec)).
    #
    # arg is the name of the argument that selects the format. format is the
    # name of the format. dirspec is None if the output is a single file; if
    # the output needs a directory, it is a default path relative to the source
    # file (used when --outdir is not given).
    arg_to_format = [('j', ('json', None)), ('c', ('compact', None)),
                     ('d', ('html', None)), ('doc', ('doc', None)),
                     ('r', ('rtl', 'rtl')), ('s', ('dv', 'dv')),
                     ('f', ('fpv', 'fpv/vip')), ('cdefines', ('cdh', None))]
    format = None
    dirspec = None
    for arg_name, spec in arg_to_format:
        if getattr(args, arg_name):
            if format is not None:
                log.error('Multiple output formats specified on '
                          'command line ({} and {}).'.format(format, spec[0]))
                sys.exit(1)
            format, dirspec = spec
    if format is None:
        format = 'hjson'

    infile = args.input

    # Split parameters into key=value pairs.
    raw_params = args.param.split(';') if args.param else []
    params = []
    for idx, raw_param in enumerate(raw_params):
        tokens = raw_param.split('=')
        if len(tokens) != 2:
            raise ValueError('Entry {} in list of parameter defaults to '
                             'apply is {!r}, which is not of the form '
                             'param=value.'.format(idx, raw_param))
        params.append((tokens[0], tokens[1]))

    # Define either outfile or outdir (but not both), depending on the output
    # format.
    outfile = None
    outdir = None
    if dirspec is None:
        if args.outdir is not None:
            log.error('The {} format expects an output file, '
                      'not an output directory.'.format(format))
            sys.exit(1)

        outfile = args.outfile
    else:
        if args.outfile is not sys.stdout:
            log.error('The {} format expects an output directory, '
                      'not an output file.'.format(format))
            sys.exit(1)

        if args.outdir is not None:
            outdir = args.outdir
        elif infile is not sys.stdin:
            outdir = str(PurePath(infile.name).parents[1].joinpath(dirspec))
        else:
            # We're using sys.stdin, so can't infer an output directory name
            log.error(
                'The {} format writes to an output directory, which '
                'cannot be inferred automatically if the input comes '
                'from stdin. Use --outdir to specify it manually.'.format(
                    format))
            sys.exit(1)

    if format == 'doc':
        with outfile:
            gen_selfdoc.document(outfile)
        exit(0)

    srcfull = infile.read()

    try:
        obj = IpBlock.from_text(srcfull, params, infile.name)
    except ValueError as err:
        log.error(str(err))
        exit(1)

    if args.novalidate:
        with outfile:
            gen_json.gen_json(obj, outfile, format)
            outfile.write('\n')
    else:
        if format == 'rtl':
            return gen_rtl.gen_rtl(obj, outdir)
        if format == 'dv':
            return gen_dv.gen_dv(obj, args.dv_base_prefix, outdir)
        if format == 'fpv':
            return gen_fpv.gen_fpv(obj, outdir)
        src_lic = None
        src_copy = ''
        found_spdx = None
        found_lunder = None
        copy = re.compile(r'.*(copyright.*)|(.*\(c\).*)', re.IGNORECASE)
        spdx = re.compile(r'.*(SPDX-License-Identifier:.+)')
        lunder = re.compile(r'.*(Licensed under.+)', re.IGNORECASE)
        for line in srcfull.splitlines():
            mat = copy.match(line)
            if mat is not None:
                src_copy += mat.group(1)
            mat = spdx.match(line)
            if mat is not None:
                found_spdx = mat.group(1)
            mat = lunder.match(line)
            if mat is not None:
                found_lunder = mat.group(1)
        if found_lunder:
            src_lic = found_lunder
        if found_spdx:
            src_lic += '\n' + found_spdx

        with outfile:
            if format == 'html':
                return gen_html.gen_html(obj, outfile)
            elif format == 'cdh':
                return gen_cheader.gen_cdefines(obj, outfile, src_lic,
                                                src_copy)
            else:
                return gen_json.gen_json(obj, outfile, format)

            outfile.write('\n')
Ejemplo n.º 2
0
def generate_pinmux(top, out_path):

    topname = top['name']
    pinmux = top['pinmux']

    # Generation without pinmux and pinout configuration is not supported.
    assert 'pinmux' in top
    assert 'pinout' in top

    # Get number of wakeup detectors
    if 'num_wkup_detect' in pinmux:
        num_wkup_detect = pinmux['num_wkup_detect']
    else:
        num_wkup_detect = 1

    if num_wkup_detect <= 0:
        # TODO: add support for no wakeup counter case
        log.error('Topgen does currently not support generation of a top ' +
                  'without DIOs.')
        return

    if 'wkup_cnt_width' in pinmux:
        wkup_cnt_width = pinmux['wkup_cnt_width']
    else:
        wkup_cnt_width = 8

    if wkup_cnt_width <= 1:
        log.error('Wakeup counter width must be greater equal 2.')
        return

    # MIO Pads
    n_mio_pads = pinmux['io_counts']['muxed']['pads']

    # Total inputs/outputs
    # Reuse the counts from the merge phase
    n_mio_periph_in = (pinmux['io_counts']['muxed']['inouts'] +
                       pinmux['io_counts']['muxed']['inputs'])
    n_mio_periph_out = (pinmux['io_counts']['muxed']['inouts'] +
                        pinmux['io_counts']['muxed']['outputs'])
    n_dio_periph_in = (pinmux['io_counts']['dedicated']['inouts'] +
                       pinmux['io_counts']['dedicated']['inputs'])
    n_dio_periph_out = (pinmux['io_counts']['dedicated']['inouts'] +
                        pinmux['io_counts']['dedicated']['outputs'])
    n_dio_pads = (pinmux['io_counts']['dedicated']['inouts'] +
                  pinmux['io_counts']['dedicated']['inputs'] +
                  pinmux['io_counts']['dedicated']['outputs'])

    # TODO: derive this value
    attr_dw = 13

    # Generation with zero MIO/DIO pads is currently not supported.
    assert (n_mio_pads > 0)
    assert (n_dio_pads > 0)

    log.info('Generating pinmux with following info from hjson:')
    log.info('attr_dw:         %d' % attr_dw)
    log.info('num_wkup_detect: %d' % num_wkup_detect)
    log.info('wkup_cnt_width:  %d' % wkup_cnt_width)
    log.info('n_mio_periph_in:  %d' % n_mio_periph_in)
    log.info('n_mio_periph_out: %d' % n_mio_periph_out)
    log.info('n_dio_periph_in:  %d' % n_dio_periph_in)
    log.info('n_dio_periph_out: %d' % n_dio_periph_out)
    log.info('n_dio_pads:       %d' % n_dio_pads)

    # Target path
    #   rtl: pinmux_reg_pkg.sv & pinmux_reg_top.sv
    #   data: pinmux.hjson
    rtl_path = out_path / 'ip/pinmux/rtl/autogen'
    rtl_path.mkdir(parents=True, exist_ok=True)
    data_path = out_path / 'ip/pinmux/data/autogen'
    data_path.mkdir(parents=True, exist_ok=True)

    # Template path
    tpl_path = Path(
        __file__).resolve().parent / '../hw/ip/pinmux/data/pinmux.hjson.tpl'

    # Generate register package and RTLs
    gencmd = ("// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson "
              "-o hw/top_{topname}/\n\n".format(topname=topname))

    hjson_gen_path = data_path / "pinmux.hjson"

    out = StringIO()
    with tpl_path.open(mode='r', encoding='UTF-8') as fin:
        hjson_tpl = Template(fin.read())
        try:
            out = hjson_tpl.render(
                n_mio_periph_in=n_mio_periph_in,
                n_mio_periph_out=n_mio_periph_out,
                n_mio_pads=n_mio_pads,
                # each DIO has in, out and oe wires
                # some of these have to be tied off in the
                # top, depending on the type.
                n_dio_periph_in=n_dio_pads,
                n_dio_periph_out=n_dio_pads,
                n_dio_pads=n_dio_pads,
                attr_dw=attr_dw,
                n_wkup_detect=num_wkup_detect,
                wkup_cnt_width=wkup_cnt_width)
        except:  # noqa: E722
            log.error(exceptions.text_error_template().render())
        log.info("PINMUX HJSON: %s" % out)

    if out == "":
        log.error("Cannot generate pinmux HJSON")
        return

    with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout:
        fout.write(genhdr + gencmd + out)

    gen_rtl.gen_rtl(IpBlock.from_text(out, [], str(hjson_gen_path)),
                    str(rtl_path))
Ejemplo n.º 3
0
def generate_plic(top, out_path):
    topname = top["name"]
    # Count number of interrupts
    # Interrupt source 0 is tied to 0 to conform RISC-V PLIC spec.
    # So, total number of interrupts are the number of entries in the list + 1
    src = sum([x["width"] if "width" in x else 1
               for x in top["interrupt"]]) + 1

    # Target and priority: Currently fixed
    target = int(top["num_cores"], 0) if "num_cores" in top else 1
    prio = 3

    # Define target path
    #   rtl: rv_plic.sv & rv_plic_reg_pkg.sv & rv_plic_reg_top.sv
    #   data: rv_plic.hjson
    rtl_path = out_path / 'ip/rv_plic/rtl/autogen'
    rtl_path.mkdir(parents=True, exist_ok=True)
    doc_path = out_path / 'ip/rv_plic/data/autogen'
    doc_path.mkdir(parents=True, exist_ok=True)
    hjson_path = out_path / 'ip/rv_plic/data/autogen'
    hjson_path.mkdir(parents=True, exist_ok=True)

    # Generating IP top module script is not generalized yet.
    # So, topgen reads template files from rv_plic directory directly.
    # Next, if the ip top gen tool is placed in util/ we can import the library.
    tpl_path = Path(__file__).resolve().parent / '../hw/ip/rv_plic/data'
    hjson_tpl_path = tpl_path / 'rv_plic.hjson.tpl'
    rtl_tpl_path = tpl_path / 'rv_plic.sv.tpl'

    # Generate Register Package and RTLs
    out = StringIO()
    with hjson_tpl_path.open(mode='r', encoding='UTF-8') as fin:
        hjson_tpl = Template(fin.read())
        try:
            out = hjson_tpl.render(src=src, target=target, prio=prio)
        except:  # noqa: E722
            log.error(exceptions.text_error_template().render())
        log.info("RV_PLIC hjson: %s" % out)

    if out == "":
        log.error("Cannot generate interrupt controller config file")
        return

    hjson_gen_path = hjson_path / "rv_plic.hjson"
    gencmd = (
        "// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson --plic-only "
        "-o hw/top_{topname}/\n\n".format(topname=topname))
    with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout:
        fout.write(genhdr + gencmd + out)

    # Generate register RTLs (currently using shell execute)
    # TODO: More secure way to generate RTL
    gen_rtl.gen_rtl(IpBlock.from_text(out, [], str(hjson_gen_path)),
                    str(rtl_path))

    # Generate RV_PLIC Top Module
    with rtl_tpl_path.open(mode='r', encoding='UTF-8') as fin:
        rtl_tpl = Template(fin.read())
        try:
            out = rtl_tpl.render(src=src, target=target, prio=prio)
        except:  # noqa: E722
            log.error(exceptions.text_error_template().render())
        log.info("RV_PLIC RTL: %s" % out)

    if out == "":
        log.error("Cannot generate interrupt controller RTL")
        return

    rtl_gen_path = rtl_path / "rv_plic.sv"
    with rtl_gen_path.open(mode='w', encoding='UTF-8') as fout:
        fout.write(genhdr + gencmd + out)
Ejemplo n.º 4
0
def generate_alert_handler(top, out_path):
    # default values
    esc_cnt_dw = 32
    accu_cnt_dw = 16
    async_on = "'0"
    # leave this constant
    n_classes = 4

    topname = top["name"]

    if esc_cnt_dw < 1:
        log.error("EscCntDw must be larger than 0")
    if accu_cnt_dw < 1:
        log.error("AccuCntDw must be larger than 0")

    # Count number of alerts
    n_alerts = sum([x["width"] if "width" in x else 1 for x in top["alert"]])

    if n_alerts < 1:
        # set number of alerts to 1 such that the config is still valid
        # that input will be tied off
        n_alerts = 1
        log.warning("no alerts are defined in the system")
    else:
        async_on = ""
        for alert in top['alert']:
            for k in range(alert['width']):
                async_on = str(alert['async']) + async_on
        # convert to hexstring to shorten line length
        async_on = ("%d'h" % n_alerts) + hex(int(async_on, 2))[2:]

    log.info("alert handler parameterization:")
    log.info("NAlerts   = %d" % n_alerts)
    log.info("EscCntDw  = %d" % esc_cnt_dw)
    log.info("AccuCntDw = %d" % accu_cnt_dw)
    log.info("AsyncOn   = %s" % async_on)

    # Define target path
    rtl_path = out_path / 'ip/alert_handler/rtl/autogen'
    rtl_path.mkdir(parents=True, exist_ok=True)
    doc_path = out_path / 'ip/alert_handler/data/autogen'
    doc_path.mkdir(parents=True, exist_ok=True)

    # Generating IP top module script is not generalized yet.
    # So, topgen reads template files from alert_handler directory directly.
    tpl_path = Path(__file__).resolve().parent / '../hw/ip/alert_handler/data'
    hjson_tpl_path = tpl_path / 'alert_handler.hjson.tpl'

    # Generate Register Package and RTLs
    out = StringIO()
    with hjson_tpl_path.open(mode='r', encoding='UTF-8') as fin:
        hjson_tpl = Template(fin.read())
        try:
            out = hjson_tpl.render(n_alerts=n_alerts,
                                   esc_cnt_dw=esc_cnt_dw,
                                   accu_cnt_dw=accu_cnt_dw,
                                   async_on=async_on,
                                   n_classes=n_classes)
        except:  # noqa: E722
            log.error(exceptions.text_error_template().render())
        log.info("alert_handler hjson: %s" % out)

    if out == "":
        log.error("Cannot generate alert_handler config file")
        return

    hjson_gen_path = doc_path / "alert_handler.hjson"
    gencmd = (
        "// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson --alert-handler-only "
        "-o hw/top_{topname}/\n\n".format(topname=topname))
    with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout:
        fout.write(genhdr + gencmd + out)

    # Generate register RTLs (currently using shell execute)
    # TODO: More secure way to gneerate RTL
    gen_rtl.gen_rtl(IpBlock.from_text(out, [], str(hjson_gen_path)),
                    str(rtl_path))
Ejemplo n.º 5
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
Ejemplo n.º 6
0
def generate_pinmux(top, out_path):
    topname = top["name"]
    # MIO Pads
    n_mio_pads = top["pinmux"]["num_mio"]
    if n_mio_pads <= 0:
        # TODO: add support for no MIO case
        log.error("Topgen does currently not support generation of a top " +
                  "without a pinmux.")
        return

    if "padctrl" not in top:
        # TODO: add support for no MIO case
        log.error("Topgen does currently not support generation of a top " +
                  "without a padctrl instance.")
        return

    # Get number of wakeup detectors
    if "num_wkup_detect" in top["pinmux"]:
        num_wkup_detect = top["pinmux"]["num_wkup_detect"]
    else:
        num_wkup_detect = 1

    if num_wkup_detect <= 0:
        # TODO: add support for no wakeup counter case
        log.error("Topgen does currently not support generation of a top " +
                  "without DIOs.")
        return

    if "wkup_cnt_width" in top["pinmux"]:
        wkup_cnt_width = top["pinmux"]["wkup_cnt_width"]
    else:
        wkup_cnt_width = 8

    if wkup_cnt_width <= 1:
        log.error("Wakeup counter width must be greater equal 2.")
        return

    # Total inputs/outputs
    # Validation ensures that the width field is present.
    num_mio_inputs = sum([x["width"] for x in top["pinmux"]["inputs"]])
    num_mio_outputs = sum([x["width"] for x in top["pinmux"]["outputs"]])

    num_dio_inputs = sum([
        x["width"] if x["type"] == "input" else 0 for x in top["pinmux"]["dio"]
    ])
    num_dio_outputs = sum([
        x["width"] if x["type"] == "output" else 0
        for x in top["pinmux"]["dio"]
    ])
    num_dio_inouts = sum([
        x["width"] if x["type"] == "inout" else 0 for x in top["pinmux"]["dio"]
    ])

    n_mio_periph_in = num_mio_inputs
    n_mio_periph_out = num_mio_outputs
    n_dio_periph_in = num_dio_inouts + num_dio_inputs
    n_dio_periph_out = num_dio_inouts + num_dio_outputs
    n_dio_pads = num_dio_inouts + num_dio_inputs + num_dio_outputs

    # TODO: derive this value
    attr_dw = 10

    if n_dio_pads <= 0:
        # TODO: add support for no DIO case
        log.error("Topgen does currently not support generation of a top " +
                  "without DIOs.")
        return

    # find the start and end pin positions for usbdev
    usb_pin_pos = _calc_dio_pin_pos(top, "usbdev")
    usb_start_pos = usb_pin_pos[-1]
    n_usb_pins = usb_pin_pos[0] - usb_pin_pos[-1] + 1
    usb_dp_sel = _find_dio_pin_pos(top, "usbdev_dp")
    usb_dn_sel = _find_dio_pin_pos(top, "usbdev_dn")
    usb_dp_pull_sel = _find_dio_pin_pos(top, "usbdev_dp_pullup")
    usb_dn_pull_sel = _find_dio_pin_pos(top, "usbdev_dn_pullup")

    log.info("Generating pinmux with following info from hjson:")
    log.info("num_mio_inputs:  %d" % num_mio_inputs)
    log.info("num_mio_outputs: %d" % num_mio_outputs)
    log.info("num_dio_inputs:  %d" % num_dio_inputs)
    log.info("num_dio_outputs: %d" % num_dio_outputs)
    log.info("attr_dw:         %d" % attr_dw)
    log.info("num_wkup_detect: %d" % num_wkup_detect)
    log.info("wkup_cnt_width:  %d" % wkup_cnt_width)
    log.info("This translates to:")
    log.info("n_mio_periph_in:  %d" % n_mio_periph_in)
    log.info("n_mio_periph_out: %d" % n_mio_periph_out)
    log.info("n_dio_periph_in:  %d" % n_dio_periph_in)
    log.info("n_dio_periph_out: %d" % n_dio_periph_out)
    log.info("n_dio_pads:       %d" % n_dio_pads)
    log.info("usb_start_pos:    %d" % usb_start_pos)
    log.info("n_usb_pins:       %d" % n_usb_pins)

    # Target path
    #   rtl: pinmux_reg_pkg.sv & pinmux_reg_top.sv
    #   data: pinmux.hjson
    rtl_path = out_path / 'ip/pinmux/rtl/autogen'
    rtl_path.mkdir(parents=True, exist_ok=True)
    data_path = out_path / 'ip/pinmux/data/autogen'
    data_path.mkdir(parents=True, exist_ok=True)

    # Template path
    tpl_path = Path(
        __file__).resolve().parent / '../hw/ip/pinmux/data/pinmux.hjson.tpl'

    # Generate register package and RTLs
    gencmd = ("// util/topgen.py -t hw/top_{topname}/data/top_{topname}.hjson "
              "-o hw/top_{topname}/\n\n".format(topname=topname))

    hjson_gen_path = data_path / "pinmux.hjson"

    out = StringIO()
    with tpl_path.open(mode='r', encoding='UTF-8') as fin:
        hjson_tpl = Template(fin.read())
        try:
            # TODO: pass in information about always-on peripherals
            # TODO: pass in information on which DIOs can be selected
            # as wakeup signals
            # TODO: pass in signal names such that we can introduce
            # named enums for select signals
            out = hjson_tpl.render(
                n_mio_periph_in=n_mio_periph_in,
                n_mio_periph_out=n_mio_periph_out,
                n_mio_pads=n_mio_pads,
                # each DIO has in, out and oe wires
                # some of these have to be tied off in the
                # top, depending on the type.
                n_dio_periph_in=n_dio_pads,
                n_dio_periph_out=n_dio_pads,
                n_dio_pads=n_dio_pads,
                attr_dw=attr_dw,
                n_wkup_detect=num_wkup_detect,
                wkup_cnt_width=wkup_cnt_width,
                usb_start_pos=usb_start_pos,
                n_usb_pins=n_usb_pins,
                usb_dp_sel=usb_dp_sel,
                usb_dn_sel=usb_dn_sel,
                usb_dp_pull_sel=usb_dp_pull_sel,
                usb_dn_pull_sel=usb_dn_pull_sel
            )
        except:  # noqa: E722
            log.error(exceptions.text_error_template().render())
        log.info("PINMUX HJSON: %s" % out)

    if out == "":
        log.error("Cannot generate pinmux HJSON")
        return

    with hjson_gen_path.open(mode='w', encoding='UTF-8') as fout:
        fout.write(genhdr + gencmd + out)

    gen_rtl.gen_rtl(IpBlock.from_text(out, [], str(hjson_gen_path)),
                    str(rtl_path))