Exemple #1
0
def load_other_cells(xml_placement, cellgrid, cells_library):

    # Loop over XML entries
    for xml in xml_placement:

        # Got a "Cell" tag
        if xml.tag == "Cell":
            cell_name = xml.get("name")
            cell_type = xml.get("type")

            assert cell_type in cells_library, (
                cell_type,
                cell_name,
            )

            # Cell matrix
            xml_matrices = [x for x in xml if x.tag.startswith("Matrix")]
            for xml_matrix in xml_matrices:
                x0 = int(xml_matrix.get("START_COLUMN"))
                nx = int(xml_matrix.get("COLUMNS"))
                y0 = int(xml_matrix.get("START_ROW"))
                ny = int(xml_matrix.get("ROWS"))

                for j in range(ny):
                    for i in range(nx):
                        loc = Loc(x0 + i, y0 + j, 0)

                        cellgrid[loc].append(
                            Cell(
                                type=cell_type,
                                index=None,
                                name=cell_name,
                                alias=None,
                            ))

            # A single cell
            if len(xml_matrices) == 0:
                x = int(xml.get("column"))
                y = int(xml.get("row"))

                loc = Loc(x, y, 0)
                alias = xml.get("Alias", None)

                cellgrid[loc].append(
                    Cell(
                        type=cell_type,
                        index=None,
                        name=cell_name,
                        alias=alias,
                    ))

        # Got something else, parse recursively
        else:
            load_other_cells(xml, cellgrid, cells_library)
Exemple #2
0
    def parse_fasm_lines(self, fasmlines):
        '''Parses FASM lines.

        Parameters
        ----------
        fasmlines: list
            A list of FasmLine objects
        '''

        loctyp = re.compile(
            r'^X(?P<x>[0-9]+)Y(?P<y>[0-9]+)\.(?P<type>[A-Z]+[0-4]?)\.(?P<signature>.*)$'
        )  # noqa: E501

        for line in fasmlines:
            if not line.set_feature:
                continue
            match = loctyp.match(line.set_feature.feature)
            if not match:
                raise self.Fasm2BelsException(
                    f'FASM features have unsupported format:  {line.set_feature}'
                )  # noqa: E501
            loc = Loc(x=int(match.group('x')), y=int(match.group('y')), z=0)
            typ = match.group('type')
            feature = Feature(loc=loc,
                              typ=typ,
                              signature=match.group('signature'),
                              value=line.set_feature.value)
            self.featureparsers[typ](feature)
Exemple #3
0
def create_column_clock_tracks(graph, clock_cells, quadrants):
    """
    This function adds tracks for clock column routes. It returns a map of
    "assess points" to that tracks to be used by switchbox connections.
    """

    CAND_RE = re.compile(
        r"^(?P<name>CAND[0-4])_(?P<quad>[A-Z]+)_(?P<col>[0-9]+)$")

    # Get segment id and switch id
    segment_id = graph.get_segment_id_from_name("clock")
    switch_id = graph.get_delayless_switch_id()

    # Process CAND cells
    cand_node_map = {}

    for cell in clock_cells.values():

        # A clock column is defined by a CAND cell
        if cell.type != "CAND":
            continue

        # Get index and quadrant
        match = CAND_RE.match(cell.name)
        if not match:
            continue

        cand_name = match.group("name")
        cand_quad = match.group("quad")

        quadrant = quadrants[cand_quad]

        # Add track chains going upwards and downwards from the CAND cell
        up_entry_node, _, up_node_map = add_track_chain(
            graph, "Y", cell.loc.x, cell.loc.y, quadrant.y0, segment_id,
            switch_id)
        dn_entry_node, _, dn_node_map = add_track_chain(
            graph, "Y", cell.loc.x, cell.loc.y + 1, quadrant.y1, segment_id,
            switch_id)

        # Connect entry nodes
        cand_entry_node = up_entry_node
        add_edge(graph, cand_entry_node.id, dn_entry_node.id, switch_id)

        # Join node maps
        node_map = {**up_node_map, **dn_node_map}

        # Populate the global clock network to switchbox access map
        for y, node in node_map.items():
            loc = Loc(x=cell.loc.x, y=y, z=0)

            if cand_name not in cand_node_map:
                cand_node_map[cand_name] = {}

            cand_node_map[cand_name][loc] = node

    return cand_node_map
Exemple #4
0
def fixup_cand_loc(vpr_loc, phy_loc):
    """
    Fixes up location of a CAND cell so that all of them occupy the same row.
    Returns the cell location in VPR coordinates
    """

    # Even, don't modify
    if not (phy_loc.y % 2):
        return vpr_loc

    # Odd, shift down by 1
    return Loc(vpr_loc.x, vpr_loc.y + 1, vpr_loc.z)
def node_joint_location(node_a, node_b):
    """
    Given two VPR nodes returns a location of the point where they touch each
    other.
    """

    loc_a1 = Loc(node_a.loc.x_low, node_a.loc.y_low, 0)
    loc_a2 = Loc(node_a.loc.x_high, node_a.loc.y_high, 0)

    loc_b1 = Loc(node_b.loc.x_low, node_b.loc.y_low, 0)
    loc_b2 = Loc(node_b.loc.x_high, node_b.loc.y_high, 0)

    if loc_a1 == loc_b1:
        return loc_a1
    if loc_a1 == loc_b2:
        return loc_a1
    if loc_a2 == loc_b1:
        return loc_a2
    if loc_a2 == loc_b2:
        return loc_a2

    assert False, (node_a, node_b)
Exemple #6
0
    def get_qmux_for_cand(self, cand, loc):
        ''' Returns a QMUX cell and its location that drives the given CAND
        cell.

        Parameters
        ----------
        cand: str
            The CAND cell name
        loc: Loc
            The CAND location

        Returns
        -------
        Tuple: A tuple holding (loc, cell)
        '''

        connections = [
            c for c in self.connections if c.dst.type == ConnectionType.CLOCK
        ]
        for connection in connections:

            # Only to a CAND at the given location
            # Note: Check also the row above. CAND cells are located in two
            # rows but with fasm features everything gets aligned to even rows
            dst = connection.dst
            if (dst.loc != loc and dst.loc != Loc(loc.x, loc.y - 1, loc.z)) or \
               "CAND" not in dst.pin:
                continue

            # CAND cells are named "CAND<index>_<quad>_<column>".
            cell, pin = dst.pin.split(".", maxsplit=1)
            match = re.match(
                r"CAND(?P<idx>[0-9]+)_(?P<quad>[A-Z]+)_(?P<col>[0-9]+)", cell)
            if match is None:
                continue

            # This is not for the given CAND
            if cand != "CAND{}".format(match.group("idx")):
                continue

            # QMUX cells are named "QMUX_<quad><index>".
            cell, pin = connection.src.pin.split(".", maxsplit=1)
            match = re.match(r"QMUX_(?P<quad>[A-Z]+)(?P<idx>[0-9]+)", cell)
            if match is None:
                continue

            # Return the QMUX and its location
            return "QMUX{}".format(match.group("idx")), connection.src.loc

        # None found
        return None, None
Exemple #7
0
def load_logic_cells(xml_placement, cellgrid, cells_library):

    # Load "LOGIC" tiles
    xml_logic = xml_placement.find("LOGIC")
    assert xml_logic is not None

    exceptions = set()
    xml_exceptions = xml_logic.find("EXCEPTIONS")
    if xml_exceptions is not None:
        for xml in xml_exceptions:
            tag = xml.tag.upper()

            # FIXME: Is this connect decoding of those werid loc specs?
            x = 1 + ord(tag[0]) - ord("A")
            y = 1 + int(tag[1:])

            exceptions.add(Loc(x=x, y=y, z=0))

    xml_logicmatrix = xml_logic.find("LOGICMATRIX")
    assert xml_logicmatrix is not None

    x0 = int(xml_logicmatrix.get("START_COLUMN"))
    nx = int(xml_logicmatrix.get("COLUMNS"))
    y0 = int(xml_logicmatrix.get("START_ROW"))
    ny = int(xml_logicmatrix.get("ROWS"))

    for j in range(ny):
        for i in range(nx):
            loc = Loc(x0 + i, y0 + j, 0)

            if loc in exceptions:
                continue

            cell_type = "LOGIC"
            assert cell_type in cells_library, cell_type

            cellgrid[loc].append(
                Cell(type=cell_type, index=None, name=cell_type, alias=None))
