clustering.py 23.4 KB
Newer Older
sakundu committed
1 2 3 4 5
import os
import time
import shutil
import sys
sys.path.append('./utils')
ZhiangWang033 committed
6
sys.path.append('../../FormatTranslators/src')
sakundu committed
7

8
from math import sqrt
ZhiangWang033 committed
9 10 11
from FormatTranslators import Port
from FormatTranslators import Macro
from FormatTranslators import MacroPin
ZhiangWang033 committed
12 13
import collections
import sortedcontainers
14

ZhiangWang033 committed
15 16 17 18 19 20 21 22 23
class Vertex:
    def __init__(self, vertex_id, name, type, x, y, width, height):
        self.vertex_id = vertex_id
        self.name = name
        self.type = type
        self.x = x
        self.y = y
        self.width = width
        self.height = height
ZhiangWang033 committed
24
        self.orient = "N"
ZhiangWang033 committed
25 26
        self.group_id = -1
        self.fanouts = set()
ZhiangWang033 committed
27 28
        self.macro_pins = set()  # only for macros
        self.group_fanouts = sortedcontainers.SortedSet()
ZhiangWang033 committed
29

30
class Clustering:
ZhiangWang033 committed
31
    def __init__(self, design, src_dir, fixed_file, n_cols = 27, n_rows = 23,
ZhiangWang033 committed
32
                 global_net_threshold = 500,
ZhiangWang033 committed
33 34
                 Nparts = 500, setup_file = "setup.tcl",
                 openroad_exe = "openroad"):
35 36 37 38
        """
        parameter: design,  help="design_name: ariane, MegaBoom_x2 ", type = str
        parameter: src_dir, help="directory for source codes", type = str
        parameter: fixed_file, help="fixed file generated by grouping", type = str
ZhiangWang033 committed
39
        parameter: global_net_threshold, help="large net threshold", type = int
ZhiangWang033 committed
40
        parameter: n_cols, n_rows from Gridding codes
41 42 43 44 45 46 47
        parameter: Nparts,  help = "number of clusters (only for hmetis, default  = 500)", type = int
        parameter: setup_file, help = "setup file for openroad (default = setup.tcl)", type = str
        """
        # initialize parameters
        self.design = design
        self.src_dir = src_dir
        self.fixed_file = fixed_file
ZhiangWang033 committed
48 49 50 51
        self.merge_threshold = 0.0
        self.breakup_threshold = 0.0
        self.closeness = 0.0
        self.global_net_threshold = global_net_threshold
52 53
        self.Nparts = Nparts
        self.setup_file = setup_file
ZhiangWang033 committed
54 55 56 57 58
        self.n_cols = n_cols
        self.n_rows = n_rows
        self.soft_macro_width = 0.0
        self.floorplan_width = 0.0
        self.floorplan_height = 0.0
ZhiangWang033 committed
59

60 61
        # Specify the location of hmetis exe and openroad exe and other utilities
        self.hmetis_exe = src_dir + "/utils/hmetis"
ZhiangWang033 committed
62
        self.openroad_exe = openroad_exe
63 64 65
        self.extract_hypergraph_file  = src_dir + "/utils/extract_hypergraph.tcl"

        # set up temp report directory
ZhiangWang033 committed
66
        rpt_dir = os.getcwd() + "/rtl_mp"
67
        self.hypergraph_file = rpt_dir + "/" + design + ".hgr"
ZhiangWang033 committed
68
        self.instance_name_file = self.hypergraph_file + ".vertex"
69
        self.outline_file = self.hypergraph_file + ".outline"
ZhiangWang033 committed
70 71
        self.pb_netlist_file = os.getcwd() + "/" + design + ".pb.txt"
        self.plc_file = os.getcwd() + "/" + design + ".plc"
72

ZhiangWang033 committed
73 74 75 76 77 78 79 80 81 82 83 84 85
        self.vertices = []
        self.hyperedges = []

        self.fixed_group_id = 0
        self.group_id_list = []
        self.group_id = 0

        # read the netlist information
        self.GenerateHypergraph() # Extract netlist information from lef/def/v
        self.hMetisPartitioner()  # Partition the hypergraph
        self.BreakClusters()  # Break clusters spreading apart
        while not self.MergeClusters():  # Merge clusters
            pass
