def main(args):
    args = parser.parse_args(args)

    iname = os.path.basename(args.pb_type)
    outfile = "{}.arch.xml".format(iname)
    if args.output is not None:
        outfile = args.output
    outfile = os.path.abspath(outfile)

    pbtype_xml = ET.parse(args.pb_type)
    pbtype_xml.xinclude()

    assert os.path.exists(TEMPLATE_PATH), TEMPLATE_PATH
    arch_tree = ET.parse(TEMPLATE_PATH)
    arch_root = arch_tree.getroot()

    pbtype_root = pbtype_xml.getroot()
    pbtype_leaf = find_leaf(pbtype_xml.getroot())
    assert pbtype_leaf is not None, "Unable to find leaf <pb_type> tag in {}".format(
        args.pb_type)

    tile_height = layout_xml(arch_root, pbtype_leaf)
    tname = tile_xml(arch_root, pbtype_root, outfile, tile_height)

    dirpath = os.path.dirname(outfile)
    models = arch_root.find("models")
    xmlinc.include_xml(
        models,
        os.path.join(dirpath, "{}.model.xml".format(tname.lower())),
        outfile,
        xptr="xpointer(models/child::node())",
    )

    with open(outfile, 'w') as f:
        f.write(pretty_xml(arch_tree, xmllint=args.xmllint))

    return 0
def tile_xml(arch_xml: ET.Element, pbtype_xml: ET.Element, outfile: str,
             tile_height: int):
    """Generate a top level pb_type containing given pb_type.

    Modifies the giving architecture XML in place.

    Returns
    -------
    str
        The name of the top level pb_type (the tile).
    """
    name, clocks, inputs, outputs, carry = ports(pbtype_xml)
    assert name != "TILE", "name ({}) must not be TILE".format(name)

    cbl = arch_xml.find("complexblocklist")
    tile = ET.SubElement(
        cbl,
        "pb_type",
        {
            "name": "TILE",
            "width": "1",
            "height": str(tile_height)
        },
    )
    dirpath = os.path.dirname(outfile)
    xmlinc.include_xml(
        tile,
        os.path.join(dirpath, "{}.pb_type.xml".format(name.lower())),
        outfile,
    )

    # Pin locations
    ploc = ET.SubElement(
        tile,
        "pinlocations",
        {"pattern": "custom"},
    )
    ilocs = []
    olocs = []
    for i in range(0, tile_height):
        ilocs.append(
            ET.SubElement(
                ploc,
                "loc",
                {
                    "side": "left",
                    "xoffset": "0",
                    "yoffset": str(i)
                },
            ))
        ilocs[i].text = ""
        olocs.append(
            ET.SubElement(
                ploc,
                "loc",
                {
                    "side": "right",
                    "xoffset": "0",
                    "yoffset": str(i)
                },
            ))
        olocs[i].text = ""

    # Interconnect
    connect = ET.SubElement(
        tile,
        "interconnect",
    )

    # Clock pins
    for d, s in flatten(clocks):
        ET.SubElement(
            tile,
            "clock",
            {
                "name": s,
                "num_pins": "1",
                "equivalent": "none"
            },
        )
        ET.SubElement(
            connect,
            "direct",
            {
                "input": "TILE.{}".format(s),
                "name": "TILE.{}-{}.{}".format(s, name, d),
                "output": "{}.{}".format(name, d),
            },
        )

        for i in range(0, tile_height):
            ilocs[i].text += "TILE.{} ".format(s)

    # Input Pins
    for d, s in flatten(inputs):
        ET.SubElement(
            tile,
            "input",
            {
                "name": s,
                "num_pins": "1",
                "equivalent": "none"
            },
        )
        ET.SubElement(
            connect,
            "direct",
            {
                "input": "TILE.{}".format(s),
                "name": "TILE.{}-{}.{}".format(s, name, d),
                "output": "{}.{}".format(name, d),
            },
        )

        for i in range(0, tile_height):
            ilocs[i].text += "TILE.{} ".format(s)

    # Output Pins
    for s, d in flatten(outputs):
        ET.SubElement(
            tile,
            "output",
            {
                "name": d,
                "num_pins": "1",
                "equivalent": "none"
            },
        )
        ET.SubElement(
            connect,
            "direct",
            {
                "input": "{}.{}".format(name, s),
                "name": "TILE.{}-{}.{}".format(s, name, d),
                "output": "TILE.{}".format(d),
            },
        )
        for i in range(0, tile_height):
            olocs[i].text += "TILE.{} ".format(d)

    return name