Exemple #8
0
    def resolve_cand(self, qmux_map):
        '''Resolves CAND cells, creates the cand_map map.

        Parameters
        ----------
        qmux_map: Dict
            A map of locations and CAND names to their driving QMUXes.

        Returns
        -------
        None
        '''

        # Process CAND
        for loc, all_features in self.colclk_data.items():
            for cand, features in all_features.items():

                hilojoint = False
                enjoint = False

                for feature in features:
                    if feature.signature == "I_hilojoint":
                        hilojoint = bool(feature.value)
                    if feature.signature == "I_enjoint":
                        enjoint = bool(feature.value)

                # TODO: Do not support dynamically enabled CANDs for now.
                assert enjoint is False, "Dynamically enabled CANDs are not supported yet"

                # Statically disabled, skip this one
                if hilojoint is False:
                    continue

                # Find a QMUX driving this CAND cell
                qmux_cell, qmux_loc = self.get_qmux_for_cand(cand, loc)
                assert qmux_cell is not None, (cand, loc)

                # The QMUX is not active, skip this one
                if qmux_loc not in qmux_map:
                    continue

                # Get the wire
                wire = qmux_map[qmux_loc][qmux_cell]

                # Populate the column clock to switchbox connection map
                quadrant = get_quadrant_for_loc(loc, self.quadrants)
                for y in range(quadrant.y0, quadrant.y1 + 1):
                    sb_loc = Loc(loc.x, y, 0)
                    self.cand_map[sb_loc][cand] = wire
Exemple #9
0
def populate_switchboxes(xml_sbox, switchbox_grid):
    """
    Assings each tile in the grid its switchbox type.
    """
    xmin = int(xml_sbox.attrib["ColStartNum"])
    xmax = int(xml_sbox.attrib["ColEndNum"])
    ymin = int(xml_sbox.attrib["RowStartNum"])
    ymax = int(xml_sbox.attrib["RowEndNum"])

    for y, x in itertools.product(range(ymin, ymax + 1), range(xmin,
                                                               xmax + 1)):
        loc = Loc(x, y, 0)

        assert loc not in switchbox_grid, loc
        switchbox_grid[loc] = xml_sbox.tag
Exemple #10
0
    def yield_locs_and_maps():
        """
        Yields locations and wire mappings associated with it.
        """
        RE_LOC = re.compile(r"^(Row|Col)_([0-9]+)_([0-9]+)$")

        # Rows
        xml_rows = [e for e in xml_root if e.tag.startswith("Row_")]
        for xml_row in xml_rows:

            # Decode row range
            match = RE_LOC.match(xml_row.tag)
            assert match is not None, xml_row.tag

            row_beg = int(xml_row.attrib["RowStartNum"])
            row_end = int(xml_row.attrib["RowEndNum"])

            assert row_beg == int(match.group(2)), \
                (xml_row.tag, row_beg, row_end)
            assert row_end == int(match.group(3)), \
                (xml_row.tag, row_beg, row_end)

            # Columns
            xml_cols = [e for e in xml_row if e.tag.startswith("Col_")]
            for xml_col in xml_cols:

                # Decode column range
                match = RE_LOC.match(xml_col.tag)
                assert match is not None, xml_col.tag

                col_beg = int(xml_col.attrib["ColStartNum"])
                col_end = int(xml_col.attrib["ColEndNum"])

                assert col_beg == int(match.group(2)), \
                    (xml_col.tag, col_beg, col_end)
                assert col_end == int(match.group(3)), \
                    (xml_col.tag, col_beg, col_end)

                # Wire maps
                xml_maps = [e for e in xml_col if e.tag.startswith("Stage_")]

                # Yield wire maps for each location
                for y in range(row_beg, row_end + 1):
                    for x in range(col_beg, col_end + 1):
                        yield (Loc(x=x, y=y, z=0), xml_maps)
Exemple #11
0
def process_switchbox_grid(
        phy_switchbox_grid, loc_map, grid_offset, grid_limit=None
):
    """
    Processes the switchbox grid
    """

    fwd_loc_map = loc_map.fwd
    bwd_loc_map = loc_map.bwd

    def add_loc_map(phy_loc, vpr_loc):

        if phy_loc in fwd_loc_map:
            assert fwd_loc_map[phy_loc] == vpr_loc, (phy_loc, vpr_loc)
        else:
            fwd_loc_map[phy_loc] = vpr_loc

        if vpr_loc in bwd_loc_map:
            assert bwd_loc_map[vpr_loc] == phy_loc, (phy_loc, vpr_loc)
        else:
            bwd_loc_map[vpr_loc] = phy_loc

    # Remap locations
    vpr_switchbox_grid = {}
    for phy_loc, switchbox_type in phy_switchbox_grid.items():

        # Limit the grid range
        if not is_loc_within_limit(phy_loc, grid_limit):
            continue

        # compute VPR grid location
        vpr_loc = Loc(
            x=phy_loc.x + grid_offset[0], y=phy_loc.y + grid_offset[1], z=0
        )

        # Place the switchbox
        vpr_switchbox_grid[vpr_loc] = switchbox_type

        # Add location correspondence
        add_loc_map(phy_loc, vpr_loc)

    return vpr_switchbox_grid, LocMap(fwd=fwd_loc_map, bwd=bwd_loc_map),
Exemple #12
0
    def resolve_hops(self):
        '''Resolves remaining hop wires.

        It determines the absolute input for the given pin by resolving hop
        wires and adds those final connections to the design connections.
        '''
        for loc, conns in self.designconnections.items():
            for pin, source in conns.items():
                hop = get_name_and_hop(source)
                tloc = loc
                while hop[1] is not None:
                    tloc = Loc(tloc[0] + hop[1][0], tloc[1] + hop[1][1], 0)
                    # in some cases BEL is distanced from a switchbox, in those
                    # cases the hop will not point to another hop. We should
                    # simply return the pin here in the correct location
                    if hop[0] in self.designhops[tloc]:
                        hop = get_name_and_hop(self.designhops[tloc][hop[0]])
                    else:
                        hop = (hop[0], None)
                self.designconnections[loc][pin] = (tloc, hop[0])
Exemple #13
0
    def process_switchbox(self, loc, switchbox, features):
        '''Processes all switchboxes and extract hops from connections.

        The function extracts final connections from inputs to outputs, and
        hops into separate structures for further processing.

        Parameters
        ----------
        loc: Loc
            location of the current switchbox
        switchbox: Switchbox
            a switchbox
        features: list
            list of features regarding given switchbox
        '''
        routes = self.decode_switchbox(switchbox, features)
        for k, v in routes.items():
            if v is not None:
                if re.match('[VH][0-9][LRBT][0-9]', k):
                    self.designhops[Loc(loc.x, loc.y, 0)][k] = v
                else:
                    self.designconnections[loc][k] = v
Exemple #14
0
    def parse_cell(xml_cell, quadrant=None):
        """
        Parses a "Cell" tag inside "CLOCK_NETWORK"
        """
        NON_PIN_TAGS = ("name", "type", "row", "column")

        cell_loc = Loc(x=int(xml_cell.attrib["column"]),
                       y=int(xml_cell.attrib["row"]),
                       z=0)

        # Get the cell's pinmap
        pin_map = {
            k: v
            for k, v in xml_cell.attrib.items() if k not in NON_PIN_TAGS
        }

        # Return the cell
        return ClockCell(type=xml_cell.attrib["type"],
                         name=xml_cell.attrib["name"],
                         loc=cell_loc,
                         quadrant=quadrant,
                         pin_map=pin_map)