ZhiangWang033 committed
86 87 88
        print("[INFO] After merge :  num_clusters = ", len(self.group_id_list))
        self.GenerateProtocolBufferNetlist()
        self.WritePlacementFile()
ZhiangWang033 committed
89 90 91 92 93 94 95 96 97 98

    def AddMacroPinToMacro(self, macro_pin_vertex_id):
        if (self.vertices[macro_pin_vertex_id].type != "macro_pin"):
            return

        macro_name = ""
        items = self.vertices[macro_pin_vertex_id].name.split('/')
        for i in range(len(items) - 1):
            macro_name += items[i] + '/'
        macro_name = macro_name[:-1]
99

ZhiangWang033 committed
100 101 102 103
        for vertex in self.vertices:
            if (vertex.name == macro_name):
                vertex.fanouts.add(macro_pin_vertex_id)
                return
104 105 106 107

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

ZhiangWang033 committed
111
        with open(self.extract_hypergraph_file) as f:
112 113 114 115 116 117 118 119 120
            content = f.read().splitlines()
        f.close()

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

ZhiangWang033 committed
121
        cmd = self.openroad_exe + " " + temp_file
122 123 124 125 126
        os.system(cmd)

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

ZhiangWang033 committed
127
        # read vertices
128 129 130 131
        with open(self.instance_name_file) as f:
            content = f.read().splitlines()
        f.close()

ZhiangWang033 committed
132 133 134
        for i in range(len(content)):
            items = content[i].split()
            self.vertices.append(Vertex(i, items[0], items[1], float(items[2]), float(items[3]), float(items[4]), float(items[5])))
ZhiangWang033 committed
135 136
            if (self.vertices[-1].type == "macro"):
                self.vertices[-1].orient = items[6]
137

ZhiangWang033 committed
138
        # read hyperedges
139 140 141 142 143 144
        with open(self.hypergraph_file) as f:
            content = f.read().splitlines()
        f.close()

        items = content[0].split()
        num_hyperedges = int(items[0])
ZhiangWang033 committed
145 146 147 148 149 150 151 152 153 154 155 156
        num_vertices   = int(items[1])

        for i in range(num_hyperedges):
            items = content[i+1].split()
            # ignore all the global nets
            if (len(items) > self.global_net_threshold or len(items) == 1):
                continue
            hyperedge = [int(item) - 1 for item in items]
            self.hyperedges.append(hyperedge)
            self.AddMacroPinToMacro(hyperedge[0])
            for i in range(1, len(hyperedge)):
                self.vertices[hyperedge[0]].fanouts.add(hyperedge[i])
157 158

        f = open(self.hypergraph_file, "w")
ZhiangWang033 committed
159
        line = str(len(self.hyperedges)) + " " + str(len(self.vertices)) + "\n"
sakundu committed
160
        f.write(line)
ZhiangWang033 committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
        for hyperedge in self.hyperedges:
            line = ""
            for vertex in hyperedge:
                line += str(vertex + 1) + " "
            f.write(line + "\n")
        f.close()

        # read boundary information
        with open(self.outline_file) as f:
            content = f.read().splitlines()
        f.close()

        items = content[0].split()
        floorplan_lx = float(items[0])
        floorplan_ly = float(items[1])
        floorplan_ux = float(items[2])
        floorplan_uy = float(items[3])

        canvas_width = floorplan_ux - floorplan_lx
        canvas_height = floorplan_uy - floorplan_ly
ZhiangWang033 committed
181 182 183
        self.floorplan_width = canvas_width
        self.floorplan_height = canvas_height

ZhiangWang033 committed
184 185
        self.breakup_threshold = sqrt(canvas_width * canvas_height / 16.0)
        self.closeness = self.breakup_threshold / 2.0
ZhiangWang033 committed
186
        self.soft_macro_width = canvas_width / self.n_cols
ZhiangWang033 committed
187 188 189 190

        self.fixed_group_id = -1
        with open(self.fixed_file) as f:
            content = f.read().splitlines()
191 192
        f.close()

