#############################################################
# Convert the OpenDB to open-source protocol buffer format
# Please refer to the Circuit Training repo for details of
# the open-source protocol buffer format
#############################################################
import os
import time
import shutil
import sys
sys.path.append('./utils')


# Port
class Port:
    def __init__(self, name, x = 0.0, y = 0.0, side = "BOTTOM"):
        self.name = name
        self.x = x
        self.y = y
        self.side = side # "BOTTOM", "TOP", "LEFT", "RIGHT"
        self.sinks = [] # standard cells, macro pins, ports driven by this cell

    def SetPos(self, x, y):
        self.x = x
        self.y = y

    def SetSide(self, side):
        self.side = side

    def AddSink(self, sink):
        if (sink not in self.sinks) and (self.name != sink):
            self.sinks.append(sink)

    def AddSinks(self, sinks):
        for sink in sinks:
            if (sink not in self.sinks) and (self.name != sink):
                self.sinks.append(sink)

    def GetType(self):
        return "PORT"

    def __str__(self):
        self.str = ""
        self.str += "node {\n"
        self.str += '  name: "' + self.name + '"\n'
        for sink in self.sinks:
            self.str += '  input: "' + sink + '"\n'
        self.str += "  attr {\n"
        self.str += '    key: "type"\n'
        self.str += '    value {\n'
        self.str += '      placeholder: "PORT"\n'
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "side"\n'
        self.str += '    value {\n'
        self.str += '      placeholder: "' + self.side +  '"\n'
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "x"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.x) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "y"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.y) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "}\n"
        return self.str


# Standard Cell
# Based on our understanding of the format description,
# the location of standard cell (x, y) is the center of
# its bounding box in def file.
# Each Standard cell only have one input pin and one output pin
class StandardCell:
    def __init__(self, name, width, height, x = 0.0, y = 0.0):
        self.name = name
        self.width = width
        self.height = height
        self.x = x
        self.y = y
        self.sinks = [] # standard cells, macro pins, ports driven by this cell

    def SetPos(self, x, y):
        self.x = x
        self.y = y

    def AddSink(self, sink):
        if (sink not in self.sinks) and (self.name != sink):
            self.sinks.append(sink)

    def AddSinks(self, sinks):
        for sink in sinks:
            if (sink not in self.sinks) and (self.name != sink):
                self.sinks.append(sink)

    def GetType(self):
        return "STDCELL"


    def __str__(self):
        self.str = ""
        self.str += "node {\n"
        self.str += '  name: "' + self.name + '"\n'
        for sink in self.sinks:
            self.str += '  input: "' + sink + '"\n'
        self.str += "  attr {\n"
        self.str += '    key: "type"\n'
        self.str += '    value {\n'
        self.str += '      placeholder: "STDCELL"\n'
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "height"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.height) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "width"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.width) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "x"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.x) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "y"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.y) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "}\n"
        return self.str

# Macro
# Based on our understanding of the format description,
# the location of standard cell (x, y) is the center of
# its bounding box in def file.
# the pins of each macro are sperately defined
# The orientation of macro can be N, FN, S, FS, E, FE, W, FW
# if the example, they set the type to "MACRO" instead of "macro"
class Macro:
    def __init__(self, name, width, height, x = 0.0, y = 0.0, orientation = "N"):
        self.name = name
        self.width = width
        self.height = height
        self.x = x
        self.y = y
        self.orientation = orientation
        self.input_pins = []
        self.output_pins = []

    def AddInputPin(self, macro_pin):
        self.input_pins.append(macro_pin)

    def GetInputPins(self):
        return self.input_pins

    def AddOutputPin(self, macro_pin):
        self.output_pins.append(macro_pin)

    def GetOutputPins(self):
        return self.output_pins

    def SetPos(self, x, y):
        self.x = x
        self.y = y

    def SetOrientation(self, orientation):
        self.orientation = orientation

    def GetPins(self):
        return self.input_pins + self.output_pins

    def GetType(self):
        return "MACRO"

    def __str__(self):
        self.str = ""
        self.str += "node {\n"
        self.str += '  name: "' + self.name + '"\n'
        self.str += "  attr {\n"
        self.str += '    key: "type"\n'
        self.str += '    value {\n'
        self.str += '      placeholder: "MACRO"\n'
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "orientation"\n'
        self.str += '    value {\n'
        self.str += '      placeholder: "' + self.orientation + '"\n'
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "height"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.height) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "width"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.width) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "x"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.x) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "y"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.y) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "}\n"
        return self.str