Exemple #15
0
def process_tilegrid(
        tile_types,
        tile_grid,
        clock_cells,
        cells_library,
        grid_size,
        grid_offset,
        grid_limit=None
):
    """
    Processes the tilegrid. May add/remove tiles. Returns a new one.
    """

    vpr_tile_grid = {}
    fwd_loc_map = {}
    bwd_loc_map = {}
    ram_blocks = []
    mult_blocks = []
    vpr_clock_cells = {}

    def add_loc_map(phy_loc, vpr_loc):
        fwd_loc_map[phy_loc] = vpr_loc
        bwd_loc_map[vpr_loc] = phy_loc

    # Add a fake constant connector pin to LOGIC tile type
    tile_type = tile_types["LOGIC"]
    tile_type.fake_const_pin = True
    tile_type.make_pins(cells_library)

    # Generate the VPR tile grid
    for phy_loc, tile in tile_grid.items():

        # Limit the grid range
        if not is_loc_within_limit(phy_loc, grid_limit):
            continue

        vpr_loc = Loc(
            x=phy_loc.x + grid_offset[0], y=phy_loc.y + grid_offset[1], z=0
        )

        # If the tile contains QMUX or CAND then strip it. Possibly create a
        # new tile type.
        tile_type = tile_types[tile.type]
        if "QMUX" in tile_type.cells or "CAND" in tile_type.cells:

            # Store the stripped cells
            for cell in tile.cells:
                if cell.type in ["QMUX", "CAND"]:

                    # Find it in the physical clock cell list
                    if cell.name not in clock_cells:
                        print(
                            "WARNING: Clock cell '{}' not on the clock cell list!"
                            .format(cell.name)
                        )
                        continue

                    # Relocate CAND cells so that they occupy only even rows
                    if cell.type == "CAND":
                        cell_loc = fixup_cand_loc(vpr_loc, phy_loc)
                    else:
                        cell_loc = vpr_loc

                    # Get the orignal cell
                    clock_cell = clock_cells[cell.name]
                    pin_map = clock_cell.pin_map

                    # If the cell is QMUX then extend its pin map with
                    # QCLKIN0 and QCLKIN1 pins that are not present in the
                    # techfile.
                    if clock_cell.type == "QMUX":
                        gmux_base = int(pin_map["QCLKIN0"].rsplit("_")[1])
                        for i in [1, 2]:
                            key = "QCLKIN{}".format(i)
                            val = "GMUX_{}".format((gmux_base + i) % 5)
                            pin_map[key] = val

                    # Add the cell
                    clock_cell = ClockCell(
                        type=clock_cell.type,
                        name=clock_cell.name,
                        loc=cell_loc,
                        quadrant=clock_cell.quadrant,
                        pin_map=pin_map
                    )

                    vpr_clock_cells[clock_cell.name] = clock_cell

            # Strip the cells
            tile = strip_cells(
                tile, ["QMUX", "CAND"], tile_types, cells_library
            )
            if tile is None:
                continue

        # The tile contains a BIDIR or CLOCK cell. it is an IO tile
        tile_type = tile_types[tile.type]
        if "BIDIR" in tile_type.cells or "CLOCK" in tile_type.cells:

            # For the BIDIR cell create a synthetic tile
            if "BIDIR" in tile_type.cells:
                assert tile_type.cells["BIDIR"] == 1

                cells = [c for c in tile.cells if c.type == "BIDIR"]
                new_type = make_tile_type(cells, cells_library, tile_types)

                add_loc_map(phy_loc, vpr_loc)
                vpr_tile_grid[vpr_loc] = Tile(
                    type=new_type.type, name=tile.name, cells=cells
                )

            # For the CLOCK cell create a synthetic tile
            if "CLOCK" in tile_type.cells:
                assert tile_type.cells["CLOCK"] == 1

                cells = [c for c in tile.cells if c.type == "CLOCK"]
                new_type = make_tile_type(cells, cells_library, tile_types)

                # If the tile has a BIDIR cell then place the CLOCK tile in a
                # free location next to the original one.
                if "BIDIR" in tile_type.cells:
                    for ox, oy in ((-1, 0), (+1, 0), (0, -1), (0, +1)):
                        test_loc = Loc(x=phy_loc.x + ox, y=phy_loc.y + oy, z=0)
                        if is_loc_free(test_loc, tile_grid):
                            new_loc = Loc(
                                x=vpr_loc.x + ox,
                                y=vpr_loc.y + oy,
                                z=vpr_loc.z
                            )
                            break
                    else:
                        assert False, (
                            "No free location to place CLOCK tile", vpr_loc
                        )

                # Don't move
                else:
                    new_loc = vpr_loc

                # Add only the backward location correspondence for CLOCK tile
                bwd_loc_map[new_loc] = phy_loc
                vpr_tile_grid[new_loc] = Tile(
                    type=new_type.type, name=tile.name, cells=cells
                )

# Mults and RAMs occupy multiple cells
# We'll create a synthetic tile with a single cell for each
# RAM and MULT block
        if "RAM" in tile_type.cells or "MULT" in tile_type.cells:
            for cell in tile.cells:
                # Check if the current location is not taken
                # this could happen because RAM and MULTS share
                # the same location. General rule here is that
                # we create a synthetic Tile/Cell in the first
                # available neighboring location of the original block of cells
                if cell.type == 'RAM':
                    cells_set = ram_blocks
                elif cell.type == 'MULT':
                    cells_set = mult_blocks
                else:
                    continue

                # Find free location in the physical tile grid close to the
                # original one. Once found, convert it to location in the
                # VPR tile grid.
                for ox, oy in ((0, 0), (0, -1), (0, +1), (-1, 0), (+1, 0)):
                    test_loc = Loc(x=phy_loc.x + ox, y=phy_loc.y + oy, z=0)
                    if is_loc_free(test_loc, tile_grid):
                        new_loc = Loc(
                            x=vpr_loc.x + ox, y=vpr_loc.y + oy, z=vpr_loc.z
                        )
                        break
                else:
                    assert False, "No free location to place {} tile".format(
                        cell.type
                    )

                # The VPR location is already occupied. Probably another
                # instance of the same cell is already there.
                if not is_loc_free(new_loc, vpr_tile_grid):
                    continue

                bwd_loc_map[new_loc] = phy_loc
                if cell.name not in cells_set:
                    cells_set.append(cell.name)
                    tile_type = make_tile_type(
                        [cell], cells_library, tile_types
                    )
                    vpr_tile_grid[new_loc] = Tile(
                        tile_type.type, name=cell.type, cells=[cell]
                    )

        # The tile contains SDIOMUX cell(s). This is an IO tile.
        if "SDIOMUX" in tile_type.cells:

            # Split the tile into individual SDIOMUX cells. Each one will be
            # inside a synthetic tile occupying different grid location.
            cells = [c for c in tile.cells if c.type == "SDIOMUX"]
            for i, cell in enumerate(cells):

                # Create a synthetic tile that will hold just the SDIOMUX cell
                new_type = make_tile_type([cell], cells_library, tile_types)

                # Choose a new location for the tile
                # FIXME: It is assumed that SDIOMUX tiles are on the left edge
                # of the grid and there is enough space to split them.
                new_loc = Loc(vpr_loc.x - i, vpr_loc.y, vpr_loc.z)
                assert new_loc.x >= 1, new_loc

                # For the offset 0 add the full mapping, for others, just the
                # backward correspondence.
                if new_loc == vpr_loc:
                    add_loc_map(phy_loc, new_loc)
                else:
                    bwd_loc_map[new_loc] = phy_loc

                # Change index of the cell
                new_cell = Cell(
                    type=cell.type, index=0, name=cell.name, alias=cell.alias
                )

                # Add the tile instance
                vpr_tile_grid[new_loc] = Tile(
                    type=new_type.type, name=tile.name, cells=[new_cell]
                )

        # A homogeneous tile
        if len(tile_type.cells) == 1:
            cell_type = list(tile_type.cells.keys())[0]

            # LOGIC, keep as is
            if cell_type == "LOGIC":
                add_loc_map(phy_loc, vpr_loc)
                vpr_tile_grid[vpr_loc] = tile
                continue

            # GMUX, split individual GMUX cells into sub-tiles
            elif cell_type == "GMUX":

                for i, cell in enumerate(tile.cells):

                    # Create a tile type for a single GMUX cell
                    new_type = make_tile_type(
                        [cell], cells_library, tile_types
                    )
                    # New location
                    new_loc = Loc(vpr_loc.x, vpr_loc.y, cell.index)

                    # For the offset 0 add the full mapping, for others, just the
                    # backward correspondence.
                    if new_loc == vpr_loc:
                        add_loc_map(phy_loc, new_loc)
                    else:
                        bwd_loc_map[new_loc] = phy_loc

                    # Change index of the cell
                    new_cell = Cell(
                        type=cell.type,
                        index=0,
                        name=cell.name,
                        alias=cell.alias
                    )

                    # Add the tile instance
                    vpr_tile_grid[new_loc] = Tile(
                        type=new_type.type, name=tile.name, cells=[new_cell]
                    )

                continue

    # Find the ASSP tile. There are multiple tiles that contain the ASSP cell
    # but in fact there is only one ASSP cell for the whole FPGA which is
    # "distributed" along top and left edge of the grid.
    if "ASSP" in tile_types:

        # Verify that the location is empty
        assp_loc = Loc(x=1, y=1, z=0)
        assert is_loc_free(vpr_tile_grid, assp_loc), ("ASSP", assp_loc)

        # Place the ASSP tile
        vpr_tile_grid[assp_loc] = Tile(
            type="ASSP",
            name="ASSP",
            cells=[Cell(type="ASSP", index=0, name="ASSP", alias=None)]
        )

        # Remove "FBIO_*" pins from the ASSP tile. These pins are handled by
        # SDIOMUX IO cells
        tile_type = tile_types["ASSP"]
        tile_type.pins = [p for p in tile_type.pins if "FBIO_" not in p.name]

    # Insert synthetic VCC and GND source tiles.
    # FIXME: This assumes that the locations specified are empty!
    for const, loc in [("VCC", Loc(x=2, y=1, z=0)), ("GND", Loc(x=3, y=1,
                                                                z=0))]:

        # Verify that the location is empty
        assert is_loc_free(vpr_tile_grid, loc), (const, loc)

        # Add the tile instance
        name = "SYN_{}".format(const)
        vpr_tile_grid[loc] = Tile(
            type=name,
            name=name,
            cells=[Cell(type=const, index=0, name=const, alias=None)]
        )

    # Extend the grid by 1 on the right and bottom side. Fill missing locs
    # with empty tiles.
    for x, y in itertools.product(range(grid_size[0]), range(grid_size[1])):
        loc = Loc(x=x, y=y, z=0)

        if loc not in vpr_tile_grid:
            vpr_tile_grid[loc] = None

    return vpr_tile_grid, vpr_clock_cells, LocMap(
        fwd=fwd_loc_map, bwd=bwd_loc_map
    ),
