class Main: def __init__(self, input_path, liberty_path, lef_path, def_path): self.input_path = input_path self.liberty_path = liberty_path self.lef_path = lef_path self.def_path = def_path self.pathFile = PathParser(self.input_path) self.lefFile = LefParser(self.lef_path) self.defFile = DefParser(self.def_path) self.liberty_file = open(self.liberty_path).read() self.library = parse_liberty(self.liberty_file) self.SCALE = 0 self.CELL_HEIGHT = 0 def parse_files(self): self.pathFile.parse_user_file() self.lefFile.parse() self.defFile.parse() self.SCALE = float(self.defFile.scale) self.CELL_HEIGHT = self.scale_dimension(self.lefFile.cell_height) def run(self): self.parse_files() self.check_path_continuity() self.get_worst_delay() def get_worst_delay(self): fall = self.get_path_delay('fall') rise = self.get_path_delay('rise') print("worst", max(fall, rise), "secs") def scale_dimension(self, dimension): return int(self.SCALE * dimension) def find_net_of_comp_pin(self, component, pin): for net in self.defFile.nets: for entry in net.comp_pin: if component == entry[0] and pin == entry[1]: return net return -1 def find_pin_location(self, pin_name): for pin in self.defFile.pins: if pin_name == pin.name: return pin.placed return -1 def find_component_location(self, component_name): for component in self.defFile.components: if component_name == component.name: return component.placed return -1 def check_path_continuity(self): path = self.pathFile.get_path() curr_node = path.nodeat(0) while curr_node is not None: # Get current point in the path point_one = curr_node.value curr_comp_macro = self.lefFile.macro_dict.get(point_one.get_component().split('_')[0]) output_pin = Util.find_output_pin_name(curr_comp_macro.pin_dict) net = self.find_net_of_comp_pin(point_one.get_component(), output_pin) point_two = curr_node.next.value if curr_node.next is not None else None if point_two is not None: if not Util.point_is_in_net(point_two, net): raise Exception("Path is discontinuous.") curr_node = curr_node.next def get_path_delay(self, unate): path = self.pathFile.get_path() curr_node = path.nodeat(0) total_delay = 0 input_transition_time = 0 while curr_node is not None: # Get current point in the path point_one = curr_node.value # Find the starting component/pin macro in the lef file curr_comp_macro = self.lefFile.macro_dict.get(point_one.get_component().split('_')[0]) # Find the output pin name of the current from the macro output_pin = Util.find_output_pin_name(curr_comp_macro.pin_dict) # Get the center location of current component curr_comp_location = self.find_component_location(point_one.get_component()) # Get the width of the current component curr_comp_width = self.scale_dimension(curr_comp_macro.info["SIZE"][0]) # Determine the net of the current component's output pin net = self.find_net_of_comp_pin(point_one.get_component(), output_pin) # Get the next point in the path if not end of path else make none point_two = curr_node.next.value if curr_node.next is not None else None # Split logic based on end of path or not if point_two is not None and point_two.get_component() != point_one.get_component(): # Get the macro of the next component from the lef file next_comp_macro = self.lefFile.macro_dict.get(point_two.get_component().split('_')[0]) # Get the center location of the next component next_comp_location = self.find_component_location(point_two.get_component()) # Get the width of the next component next_comp_width = self.scale_dimension(next_comp_macro.info["SIZE"][0]) cell_delay, input_transition_time, unate, total_net_cap = \ self.calc_cells_delay(unate, input_transition_time, net.comp_pin, point_one.get_component(), point_one.get_pin(), output_pin) r_drive = cell_delay / total_net_cap interconnect_delay = self.cal_interconnect_delay(net.routed, curr_comp_location, curr_comp_width, next_comp_location, next_comp_width, r_drive) total_delay += cell_delay + interconnect_delay elif point_two is not None and point_two.get_component() == point_one.get_component(): cell_delay, input_transition_time, unate, total_net_cap = self.calc_cells_delay(unate, input_transition_time, net.comp_pin, point_one.get_component(), point_one.get_pin(), output_pin) total_delay += cell_delay curr_node = curr_node.next print(unate, total_delay, "secs") return total_delay def calc_cells_delay(self, unate, input_transition_time, cells, path_cell, input_pin, output_pin): """ Calculates the delay of all the cells in the net :param unate: unate of input signal :param input_transition_time: input transition time of input signal :param cells: list of cells in the net with their pins :param path_cell: path cell as it is in DEF file :param input_pin: input pin name :param output_pin: output pin name :return: delay, output transition time, output unate """ total_net_cap = 0 for comp in cells: if comp[0] != path_cell and comp[0] not in self.defFile.pins: total_net_cap += self.get_pin_cap(comp[0].split('_')[0], comp[1]) cell_delay, input_transition_time, unate = self.get_arc_cap_trans(path_cell.split('_')[0], input_pin, output_pin, unate, total_net_cap, input_transition_time) return cell_delay, input_transition_time, unate, total_net_cap def get_layer(self, layer_name): """ Retrieves the layer object from LEF file :param layer_name: the name of the layer as it is in the LEF file :return: the layer object from the LEF file """ return self.lefFile.layer_dict.get(layer_name) def cal_interconnect_delay(self, routes, curr_comp_location, curr_comp_width, next_comp_location, next_comp_width, r_drive): # Continuity Check continuous_segments = [Segment()] seg_num = 0 for route in routes: if continuous_segments[seg_num].check_continuity(route): continuous_segments[seg_num].add_route(route) else: continuous_segments.append(Segment()) seg_num += 1 continuous_segments[seg_num].add_route(route) # Main Path Check ll_rect_curr_comp = curr_comp_location ur_rect_curr_comp = (curr_comp_location[0] + curr_comp_width, curr_comp_location[1] + self.CELL_HEIGHT) ll_rect_next_comp = next_comp_location ur_rect_next_comp = (next_comp_location[0] + next_comp_width, next_comp_location[1] + self.CELL_HEIGHT) for segment in continuous_segments: terminal_points = segment.get_terminal_points() connects_comp = Util.in_rectangle(ll_rect_curr_comp, ur_rect_curr_comp, terminal_points[0]) and Util.in_rectangle(ll_rect_next_comp, ur_rect_next_comp, terminal_points[1]) connects_comp_reverse = Util.in_rectangle(ll_rect_curr_comp, ur_rect_curr_comp, terminal_points[1]) and Util.in_rectangle( ll_rect_next_comp, ur_rect_next_comp, terminal_points[0]) if connects_comp or connects_comp_reverse: segment.set_type("MAIN") return self.cal_wire_delay(Segment.get_main_path(continuous_segments).get_routes(), r_drive) def cal_wire_delay(self, routes, r_drive): """ Calculates the total delay from metal layer wires using Elmore's model :param routes: the routes to traverse :return: The delay of traversing these route in seconds """ delay = 0 for route in routes: if len(route.points) > 1: layer = self.get_layer(route.layer) length = Util.calc_length_segment(route) * 10 ** -3 width = length * self.scale_dimension(layer.width) * 10 ** -3 capacitance = (layer.capacitance[1] * 10 ** -12) * (length * width) resistance = layer.resistance[1] * (length / width) delay += r_drive * capacitance r_drive += resistance return delay def get_arc_cap_trans(self, cell_name, input_pin_name, output_pin_name, unate, total_net_capacitance, input_transition): """ Calculates the cell delay, output transition, and the output unate of a cell using NLDM from SCL :param cell_name: name of the cell as it is in the standard cell library :param input_pin_name: name of the input pin :param output_pin_name: name of the output pin :param unate: the current unate of the signal :param total_net_capacitance: the total capacitance in the net of the output pin :param input_transition: the output transition time of the previous cell :return: the delay in seconds, the output transition time of the current cell, the unate at output """ clk_transition = 0.1 sequential = (cell_name[0] == 'D') sequential_input_name = input_pin_name input_pin_name = 'CLK' if sequential else input_pin_name sequential_delay = 0 if sequential: timings = self.library.get_group("cell", cell_name).get_group("pin", sequential_input_name).get_groups("timing") for timing in timings: unate_constraint = timing.get_group(unate + "_constraint") uc_index1, uc_index2, uc_values = Util.convert_to_float_arrays(unate_constraint.attributes["index_1"], unate_constraint.attributes["index_2"], unate_constraint.attributes["values"]) sequential_delay += Util.interpolate(uc_index2, uc_index1, uc_values, clk_transition, input_transition) timings = self.library.get_group("cell", cell_name).get_group("pin", output_pin_name).get_groups("timing") input_transition = input_transition if not sequential else clk_transition for timing in timings: if timing.attributes["related_pin"] == input_pin_name: if timing.attributes["timing_sense"] == "non_unate": delay_fall, output_transition_fall = self.get_cell_delay(timing, 'fall', total_net_capacitance, input_transition) delay_rising, output_transition_rising = self.get_cell_delay(timing, 'rise', total_net_capacitance, input_transition) if delay_fall > delay_rising: return delay_fall+sequential_delay, output_transition_fall, 'fall' else: return delay_rising+sequential_delay, output_transition_rising, 'rise' else: unate = Util.determine_unate(timing.attributes["timing_sense"], unate) delay, output_transition = self.get_cell_delay(timing, unate, total_net_capacitance, input_transition) return delay+sequential_delay, output_transition, unate @staticmethod def get_cell_delay(timing, unate, total_net_capacitance, input_transition): cell_unate = timing.get_group("cell_" + unate) unate_transition = timing.get_group(unate + "_transition") cu_index1, cu_index2, cu_values = Util.convert_to_float_arrays(cell_unate.attributes["index_1"], cell_unate.attributes["index_2"], cell_unate.attributes["values"]) ut_index1, ut_index2, ut_values = Util.convert_to_float_arrays(unate_transition.attributes["index_1"], unate_transition.attributes["index_2"], unate_transition.attributes["values"]) delay = Util.interpolate(cu_index1, cu_index2, cu_values, total_net_capacitance, input_transition) output_transition = Util.interpolate(ut_index1, ut_index2, ut_values, total_net_capacitance, input_transition) return delay, output_transition def get_pin_cap(self, cell_name, pin_name, unate=None): """ Retrieves the capacitance of a pin from the SCL :param cell_name: the name of the cell as it is in the SCL :param pin_name: the name of the pin :param unate: if None use worst, else use unate :return: """ pin = self.library.get_group("cell", cell_name).get_group("pin", pin_name) return pin.attributes["capacitance"] if unate is None else pin.attributes[unate + "_capacitance"] def get_all_possible_paths(self): path = self.pathFile.get_path() starting_point = path.nodeat(0).value ending_point = path.nodeat(1).value @staticmethod def find_all_paths(self, graph, start, end, path=[]): path = path + [start] if start == end: return [path] if not graph.has_key(start): return [] paths = [] for node in graph[start]: if node not in path: newpaths = self.find_all_paths(graph, node, end, path) for newpath in newpaths: paths.append(newpath) return paths