# Macro pin
class MacroPin:
    def __init__(self, name, macro_name, x_offset, y_offset, x = 0.0, y = 0.0):
        self.name = name
        self.macro_name = macro_name
        self.x_offset = x_offset
        self.y_offset = y_offset
        self.x = x
        self.y = y
        self.sinks = [] # standard cells, macro pins, ports driven by this cell
        self.weight = 1 # If multiple std cells in a soft-macros
                        # drive std cells in another soft-macro,
                        # the weight will be larger than 1.

    def GetName(self):
        return self.name

    def SpecifyWeight(self, weight):
        self.weight = weight

    def AddSink(self, sink):
        if (sink not in self.sinks) and (self.name != sink):
            self.sinks.append(sink)

    def AddSinks(self, sinks):
        for sink in sinks:
            if (sink not in self.sinks) and (self.name != sink):
                self.sinks.append(sink)


    def __str__(self):
        self.str = ""
        self.str += "node {\n"
        self.str += '  name: "' + self.name + '"\n'
        for sink in self.sinks:
            self.str += '  input: "' + sink + '"\n'
        self.str += "  attr {\n"
        self.str += '    key: "macro_name"\n'
        self.str += '    value {\n'
        self.str += '      placeholder: "' + str(self.macro_name) + '"\n'
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "type"\n'
        self.str += '    value {\n'
        self.str += '      placeholder: "macro_pin"\n'
        self.str += '    }\n'
        self.str += '  }\n'
        if (self.weight > 1):
            self.str += "  attr {\n"
            self.str += '    key: "weight"\n'
            self.str += '    value {\n'
            self.str += '      f: ' + str(self.weight) + "\n"
            self.str += '    }\n'
            self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "x_offset"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.x_offset) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "y_offset"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.y_offset) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "x"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.x) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "  attr {\n"
        self.str += '    key: "y"\n'
        self.str += '    value {\n'
        self.str += '      f: ' + str(self.y) + "\n"
        self.str += '    }\n'
        self.str += '  }\n'
        self.str += "}\n"
        return self.str