Exemple #16
0
def main():

    # Parse arguments
    parser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter
    )

    parser.add_argument(
        "--phy-db",
        type=str,
        required=True,
        help="Input physical device database file"
    )
    parser.add_argument(
        "--sdf-dir",
        type=str,
        default=None,
        help="A directory with SDF timing files"
    )
    parser.add_argument(
        "--vpr-db",
        type=str,
        default="vpr_database.pickle",
        help="Output VPR database file"
    )
    parser.add_argument(
        "--grid-limit",
        type=str,
        default=None,
        help="Grid coordinate range to import eg. '0,0,10,10' (def. None)"
    )

    args = parser.parse_args()

    # Grid limit
    if args.grid_limit is not None:
        grid_limit = [int(q) for q in args.grid_limit.split(",")]
    else:
        grid_limit = None

    # Load data from the database
    with open(args.phy_db, "rb") as fp:
        db = pickle.load(fp)

        phy_quadrants = db["phy_quadrants"]
        cells_library = db["cells_library"]
        tile_types = db["tile_types"]
        phy_tile_grid = db["phy_tile_grid"]
        phy_clock_cells = db["phy_clock_cells"]
        switchbox_types = db["switchbox_types"]
        phy_switchbox_grid = db["switchbox_grid"]
        switchbox_timing = db["switchbox_timing"]
        connections = db["connections"]
        package_pinmaps = db["package_pinmaps"]

    # Load and parse SDF files
    if args.sdf_dir is not None:
        cell_timings = load_sdf_timings(args.sdf_dir)
    else:
        cell_timings = None

    # Process the cells library
    vpr_cells_library = process_cells_library(cells_library)

    # Add synthetic stuff
    add_synthetic_cell_and_tile_types(tile_types, vpr_cells_library)

    # Determine the grid offset so occupied locations start at GRID_MARGIN
    tl_min = min([loc.x for loc in phy_tile_grid]), min(
        [loc.y for loc in phy_tile_grid]
    )
    tl_max = max([loc.x for loc in phy_tile_grid]), max(
        [loc.y for loc in phy_tile_grid]
    )

    sb_min = min([loc.x for loc in phy_switchbox_grid]), min(
        [loc.y for loc in phy_switchbox_grid]
    )
    sb_max = max([loc.x for loc in phy_switchbox_grid]), max(
        [loc.y for loc in phy_switchbox_grid]
    )

    grid_min = min(tl_min[0], sb_min[0]), min(tl_min[1], sb_min[1])
    grid_max = max(tl_max[0], sb_max[0]), max(tl_max[1], sb_max[1])

    # Compute VPR grid offset w.r.t the physical grid and its size
    grid_offset = GRID_MARGIN[0] - grid_min[0], GRID_MARGIN[1] - grid_min[1]

    grid_size = GRID_MARGIN[0] + GRID_MARGIN[2] + (
        grid_max[0] - grid_min[0] + 1
    ), GRID_MARGIN[1] + GRID_MARGIN[3] + (
        grid_max[1] - grid_min[1] + 1
    )

    # Remap quadrant locations
    vpr_quadrants = {}
    for quadrant in phy_quadrants.values():
        vpr_quadrants[quadrant.name] = Quadrant(
            name=quadrant.name,
            x0=quadrant.x0 + grid_offset[0],
            x1=quadrant.x1 + grid_offset[0],
            y0=quadrant.y0 + grid_offset[1],
            y1=quadrant.y1 + grid_offset[1]
        )

    # Process the tilegrid
    vpr_tile_grid, vpr_clock_cells, loc_map = process_tilegrid(
        tile_types, phy_tile_grid, phy_clock_cells, vpr_cells_library,
        grid_size, grid_offset, grid_limit
    )

    # Process the switchbox grid
    vpr_switchbox_grid, loc_map = process_switchbox_grid(
        phy_switchbox_grid, loc_map, grid_offset, grid_limit
    )

    # Process connections
    connections = process_connections(
        connections, loc_map, vpr_tile_grid, phy_tile_grid, grid_limit
    )

    # Process package pinmaps
    vpr_package_pinmaps = {}
    for package, pkg_pin_map in package_pinmaps.items():
        vpr_package_pinmaps[package] = process_package_pinmap(
            pkg_pin_map, vpr_tile_grid, grid_limit
        )

    # Get tile types present in the grid
    vpr_tile_types = set(
        [t.type for t in vpr_tile_grid.values() if t is not None]
    )
    vpr_tile_types = {
        k: v
        for k, v in tile_types.items()
        if k in vpr_tile_types
    }

    # Get the switchbox types present in the grid
    vpr_switchbox_types = set(
        [s for s in vpr_switchbox_grid.values() if s is not None]
    )
    vpr_switchbox_types = {
        k: v
        for k, v in switchbox_types.items()
        if k in vpr_switchbox_types
    }

    # Make tile -> site equivalence list
    vpr_equivalent_sites = {}

    # Make switch list
    vpr_switches = build_switch_list()
    # Make segment list
    vpr_segments = build_segment_list()

    # Process timing data
    if switchbox_timing is not None or cell_timings is not None:
        print("Processing timing data...")

    if switchbox_timing is not None:

        # The timing data seems to be the same for each switchbox type and is
        # stored under the SB_LC name.
        timing_data = switchbox_timing["SB_LC"]

        # Compute the timing model for the most generic SB_LC switchbox.
        switchbox = vpr_switchbox_types["SB_LC"]
        driver_timing, sink_map = compute_switchbox_timing_model(
            switchbox, timing_data
        )

        # Populate the model, create and assign VPR switches.
        populate_switchbox_timing(
            switchbox, driver_timing, sink_map, vpr_switches
        )

        # Propagate the timing data to all other switchboxes. Even though they
        # are of different type, physically they are the same.
        for dst_switchbox in vpr_switchbox_types.values():
            if dst_switchbox.type != "SB_LC":
                copy_switchbox_timing(switchbox, dst_switchbox)

    if cell_timings is not None:

        sw = add_vpr_switches_for_cell("QMUX", cell_timings)
        vpr_switches.update(sw)

        sw = add_vpr_switches_for_cell("CAND", cell_timings)
        vpr_switches.update(sw)

    if DEBUG:
        # DBEUG
        print("Tile grid:")
        xmax = max([loc.x for loc in vpr_tile_grid])
        ymax = max([loc.y for loc in vpr_tile_grid])
        for y in range(ymax + 1):
            line = " {:>2}: ".format(y)
            for x in range(xmax + 1):
                loc = Loc(x=x, y=y, z=0)
                if loc not in vpr_tile_grid:
                    line += " "
                elif vpr_tile_grid[loc] is not None:
                    tile_type = vpr_tile_types[vpr_tile_grid[loc].type]
                    label = sorted(list(tile_type.cells.keys()))[0][0].upper()
                    line += label
                else:
                    line += "."
            print(line)

        # DBEUG
        print("Tile capacity / sub-tile count")
        xmax = max([loc.x for loc in vpr_tile_grid])
        ymax = max([loc.y for loc in vpr_tile_grid])
        for y in range(ymax + 1):
            line = " {:>2}: ".format(y)
            for x in range(xmax + 1):

                tiles = {
                    loc: tile
                    for loc, tile in vpr_tile_grid.items()
                    if loc.x == x and loc.y == y
                }
                count = len([t for t in tiles.values() if t is not None])

                if len(tiles) == 0:
                    line += " "
                elif count == 0:
                    line += "."
                else:
                    line += "{:X}".format(count)

            print(line)

        # DEBUG
        print("Switchbox grid:")
        xmax = max([loc.x for loc in vpr_switchbox_grid])
        ymax = max([loc.y for loc in vpr_switchbox_grid])
        for y in range(ymax + 1):
            line = " {:>2}: ".format(y)
            for x in range(xmax + 1):
                loc = Loc(x=x, y=y, z=0)
                if loc not in vpr_switchbox_grid:
                    line += " "
                elif vpr_switchbox_grid[loc] is not None:
                    line += "X"
                else:
                    line += "."
            print(line)

        # DBEUG
        print("Route-through global clock cells:")
        xmax = max([loc.x for loc in vpr_tile_grid])
        ymax = max([loc.y for loc in vpr_tile_grid])
        for y in range(ymax + 1):
            line = " {:>2}: ".format(y)
            for x in range(xmax + 1):
                loc = Loc(x=x, y=y, z=0)

                for cell in vpr_clock_cells.values():
                    if cell.loc == loc:
                        line += cell.name[0].upper()
                        break
                else:
                    line += "."
            print(line)

        # DEBUG
        print("VPR Segments:")
        for s in vpr_segments.values():
            print("", s)

        # DEBUG
        print("VPR Switches:")
        for s in vpr_switches.values():
            print("", s)

    # Prepare the VPR database and write it
    db_root = {
        "vpr_cells_library": vpr_cells_library,
        "loc_map": loc_map,
        "vpr_quadrants": vpr_quadrants,
        "vpr_tile_types": vpr_tile_types,
        "vpr_tile_grid": vpr_tile_grid,
        "vpr_clock_cells": vpr_clock_cells,
        "vpr_equivalent_sites": vpr_equivalent_sites,
        "vpr_switchbox_types": vpr_switchbox_types,
        "vpr_switchbox_grid": vpr_switchbox_grid,
        "connections": connections,
        "vpr_package_pinmaps": vpr_package_pinmaps,
        "segments": list(vpr_segments.values()),
        "switches": list(vpr_switches.values()),
    }

    with open("{}.tmp".format(args.vpr_db), "wb") as fp:
        pickle.dump(db_root, fp, protocol=3)

    os.rename("{}.tmp".format(args.vpr_db), args.vpr_db)