ZhiangWang033 committed
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
        for i in range(len(content)):
            group_id = int(content[i])
            self.fixed_group_id = max(self.fixed_group_id, group_id)
            self.vertices[i].group_id = group_id

        self.fixed_group_id += 1
        self.Nparts += self.fixed_group_id

        print("[INFO] Breakup threshold : ", self.breakup_threshold)
        print("[INFO] Merge closeness : ", self.closeness)

    def hMetisPartitioner(self):
        # Partitioning the hypergraph using hmetis
        # The parameter configuration is the same as Google Brain paper
        # UBfactor = 5
        # Nruns    = 10
        # CType    = 5
        # RType    = 3
        # Vcycle   = 3
        # The random seed is 0 by default (in our implementation)
        # We use the hMetis C++ API to implement hMetis
        cmd = self.hmetis_exe + " " + self.hypergraph_file + " "
        cmd += self.fixed_file + " "
        cmd += str(self.Nparts) + " 5 10 5 3 3 0 0"
ZhiangWang033 committed
217
        os.system(cmd)
ZhiangWang033 committed
218

ZhiangWang033 committed
219
        solution_file = self.hypergraph_file + ".part." + str(self.Nparts)
ZhiangWang033 committed
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 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 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
        # read solution vector
        with open(solution_file) as f:
            content = f.read().splitlines()
        f.close()

        for i in range(len(self.vertices)):
            if (self.vertices[i].group_id > -1):
                continue
            group_id = int(content[i]) + self.fixed_group_id
            self.vertices[i].group_id = group_id

        for vertex in self.vertices:
            group_id = vertex.group_id
            self.group_id = max(self.group_id, group_id)
            if group_id not in self.group_id_list:
                self.group_id_list.append(group_id)

        self.merge_threshold = len(self.vertices) // len(self.group_id_list) // 4
        print("[INFO] Merge threshold : ", self.merge_threshold)


    def GetBoundingBox(self, group_id):
        lx = 1e9
        ly = 1e9
        ux = -1e9
        uy = -1e9

        for vertex in self.vertices:
            if (vertex.group_id == group_id):
                lx = min(lx, vertex.x)
                ly = min(ly, vertex.y)
                ux = max(ux, vertex.x)
                uy = max(uy, vertex.y)

        return [lx, ly, ux, uy]


    def GetWeightedCenter(self, group_id):
        x_weighted_sum = 0.0
        y_weighted_sum = 0.0
        divisor = 0.0
        for vertex in self.vertices:
            if (vertex.group_id == group_id):
                if (vertex.type != "stdcell"):
                    continue

                area = vertex.width * vertex.height
                x_weighted_sum += vertex.x * area
                y_weighted_sum += vertex.y * area
                divisor += area

        if divisor == 0.0:
            return 0.0, 0.0

        return x_weighted_sum / divisor, y_weighted_sum / divisor

    def Bucket(self, x, threshold, center, min_x, max_x):
        if (max_x - min_x < threshold):
            return 0

        if x > center:
            return int(0.5 + (x - center) / threshold)
        else:
            return int(-0.5 + (x - center) / threshold)


    def BreakClusters(self):
        # In this step, we break clusters which spread around the canvas.
        self.group_vertices = {  }
        for i in range(len(self.vertices)):
            group_id = self.vertices[i].group_id
            if group_id not in self.group_vertices:
                self.group_vertices[group_id] = [i]
            else:
                self.group_vertices[group_id].append(i)

        self.breakup_threshold = 400.006
        self.group_id_list.sort()
        for group_id in self.group_id_list:
            gcell_vs_group_id = {  }
            group_box = self.GetBoundingBox(group_id)
            if (group_box[2] - group_box[0] > self.breakup_threshold or
                    group_box[3] - group_box[1] > self.breakup_threshold):
                group_x, group_y = self.GetWeightedCenter(group_id)
                for vertex_id in self.group_vertices[group_id]:
                    vertex = self.vertices[vertex_id]
                    xb = self.Bucket(vertex.x, self.breakup_threshold, group_x, group_box[0], group_box[2])
                    yb = self.Bucket(vertex.y, self.breakup_threshold, group_y, group_box[1], group_box[3])

                    if xb == 0 and yb == 0:
                        continue

                    if (xb, yb) not in gcell_vs_group_id:
                        self.group_id += 1
                        gcell_vs_group_id[(xb, yb)] = self.group_id

                    self.vertices[vertex_id].group_id = gcell_vs_group_id[(xb, yb)]


        self.group_id_list = []
        for vertex in self.vertices:
            if vertex.group_id not in self.group_id_list:
                self.group_id_list.append(vertex.group_id)

        print("After break up clusters : ", len(self.group_id_list))


    def IsClose(self, group_loc_a, group_loc_b):
        dist = abs(group_loc_a[0] - group_loc_b[0])
        dist += abs(group_loc_a[1] - group_loc_b[1])
        return dist <= self.closeness



    def MergeClusters(self):
        # In this step, we merge small groups to the most adjacent group if they are
        # with a certain distance
        # self.merge_threshold is the minimum number of vertices in a group
        # self.closeness is the distance used to determine if two vertices are close or not
        adj_matrix = [0] * (self.group_id + 1) * (self.group_id + 1)
        for vertex in self.vertices:
            groups = set()
            groups.add(vertex.group_id)
            for vertex_id in vertex.fanouts:
                groups.add(self.vertices[vertex_id].group_id)
            for i in groups:
                for j in groups:
                    if i == j:
                        continue
                    adj_matrix[i * (self.group_id + 1) + j] += 1

        group_locs = [None] * (self.group_id + 1)
        for group_id in self.group_id_list:
            group_x, group_y = self.GetWeightedCenter(group_id)
            group_locs[group_id] = [group_x, group_y]

        group_size = [0] * (self.group_id + 1)
        for vertex in self.vertices:
            group_size[vertex.group_id] += 1


        group_vertices = [ [] for i in range(self.group_id + 1) ]
        for i in range(len(self.vertices)):
            group_vertices[self.vertices[i].group_id].append(i)

        finished = True
        # Going through the small clusters, find the highest adjacency group
        # within the self.closeness
        self.group_id_list.sort()
        for group_id in self.group_id_list:
            if (group_size[group_id] > self.merge_threshold):
                continue

            max_adj_grp = -1
            max_adj = 0
            for i in self.group_id_list:
                if (group_id == i):
                    continue
                # get number of connections
                adj = adj_matrix[i * (self.group_id + 1) + group_id]
                # check if connected
                if (adj == 0):
                    continue

                # check if close to each other
                if (self.IsClose(group_locs[group_id], group_locs[i]) == True and max_adj < adj):
                    max_adj = adj
                    max_adj_grp = i

            if (max_adj_grp > -1):
                for vertex_id in group_vertices[group_id]:
                    self.vertices[vertex_id].group_id = max_adj_grp
                    group_vertices[max_adj_grp].append(vertex_id)
                    group_size[max_adj_grp] += 1

                group_size[group_id] = 0
                group_vertices[group_id] = [  ]
                if (group_size[max_adj_grp] <= self.merge_threshold):
                    finished = False

        self.group_id_list = []
        for vertex in self.vertices:
            if vertex.group_id not in self.group_id_list:
                self.group_id_list.append(vertex.group_id)


        return finished




