gridding.py 10.4 KB
Newer Older
sakundu committed
1 2 3 4 5 6 7 8
### Implement the gridding component
### Input:  a list of macros (each macro has a width and a height)
### Output: best choice of n_rows and n_cols


import os
from math import floor
from math import ceil
ZhiangWang033 committed
9 10 11
import shutil
import sys
sys.path.append('./utils')
sakundu committed
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

# Note that we use the center point of an object (macro, grid)
# to represent the position of that object
# Define the grid class
class Grid:
    def __init__(self, grid_id, width, height, x, y):
        self.grid_id_ = grid_id
        self.width_   = width
        self.height_  = height
        self.x_       = x
        self.y_       = y
        self.placed_  = False  # if there is macro placed on the center of this grid
        self.macros_id_ = [] # the id of macros intersecting with this grid

# Check if there is an overlap with other placed macros
def CheckOverlap(lx, ly, ux, uy, macro_box):
    # macro_box : bounding boxes for all the placed macros
    # lx, ly, ux, uy for current macro
    for bbox in macro_box:
        bbox_lx, bbox_ly, bbox_ux, bbox_uy = bbox
        if (lx >= bbox_ux or ly >= bbox_uy or ux <= bbox_lx or uy <= bbox_ly):
            pass
        else:
            return True  # there is an overlap

    return False



# Place macros one by one
# n = num_cols
def PlaceMacros(macro_map, grid_list, chip_width, chip_height, n):
    ### All the macro must be placed on the center of one grid

    #Initialize the position of macros
    macro_bbox = []

    # Place macro one by one
    for key, value in macro_map.items():
        width = value[0]
ZhiangWang033 committed
52
        height = value[1]
sakundu committed
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
        macro_id = key
        placed_flag = False
        for grid in grid_list:
            if (grid.placed_ == True):
                continue # this grid has been occupied

            # if the macro is placed on this
            x = grid.x_
            y = grid.y_
            lx = x - width / 2.0
            ly = y - height / 2.0
            ux = lx + width
            uy = ly + height

            # check if the macro is within the outline
            if (ux > chip_width or uy > chip_height):
                continue

            # check if there is an overlap with other macros
            if (CheckOverlap(lx, ly, ux, uy, macro_bbox) == True):
                continue


            # place current macro on this grid
            grid.placed_ = True
            placed_flag  = True

            # update the canvas status
            macro_bbox.append([lx, ly, ux, uy])

            grid_width = grid.width_
            grid_height = grid.height_
            # update covered grids
            min_col_id = floor(lx / grid_width)
ZhiangWang033 committed
87
            max_col_id = floor(ux / grid_width)
sakundu committed
88
            min_row_id = floor(ly / grid_height)
ZhiangWang033 committed
89
            max_row_id = floor(uy / grid_height)
sakundu committed
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
            for i in range(min_row_id, max_row_id + 1):
                for j in range(min_col_id, max_col_id + 1):
                    grid_id = i * n + j # n is the num_cols
                    grid_list[grid_id].macros_id_.append(macro_id)
            break # stop search remaining candidates

        # cannot find a valid position for the macro
        if (placed_flag == False):
            return False

    return  True

# Define the gridding function
def Gridding(macro_width_list, macro_height_list,
             chip_width, chip_height, tolerance = 0.1,
             min_n_rows = 10, min_n_cols = 10,
             max_n_rows = 100, max_n_cols = 100,
ZhiangWang033 committed
107 108 109
             max_rows_times_cols = 3000,
             min_rows_times_cols = 500,
             max_aspect_ratio = 1.5):
sakundu committed
110 111 112 113 114 115 116 117 118 119 120 121 122
    ### Sort all the macros in a non-decreasing order
    if (len(macro_width_list) != len(macro_height_list)):
      print("[Error] The macro information is wrong!!!")
      exit()

    ### Sort all the macros based on area in a non-decreasing order
    macro_map = {  }
    for i in range(len(macro_width_list)):
        macro_map[i] = [macro_width_list[i], macro_height_list[i]]

    macro_map = dict(sorted(macro_map.items(), key=lambda item: item[1][0] * item[1][1], reverse = True))
    macro_bbox = [] # (lx, ly, ux, uy) for each bounding box

ZhiangWang033 committed
123 124 125 126 127 128 129 130
    print("*"*80)
    print("[INFO] Outline Information :  outline_width =", chip_width,  "  outline_height =", chip_height)
    print("\n")
    print("[INFO] Sorted Macro Information")
    for key, value in macro_map.items():
        print("macro_" + str(key), "  macro_width =", round(value[0], 2), "  macro_height =", round(value[1], 2), "  macro_area =", round(value[0] * value[1], 2))
    print("\n")

sakundu committed
131 132 133 134 135 136 137 138 139 140 141
    # we use m for max_n_rows and n for max_n_cols
    m_best = -1
    n_best = -1
    best_cost = 2.0 # cost should be less than 2.0 based on definition
    choice_map = {  }

    for m in range(min_n_rows, max_n_rows + 1):
        choice_map[m] = {  }
        for n in range(min_n_cols, max_n_cols + 1):
            if (m * n > max_rows_times_cols):
                break
ZhiangWang033 committed
142 143
            if (m * n < min_rows_times_cols):
                continue
sakundu committed
144 145 146 147 148

            ### Step1:  Divide the canvas into grids
            ### We arrange all the grids in a row-major manner
            grid_height = chip_height / m
            grid_width  = chip_width / n
ZhiangWang033 committed
149 150 151 152 153 154
            if (grid_height / grid_width > max_aspect_ratio):
                continue

            if (grid_width / grid_height > max_aspect_ratio):
                continue