Exemple #17
0
def build_hop_connections(switchbox_types, switchbox_grid):
    """
    Builds HOP connections between switchboxes.
    """
    connections = []

    # Determine the switchbox grid limits
    xs = set([loc.x for loc in switchbox_grid.keys()])
    ys = set([loc.y for loc in switchbox_grid.keys()])
    loc_min = Loc(min(xs), min(ys), 0)
    loc_max = Loc(max(xs), max(ys), 0)

    # Identify all connections that go out of switchboxes
    for dst_loc, dst_switchbox_type in switchbox_grid.items():
        dst_switchbox = switchbox_types[dst_switchbox_type]

        # Process HOP inputs. No need for looping over outputs as each output
        # should go into a HOP input.
        dst_pins = [
            pin for pin in dst_switchbox.inputs.values()
            if pin.type == SwitchboxPinType.HOP
        ]
        for dst_pin in dst_pins:

            # Parse the name, determine hop offset. Skip non-hop wires.
            hop_name, hop_ofs = get_name_and_hop(dst_pin.name)
            if hop_ofs is None:
                continue

            # Check if we don't hop outside the FPGA grid.
            src_loc = Loc(dst_loc.x + hop_ofs[0], dst_loc.y + hop_ofs[1], 0)
            if src_loc.x < loc_min.x or src_loc.x > loc_max.x:
                continue
            if src_loc.y < loc_min.y or src_loc.y > loc_max.y:
                continue

            # Get the switchbox at the source location
            if src_loc not in switchbox_grid:
                print(
                    "WARNING: No switchbox at '{}' for input '{}' of switchbox '{}' at '{}'"
                    .format(src_loc, dst_pin.name, dst_switchbox_type,
                            dst_loc))
                continue

            src_switchbox_type = switchbox_grid[src_loc]
            src_switchbox = switchbox_types[src_switchbox_type]

            # Check if there is a matching input pin in that switchbox
            src_pins = [
                pin for pin in src_switchbox.outputs.values()
                if pin.name == hop_name
            ]

            if len(src_pins) != 1:
                print(
                    "WARNING: No output pin '{}' in switchbox '{}'"
                    " at '{}' for input '{}' of switchbox '{}' at '{}'".format(
                        hop_name, src_switchbox_type, src_loc, dst_pin.name,
                        dst_switchbox_type, dst_loc))
                continue

            src_pin = src_pins[0]

            # Add the connection
            connection = Connection(src=ConnectionLoc(
                loc=src_loc,
                pin=src_pin.name,
                type=ConnectionType.SWITCHBOX,
            ),
                                    dst=ConnectionLoc(
                                        loc=dst_loc,
                                        pin=dst_pin.name,
                                        type=ConnectionType.SWITCHBOX,
                                    ),
                                    is_direct=False)

            connections.append(connection)

    return connections
Exemple #18
0
def build_tile_connections(tile_types, tile_grid, switchbox_types,
                           switchbox_grid):
    """
    Build local and foreign connections between all switchboxes and tiles.
    """
    connections = []

    # Process switchboxes
    for loc, switchbox_type in switchbox_grid.items():
        switchbox = switchbox_types[switchbox_type]

        # Get pins
        sbox_pins = [
            pin for pin in switchbox.pins
            if pin.type in [SwitchboxPinType.LOCAL, SwitchboxPinType.FOREIGN]
        ]

        for sbox_pin in sbox_pins:
            tile = None

            # A local connection
            if sbox_pin.type == SwitchboxPinType.LOCAL:
                pin_name = sbox_pin.name
                tile_loc = loc

            # A foreign connection
            elif sbox_pin.type == SwitchboxPinType.FOREIGN:

                # Get the hop offset
                pin_name, hop = get_name_and_hop(sbox_pin.name)
                assert hop is not None, sbox_pin

                tile_loc = Loc(x=loc.x + hop[0], y=loc.y + hop[1], z=loc.z)

            # Get the tile
            if tile_loc not in tile_grid:
                print("WARNING: No tile at loc '{}' for pin '{}'".format(
                    tile_loc, sbox_pin.name))
                continue

            tile = tile_types[tile_grid[tile_loc].type]

            # Find the pin in the tile
            for pin in tile.pins:
                if pin.direction == OPPOSITE_DIRECTION[sbox_pin.direction]:

                    # Check if the pin name refers to the full tile pin name
                    # ie. with the cell name.
                    if pin.name == pin_name:
                        tile_pin = pin
                        break

                    # Split the pin name into cell name + pin name, check only
                    # if the latter matches.
                    cell, name = pin.name.split("_", maxsplit=1)
                    if name == pin_name:
                        tile_pin = pin
                        break
            else:
                tile_pin = None

            # Pin not found
            if tile_pin is None:
                print(
                    "WARNING: No pin in tile at '{}' found for switchbox pin '{}' of '{}' at '{}'"
                    .format(tile_loc, sbox_pin.name, switchbox.type, loc))
                continue

            # Add the connection
            src = ConnectionLoc(
                loc=loc,
                pin=sbox_pin.name,
                type=ConnectionType.SWITCHBOX,
            )
            dst = ConnectionLoc(
                loc=tile_loc,
                pin=tile_pin.name,
                type=ConnectionType.TILE,
            )

            if sbox_pin.direction == PinDirection.OUTPUT:
                connection = Connection(src=src, dst=dst, is_direct=False)
            if sbox_pin.direction == PinDirection.INPUT:
                connection = Connection(src=dst, dst=src, is_direct=False)

            connections.append(connection)

    return connections
Exemple #19
0
def add_tracks_for_const_network(graph, const, tile_grid):
    """
    Builds a network of CHANX/CHANY and edges to propagate signal from a
    const source.

    The const network is purely artificial and does not correspond to any
    physical routing resources.

    Returns a map of const network nodes for each location.
    """

    # Get the tilegrid span
    xs = set([loc.x for loc in tile_grid])
    ys = set([loc.y for loc in tile_grid])
    xmin, ymin = min(xs), min(ys)
    xmax, ymax = max(xs), max(ys)

    # Get segment id and switch id
    segment_id = graph.get_segment_id_from_name(const.lower())
    switch_id = graph.get_delayless_switch_id()

    # Find the source tile
    src_loc = [
        loc for loc, t in tile_grid.items()
        if t is not None and t.type == "SYN_{}".format(const)
    ]
    assert len(src_loc) == 1, const
    src_loc = src_loc[0]

    # Go down from the source to the edge of the tilegrid
    entry_node, col_node, _ = add_track_chain(graph, "Y", src_loc.x, src_loc.y,
                                              1, segment_id, switch_id)

    # Connect the tile OPIN to the column
    pin_name = "TL-SYN_{const}.{const}0_{const}[0]".format(const=const)
    opin_node = graph.get_nodes_for_pin((src_loc[0], src_loc[1]), pin_name)
    assert len(opin_node) == 1, pin_name

    add_edge(graph, opin_node[0][0], entry_node.id, switch_id)

    # Got left and right from the source column over the bottommost row
    row_entry_node1, _, row_node_map1 = add_track_chain(
        graph, "X", 0, src_loc.x, 1, segment_id, switch_id)
    row_entry_node2, _, row_node_map2 = add_track_chain(
        graph, "X", 0, src_loc.x + 1, xmax - 1, segment_id, switch_id)

    # Connect rows to the column
    add_edge(graph, col_node.id, row_entry_node1.id, switch_id)
    add_edge(graph, col_node.id, row_entry_node2.id, switch_id)
    row_node_map = {**row_node_map1, **row_node_map2}

    row_node_map[0] = row_node_map[1]

    # For each column add one that spand over the entire grid height
    const_node_map = {}
    for x in range(xmin, xmax):

        # Add the column
        col_entry_node, _, col_node_map = add_track_chain(
            graph, "Y", x, ymin + 1, ymax - 1, segment_id, switch_id)

        # Add edge fom the horizontal row
        add_edge(graph, row_node_map[x].id, col_entry_node.id, switch_id)

        # Populate the const node map
        for y, node in col_node_map.items():
            const_node_map[Loc(x=x, y=y, z=0)] = node

    return const_node_map