ZhiangWang033 committed
411 412 413
    def AddMacroPins(self, macro_pin_vertex_id):
        if (self.vertices[macro_pin_vertex_id].type != "macro_pin"):
            return
ZhiangWang033 committed
414

ZhiangWang033 committed
415 416 417 418 419
        macro_name = ""
        items = self.vertices[macro_pin_vertex_id].name.split('/')
        for i in range(len(items) - 1):
            macro_name += items[i] + '/'
        macro_name = macro_name[:-1]
ZhiangWang033 committed
420

ZhiangWang033 committed
421 422 423 424
        for vertex in self.vertices:
            if (vertex.name == macro_name):
                vertex.macro_pins.add(macro_pin_vertex_id)
                return
ZhiangWang033 committed
425

ZhiangWang033 committed
426 427 428 429 430 431 432
    def GenerateProtocolBufferNetlist(self):
        self.soft_macro_area_bloating_ratio = 2.0
        # Get the bounding box first
        die_lx = 1e9
        die_ly = 1e9
        die_ux = 0.0
        die_uy = 0.0
ZhiangWang033 committed
433

ZhiangWang033 committed
434 435 436 437 438 439 440 441 442 443 444 445 446
        for vertex in self.vertices:
            die_lx = min(die_lx, vertex.x)
            die_ly = min(die_ly, vertex.y)
            die_ux = max(die_ux, vertex.x)
            die_uy = max(die_uy, vertex.y)
            self.AddMacroPins(vertex.vertex_id)

        # check all the ports
        self.ports = []
        self.macros = []
        for vertex in self.vertices:
            if vertex.type != "port":
                continue
ZhiangWang033 committed
447

ZhiangWang033 committed
448 449
            side = "NONE"
            if (vertex.x == die_lx):