# BookShelf Format to open-source protocol buffer format
# Please check http://vlsicad.eecs.umich.edu/BK/ISPD06bench/BookshelfFormat.txt for details of BookShelf Format
class BookShelf2ProBufFormat:
    def __init__(self, file_dir, design,  output_file):
        self.design = design
        self.file_dir = file_dir
        self.nets_file = file_dir + "/" + design + ".nets"
        self.nodes_file = file_dir + "/" + design + ".nodes"
        self.pl_file = file_dir + "/" + design + ".pl"
        self.scl_file = file_dir + "/" + design + ".scl"
        self.output_file = output_file
        self.insts = {  }  # map name to Port, Macro, StandardCell

        # circuit row information
        self.site_height = 0.0
        self.fp_lx = 0.0
        self.fp_ly = 0.0
        self.fp_ux = 0.0
        self.fp_uy = 0.0


        # functions
        self.CheckFiles()
        self.ReadSclFile()
        self.ReadNodesFile()
        self.ReadPlFile()
        self.ReadNetsFile()
        self.Output()

    def CheckFiles(self):
        ###
        # zzz.nodes: specifies the set of objects. For each object,
        # object_name        width         height        terminal
        # keyword "terminal" appears only for fixed objects.
        if (os.path.exists(self.nodes_file) == False):
            print("[INFO]  Error! ",  self.nodes_file, " does not exist!!!\n")
            exit()

        ###
        # zzz.nets: specifies the set of nets. For each net,
        # NetDegree : k net_name
        # input pin  pin offset is measured from the center of corresponding object
        #  if pin offsets are unavailable, assumed to be (0,0)
        #        object_name        I : x_offset        y_offset
        #  output pin
        #        object_name        O : x_offset        y_offset
        if (os.path.exists(self.nets_file) == False):
            print("[INFO]  Error! ",  self.nets_file, " does not exist!!!\n")
            exit()

        ###
        # zzz.pl: specifies the location & orientation of objects. For each object,
        # x_location & y_location are bottom left coordinates of an object
        # orientation is one of N, S, E, W, FN, FS, FE, FW
        # and interpreted as in LEF/DEF (see Orientation.ppt in this
        # documentation for visual representations of each orientation)
        # object_name        x_location        y_location        : orientation
        # fixed object
        # object_name        x_location        y_location        : orientation    /FIXED
        if (os.path.exists(self.pl_file) == False):
             print("[INFO]  Error! ",  self.pl_file, " does not exist!!!\n")
             exit()

        ###
        #zzz.scl: circuit row information. For each row,
        #CoreRow Horizontal
        #   Coordinate :    www  # row starts at coordinate www
                                 # (y-coordinate if horizontal, x-coordinate if vertical row)
        #   Height :        yyy  # row height is yyy
        #   Sitewidth :     1
        #   Sitespacing :   1    # distance between the left edges of two adjacent sites
        #   Siteorient :    1
        #   Sitesymmetry :  1
        # You can specify subrows within a row. They following says the subrow spans from
        # aaa to (aaa + (bbb-1)*Sitespacing + Sitewidth) with yyy height.
        #   SubrowOrigin :  aaa  NumSites : bbb
        # Additional Subrows may be specified if there are breaks in the row
        # due to obstacles, but are not required. One subrow is always required.
        if (os.path.exists(self.scl_file) == False):
             print("[INFO]  Error! ",  self.scl_file, " does not exist!!!\n")
             exit()


    # Read Circuit Row information from zzz.scl
    def ReadSclFile(self):
        with open(self.scl_file) as f:
            content = f.read().splitlines()
        f.close()

        line_id = 0
        # read Numrows
        num_rows = 0
        while(line_id < len(content)):
            items = content[line_id].split()
            if((len(items) == 3) and ((items[0] == "Numrows") or (items[0] == "NumRows"))):
                num_rows = int(items[-1])
                break
            else:
                line_id += 1

        # Read site_height, site_width,
        # num_sites, fp_lx, fp_ly
        site_width = 0.0
        num_sites = 0
        while(line_id < len(content)):
            items = content[line_id].split()
            if((len(items) == 2) and (items[0] == "CoreRow")):
                line_id += 1
                items = content[line_id].split()
                self.fp_ly = float(items[-1])
                line_id += 1
                items = content[line_id].split()
                self.site_height = float(items[-1])
                line_id += 1
                items = content[line_id].split()
                site_width = float(items[-1])
                line_id += 4
                items = content[line_id].split()
                self.fp_lx = float(items[2])
                num_sites = int(items[-1])
                break
            else:
                line_id += 1

        self.fp_ux = self.fp_lx + site_width * num_sites
        self.fp_uy = self.fp_ly + num_rows * self.site_height

        print('*'*80)
        print("Circuit Row Information")
        print("[INFO] Core Size : ", self.fp_lx, self.fp_ly, self.fp_ux, self.fp_uy)
        print("[INFO] Site Height : ", self.site_height)
        print("\n\n")


    # Read zzz.nodes file and create instances
    # We determine the type of node based on its width and height
    # if height < self.site_height, type = Node
    # if height > self.site_height, type = Macro
    # otherwise,  type = StdandCell
    def ReadNodesFile(self):
        with open(self.nodes_file) as f:
            content = f.read().splitlines()
        f.close()

        start_line = 0
        for i in range(len(content)):
            items = content[i].split()
            if ((len(items) == 3) and (items[0] == "NumTerminals")):
                start_line = i  +  1
                break

        for i in range(start_line, len(content)):
            items = content[i].split()
            if (len(items) < 3):
                continue

            inst_name = items[0]
            width = float(items[1])
            height = float(items[2])
            if (height < self.site_height):
                self.insts[inst_name] = Port(inst_name)
            elif (height == self.site_height):
                self.insts[inst_name] = StandardCell(inst_name, width, height)
            else:
                self.insts[inst_name] = Macro(inst_name, width, height)

    # Read Pl file
    def ReadPlFile(self):
        with open(self.pl_file) as f:
            content = f.read().splitlines()
        f.close()

        start_line = 0
        for i in range(len(content)):
            items = content[i].split()
            if (len(items) == 5):
                start_line = i
                break

        for i in range(start_line, len(content)):
            items = content[i].split()
            if (len(items) < 5):
                continue

            inst_name = items[0]
            x = float(items[1])
            y = float(items[2])
            orientation = items[4]
            self.insts[inst_name].SetPos(x, y)
            if (self.insts[inst_name].GetType() == "MACRO"):
                self.insts[inst_name].SetOrientation(orientation)
            elif (self.insts[inst_name].GetType() == "PORT"):
                if (x <= self.fp_lx):
                    self.insts[inst_name].SetSide("LEFT")
                elif ( x >= self.fp_ux):
                    self.insts[inst_name].SetSide("RIGHT")
                elif ( y >= self.fp_uy):
                    self.insts[inst_name].SetSide("TOP")
                else:
                    self.insts[inst_name].SetSide("BOTTOM")

    # Read Nets file
    def ReadNetsFile(self):
        with open(self.nets_file) as f:
            content = f.read().splitlines()
        f.close()

        start_line = 0
        for i in range(len(content)):
            items = content[i].split()
            if ((len(items) == 3) and (items[0] == "NumPins")):
                start_line = i + 1
                break

        # We ignore all the bidirectional nets
        # We don't see any definition related to bidirectional nets
        # in circuit training repo
        line_id = start_line
        while (line_id < len(content)):
            items = content[line_id].split()
            if ((len(items) == 4) and (items[0] == "NetDegree")):
                line_id += 1
                sink_list = []
                driver = None
                while((line_id < len(content)) and (len(content[line_id].split()) == 5)):
                    items = content[line_id].split()
                    inst_name = items[0]
                    io_type = items[1]
                    x_offset = float(items[3])
                    y_offset = float(items[4])
                    if (io_type == "O"):
                        driver = [inst_name, x_offset, y_offset]
                    else:
                        sink_list.append([inst_name, x_offset, y_offset])
                    line_id += 1
                # if the net have a driver
                if ((driver != None) and (len(sink_list) > 0)):
                    sink_name_list = [] # Port, StandardCell, MacroPin
                    for sink in sink_list:
                        inst_name = sink[0]
                        if (self.insts[inst_name].GetType() == "MACRO"):
                            pin_name = "Pinput_" + str(len(self.insts[inst_name].GetInputPins()))
                            self.insts[inst_name].AddInputPin(MacroPin(pin_name, inst_name, sink[1], sink[2]))
                            sink_name_list.append(pin_name)
                        else:
                            sink_name_list.append(inst_name)

                    driver_name = driver[0]
                    if (self.insts[driver_name].GetType() == "MACRO"):
                        pin_id = len(self.insts[driver_name].GetOutputPins())
                        pin_name = "Poutput_" + str(pin_id)
                        macro_pin = MacroPin(pin_name, driver_name, driver[1], driver[2])
                        macro_pin.AddSinks(sink_name_list)
                        self.insts[driver_name].AddOutputPin(macro_pin)
                    else:
                        self.insts[driver_name].AddSinks(sink_name_list)
            else:
                line_id += 1


    # Generate.pb.txt file
    def Output(self):
        f = open(self.output_file, "w")
        for inst_name, inst in self.insts.items():
            f.write(str(inst))
            if (inst.GetType() == "MACRO"):
                for macro_pin in inst.GetPins():
                    f.write(str(macro_pin))
        f.close()



