2019-05-14 23:44:49 +02:00
|
|
|
import os, copy
|
2019-04-19 10:27:06 +02:00
|
|
|
from collections import defaultdict
|
|
|
|
|
|
|
|
|
|
import gdsMill
|
|
|
|
|
import tech
|
|
|
|
|
import math
|
|
|
|
|
import globals
|
|
|
|
|
import debug
|
|
|
|
|
from vector import vector
|
|
|
|
|
from pin_layout import pin_layout
|
2019-05-27 22:08:59 +02:00
|
|
|
|
2019-04-30 08:57:25 +02:00
|
|
|
class timing_graph():
|
|
|
|
|
"""Implements a directed graph
|
|
|
|
|
Nodes are currently just Strings.
|
|
|
|
|
"""
|
2019-04-19 10:27:06 +02:00
|
|
|
|
|
|
|
|
def __init__(self):
|
2019-04-30 08:57:25 +02:00
|
|
|
self.graph = defaultdict(set)
|
2019-05-14 23:44:49 +02:00
|
|
|
self.all_paths = []
|
2019-04-19 10:27:06 +02:00
|
|
|
|
2019-04-30 08:57:25 +02:00
|
|
|
def add_edge(self, src_node, dest_node):
|
2019-04-19 10:27:06 +02:00
|
|
|
"""Adds edge to graph. Nodes added as well if they do not exist."""
|
2019-04-30 08:57:25 +02:00
|
|
|
src_node = src_node.lower()
|
|
|
|
|
dest_node = dest_node.lower()
|
|
|
|
|
self.graph[src_node].add(dest_node)
|
2019-04-19 10:27:06 +02:00
|
|
|
|
2019-04-30 08:57:25 +02:00
|
|
|
def add_node(self, node):
|
2019-04-19 10:27:06 +02:00
|
|
|
"""Add node to graph with no edges"""
|
2019-04-30 08:57:25 +02:00
|
|
|
node = node.lower()
|
|
|
|
|
if not node in self.graph:
|
|
|
|
|
self.graph[node] = set()
|
2019-04-19 10:27:06 +02:00
|
|
|
|
|
|
|
|
def remove_edges(self, node):
|
|
|
|
|
"""Helper function to remove edges, useful for removing vdd/gnd"""
|
2019-04-30 08:57:25 +02:00
|
|
|
node = node.lower()
|
|
|
|
|
self.graph[node] = set()
|
2019-04-19 10:27:06 +02:00
|
|
|
|
2019-05-14 23:44:49 +02:00
|
|
|
def get_all_paths(self, src_node, dest_node, rmv_rail_nodes=True):
|
2019-04-30 08:57:25 +02:00
|
|
|
"""Traverse all paths from source to destination"""
|
|
|
|
|
src_node = src_node.lower()
|
|
|
|
|
dest_node = dest_node.lower()
|
|
|
|
|
|
|
|
|
|
#Remove vdd and gnd by default
|
|
|
|
|
#Will require edits if separate supplies are implemented.
|
|
|
|
|
if rmv_rail_nodes:
|
|
|
|
|
#Names are also assumed.
|
|
|
|
|
self.remove_edges('vdd')
|
|
|
|
|
self.remove_edges('gnd')
|
|
|
|
|
|
2019-04-19 10:27:06 +02:00
|
|
|
# Mark all the vertices as not visited
|
|
|
|
|
visited = set()
|
|
|
|
|
|
|
|
|
|
# Create an array to store paths
|
|
|
|
|
path = []
|
2019-05-14 23:44:49 +02:00
|
|
|
self.all_paths = []
|
2019-04-19 10:27:06 +02:00
|
|
|
|
|
|
|
|
# Call the recursive helper function to print all paths
|
2019-05-14 23:44:49 +02:00
|
|
|
self.get_all_paths_util(src_node, dest_node, visited, path)
|
|
|
|
|
debug.info(1, "Paths found={}".format(len(self.all_paths)))
|
2019-04-19 10:27:06 +02:00
|
|
|
|
2019-05-14 23:44:49 +02:00
|
|
|
return self.all_paths
|
|
|
|
|
|
|
|
|
|
def get_all_paths_util(self, cur_node, dest_node, visited, path):
|
2019-04-30 08:57:25 +02:00
|
|
|
"""Recursive function to find all paths in a Depth First Search manner"""
|
2019-04-19 10:27:06 +02:00
|
|
|
# Mark the current node as visited and store in path
|
2019-04-30 08:57:25 +02:00
|
|
|
visited.add(cur_node)
|
|
|
|
|
path.append(cur_node)
|
2019-04-19 10:27:06 +02:00
|
|
|
|
|
|
|
|
# If current vertex is same as destination, then print
|
|
|
|
|
# current path[]
|
2019-04-30 08:57:25 +02:00
|
|
|
if cur_node == dest_node:
|
2019-04-19 10:27:06 +02:00
|
|
|
debug.info(1,"{}".format(path))
|
2019-05-14 23:44:49 +02:00
|
|
|
self.all_paths.append(copy.deepcopy(path))
|
2019-04-19 10:27:06 +02:00
|
|
|
else:
|
|
|
|
|
# If current vertex is not destination
|
|
|
|
|
#Recur for all the vertices adjacent to this vertex
|
2019-04-30 08:57:25 +02:00
|
|
|
for node in self.graph[cur_node]:
|
|
|
|
|
if node not in visited:
|
2019-05-14 23:44:49 +02:00
|
|
|
self.get_all_paths_util(node, dest_node, visited, path)
|
2019-04-19 10:27:06 +02:00
|
|
|
|
|
|
|
|
# Remove current vertex from path[] and mark it as unvisited
|
|
|
|
|
path.pop()
|
2019-04-30 08:57:25 +02:00
|
|
|
visited.remove(cur_node)
|
2019-04-19 10:27:06 +02:00
|
|
|
|
2019-05-14 23:44:49 +02:00
|
|
|
def get_path_preconvergence_point(self, path1, path2):
|
|
|
|
|
"""Assuming the inputs paths have the same starting point and end point, the
|
|
|
|
|
paths should split and converge at some point before/at the last stage. Finds the
|
|
|
|
|
point before convergence."""
|
|
|
|
|
debug.check(path1[0] == path2[0], "Paths must start from the same point.")
|
|
|
|
|
debug.check(path1[-1] == path2[-1], "Paths must end from the same point.")
|
|
|
|
|
#Paths must end at the same point, so the paths are traversed backwards to find
|
|
|
|
|
#point of convergence. There could be multiple points, only finds first.
|
|
|
|
|
for point1,point2 in zip(reversed(path1), reversed(path2)):
|
|
|
|
|
if point1 != point2:
|
|
|
|
|
return (point1,point2)
|
|
|
|
|
debug.info(1,"Pre-convergence point not found, paths are equals.")
|
|
|
|
|
return path1[0],path2[0]
|
|
|
|
|
|
2019-04-19 10:27:06 +02:00
|
|
|
def __str__(self):
|
|
|
|
|
""" override print function output """
|
|
|
|
|
return "Nodes: {}\nEdges:{} ".format(list(self.graph), self.graph)
|