Exemple #20
0
    def __init__(self, vpr_db, package_name):
        '''Prepares required structures for converting FASM to BELs.

        Parameters
        ----------
        vpr_db: dict
            A dictionary containing cell_library, loc_map, vpr_tile_types,
            vpr_tile_grid, vpr_switchbox_types, vpr_switchbox_grid,
            connections, vpr_package_pinmaps
        '''

        # load vpr_db data
        self.quadrants = vpr_db["phy_quadrants"]
        self.cells_library = vpr_db["cells_library"]
        self.vpr_tile_types = vpr_db["tile_types"]
        self.vpr_tile_grid = vpr_db["phy_tile_grid"]
        self.vpr_switchbox_types = vpr_db["switchbox_types"]
        self.vpr_switchbox_grid = vpr_db["switchbox_grid"]
        self.connections = vpr_db["connections"]
        self.package_name = package_name

        self.io_to_fbio = dict()

        for name, package in db['package_pinmaps'][self.package_name].items():
            self.io_to_fbio[package[0].loc] = name

        # Add ASSP to all locations it covers
        # TODO maybe this should be added in original vpr_tile_grid
        # set all cels in row 1 and column 2 to ASSP
        # In VPR grid, the ASSP tile is located in (1, 1)
        assplocs = set()
        ramlocs = dict()
        multlocs = dict()

        for phy_loc, tile in self.vpr_tile_grid.items():
            tile_type = self.vpr_tile_types[tile.type]
            if "ASSP" in tile_type.cells:
                assplocs.add(phy_loc)

            if "RAM" in tile_type.cells:
                ramcell = [cell for cell in tile.cells if cell.type == "RAM"]
                cellname = ramcell[0].name
                if cellname not in ramlocs:
                    ramlocs[cellname] = set()

                ramlocs[cellname].add(phy_loc)

            if "MULT" in tile_type.cells:
                multcell = [cell for cell in tile.cells if cell.type == "MULT"]
                cellname = multcell[0].name
                if cellname not in multlocs:
                    multlocs[cellname] = set()

                multlocs[cellname].add(phy_loc)

        # this map represents the mapping from input name to its inverter name
        self.inversionpins = {
            'LOGIC': {
                'TA1': 'TAS1',
                'TA2': 'TAS2',
                'TB1': 'TBS1',
                'TB2': 'TBS2',
                'BA1': 'BAS1',
                'BA2': 'BAS2',
                'BB1': 'BBS1',
                'BB2': 'BBS2',
                'QCK': 'QCKS'
            }
        }

        # prepare helper structure for connections
        self.connections_by_loc = defaultdict(list)
        for connection in self.connections:
            self.connections_by_loc[connection.dst].append(connection)
            self.connections_by_loc[connection.src].append(connection)

        # a mapping from the type of cell FASM line refers to to its parser
        self.featureparsers = {
            'LOGIC': self.parse_logic_line,
            'QMUX': self.parse_logic_line,
            'GMUX': self.parse_logic_line,
            'INTERFACE': self.parse_interface_line,
            'ROUTING': self.parse_routing_line,
            'CAND0': self.parse_colclk_line,
            'CAND1': self.parse_colclk_line,
            'CAND2': self.parse_colclk_line,
            'CAND3': self.parse_colclk_line,
            'CAND4': self.parse_colclk_line,
        }

        # a mapping from cell type to a set of possible pin names
        self.pinnames = defaultdict(set)
        for celltype in self.cells_library.values():
            typ = celltype.type
            for pin in celltype.pins:
                self.pinnames[typ].add(pin.name)

        # a mapping from cell types that occupy multiple locations
        # to a single location
        self.multiloccells = {
            'ASSP':
            MultiLocCellMapping('ASSP', assplocs, Loc(1, 1, 0),
                                self.pinnames['ASSP'])
        }
        for ram in ramlocs:
            self.multiloccells[ram] = MultiLocCellMapping(
                ram, ramlocs[ram],
                list(ramlocs[ram])[0], self.pinnames['RAM'])
        for mult in multlocs:
            self.multiloccells[mult] = MultiLocCellMapping(
                mult, multlocs[mult],
                list(multlocs[mult])[1], self.pinnames['MULT'])

        # helper routing data
        self.routingdata = defaultdict(list)
        # a dictionary holding bit settings for BELs
        self.belinversions = defaultdict(lambda: defaultdict(list))
        # a dictionary holding bit settings for IOs
        self.interfaces = defaultdict(lambda: defaultdict(list))
        # a dictionary holding simplified connections between BELs
        self.designconnections = defaultdict(dict)
        # a dictionary holding hops from routing
        self.designhops = defaultdict(dict)

        # Clock column drivers (CAND) data
        self.colclk_data = defaultdict(lambda: defaultdict(list))
        # A map of clock wires that connect to switchboxes
        self.cand_map = defaultdict(lambda: dict())

        # A map of original (loc, pin) to new (loc, pin). Created during
        # aggregation of multi-loc cells.
        self.org_loc_map = {}
Exemple #21
0
def parse_port_mapping_table(xml_root, switchbox_grid):
    """
    Parses switchbox port mapping tables. Returns a dict indexed by locations
    containing a dict with switchbox port to tile port name correspondence.
    """
    port_maps = defaultdict(lambda: {})

    # Sections are named "*_Table"
    xml_tables = [e for e in xml_root if e.tag.endswith("_Table")]
    for xml_table in xml_tables:

        # Get the origin
        origin = xml_table.tag.split("_")[0]
        assert origin in ["Left", "Right", "Top", "Bottom"], origin

        # Get switchbox types affected by the mapping
        sbox_types_xml = xml_table.find("SBoxTypes")
        assert sbox_types_xml is not None
        switchbox_types = set([
            v for k, v in sbox_types_xml.attrib.items() if k.startswith("type")
        ])

        # Get their locations
        locs = [
            loc for loc, type in switchbox_grid.items()
            if type in switchbox_types
        ]

        # Get the first occurrence of a switchbox with one of considered types
        # that is closes to the (0, 0) according to manhattan distance.
        base_loc = None
        for loc in locs:
            if not base_loc:
                base_loc = loc
            elif (loc.x + loc.y) < (base_loc.x + base_loc.y):
                base_loc = loc

        # Parse the port mapping table(s)
        for port_mapping_xml in xml_table.findall("PortMappingTable"):

            # Get the direction of the switchbox offset
            orientation = port_mapping_xml.attrib["Orientation"]
            if orientation == "Horizontal":
                assert origin in ["Top", "Bottom"], (origin, orientation)
                dx, dy = (+1, 0)
            elif orientation == "Vertical":
                assert origin in ["Left", "Right"], (origin, orientation)
                dx, dy = (0, +1)

            # Process the mapping of switchbox output ports
            for index_xml in port_mapping_xml.findall("Index"):
                pin_name = index_xml.attrib["Mapped_Interface_Name"]
                output_num = index_xml.attrib["SwitchOutputNum"]

                # Determine the mapped port direction
                if output_num == "-1":
                    pin_direction = PinDirection.INPUT
                else:
                    pin_direction = PinDirection.OUTPUT

                sbox_xmls = [e for e in index_xml if e.tag.startswith("SBox")]
                for sbox_xml in sbox_xmls:

                    offset = int(sbox_xml.attrib["Offset"])
                    mapped_name = sbox_xml.get("MTB_PortName", None)

                    # "-1" means unconnected
                    if mapped_name == "-1":
                        mapped_name = None

                    # Get the location for the map
                    loc = Loc(x=base_loc.x + dx * offset,
                              y=base_loc.y + dy * offset,
                              z=0)

                    # Append mapping
                    key = (pin_name, pin_direction)
                    assert key not in port_maps[loc], (loc, key)

                    port_maps[loc][key] = mapped_name

    # Make a normal dict
    port_maps = dict(port_maps)

    return port_maps