# OpenDB to open-source protocol buffer format
# We use the OpenDB database in OpenROAD to read lef/def files
# We have provided an openroad exe
class ODB2ProBufFormat:
    def __init__(self, file_dir, design, output_file, openroad_exe, net_size_threshold):
        self.design = design
        self.file_dir = file_dir
        self.db_file = file_dir + "/" + design + ".odb"
        self.openroad_exe = openroad_exe
        self.net_size_threshold = net_size_threshold
        self.output_file = output_file

        self.io_file = file_dir + "/" + design + ".hgr.io"
        self.macro_pin_file = file_dir + "/" + design + ".hgr.macro_pin"
        self.inst_file  = file_dir + "/" + design + ".hgr.instance"
        self.outline_file = file_dir + "/" + design + ".hgr.outline"
        self.net_file = file_dir + "/" + design + ".hgr.net"
        self.insts = {  }  # map name to Port, Macro, StandardCell
        self.macro_pin_offset = {  } # map macro_pin_name to offset

        # circuit row information
        self.fp_lx = 0.0
        self.fp_ly = 0.0
        self.fp_ux = 0.0
        self.fp_uy = 0.0


        # functions
        self.CheckFiles()
        self.ReadOutlineFile()
        self.ReadIOFile()
        self.ReadInstFile()
        self.ReadMacroPinFile()
        self.ReadNetFile()
        self.Output()


    def CheckFiles(self):
        # IO file
        if (os.path.exists(self.io_file) == False):
            print("[INFO]  Error! ",  self.io_file, " does not exist!!!\n")
            exit()

        # macro_pin file
        if (os.path.exists(self.macro_pin_file) == False):
            print("[INFO]  Error! ",  self.macro_pin_file, " does not exist!!!\n")
            exit()

        # inst file
        if (os.path.exists(self.inst_file) == False):
             print("[INFO]  Error! ",  self.inst_file, " does not exist!!!\n")
             exit()

        # outline file
        if (os.path.exists(self.outline_file) == False):
             print("[INFO]  Error! ",  self.outline_file, " does not exist!!!\n")
             exit()

        # net file
        if (os.path.exists(self.net_file) == False):
             print("[INFO]  Error! ",  self.net_file, " does not exist!!!\n")
             exit()


    # Read outline file
    def ReadOutlineFile(self):
        with open(self.outline_file) as f:
            content = f.read().splitlines()
        f.close()

        items = content[0].split()
        self.fp_lx = float(items[0])
        self.fp_ly = float(items[1])
        self.fp_ux = float(items[2])
        self.fp_uy = float(items[3])

        print('*'*80)
        print("Outline Information")
        print("[INFO] Core Size : ", self.fp_lx, self.fp_ly, self.fp_ux, self.fp_uy)
        print("\n\n")


    # Read IO file
    def ReadIOFile(self):
        with open(self.io_file) as f:
            content = f.read().splitlines()
        f.close()

        for line in content:
            items = line.split()
            io_name = items[0]
            lx = float(items[1])
            ly = float(items[2])
            ux = float(items[3])
            uy = float(items[4])
            side = "LEFT"

            if (lx <= self.fp_lx):
                side = "LEFT"
            elif (ux >= self.fp_ux):
                side = "RIGHT"
            elif (uy >= self.fp_uy):
                side = "TOP"
            else:
                side = "BOTTOM"

            self.insts[io_name] = Port(io_name, (lx + ux) / 2.0, (ly + uy) / 2.0, side)


    # Read instance file
    def ReadInstFile(self):
        with open(self.inst_file) as f:
            content = f.read().splitlines()
        f.close()

        for line in content:
            items = line.split()
            inst_name = items[0]
            block_flag = items[1]
            lx = float(items[2])
            ly = float(items[3])
            ux = float(items[4])
            uy = float(items[5])
            orientation = items[6]
            width = ux - lx
            height = uy - ly
            x = (lx + ux) / 2.0
            y = (ly + uy) / 2.0
            if block_flag == '1':
                self.insts[inst_name] = Macro(inst_name, width, height, x, y, orientation)
            else:
                self.insts[inst_name] = StandardCell(inst_name, width, height, x, y)



    # Read Macro Pin file
    def ReadMacroPinFile(self):
        with open(self.macro_pin_file) as f:
            content = f.read().splitlines()
        f.close()

        for line in content:
            items = line.split()
            pin_name = items[0] + '/' + items[1]
            x_offset = float(items[2])
            y_offset = float(items[3])
            self.macro_pin_offset[pin_name] = [x_offset, y_offset]


    # Read Net file
    def ReadNetFile(self):
        with open(self.net_file) as f:
            content = f.read().splitlines()
        f.close()

        for line in content:
            items = line.split()
            num_pin = len(items) / 4
            if (num_pin > self.net_size_threshold):
                pass
            else:
                driver_name = items[2]
                driver_pin_name = driver_name + '/' + items[0]
                sinks_name = []
                for i in range(1, int(num_pin)):
                    inst_name = items[4 * i + 2]
                    pin_name = inst_name + '/' + items[4 * i]
                    if (self.insts[inst_name].GetType() == "MACRO"):
                        offset = self.macro_pin_offset[pin_name]
                        self.insts[inst_name].AddInputPin(MacroPin(pin_name, inst_name, offset[0], offset[1]))
                        sinks_name.append(pin_name)
                    else:
                        sinks_name.append(inst_name)
                # Add sinks to driver
                if (self.insts[driver_name].GetType() == "MACRO"):
                    offset = self.macro_pin_offset[driver_pin_name]
                    macro_pin = MacroPin(driver_pin_name, driver_name, offset[0], offset[1])
                    macro_pin.AddSinks(sinks_name)
                    self.insts[driver_name].AddOutputPin(macro_pin)
                else:
                    self.insts[driver_name].AddSinks(sinks_name)


    # Generate.pb.txt file
    def Output(self):
        f = open(self.output_file, "w")
        for inst_name, inst in self.insts.items():
            f.write(str(inst))
            if (inst.GetType() == "MACRO"):
                for macro_pin in inst.GetPins():
                    f.write(str(macro_pin))
        f.close()