450
                side = "LEFT"
ZhiangWang033 committed
451
            elif (vertex.x == die_ux):
452
                side = "RIGHT"
ZhiangWang033 committed
453
            elif (vertex.y == die_ly):
454 455
                side = "BOTTOM"
            else:
ZhiangWang033 committed
456
                side = "TOP"
457

ZhiangWang033 committed
458 459 460 461 462
            sinks = set()
            for vertex_id in vertex.fanouts:
                if (self.vertices[vertex_id].type != "stdcell"):
                    continue
                sinks.add("Grp_" + str(self.vertices[vertex_id].group_id) + "/Pinput")
463

ZhiangWang033 committed
464 465
            self.ports.append(Port(vertex.name, vertex.x, vertex.y, side))
            self.ports[-1].AddSinks(sinks)
466

ZhiangWang033 committed
467 468 469 470
        # check each hard macro and its macro pins
        for vertex in self.vertices:
            if vertex.type != "macro":
                continue
471

ZhiangWang033 committed
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
            macro_x = vertex.x
            macro_y = vertex.y
            macro_name = vertex.name
            self.macros.append(Macro(vertex.name, vertex.width, vertex.height, macro_x, macro_y, vertex.orient))

            # check all the macro pins
            for macro_pin_id in vertex.macro_pins:
                pin_vertex = self.vertices[macro_pin_id]
                macro_pin = MacroPin(pin_vertex.name, macro_name, pin_vertex.x - vertex.x, pin_vertex.y - vertex.y, "MACRO", pin_vertex.x, pin_vertex.y)
                sinks = set()
                for vertex_id in pin_vertex.fanouts:
                    if (self.vertices[vertex_id].type != "stdcell"):
                        continue
                    sinks.add("Grp_" + str(self.vertices[vertex_id].group_id) + "/Pinput")
                macro_pin.AddSinks(sinks)
                self.macros[-1].AddOutputPin(macro_pin)
488 489


ZhiangWang033 committed
490 491 492 493 494 495 496 497 498 499
        self.soft_macros = []
        # check all the soft macros
        std_cell_groups = {  }
        for vertex in self.vertices:
            if vertex.type != "stdcell":
                continue
            if vertex.group_id not in std_cell_groups:
                std_cell_groups[vertex.group_id] = [vertex.vertex_id]
            else:
                std_cell_groups[vertex.group_id].append(vertex.vertex_id)
500

ZhiangWang033 committed
501
        std_cell_groups = dict(sorted(std_cell_groups.items()))
502

ZhiangWang033 committed
503 504 505 506 507 508 509 510 511
        # convert the fanouts into group fanouts
        for vertex in self.vertices:
            if (vertex.type != "stdcell"):
                continue
            for vertex_id in vertex.fanouts:
                if (self.vertices[vertex_id].type != "stdcell"):
                    vertex.group_fanouts.add(self.vertices[vertex_id].name)
                elif (self.vertices[vertex_id].group_id != vertex.group_id):
                    vertex.group_fanouts.add("Grp_" + str(self.vertices[vertex_id].group_id) + "/Pinput")
512