def write_tilegrid(xml_arch, arch_tile_grid, loc_map, layout_name):
    """
    Generates the "layout" section of the arch XML and appends it to the
    root given.
    """

    # Remove the "layout" tag if any
    xml_layout = xml_arch.find("layout")
    if xml_layout is not None:
        xml_arch.remove(xml_layout)

    # Grid size
    xs = [flat_loc[0] for flat_loc in arch_tile_grid]
    ys = [flat_loc[1] for flat_loc in arch_tile_grid]
    w = max(xs) + 1
    h = max(ys) + 1

    # Fixed layout
    xml_layout = ET.SubElement(xml_arch, "layout")
    xml_fixed = ET.SubElement(xml_layout, "fixed_layout", {
        "name": layout_name,
        "width": str(w),
        "height": str(h),
    })

    # Individual tiles
    for flat_loc, tile in arch_tile_grid.items():

        if tile is None:
            continue

        # Unpack
        tile_type, capacity = tile

        # Single tile
        xml_sing = ET.SubElement(
            xml_fixed,
            "single",
            {
                "type": "TL-{}".format(tile_type.upper()),
                "x": str(flat_loc[0]),
                "y": str(flat_loc[1]),
                "priority": str(10),  # Not sure if we need this
            })

        # Gather metadata
        metadata = []
        for i in range(capacity):
            loc = Loc(x=flat_loc[0], y=flat_loc[1], z=i)

            if loc in loc_map.bwd:
                phy_loc = loc_map.bwd[loc]
                metadata.append("X{}Y{}".format(phy_loc.x, phy_loc.y))

        # Emit metadata if any
        if len(metadata):
            xml_metadata = ET.SubElement(xml_sing, "metadata")
            xml_meta = ET.SubElement(xml_metadata, "meta", {
                "name": "fasm_prefix",
            })
            xml_meta.text = " ".join(metadata)
def write_direct_connections(xml_arch, tile_grid, connections):
    """
    """
    def get_tile(ep):
        """
        Retireves tile for the given connection endpoint
        """

        if ep.loc in tile_grid and tile_grid[ep.loc] is not None:
            return tile_grid[ep.loc]

        else:
            print("ERROR: No tile found for the connection endpoint", ep)
            return None

    # Remove the "directlist" tag if any
    xml_directlist = xml_arch.find("directlist")
    if xml_directlist is not None:
        xml_arch.remove(xml_directlist)

    # Make a new one
    xml_directlist = ET.SubElement(xml_arch, "directlist")

    # Populate connections
    conns = [c for c in connections if is_direct(c)]
    for connection in conns:

        src_tile = get_tile(connection.src)
        dst_tile = get_tile(connection.dst)

        if not src_tile or not dst_tile:
            continue

        src_name = "TL-{}.{}".format(src_tile.type, connection.src.pin)
        dst_name = "TL-{}.{}".format(dst_tile.type, connection.dst.pin)

        name = "{}_at_X{}Y{}Z{}_to_{}_at_X{}Y{}Z{}".format(
            src_name,
            connection.src.loc.x,
            connection.src.loc.y,
            connection.src.loc.z,
            dst_name,
            connection.dst.loc.x,
            connection.dst.loc.y,
            connection.dst.loc.z,
        )

        delta_loc = Loc(
            x=connection.dst.loc.x - connection.src.loc.x,
            y=connection.dst.loc.y - connection.src.loc.y,
            z=connection.dst.loc.z - connection.src.loc.z,
        )

        # Format the direct connection tag
        ET.SubElement(
            xml_directlist, "direct", {
                "name": name,
                "from_pin": src_name,
                "to_pin": dst_name,
                "x_offset": str(delta_loc.x),
                "y_offset": str(delta_loc.y),
                "z_offset": str(delta_loc.z),
            })
Exemple #24
0
def process_connections(
        phy_connections, loc_map, vpr_tile_grid, phy_tile_grid, grid_limit=None
):
    """
    Process the connection list.
    """

    # Remap locations, create the VPR connection list
    vpr_connections = []
    for connection in phy_connections:

        # Reject connections that reach outsite the grid limit
        if not is_loc_within_limit(connection.src.loc, grid_limit):
            continue
        if not is_loc_within_limit(connection.dst.loc, grid_limit):
            continue

        # Remap source and destination coordinates
        eps = [connection.src, connection.dst]
        for j, ep in enumerate(eps):
            phy_loc = ep.loc
            vpr_loc = loc_map.fwd[phy_loc]

            vpr_pin = ep.pin

            # If the connection mentions a CAND cell, fixup its location
            if "CAND" in ep.pin and ep.type == ConnectionType.CLOCK:
                vpr_loc = fixup_cand_loc(vpr_loc, phy_loc)

            # If the connection mentions a GMUX cell, remap its Z location so
            # it points to the correct sub-tile
            if "GMUX" in ep.pin and ep.type == ConnectionType.TILE:

                # Modify the cell name, use always "GMUX0"
                cell, pin = ep.pin.split("_", maxsplit=1)
                vpr_pin = "GMUX0_{}".format(pin)

                # Modify the location according to the cell index
                z = int(cell[-1])  # FIXME: Assuming indices 0-9
                vpr_loc = Loc(vpr_loc.x, vpr_loc.y, z)

            # Update the endpoint
            eps[j] = ConnectionLoc(loc=vpr_loc, pin=vpr_pin, type=ep.type)

        # Add the connection
        vpr_connections.append(
            Connection(src=eps[0], dst=eps[1], is_direct=connection.is_direct)
        )

    # Remap locations of connections that go to CLOCK pads. A physical
    # BIDIR+CLOCK tile is split into separate BIDIR and CLOCK tiles.
    for i, connection in enumerate(vpr_connections):

        eps = [connection.src, connection.dst]
        for j, ep in enumerate(eps):

            # This endpoint is not relevant to a CLOCK cell
            if not ep.pin.startswith("CLOCK"):
                continue

            # The endpoint location points to a BIDIR tile. Find the assocated
            # CLOCK tile
            org_loc = loc_map.bwd[ep.loc]
            for vpr_loc, phy_loc in loc_map.bwd.items():
                if phy_loc == org_loc and vpr_loc != ep.loc:
                    clock_loc = vpr_loc
                    break
            else:
                assert False, (
                    "Couldn't find a CLOCK cell in the VPR grid!", connection
                )

            eps[j] = ConnectionLoc(
                loc=clock_loc,
                pin=ep.pin,
                type=ep.type,
            )

        # Modify the connection
        vpr_connections[i] = Connection(
            src=eps[0], dst=eps[1], is_direct=connection.is_direct
        )

    # Find SFBIO connections, map their endpoints to SDIOMUX tiles
    # FIXME: This should be read from the techfine. Definition of the SDIOMUX
    # cell has  "realPortName" fields.
    SDIOMUX_PIN_MAP = {
        "FBIO_In": "IZ",
        "FBIO_In_En": "IE",
        "FBIO_Out": "OQI",
        "FBIO_Out_En": "OE",
    }

    for i, connection in enumerate(vpr_connections):

        eps = [connection.src, connection.dst]
        for j, ep in enumerate(eps):

            # Must have "ASSP" and "FBIO_" in name and refer to a tile.
            if "ASSP" not in ep.pin:
                continue
            if "FBIO_" not in ep.pin:
                continue
            if ep.type != ConnectionType.TILE:
                continue

            # Get the pin name and index
            pin_name, pin_index = get_pin_name(ep.pin)
            assert pin_index is not None, ep

            # Strip cell name
            pin_name = pin_name.split("_", maxsplit=1)[1]

            # Find where is an SDIOMUX cell for that index
            cell_name = "SFB_{}_IO".format(pin_index)

            # New location and pin name
            new_loc = get_loc_of_cell(cell_name, vpr_tile_grid)
            cell = find_cell_in_tile(cell_name, vpr_tile_grid[new_loc])

            new_pin = "{}{}_{}".format(
                cell.type, cell.index, SDIOMUX_PIN_MAP[pin_name]
            )

            eps[j] = ConnectionLoc(
                loc=new_loc,
                pin=new_pin,
                type=ep.type,
            )

        # Modify the connection
        vpr_connections[i] = Connection(
            src=eps[0], dst=eps[1], is_direct=connection.is_direct
        )

    # Find locations of "special" tiles
    special_tile_loc = {"ASSP": None}

    for loc, tile in vpr_tile_grid.items():
        if tile is not None and tile.type in special_tile_loc:
            assert special_tile_loc[tile.type] is None, tile
            special_tile_loc[tile.type] = loc

    # Map connections going to/from them to their locations in the VPR grid
    for i, connection in enumerate(vpr_connections):

        # Process connection endpoints
        eps = [connection.src, connection.dst]
        for j, ep in enumerate(eps):

            if ep.type != ConnectionType.TILE:
                continue

            cell_name, pin = ep.pin.split("_", maxsplit=1)
            cell_type = cell_name[:-1]
            # FIXME: The above will fail on cell with index >= 10

            if cell_type in special_tile_loc:
                loc = special_tile_loc[cell_type]

                eps[j] = ConnectionLoc(
                    loc=loc,
                    pin=ep.pin,
                    type=ep.type,
                )

        # Modify the connection
        vpr_connections[i] = Connection(
            src=eps[0], dst=eps[1], is_direct=connection.is_direct
        )

    # handle RAM and MULT locations
    ram_locations = {}
    mult_locations = {}
    for loc, tile in vpr_tile_grid.items():
        if tile is None:
            continue
        cell = tile.cells[0]
        cell_name = cell.name
        if tile.type == "RAM":
            ram_locations[cell_name] = loc
        if tile.type == "MULT":
            mult_locations[cell_name] = loc

    for i, connection in enumerate(vpr_connections):
        # Process connection endpoints
        eps = [connection.src, connection.dst]
        for j, ep in enumerate(eps):

            if ep.type != ConnectionType.TILE:
                continue

            cell_name, pin = ep.pin.split("_", maxsplit=1)
            cell_type = cell_name[:-1]
            # FIXME: The above will fail on cell with index >= 10

            # We handle on MULT and RAM here
            if cell_type != "MULT" and cell_type != "RAM":
                continue

            loc = loc_map.bwd[ep.loc]
            tile = phy_tile_grid[loc]
            cell = [cell for cell in tile.cells if cell.type == cell_type]

            cell_name = cell[0].name

            if cell_type == "MULT":
                loc = mult_locations[cell_name]
            else:
                loc = ram_locations[cell_name]

            eps[j] = ConnectionLoc(
                loc=loc,
                pin=ep.pin,
                type=ep.type,
            )

        # Modify the connection
        vpr_connections[i] = Connection(
            src=eps[0], dst=eps[1], is_direct=connection.is_direct
        )

    # A QMUX should have 3 QCLKIN inputs but accorting to the EOS S3/PP3E
    # techfile it has only one. It is assumed then when "QCLKIN0=GMUX_1" then
    # "QCLKIN1=GMUX_2" etc.
    new_qmux_connections = []
    for connection in vpr_connections:

        # Get only those that target QCLKIN0 of a QMUX.
        if connection.dst.type != ConnectionType.CLOCK:
            continue
        if connection.src.type != ConnectionType.TILE:
            continue

        dst_cell_name, dst_pin = connection.dst.pin.split(".", maxsplit=1)
        if not dst_cell_name.startswith("QMUX") or dst_pin != "QCLKIN0":
            continue

        src_cell_name, src_pin = connection.src.pin.split("_", maxsplit=1)
        if not src_cell_name.startswith("GMUX"):
            continue

        # Add two new connections for QCLKIN1 and QCLKIN2.
        # GMUX connections are already spread along the Z axis so the Z
        # coordinate indicates the GMUX cell index.
        gmux_base = connection.src.loc.z
        for i in [1, 2]:
            gmux_idx = (gmux_base + i) % 5

            c = Connection(
                src=ConnectionLoc(
                    loc=Loc(
                        x=connection.src.loc.x,
                        y=connection.src.loc.y,
                        z=gmux_idx
                    ),
                    pin="GMUX0_IZ",
                    type=connection.src.type
                ),
                dst=ConnectionLoc(
                    loc=connection.dst.loc,
                    pin="{}.QCLKIN{}".format(dst_cell_name, i),
                    type=connection.dst.type
                ),
                is_direct=connection.is_direct
            )
            new_qmux_connections.append(c)

    vpr_connections.extend(new_qmux_connections)

    # Handle QMUX connections. Instead of making them SWITCHBOX -> TILE convert
    # to SWITCHBOX -> CLOCK
    for i, connection in enumerate(vpr_connections):

        # Process connection endpoints
        eps = [connection.src, connection.dst]
        for j, ep in enumerate(eps):

            if ep.type != ConnectionType.TILE:
                continue

            cell_name, pin = ep.pin.split("_", maxsplit=1)

            cell_index = int(cell_name[-1])
            cell_type = cell_name[:-1]
            # FIXME: The above will fail on cell with index >= 10

            # Only QMUX
            if cell_type != "QMUX":
                continue

            # Get the physical tile
            loc = loc_map.bwd[ep.loc]
            tile = phy_tile_grid[loc]

            # Find the cell in the tile
            cells = [
                c for c in tile.cells
                if c.type == "QMUX" and c.index == cell_index
            ]
            assert len(cells) == 1
            cell = cells[0]

            # Modify the endpoint
            eps[j] = ConnectionLoc(
                loc=ep.loc,
                pin="{}.{}".format(cell.name, pin),
                type=ConnectionType.CLOCK,
            )

        # Modify the connection
        vpr_connections[i] = Connection(
            src=eps[0], dst=eps[1], is_direct=connection.is_direct
        )

    return vpr_connections
