Source code for util.initial_hetero_placement

"""
===============================
FPGA heterogeneous Floorplanner
===============================

This is dedicated OpenFPGA floorplan shapes each block in the FPGA in a
classic tiling structure.
This floorplanning can is applied to homogeneous architecture out of the box,
but external information may be required for heterogeneous architecture.

This floor planner is sequential. It honors utilization constraints and
parameter constraints in the given order.


Paramater Based (*Preferred*):
------------------------------

Following figure details the various paramteres referred in this type of
floorplanning

.. rst-class:: ascii

::


                  |<---------  GRID_X  --------->|
                  |                              |
        ┌───────────┐┌─────────────┐┌──────────────┐┌─────────────┐┌───────────┐
        │           ││  top_cbx_w  ││              ││           ↑ ││           │
        │           ││<----------->││              ││  top_cbx_h↓ ││           │
        │        ┌──┘└─────────────┘└──┐        ┌──┘└─────────────┘└──┐        │
        │        │┌───────────────────┐│        │┌───────────────────┐│        │
        └────────┘│                   │└────────┘│                   │└────────┘
        ┌────────┐│                   │┌────────┐│                   │┌────────┐
        │      ↑ ││                   ││       ↑││                   ││      ↑ │
        │      | ││                   ││       |││                   ││      | │
        │      | ││                   ││       |││                   ││      | │
        │left_ | ││                   ││cby11_h|││                   ││right | │
        |cby_h ↓ ││                   ││       ↓││                   ││cby_h ↓ │
        └────────┘│                   │└────────┘│                   │└────────┘
        ┌────────┐│                   │┌────────┐│                   │┌────────┐
        │        │└───────────────────┘│        │└───────────────────┘│        │
        │        └──┐┌─────────────┐┌──┘        └──┐┌─────────────┐┌──┘        │
        │           ││   cbx11_w   ││              ││           ↑ ││           │
        │           ││<----------->││              ││   cbx11_h ↓ ││           │
        │        ┌──┘└─────────────┘└──┐        ┌──┘└─────────────┘└──┐        │
        │        │┌───────────────────┐│        │┌───────────────────┐│        │
        └────────┘│                ↑  │└────────┘│                   │└────────┘
        ┌────────┐│                |  │┌────────┐│                   │┌────────┐
        │        ││                |  ││        ││                   ││        │
        │        ││                |  ││        ││                   ││        │
        │left_   ││          clb_h |  ││        ││                   ││right   │
        │cby_w   ││                |  ││cby11_w ││                   ││cby_W   │
        │<------>││                |  ││<------>││                   ││<------>│
        └────────┘│<----------------->│└────────┘│                   │└────────┘
        ┌────────┐│      clb_w     ↓  │┌────────┐│                   │┌────────┐
        │        │└───────────────────┘│        │└───────────────────┘│        │
        │        └──┐┌─────────────┐┌──┘        └──┐┌─────────────┐┌──┘        │
        │           ││bottom_cbx_w ││              ││            ↑││           │
        │           ││<----------->││              ││bottom_cbx_h↓││           │
        └───────────┘└─────────────┘└──────────────┘└─────────────┘└───────────┘


Utilization Based
-----------------

**Ideas**:

* Optionally provide a method to apply shaping and placement to the netlist elements

This function creates a parameterized dictionary detailig `SHAPE`, `POINTS` and `PLACEMENT` information

::

    self.module_shape = {
        "SHAPE" : 'rect' and 'cross',
        "POINTS" : (w, h) or (a, b, c, d, e, f),
        "PLACEMENT" : (x_off, v_off),
    }

"""

import logging
import math
from typing import Callable

from spydrnet_physical import PROP
from spydrnet_physical.util.get_names import get_names

from spydrnet_physical.util import (FPGAGridGen, OpenFPGA_Placement_Generator)

logger = logging.getLogger("spydrnet_logs")

AREA, WIDTH, HEIGHT = 0, 1, 2