# Convert LEF/DEF files to Protocol Buffer Format
class LefDef2ProBufFormat:
    def __init__(self, lef_list, def_file, design, openroad_exe, net_size_threshold):
        self.lef_list = lef_list
        self.def_file = def_file
        self.design = design
        self.output_file = self.design + ".pb.txt"
        self.openroad_exe = openroad_exe
        self.net_size_threshold = net_size_threshold
        self.file_dir = os.getcwd() + "/rtl_mp"

        if not os.path.exists(self.file_dir):
            os.makedirs(self.file_dir)

        self.CreateODB()
        ODB2ProBufFormat(self.file_dir, self.design, self.output_file, self.openroad_exe, self.net_size_threshold)

        cmd = "rm -rf " + self.file_dir
        os.system(cmd)

    def CreateODB(self):
        file_name = "generate_odb.tcl"
        line  = "set top_design " + self.design + "\n"
        line += "set def_file " + self.def_file + "\n"
        line += 'set ALL_LEFS "\n'
        for lef in self.lef_list:
            line += '    ' + lef + '\n'
        line += '"\n'
        line += 'foreach lef_file ${ALL_LEFS} {\n'
        line += '    read_lef $lef_file\n'
        line += '}\n'
        line += "read_def " + self.def_file + "\n"
        line += "partition_design -max_num_inst 2000000 -min_num_inst 40000 "
        line += "-max_num_macro 12 -min_num_macro 4 "
        line += "-net_threshold 20  -virtual_weight 50 "
        line += "-num_hop 0 -timing_weight 1000 "
        line += "-report_file ${top_design}.hgr \n"
        line += "exit\n"
        f = open(file_name, "w")
        f.write(line)
        f.close()

        cmd = self.openroad_exe + ' ' + file_name
        os.system(cmd)

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


