Source code for fabulous.fabric_definition.supertile

"""Supertile definition for FPGA fabric.

This module contains the `SuperTile` class, which represents a composite tile made
up of multiple smaller, individual tiles. Supertiles allow for the creation of more
larger, complex and hierarchical structures within the FPGA fabric, combining different
functionalities into a single, reusable block.
"""

from collections.abc import Generator
from dataclasses import dataclass, field
from decimal import Decimal
from pathlib import Path

from fabulous.fabric_definition.bel import Bel
from fabulous.fabric_definition.define import Side
from fabulous.fabric_definition.port import Port
from fabulous.fabric_definition.tile import Tile


@dataclass
[docs] class SuperTile: """Store the information about a super tile. Attributes ---------- name : str The name of the super tile. tileDir : Path Path to the tile directory. tiles : list[Tile] The list of tiles that make up the super tile. tileMap : list[list[Tile]] The map of the tiles that make up the super tile bels : list[Bel] The list of bels of that the super tile contains withUserCLK : bool Whether the super tile has a userCLK port. Default is False. """ name: str tileDir: Path tiles: list[Tile] tileMap: list[list[Tile]] bels: list[Bel] = field(default_factory=list) withUserCLK: bool = False
[docs] def getPortsAroundTile(self) -> dict[str, list[list[Port]]]: """Return all the ports that are around the supertile. The dictionary key is the location of where the tile is located in the supertile map with the format of "X{x}Y{y}", where x is the x coordinate of the tile and y is the y coordinate of the tile. The top left tile will have key "00". Returns ------- dict[str, list[list[Port]]] The dictionary of the ports around the super tile. """ ports = {} for y, row in enumerate(self.tileMap): for x, tile in enumerate(row): if self.tileMap[y][x] is None: continue ports[f"{x},{y}"] = [] if y - 1 < 0 or self.tileMap[y - 1][x] is None: ports[f"{x},{y}"].append(tile.getNorthSidePorts()) if x + 1 >= len(self.tileMap[y]) or self.tileMap[y][x + 1] is None: ports[f"{x},{y}"].append(tile.getEastSidePorts()) if y + 1 >= len(self.tileMap) or self.tileMap[y + 1][x] is None: ports[f"{x},{y}"].append(tile.getSouthSidePorts()) if x - 1 < 0 or self.tileMap[y][x - 1] is None: ports[f"{x},{y}"].append(tile.getWestSidePorts()) return ports
def __iter__(self) -> Generator[tuple[tuple[int, int], Tile], None, None]: """Iterate over all sub-tiles in the supertile.""" for x, row in enumerate(self.tileMap): for y, tile in enumerate(row): if tile is not None: yield (x, y), tile
[docs] def getInternalConnections(self) -> list[tuple[list[Port], int, int]]: """Return all the internal connections of the supertile. Returns ------- list[tuple[list[Port], int, int]] A list of tuples which contains the internal connected port and the x and y coordinate of the tile. """ internalConnections = [] for y, row in enumerate(self.tileMap): for x, tile in enumerate(row): if ( 0 <= y - 1 < len(self.tileMap) and self.tileMap[y - 1][x] is not None ): internalConnections.append((tile.getNorthSidePorts(), x, y)) if ( 0 <= x + 1 < len(self.tileMap[0]) and self.tileMap[y][x + 1] is not None ): internalConnections.append((tile.getEastSidePorts(), x, y)) if ( 0 <= y + 1 < len(self.tileMap) and self.tileMap[y + 1][x] is not None ): internalConnections.append((tile.getSouthSidePorts(), x, y)) if ( 0 <= x - 1 < len(self.tileMap[0]) and self.tileMap[y][x - 1] is not None ): internalConnections.append((tile.getWestSidePorts(), x, y)) return internalConnections
@property
[docs] def max_width(self) -> int: """Return the maximum width of the supertile.""" return max(len(i) for i in self.tileMap)
@property
[docs] def max_height(self) -> int: """Return the maximum height of the supertile.""" return len(self.tileMap)
[docs] def get_min_die_area( self, x_pitch: Decimal, y_pitch: Decimal, x_pin_thickness_mult: Decimal = Decimal(1), y_pin_thickness_mult: Decimal = Decimal(1), edge_offset: int = 2, ) -> tuple[Decimal, Decimal]: """Calculate minimum SuperTile dimensions based on IO pin track requirements. Takes the maximum per-side IO pin count across all constituent subtiles as a conservative upper bound, then derives the minimum physical width and height required. See ``Tile.get_min_die_area`` for the track-based derivation. Parameters ---------- x_pitch : Decimal Vertical-layer track pitch (for north/south pins). y_pitch : Decimal Horizontal-layer track pitch (for east/west pins). x_pin_thickness_mult : Decimal Number of tracks each north/south pin spans, by default 1. y_pin_thickness_mult : Decimal Number of tracks each east/west pin spans, by default 1. edge_offset : int, optional Reserved tracks at tile edge, by default 2. Returns ------- tuple[Decimal, Decimal] (min_width, min_height) """ max_north = 0 max_south = 0 max_west = 0 max_east = 0 for subtile in self.tiles: max_north = max(max_north, subtile.get_port_count(Side.NORTH)) max_south = max(max_south, subtile.get_port_count(Side.SOUTH)) max_west = max(max_west, subtile.get_port_count(Side.WEST)) max_east = max(max_east, subtile.get_port_count(Side.EAST)) x_io_count = Decimal(max(max_north, max_south)) min_width_io = (x_io_count * x_pin_thickness_mult + edge_offset) * x_pitch y_io_count = Decimal(max(max_west, max_east)) min_height_io = (y_io_count * y_pin_thickness_mult + edge_offset) * y_pitch return min_width_io, min_height_io