CBX_COLOR = "#d9d9f3"
CBY_COLOR = "#a8d0db"
SB_COLOR = "#ceefe4"
GRID_COLOR = "#ddd0b1"


[docs]class initial_hetero_placement(OpenFPGA_Placement_Generator): CPP = 2 """int: ``Contated-poly-pitch`` (`default`=2) """ SC_HEIGHT = 10 """int: ``Standard cell height`` (`default`=10) """ SCALE = 100 """int: Module level variable documented inline. (`default`=100) """ margins = {} """dict: Stores module database without any margin""" module_shapes_final = {} """dict: Stores module database without any margin""" module_final = {} """dict: Stores module database with margin""" s_param = { "TILE_ASPECT_RATIO": 1, "OFFSET_X": 20, "OFFSET_Y": 2, } """dict: All the shaping paramteres """
[docs] def __init__( self, grid, netlist, fpga_grid: FPGAGridGen, debug=False, areaFile=None, shapingConf=None, ): super().__init__(grid, netlist, fpga_grid) self.SC_GRID = self.SC_HEIGHT * self.CPP self.calculate_shapes() self.create_shapes() self.add_module_colors()
def add_module_colors(self): for cbx in self._top_module.get_definitions("cbx_*"): cbx.data[PROP]["COLOR"] = CBX_COLOR for cby in self._top_module.get_definitions("cby_*"): cby.data[PROP]["COLOR"] = CBY_COLOR for sb in self._top_module.get_definitions("sb_*"): sb.data[PROP]["COLOR"] = SB_COLOR for grid in self._top_module.get_definitions("grid_*"): grid.data[PROP]["COLOR"] = GRID_COLOR
[docs] def update_placement(self): pass
[docs] def create_placement(self): """ Overrides the base method to create placement information """ self.update_shapes() # Perform placement top_module = self._top_module for x_indx in range((self.fpga_size[0] * 2) + 1, 0, -1): for y_indx in range((self.fpga_size[1] * 2) + 1, 0, -1): x_off, y_off = 0, 0 inst_name = self.fpga_grid.get_top_instance(x_indx, y_indx) anchor = self.design_grid.get_x_y(x_indx - 1, y_indx - 1) try: inst = next(top_module.get_instances(f"*{inst_name}")) except StopIteration: logger.warning( "Skipping placment : %s [Not found]", inst_name) module = self.module_shapes[inst.reference.name] if isinstance(module["PLACEMENT"], Callable): x_off, y_off = module["PLACEMENT"](x_indx, y_indx) if isinstance(module["PLACEMENT"], tuple): x_off, y_off = module["PLACEMENT"] if isinstance(module["PLACEMENT"], list): x_off, y_off = module["PLACEMENT"] inst.data[PROP]["LOC_X"] = math.floor( anchor[0] + (x_off * self.CPP)) inst.data[PROP]["LOC_Y"] = math.floor( anchor[1] + (y_off * self.SC_HEIGHT) ) top_module.data[PROP]["WIDTH"] = self.design_grid.width + ( 2 * self.s_param["OFFSET_X"] * self.CPP ) top_module.data[PROP]["HEIGHT"] = self.design_grid.height + ( 2 * self.s_param["OFFSET_Y"] * self.SC_HEIGHT )
[docs] def update_placement_grid(self): """ Update two dimensional placement grid """ # Adjusting placement grids self.design_grid.offset_x = self.s_param["OFFSET_X"] * self.CPP self.design_grid.offset_y = self.s_param["OFFSET_Y"] * self.SC_HEIGHT W = self.fpga_size[0] H = self.fpga_size[1] # Set grid_clb column for i in range(2, (self.fpga_size[0] * 2) + 1, 2): self.design_grid.set_column_width( i, self.s_param["clb_w"] * self.CPP) # Set grid_clb row for i in range(2, (self.fpga_size[1] * 2) + 1, 2): self.design_grid.set_row_height( i, self.s_param["clb_h"] * self.SC_HEIGHT) for i in range(1, (self.fpga_size[0] * 2) + 2, 2): self.design_grid.set_column_width( i, self.s_param["cby11_w"] * self.CPP) for i in range(1, (self.fpga_size[1] * 2) + 2, 2): self.design_grid.set_row_height( i, self.s_param["cbx11_h"] * self.SC_HEIGHT) self.design_grid.set_row_height( 1, self.s_param["bottom_cbx_h"] * self.SC_HEIGHT ) self.design_grid.set_row_height(-1, self.s_param["top_cbx_h"] * self.SC_HEIGHT) self.design_grid.set_column_width( 1, self.s_param["left_cby_w"] * self.CPP) self.design_grid.set_column_width(-1, self.s_param["right_cby_w"] * self.CPP)
[docs] def update_shapes(self): """ This method updates the shape of all the modules based on the s_params variable """ for eachm, param in self.module_shapes.items(): if not param: logger.warning("Parameters not found for %s", eachm) continue try: module = next(self._top_module.get_definitions(eachm)) except StopIteration: logger.warning("Shape %s not found", eachm) continue shape = param.get("SHAPE", "rect") if (shape == "cross") or (shape == "custom"): points = self._scale_shape(shape, param["POINTS"]) module.data[PROP]["SHAPE"] = shape module.data[PROP]["POINTS"] = points module.data[PROP]["WIDTH"] = sum( [points[i] for i in [1, 3, 4]] ) module.data[PROP]["HEIGHT"] = sum( [points[i] for i in [0, 2, 5]] ) else: module.data[PROP]["SHAPE"] = "rect" module.data[PROP]["WIDTH"] = int(param["POINTS"][0] * self.CPP) module.data[PROP]["HEIGHT"] = int( param["POINTS"][1] * self.SC_HEIGHT)
def _scale_shape(self, shape, points): if shape == "cross": points = [ a * b for a, b in ( zip( points, ( self.SC_HEIGHT, self.CPP, self.SC_HEIGHT, self.CPP, self.CPP, self.SC_HEIGHT, ), ) ) ] if shape == "custom": pass return points
[docs] def update_shaping_param(self, update_module_shapes): """ Overwrite default configuration variables """ self.s_param.update(update_module_shapes)
@staticmethod def _get_location(x, y): """""" return 0, 0
[docs] @staticmethod def base2(number, multiple=2): """Snaps the point in multiple for 2""" return multiple * round(number / multiple)
[docs] @staticmethod def base4(number, multiple=4): """Snaps the point in multiple for 4""" return multiple * round(number / multiple)
def _get_width_height(self, area, aspect_ratio=1, width=None, height=None): """ Return the width and height given the area anad aspect ratio if any fixed values is provides (like width and height) other values is calculated without honoring aspect ratio Args: area (float): aspect_ratio (float): width (float): height (float): Return: (flota, float) """ area_um = area * self.SC_GRID if width is None and height is None: height_um = int(math.sqrt(area_um / aspect_ratio)) width_um = int(area_um / height_um) elif width: width_um = width * self.CPP height_um = int(area_um / width_um) elif height: height_um = height * self.SC_HEIGHT width_um = int(area_um / height_um) width = self.base2(width_um / self.CPP) height = self.base2(height_um / self.SC_HEIGHT) return width, height
[docs] def get_area(self, module): """ Return the area of the given module after considering the utilisation """ module_inst = next(self._top_module.get_definitions(module)) area = module_inst.data[PROP]["AREA"] area *= 1 / self.s_param[f"{module}_util"] return area
[docs] def calculate_shapes(self): """ This function compute different base variable for shaping FPGA fabric """ m = self.s_param for each_module in self._top_module.get_definitions("*"): m[f"{each_module.name}_util"] = 0.85 # TODO : Need to genrate these parameters automatically m["clb_w"], m["clb_h"] = 100, 20 m["cbx11_w"], m["cbx11_h"] = 60, 6 m["bottom_cbx_w"], m["bottom_cbx_h"] = 70, 10 m["top_cbx_w"], m["top_cbx_h"] = 70, 10 m["cby11_w"], m["cby11_h"] = 30, 12 m["left_cby_w"], m["left_cby_h"] = 40, 16 m["right_cby_w"], m["right_cby_h"] = 40, 16 self.derive_sb_paramters()
[docs] def derive_sb_paramters(self): """ This method calculated switch block dimensions from grid_clb and cb """ m = self.s_param # Dervived calcualation # Center switch box m["a"] = math.floor(m["cbx11_h"]) m["b"] = math.floor(0.5 * (m["clb_w"] - m["cbx11_w"])) m["c"] = math.floor(0.5 * (m["clb_h"] - m["cby11_h"])) m["d"] = math.floor(m["cby11_w"]) m["e"] = math.floor(0.5 * (m["clb_w"] - m["cbx11_w"])) m["f"] = math.floor(0.5 * (m["clb_h"] - m["cby11_h"])) # Left switch block dimensions m["la"] = math.floor(m["cbx11_h"]) m["lb"] = math.floor(0) m["lc"] = math.floor(0.5 * (m["clb_h"] - m["left_cby_h"])) m["ld"] = math.floor(m["left_cby_w"]) m["le"] = math.floor(0.5 * (m["clb_w"] - m["cbx11_w"])) m["lf"] = math.floor(0.5 * (m["clb_h"] - m["left_cby_h"])) # Right switch block dimensions m["ra"] = math.floor(m["cbx11_h"]) m["rb"] = math.floor(0.5 * (m["clb_w"] - m["cbx11_w"])) m["rc"] = math.floor(0.5 * (m["clb_h"] - m["right_cby_h"])) m["rd"] = math.floor(m["right_cby_w"]) m["re"] = math.floor(0) m["rf"] = math.floor(0.5 * (m["clb_h"] - m["right_cby_h"])) # Top switch block dimensions m["ta"] = math.floor(m["top_cbx_h"]) m["tb"] = math.floor(0.5 * (m["clb_w"] - m["top_cbx_w"])) m["tc"] = math.floor(0) m["td"] = math.floor(m["cby11_w"]) m["te"] = math.floor(0.5 * (m["clb_w"] - m["top_cbx_w"])) m["tf"] = math.floor(0.5 * (m["clb_h"] - m["cby11_h"])) # Bottom switch block dimensions m["ba"] = math.floor(m["bottom_cbx_h"]) m["bb"] = math.floor(0.5 * (m["clb_w"] - m["bottom_cbx_w"])) m["bc"] = math.floor(0.5 * (m["clb_h"] - m["cby11_h"])) m["bd"] = math.floor(m["cby11_w"]) m["be"] = math.floor(0.5 * (m["clb_w"] - m["bottom_cbx_w"])) m["bf"] = math.floor(0) self.update_placement_grid()
def create_shapes(self, w_override=None, h_override=None, shape_all=False): m = self.s_param W = w_override or self.fpga_size[0] H = h_override or self.fpga_size[1] # Placement is called with PLACEMENT(x, y, instance, module_shapes, variables) self.module_shapes = { "grid_clb": { "SHAPE": "rect", "POINTS": [m["clb_w"], m["clb_h"]], "PLACEMENT": [0, 0], }, # Common connection blocks [Auto calculated] "cbx_1__0_": { "SHAPE": "rect", "POINTS": [m["bottom_cbx_w"], m["bottom_cbx_h"]], "PLACEMENT": [0.5 * (m["clb_w"] - m["bottom_cbx_w"]), 0], }, "cbx_1__1_": { "SHAPE": "rect", "POINTS": [m["cbx11_w"], m["cbx11_h"]], "PLACEMENT": [0.5 * (m["clb_w"] - m["cbx11_w"]), 0], }, f"cbx_1__{H}_": { "SHAPE": "rect", "POINTS": [m["top_cbx_w"], m["top_cbx_h"]], "PLACEMENT": [0.5 * (m["clb_w"] - m["top_cbx_w"]), 0], }, "cby_0__1_": { "SHAPE": "rect", "POINTS": [m["left_cby_w"], m["left_cby_h"]], "PLACEMENT": [0, 0.5 * (m["clb_h"] - m["left_cby_h"])], }, f"cby_{W}__1_": { "SHAPE": "rect", "POINTS": [m["right_cby_w"], m["right_cby_h"]], "PLACEMENT": [0, 0.5 * (m["clb_h"] - m["right_cby_h"])], }, "cby_1__1_": { "SHAPE": "rect", "POINTS": [m["cby11_w"], m["cby11_h"]], "PLACEMENT": [0, 0.5 * (m["clb_h"] - m["cby11_h"])], }, # Common swith blocks [Auto calculated] "sb_0__0_": { "SHAPE": "cross", "POINTS": [m["ba"], 0, m["lc"], m["ld"], m["be"], 0], "PLACEMENT": [0, 0], }, "sb_0__1_": { "SHAPE": "cross", "POINTS": [m["la"], m["lb"], m["lc"], m["ld"], m["le"], m["lf"]], "PLACEMENT": [0, -0.5 * (m["clb_h"] - m["left_cby_h"])], }, f"sb_0__{H}_": { "SHAPE": "cross", "POINTS": [m["ta"], 0, 0, m["ld"], m["te"], m["lf"]], "PLACEMENT": [0, -0.5 * (m["clb_h"] - m["left_cby_h"])], }, "sb_1__0_": { "SHAPE": "cross", "POINTS": [m["ba"], m["bb"], m["bc"], m["bd"], m["be"], m["bf"]], "PLACEMENT": [-0.5 * (m["clb_w"] - m["bottom_cbx_w"]), 0], }, "sb_1__1_": { "SHAPE": "cross", "POINTS": [m["a"], m["b"], m["c"], m["d"], m["e"], m["f"]], "PLACEMENT": [ -0.5 * (m["clb_w"] - m["cbx11_w"]), -0.5 * (m["clb_h"] - m["cby11_h"]), ], }, f"sb_1__{H}_": { "SHAPE": "cross", "POINTS": [m["ta"], m["tb"], m["tc"], m["td"], m["te"], m["tf"]], "PLACEMENT": [ -0.5 * (m["clb_w"] - m["top_cbx_w"]), -0.5 * (m["clb_h"] - m["cby11_h"]), ], }, f"sb_{W}__0_": { "SHAPE": "cross", "POINTS": [m["ba"], m["bb"], m["rc"], m["rd"], 0, 0], "PLACEMENT": [-0.5 * (m["clb_w"] - m["bottom_cbx_w"]), 0], }, f"sb_{W}__1_": { "SHAPE": "cross", "POINTS": [m["ra"], m["rb"], m["rc"], m["rd"], m["re"], m["rf"]], "PLACEMENT": [ -0.5 * (m["clb_w"] - m["cbx11_w"]), -0.5 * (m["clb_h"] - m["right_cby_h"]), ], }, f"sb_{W}__{H}_": { "SHAPE": "cross", "POINTS": [m["ta"], m["tb"], 0, m["rd"], 0, m["rf"]], "PLACEMENT": [ -0.5 * (m["clb_w"] - m["top_cbx_w"]), -0.5 * (m["clb_h"] - m["right_cby_h"]), ], }, } unshaped_modules = set(get_names(self._top_module.get_definitions())) - \ set(list(self.module_shapes.keys())) if shape_all: for modules in unshaped_modules: self.module_shapes[modules] = { "SHAPE": "rect", "POINTS": [int(200/self.CPP), int(200/self.SC_HEIGHT)], "PLACEMENT": [0, 0], } return unshaped_modules