ZhiangWang033 committed
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
        for group_id, vertices_list in std_cell_groups.items():
            group_x, group_y = self.GetWeightedCenter(group_id)
            macro_name = 'Grp_' + str(group_id)
            area = 0.0
            for vertex_id in vertices_list:
                area += self.vertices[vertex_id].width * self.vertices[vertex_id].height
            area *= self.soft_macro_area_bloating_ratio
            macro_height = area / self.soft_macro_width
            self.soft_macros.append(Macro(macro_name, self.soft_macro_width, macro_height, group_x, group_y))
            self.soft_macros[-1].IsSoft()  # set to soft macro
            # add the input pins
            self.soft_macros[-1].AddInputPin(MacroPin(macro_name + "/Pinput", macro_name, 0.0, 0.0, "macro", group_x, group_y))
            # add output pins based on weight
            single_sink = sortedcontainers.SortedDict()
            p_index = 0
            for vertex_id in vertices_list:
                group_fanouts = self.vertices[vertex_id].group_fanouts
                if (len(group_fanouts) == 0):
                    continue
                elif (len(group_fanouts) == 1):
                    fanout = list(group_fanouts)[0]
                    if fanout not in single_sink:
                        single_sink[fanout] = 1
                    else:
                        single_sink[fanout] += 1
                else:
                    pin_name = "Grp_" + str(group_id) + "/Poutput_multi_" + str(p_index)
                    p_index += 1
                    macro_pin = MacroPin(pin_name, macro_name, 0.0, 0.0, "macro", group_x, group_y)
                    macro_pin.AddSinks(group_fanouts)
                    self.soft_macros[-1].AddOutputPin(macro_pin)

            p_index = 0
            single_sinks = dict(sorted(single_sink.items(), reverse = True))
            for pin, weight in single_sink.items():
                pin_name = "Grp_" + str(group_id) + "/Poutput_single_" + str(p_index)
                p_index += 1
                macro_pin = MacroPin(pin_name, macro_name, 0.0, 0.0, "macro", group_x, group_y)
                macro_pin.AddSink(pin)
                macro_pin.SpecifyWeight(weight)
                self.soft_macros[-1].AddOutputPin(macro_pin)

        self.WriteProtocolBufferNetlist()

    def WriteProtocolBufferNetlist(self):
        f = open(self.pb_netlist_file, "w")
        line  = "node {\n"
        line += '  name: "__metadata__"\n'
        line += '  attr {\n'
        line += '    key: "soft_macro_area_bloating_ratio"\n'
        line += '    value {\n'
        line += '      f: ' + str(self.soft_macro_area_bloating_ratio) + '\n'
        line += '    }\n'
        line += '  }\n'
        line += '}\n'
        f.write(line)
        for port in self.ports:
            f.write(str(port))
571

ZhiangWang033 committed
572 573 574 575
        for macro in self.macros:
            f.write(str(macro))
            for macro_pin in macro.GetPins():
                f.write(str(macro_pin))
576

ZhiangWang033 committed
577 578 579 580 581 582
        for macro in self.soft_macros:
            f.write(str(macro))
            for macro_pin in macro.GetOutputPins():
                f.write(str(macro_pin))
            for macro_pin in macro.GetInputPins():
                f.write(str(macro_pin))
583

ZhiangWang033 committed
584
        f.close()
585

ZhiangWang033 committed
586 587 588 589 590 591
    def WritePlacementFile(self):
        f = open(self.plc_file, "w")
        line = "# Columns : " + str(self.n_cols) + "  Rows : " + str(self.n_rows)  + "\n"
        line += "# Width : " + str(round(self.floorplan_width, 2)) + "  Height : " + str(round(self.floorplan_height, 2)) + "\n"
        line += "# Area : " + str(round(self.floorplan_width * self.floorplan_height, 2)) + "\n"
        f.write(line)
592 593


ZhiangWang033 committed
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
        node_index = 0
        line = "# node_index x y orientation fixed\n"
        for port in self.ports:
            line += str(node_index) + " " + str(round(port.x, 2)) + " " + str(round(port.y, 2)) + " - 0\n"
            node_index += 1
        num_hard_macro_pins = 0
        for macro in self.macros:
            line += str(node_index) + " " + str(round(macro.x, 2)) + " " + str(round(macro.y, 2)) + " " + macro.orientation + " 0\n"
            node_index += 1
            for pin in macro.GetPins():
                node_index += 1
                num_hard_macro_pins += 1

        num_soft_macro_pins = 0
        for macro in self.soft_macros:
            line += str(node_index) + " " + str(round(macro.x, 2)) + " " + str(round(macro.y, 2)) + " " + macro.orientation + " 0\n"
            node_index += 1
            for pin in macro.GetPins():
                node_index += 1
                num_soft_macro_pins += 1
        f.write("# Counts of node types:\n")
        f.write("# HARD_MACROs     :       " + str(len(self.macros)) + "\n")
        f.write("# HARD_MACRO_PINs :       " + str(num_hard_macro_pins) + "\n")
        f.write("# MACROs          :       " + str(len(self.macros) + len(self.soft_macros)) + "\n")
        f.write("# MACRO_PINs      :       " + str(num_hard_macro_pins + num_soft_macro_pins) + "\n")
        f.write("# PORTs           :       " + str(len(self.ports)) + "\n")
        f.write("# SOFT_MACROs     :       " + str(len(self.soft_macros)) + "\n")
        f.write("# SOFT_MACRO_PINs :       " + str(num_soft_macro_pins)  + "\n")
        f.write("# STDCELLs        :        0\n")
        f.write(line)
        f.close()
625 626