sakundu committed
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
            grid_list = []
            for i in range(m):
                for j in range(n):
                    x = (j + 0.5) * grid_width
                    y = (i + 0.5) * grid_height
                    grid_id = len(grid_list)
                    grid_list.append(Grid(grid_id, grid_width, grid_height, x, y))

            ### Place macros one by one
            if (PlaceMacros(macro_map, grid_list, chip_width, chip_height, n) == False):
                continue
            else:
                ### Calculate the cost
                total_grid_width = 0.0
                total_grid_height = 0.0
                for grid in grid_list:
                    if (len(grid.macros_id_) > 0):
                        total_grid_width += grid.width_
                        total_grid_height += grid.height_

                # calculate h_cost
                cost = 1.0 - sum(macro_width_list) / total_grid_width
                cost += 1.0 - sum(macro_height_list) / total_grid_height
                choice_map[m][n] = cost
                if (cost < best_cost):
                    best_cost = cost
                    m_best = m
                    n_best = n
    m_opt = m_best
    n_opt = n_best
    num_grids_opt = m_opt * n_opt

ZhiangWang033 committed
187 188 189
    print("m_best = ", m_best)
    print("n_best = ", n_best)
    print("tolerance = ", tolerance)
sakundu committed
190 191
    for [m, m_map] in choice_map.items():
        for [n, cost] in m_map.items():
ZhiangWang033 committed
192
            print("m = ", m , "  n = ", n, "  cost = ", cost)
sakundu committed
193 194 195 196 197 198 199 200 201
            if ((cost <= (1.0 + tolerance) * best_cost) and (m * n < num_grids_opt)):
                m_opt = m
                n_opt = n
                num_grids_opt = m * n

    print("[INFO] Optimal configuration :  num_rows = ", m_opt, " num_cols = ", n_opt)
    return m_opt, n_opt


ZhiangWang033 committed
202
class GriddingLefDefInterface:
ZhiangWang033 committed
203 204 205 206
    def __init__(self, src_dir, design, setup_file = "setup.tcl", tolerance = 0.05,
                 halo_width = 5.0, min_n_rows = 10, min_n_cols = 10, max_n_rows = 128,
                 max_n_cols = 128, max_rows_times_cols = 2500,  min_rows_times_cols = 500,
                 max_aspect_ratio = 1.5):
ZhiangWang033 committed
207 208 209 210
        self.src_dir = src_dir
        self.design = design
        self.setup_file = setup_file
        self.tolerance = tolerance
211
        self.halo_width = halo_width
ZhiangWang033 committed
212 213 214 215 216
        self.min_n_rows = min_n_rows
        self.min_n_cols = min_n_cols
        self.max_n_rows = max_n_rows
        self.max_n_cols = max_n_cols
        self.max_rows_times_cols = max_rows_times_cols
ZhiangWang033 committed
217 218
        self.min_rows_times_cols = min_rows_times_cols
        self.max_aspect_ratio = max_aspect_ratio
ZhiangWang033 committed
219 220 221 222 223 224 225 226 227 228
        self.macro_width_list = []
        self.macro_height_list = []
        self.chip_width = 0.0
        self.chip_height = 0.0
        self.num_std_cells = 0
        self.extract_hypergraph_file = self.src_dir + '/utils/extract_hypergraph.tcl'
        self.openroad_exe = self.src_dir + "/utils/openroad"

        self.GenerateHypergraph()
        self.ExtractInputs()
ZhiangWang033 committed
229 230 231 232 233 234
        self.m_opt, self.n_opt = Gridding(self.macro_width_list, self.macro_height_list,
                                          self.chip_width, self.chip_height, self.tolerance,
                                          self.min_n_rows, self.min_n_cols,
                                          self.max_n_rows, self.max_n_cols,
                                          self.max_rows_times_cols, self.min_rows_times_cols,
                                          self.max_aspect_ratio)
ZhiangWang033 committed
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291

    def GetNumRows(self):
        return self.m_opt

    def GetNumCols(self):
        return self.n_opt

    def GetChipWidth(self):
        return self.chip_width

    def GetChipHeight(self):
        return self.chip_height

    def GetNumStdCells(self):
        return self.num_std_cells


    def GenerateHypergraph(self):
        # Extract hypergraph from netlist
        temp_file = os.getcwd() + "/extract_hypergraph.tcl"
        cmd = "cp " + self.setup_file + " " + temp_file
        os.system(cmd)

        with open(self.extract_hypergraph_file) as f:
            content = f.read().splitlines()
        f.close()

        f = open(temp_file, "a")
        f.write("\n")
        for line in content:
            f.write(line + "\n")
        f.close()

        cmd = self.openroad_exe + " " + temp_file
        os.system(cmd)

        cmd = "rm " + temp_file
        os.system(cmd)

    def ExtractInputs(self):
        file_name = os.getcwd() + "/rtl_mp/" + self.design + ".hgr.outline"
        with open(file_name) as f:
            content = f.read().splitlines()
        f.close()

        items = content[0].split()
        self.chip_width = float(items[2]) - float(items[0])
        self.chip_height = float(items[3]) - float(items[1])

        file_name = os.getcwd() + "/rtl_mp/" + self.design + ".hgr.instance"
        with open(file_name) as f:
            content = f.read().splitlines()
        f.close()

        for line in content:
            items = line.split()
            if (items[1] == "1"):
292 293
                self.macro_width_list.append(float(items[4]) - float(items[2]) + 2 * self.halo_width)
                self.macro_height_list.append(float(items[5]) - float(items[3]) + 2 * self.halo_width)
ZhiangWang033 committed
294 295 296 297 298 299 300
            else:
                self.num_std_cells += 1

        rpt_dir = os.getcwd() + "/rtl_mp"
        shutil.rmtree(rpt_dir)


sakundu committed
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322