Beispiel #3
0
        abs_dep = os.path.normpath(os.path.join(abs_base, df))
        module_path = os.path.dirname(abs_dep)
        module_basename = os.path.basename(abs_dep)
        wm = re.match(r"([A-Za-z0-9_]+)\.sim\.v", module_basename)
        if wm:
            model_path = "{}/{}.model.xml".format(
                module_path,
                wm.group(1).lower()
            )
        else:
            assert False, "included Verilog file name {} does not follow pattern %%.sim.v".format(
                module_basename
            )
        xmlinc.include_xml(
            parent=models_xml,
            href=model_path,
            outfile=outfile,
            xptr="xpointer(models/child::node())"
        )
else:
    # Is a leaf model
    topname = tmod.attr("MODEL_NAME", top)
    assert topname == topname.upper(
    ), "Leaf model names should be all uppercase!"
    modclass = tmod.attr("CLASS", "")

    if modclass not in ("lut", "routing", "flipflop"):
        model_xml = ET.SubElement(models_xml, "model", {'name': topname})
        ports = tmod.ports

        inports_xml = ET.SubElement(model_xml, "input_ports")
        outports_xml = ET.SubElement(model_xml, "output_ports")
Beispiel #4
0
def make_pb_content(yj, mod, xml_parent, mod_pname, is_submode=False):
    """Build the pb_type content - child pb_types, timing and direct interconnect,
    but not IO. This may be put directly inside <pb_type>, or inside <mode>."""
    def get_module_name(pin, instance=INVALID_INSTANCE):
        """Returns the name of the module relative to the pin and a boolean that indicates whether
        the module is a cell (True) or the top one (False)"""
        if instance <= INVALID_INSTANCE:
            instance = ""
        else:
            instance = "[" + str(instance) + "]"
        cname, cellpin = pin
        if cname.startswith("$"):
            return mod.cell_type(cname) + instance, True
        elif cname != mod.name:
            cname = mod.cell_type(cname)
            modname = mod_pb_name(yj.module(cname)) + instance
            return modname, True
        else:
            return mod_pname, False

    def get_cellpin(pin):
        cname, cellpin = pin
        return cellpin

    def create_port(pin_name, mod_name, is_cell, direction):
        """Returns a dictionary containing the port definition. If the module is a cell, the port
        contains the 'from' attribute."""
        port = dict()
        port['name'] = pin_name
        port['type'] = direction

        if is_cell:
            port['from'] = mod_name

        return port

    def make_direct_conn(ic_xml,
                         src,
                         dst,
                         source_instance=INVALID_INSTANCE,
                         dest_instance=INVALID_INSTANCE):
        s_cellpin = get_cellpin(src)
        d_cellpin = get_cellpin(dst)
        s_cname, s_is_cell = get_module_name(src, source_instance)
        d_cname, d_is_cell = get_module_name(dst, dest_instance)

        s_port = create_port(s_cellpin, s_cname, s_is_cell, "input")
        d_port = create_port(d_cellpin, d_cname, d_is_cell, "output")

        dir_xml = ET.SubElement(ic_xml, 'direct')

        s_port_xml = ET.SubElement(dir_xml, 'port', s_port)
        d_port_xml = ET.SubElement(dir_xml, 'port', d_port)

    # Find out whether or not the module we are generating content for is a blackbox
    is_blackbox = is_mod_blackbox(mod) or not mod.cells

    # List of entries in format ((from_cell, from_pin), (to_cell, to_pin))
    interconn = []

    # Determine multiple instances of the same cell:
    cells = dict()
    for cname, i_of in mod.cells:
        if i_of in cells:
            cells[i_of]['count'] += 1
            cells[i_of]['is_multi_instance'] = True
            # assign unique instance number
            cells[i_of][cname] = cells[i_of]['count']
        else:
            cells[i_of] = dict()
            cells[i_of]['is_multi_instance'] = False
            cells[i_of]['count'] = 0
            cells[i_of][cname] = 0

    # Blackbox modules don't have inner cells or interconnect (but do still have timing)
    if (not is_blackbox) or is_submode:
        # Process cells. First build the list of cnames.
        processed_cells = list()
        for cname, i_of in mod.cells:
            pb_name = strip_name(i_of)
            pbtype_already_included = False
            if i_of in processed_cells:
                # do not emit xml include for every instance of multi instace cell
                pbtype_already_included = True
            else:
                processed_cells.append(i_of)
            instance = INVALID_INSTANCE

            # If currently considered cell is a multi instance one, pass it's unique
            # instance number to connection creator. If not, pass INVALID_INSTANCE
            # constant
            if cells[i_of]['is_multi_instance']:
                instance = cells[i_of][cname]
            module_file = yj.get_module_file(i_of)
            module_path = os.path.dirname(module_file)
            module_basename = os.path.basename(module_file)

            # Heuristic for autogenerated files from w.py
            if not pbtype_already_included:
                wm = re.match(r"([A-Za-z0-9_]+)\.sim\.v", module_basename)
                if wm:
                    pb_type_path = "{}/{}.pb_type.xml".format(
                        module_path,
                        wm.group(1).lower())
                else:
                    pb_type_path = "{}/pb_type.xml".format(module_path)

                # inlude contents of the included pb_type, but update it's
                # num_pb value
                with open(pb_type_path, 'r') as inc_xml:
                    xml_inc = ET.fromstring(inc_xml.read().encode('utf-8'))
                    inc_attrib = xml_inc.attrib
                    inc_attrib['num_pb'] = str(cells[i_of]['count'] + 1)

                inc_pb_type = ET.SubElement(xml_parent, 'pb_type', inc_attrib)
                xmlinc.include_xml(parent=inc_pb_type,
                                   href=pb_type_path,
                                   outfile=outfile,
                                   xptr="xpointer(pb_type/child::node())")

            # In order to avoid overspecifying interconnect, there are two directions we currently
            # consider. All interconnect going INTO a cell, and interconnect going out of a cell
            # into a top level output - or all outputs if "mode" is used.
            inp_cons = mod.cell_conns(cname, "input")
            for pin, net in inp_cons:
                drvs = mod.net_drivers(net)
                assert len(drvs) > 0, (
                    "ERROR: pin {}.{} has no driver, interconnect will be missing\n{}"
                    .format(pb_name, pin, mod))
                assert len(drvs) < 2, (
                    "ERROR: pin {}.{} has multiple drivers, interconnect will be overspecified"
                    .format(pb_name, pin))
                for drv_cell, drv_pin in drvs:
                    print(pin, net, drv_cell, drv_pin)
                    # check if we're driven by multi instance cell
                    drive_instance = INVALID_INSTANCE
                    drv_cell_type = [
                        c[1] for c in mod.cells if c[0] == drv_cell
                    ]
                    if len(drv_cell_type) != 0:
                        drv_cell_type = drv_cell_type[0]
                        if cells[drv_cell_type]['is_multi_instance']:
                            # get drv_cell unique instance number
                            drive_instance = cells[drv_cell_type][drv_cell]
                    interconn.append(((drv_cell, drv_pin), (cname, pin),
                                      drive_instance, instance))

            out_cons = mod.cell_conns(cname, "output")
            for pin, net in out_cons:
                sinks = mod.net_sinks(net)
                for sink_cell, sink_pin in sinks:
                    if sink_cell != mod.name:
                        continue
                    # Only consider outputs from cell to top level IO. Inputs to other cells will be dealt with
                    # in those cells.
                    interconn.append(((cname, pin), (sink_cell, sink_pin),
                                      instance, INVALID_INSTANCE))

        # Direct pin->pin connections
        for net in mod.nets:
            drv = mod.conn_io(net, "input")
            if not drv:
                continue
            assert len(drv) == 1, (
                "ERROR: net {} has multiple drivers {}, interconnect will be over specified"
                .format(net, drv))
            for snk in mod.conn_io(net, "output"):
                conn = ((mod.name, drv[0]), (mod.name, snk))
                interconn.append(conn)

        ic_xml = ET.SubElement(xml_parent, "interconnect")
        # Process interconnect
        for source, dest, src_instance, dst_instance in interconn:
            make_direct_conn(ic_xml, source, dest, src_instance, dst_instance)

    def process_clocked_tmg(tmgspec, port, xmltype, xml_parent):
        """Add a suitable timing spec if necessary to the pb_type"""
        if tmgspec is not None:
            splitspec = tmgspec.split(" ")
            assert len(
                splitspec
            ) == 2, 'bad timing specification "{}", must be of format "clock value"'.format(
                tmgspec)
            attrs = {"port": port, "clock": splitspec[0]}
            if xmltype == "T_clock_to_Q":
                attrs["max"] = splitspec[1]
            else:
                attrs["value"] = splitspec[1]
            ET.SubElement(xml_parent, xmltype, attrs)

    # Process timing
    for name, width, bits, iodir in mod.ports:
        port = "{}".format(name)
        # Clocked timing
        Tsetup = mod.net_attr(name, "SETUP")
        Thold = mod.net_attr(name, "HOLD")
        Tctoq = mod.net_attr(name, "CLK_TO_Q")
        process_clocked_tmg(Tsetup, port, "T_setup", xml_parent)
        process_clocked_tmg(Thold, port, "T_hold", xml_parent)
        process_clocked_tmg(Tctoq, port, "T_clock_to_Q", xml_parent)

        # Combinational delays
        dly_prefix = "DELAY_CONST_"
        dly_mat_prefix = "DELAY_MATRIX_"
        for attr, atvalue in sorted(mod.net_attrs(name).items()):
            if attr.startswith(dly_prefix):
                # Single, constant delays
                inp = attr[len(dly_prefix):]
                inport = "{}".format(inp)
                ET.SubElement(xml_parent, "delay_constant", {
                    "in_port": inport,
                    "out_port": port,
                    "max": str(atvalue)
                })
            elif attr.startswith(dly_mat_prefix):
                # Constant delay matrices
                inp = attr[len(dly_mat_prefix):]
                inport = "{}".format(inp)
                mat = "\n" + atvalue.replace(";", "\n") + "\n"
                xml_mat = ET.SubElement(xml_parent, "delay_matrix", {
                    "in_port": inport,
                    "out_port": port,
                    "type": "max"
                })
                xml_mat.text = mat