Exemple #25
0
def parse_wire_mapping_table(xml_root, switchbox_grid, switchbox_types):
    """
    Parses the "DeviceWireMappingTable" section. Returns a dict indexed by
    locations.
    """
    def yield_locs_and_maps():
        """
        Yields locations and wire mappings associated with it.
        """
        RE_LOC = re.compile(r"^(Row|Col)_([0-9]+)_([0-9]+)$")

        # Rows
        xml_rows = [e for e in xml_root if e.tag.startswith("Row_")]
        for xml_row in xml_rows:

            # Decode row range
            match = RE_LOC.match(xml_row.tag)
            assert match is not None, xml_row.tag

            row_beg = int(xml_row.attrib["RowStartNum"])
            row_end = int(xml_row.attrib["RowEndNum"])

            assert row_beg == int(match.group(2)), \
                (xml_row.tag, row_beg, row_end)
            assert row_end == int(match.group(3)), \
                (xml_row.tag, row_beg, row_end)

            # Columns
            xml_cols = [e for e in xml_row if e.tag.startswith("Col_")]
            for xml_col in xml_cols:

                # Decode column range
                match = RE_LOC.match(xml_col.tag)
                assert match is not None, xml_col.tag

                col_beg = int(xml_col.attrib["ColStartNum"])
                col_end = int(xml_col.attrib["ColEndNum"])

                assert col_beg == int(match.group(2)), \
                    (xml_col.tag, col_beg, col_end)
                assert col_end == int(match.group(3)), \
                    (xml_col.tag, col_beg, col_end)

                # Wire maps
                xml_maps = [e for e in xml_col if e.tag.startswith("Stage_")]

                # Yield wire maps for each location
                for y in range(row_beg, row_end + 1):
                    for x in range(col_beg, col_end + 1):
                        yield (Loc(x=x, y=y, z=0), xml_maps)

    # Process wire maps
    wire_maps = defaultdict(lambda: {})

    RE_STAGE = re.compile(r"^Stage_([0-9])$")
    RE_JOINT = re.compile(r"^Join\.([0-9]+)\.([0-9]+)\.([0-9]+)$")
    RE_WIREMAP = re.compile(
        r"^WireMap\.(Top|Bottom|Left|Right)\.Length_([0-9])\.(.*)$")

    for loc, xml_maps in yield_locs_and_maps():
        for xml_map in xml_maps:

            # Decode stage id
            match = RE_STAGE.match(xml_map.tag)
            assert match is not None, xml_map.tag

            stage_id = int(xml_map.attrib["StageNumber"])
            assert stage_id == int(match.group(1)), \
                (xml_map.tag, stage_id)

            # Decode wire joints
            joints = {
                k: v
                for k, v in xml_map.attrib.items() if k.startswith("Join.")
            }
            for joint_key, joint_map in joints.items():

                # Decode the joint key
                match = RE_JOINT.match(joint_key)
                assert match is not None, joint_key

                pin_loc = SwitchboxPinLoc(
                    stage_id=stage_id,
                    switch_id=int(match.group(1)),
                    mux_id=int(match.group(2)),
                    pin_id=int(match.group(3)),
                    pin_direction=PinDirection.
                    INPUT  # FIXME: Are those always inputs ?
                )

                # Decode the wire name
                match = RE_WIREMAP.match(joint_map)
                assert match is not None, joint_map

                wire_hop_dir = match.group(1)
                wire_hop_len = int(match.group(2))
                wire_name = match.group(3)

                # Compute location of the tile that the wire is connected to
                if wire_hop_dir == "Top":
                    tile_loc = Loc(x=loc.x, y=loc.y - wire_hop_len, z=0)
                elif wire_hop_dir == "Bottom":
                    tile_loc = Loc(x=loc.x, y=loc.y + wire_hop_len, z=0)
                elif wire_hop_dir == "Left":
                    tile_loc = Loc(x=loc.x - wire_hop_len, y=loc.y, z=0)
                elif wire_hop_dir == "Right":
                    tile_loc = Loc(x=loc.x + wire_hop_len, y=loc.y, z=0)
                else:
                    assert False, wire_hop_dir

                # Append to the map
                wire_maps[loc][pin_loc] = (wire_name, tile_loc)

    return wire_maps