### Map Bookshelf to LEF/DEF files for different technology
### Currently we support NanGate45 and ASAP7
### Technology can be NanGate45 and ASAP7
class Bookshelf2LefDef2ProBufFormat:
    def __init__(self, lef_list, openroad_exe, file_dir, design, tech, src_dir, net_size_threshold):
        self.lef_list = lef_list
        self.openroad_exe = openroad_exe
        self.file_dir = file_dir
        self.design = design
        self.tech = tech
        self.src_dir = src_dir
        self.net_size_threshold = net_size_threshold

        self.Mapper()

        self.lef_list.append(self.design + "_macro.lef")
        self.def_file = self.design + ".def"
        LefDef2ProBufFormat(self.lef_list, self.def_file, self.design, self.openroad_exe, self.net_size_threshold)

    def Mapper(self):
        f = open("map_tech.py", "w")
        line = 'import sys\n'
        line += 'sys.path.append("'+ self.src_dir + '")\n'
        line += 'sys.path.append("'+ self.src_dir + '/utils")\n'
        line += 'from mapper import MapNanGate45\n'
        line += 'from mapper import MapASAP7\n'
        line += 'file_dir = "' + self.file_dir + '"\n'
        line += 'design = "' + self.design + '"\n'
        line += 'utils_dir = "' + self.src_dir + '/utils"\n'
        line += 'lef_list = ['
        for i in range(len(self.lef_list)):
            line += '"'+ self.lef_list[i] + '"'
            if (i < (len(self.lef_list) - 1)):
                line += ','
            else:
                line += ']\n'
        if (self.tech == "NanGate45"):
            line += "MapNanGate45(file_dir, lef_list, design, utils_dir)\n"
        else:
            line += "MapASAP7(file_dir, lef_list, design, utils_dir)\n"
        f.write(line)
        f.close()

        cmd = self.openroad_exe + " -python map_tech.py"
        os.system(cmd)

        cmd = "rm map_tech.py"
        os.system(cmd)