Beispiel #5
0
def make_container_pb(
        outfile, yj, mod, mod_pname, pb_type_xml, routing, children
):
    # Containers have to include children
    # ------------------------------------------------------------
    for child_prefix, (child_type, children_names) in children.items():
        # Work out were the child pb_type file can be found
        module_file = yj.get_module_file(child_type)
        module_path = os.path.dirname(module_file)
        module_basename = os.path.basename(module_file)
        module_prefix = re.match(r"([A-Za-z0-9_]+)\.sim\.v",
                                 module_basename).groups()[0]

        pb_type_path = "{}/{}.pb_type.xml".format(module_path, module_prefix)

        include_as_is = True
        comment_str = ""
        # Read the top level properties of the pb_type
        with open(pb_type_path, 'r') as inc_xml:
            xml_inc = ET.fromstring(inc_xml.read().encode('utf-8'))
            inc_attrib = xml_inc.attrib
            normalized_name = normalize_pb_name(child_prefix)
            num_pb = str(len(children_names))
            if normalized_name != inc_attrib['name']:
                comment_str += "old_name {}".format(inc_attrib['name'])
                inc_attrib['name'] = normalize_pb_name(child_prefix)
                include_as_is = False
            if num_pb != inc_attrib['num_pb']:
                comment_str += " old_num_pb {}".format(inc_attrib['num_pb'])
                inc_attrib['num_pb'] = str(len(children_names))
                include_as_is = False

        xptr = None
        parent_xml = pb_type_xml
        if include_as_is is not True:
            xptr = "xpointer(pb_type/child::node())"
            parent_xml = ET.SubElement(pb_type_xml, 'pb_type', inc_attrib)
            parent_xml.append(ET.Comment(comment_str))

        xmlinc.include_xml(
            parent=parent_xml, href=pb_type_path, outfile=outfile, xptr=xptr
        )

    # Contains need interconnect to their children
    # ------------------------------------------------------------
    # Work out valid names for cells to sanity check the interconnects.
    valid_names = [mod_pname]

    routing_cells = []
    for _, routing_names in routing.values():
        routing_cells.extend(routing_names)
    valid_names.extend(routing_cells)

    for _, children_names in children.values():
        valid_names.extend(children_names)

    # Extract the interconnect from the module
    interconn = get_interconnects(yj, mod, mod_pname, valid_names)
    import pprint
    print(mod_pname)
    print("--")
    pprint.pprint(interconn)
    print("--")
    print(routing_cells)
    pprint.pprint(routing)
    print("--")

    # Generate the actual interconnect
    ic_xml = ET.SubElement(pb_type_xml, "interconnect")
    for (driver_cell, driver_pin), sinks in interconn.items():
        if driver_cell in routing_cells:
            continue
        for (sink_cell, sink_pin), path_attr in sinks:
            if sink_cell in routing_cells:
                continue
            make_direct_conn(
                ic_xml, (normalize_pb_name(driver_cell), driver_pin),
                (normalize_pb_name(sink_cell), sink_pin), path_attr
            )

    # Generate the mux interconnects
    for mux_cell in routing_cells:
        mux_outputs = defaultdict(list)
        for (driver_cell, driver_pin), sinks in interconn.items():
            if driver_cell != mux_cell:
                continue
            mux_outputs[driver_pin].extend(sinks)

        assert len(mux_outputs) == 1, """\
Mux {} has multiple outputs ({})!
Currently muxes can only drive a single output.""".format(
            mux_cell, ", ".join(mux_outputs.keys())
        )
        for mux_output_pin, sinks in mux_outputs.items():
            assert len(sinks) == 1, """\
Mux {}.{} has multiple outputs ({})!
Currently muxes can only drive a single output.""".format(
                mux_cell, mux_output_pin,
                ", ".join("{}.{}".format(*pin) for pin, path_attr in sinks)
            )
            for (sink_cell, sink_pin), path_attr in sinks:
                assert sink_cell not in routing_names, """\
Mux {}.{} is trying to drive mux input pin {}.{}""".format(
                    mux_cell, mux_output_pin, sink_cell, sink_pin
                )

        mux_inputs = {}
        for (driver_cell, driver_pin), sinks in interconn.items():
            for (sink_cell, mux_pin), path_attr in sinks:
                if sink_cell != mux_cell:
                    continue
                assert driver_cell not in routing_names, \
                    "Mux {}.{} is trying to drive mux {}.{}".format(
                        driver_cell, driver_pin, mux_cell, sink_pin
                    )
                assert sink_pin not in mux_inputs, """\
Pin {}.{} is trying to drive mux pin {}.{} (already driving by {}.{})""".format(
                    driver_cell, driver_pin, mux_cell, mux_pin,
                    *mux_inputs[sink_pin]
                )
                mux_inputs[mux_pin] = (driver_cell, driver_pin)

        make_mux_conn(ic_xml, mux_cell, mux_inputs, mux_outputs)