OpenRAM/compiler/router/grid_path.py

216 lines
6.0 KiB
Python
Raw Normal View History

# See LICENSE for licensing information.
#
2021-01-22 20:23:28 +01:00
# Copyright (c) 2016-2021 Regents of the University of California and The Board
2019-06-14 17:43:41 +02:00
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from itertools import tee
2022-11-27 22:01:20 +01:00
from openram.base.vector3d import vector3d
from .grid import grid
from .direction import direction
class grid_path:
"""
2020-11-03 15:29:17 +01:00
A grid path is a list of lists of grid cells.
It can have a width that is more than one cell.
All of the sublists will be the same dimension.
Cells should be continguous.
It can have a name to define pin shapes as well.
"""
2020-11-03 15:29:17 +01:00
def __init__(self, items=[], name=""):
self.name = name
if items:
self.pathlist = [items]
else:
self.pathlist = []
def __str__(self):
p = str(self.pathlist)
if self.name != "":
return (str(self.name) + " : " + p)
return p
def __setitem__(self, index, value):
2020-11-03 15:29:17 +01:00
"""
override setitem function
can set value by pathinstance[index]=value
"""
self.pathlist[index]=value
def __getitem__(self, index):
"""
2020-11-03 15:29:17 +01:00
override getitem function
can get value by value=pathinstance[index]
"""
return self.pathlist[index]
def __contains__(self, key):
2020-11-03 15:29:17 +01:00
"""
Determine if cell exists in this path
"""
# FIXME: Could maintain a hash to make in O(1)
for sublist in self.pathlist:
for item in sublist:
if item == key:
return True
else:
return False
def __add__(self, items):
"""
2020-11-03 15:29:17 +01:00
Override add to do append
"""
return self.pathlist.extend(items)
def __len__(self):
return len(self.pathlist)
2020-11-03 15:29:17 +01:00
def trim_last(self):
"""
Drop the last item
"""
if len(self.pathlist)>0:
self.pathlist.pop()
def trim_first(self):
"""
Drop the first item
"""
if len(self.pathlist)>0:
self.pathlist.pop(0)
2020-11-03 15:29:17 +01:00
def append(self,item):
"""
Append the list of items to the cells
"""
self.pathlist.append(item)
def extend(self,item):
"""
Extend the list of items to the cells
"""
self.pathlist.extend(item)
2020-11-03 15:29:17 +01:00
def set_path(self,value=True):
for sublist in self.pathlist:
for p in sublist:
p.path=value
def set_blocked(self,value=True):
for sublist in self.pathlist:
for p in sublist:
p.blocked=value
def get_grids(self):
"""
Return a set of all the grids in this path.
"""
newset = set()
for sublist in self.pathlist:
newset.update(sublist)
return newset
def get_wire_grids(self, start_index, end_index):
"""
Return a set of all the wire grids in this path.
These are the indices in the wave path in a certain range.
"""
newset = set()
for sublist in self.pathlist:
newset.update(sublist[start_index:end_index])
return newset
2020-11-03 15:29:17 +01:00
def cost(self):
2020-11-03 15:29:17 +01:00
"""
The cost of the path is the length plus a penalty for the number
of vias. We assume that non-preferred direction is penalized.
This cost only works with 1 wide tracks.
"""
# Ignore the source pin layer change, FIXME?
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
plist = list(pairwise(self.pathlist))
cost = 0
for p0list,p1list in plist:
# This is because they are "waves" so pick the first item
p0=p0list[0]
p1=p1list[0]
if p0.z != p1.z: # via
cost += grid.VIA_COST
elif p0.x != p1.x and p0.z==1: # horizontal on vertical layer
cost += grid.NONPREFERRED_COST
elif p0.y != p1.y and p0.z==0: # vertical on horizontal layer
2020-11-03 15:29:17 +01:00
cost += grid.NONPREFERRED_COST
else:
2020-11-03 15:29:17 +01:00
cost += grid.PREFERRED_COST
return cost
2018-11-02 22:57:40 +01:00
def expand_dirs(self):
"""
Expand from the end in each of the four cardinal directions plus up
or down but not expanding to blocked cells. Expands in all
directions regardless of preferred directions.
If the width is more than one, it can only expand in one direction
(for now). This is assumed for the supply router for now.
"""
neighbors = []
2018-11-02 22:57:40 +01:00
for d in direction.cardinal_directions(True):
n = self.neighbor(d)
if n:
neighbors.append(n)
2020-11-03 15:29:17 +01:00
return neighbors
def neighbor(self, d):
offset = direction.get_offset(d)
newwave = [point + offset for point in self.pathlist[-1]]
if newwave in self.pathlist:
return None
elif newwave[0].z>1 or newwave[0].z<0:
return None
2020-11-03 15:29:17 +01:00
return newwave
2020-11-03 15:29:17 +01:00
def set_layer(self, zindex):
new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave]
self.pathlist = new_pathlist
2020-11-03 15:29:17 +01:00
def overlap(self, other):
"""
Return the overlap waves ignoring different layers
"""
2020-11-03 15:29:17 +01:00
my_zindex = self.pathlist[0][0].z
other_flat_cells = [vector3d(item.x,item.y,my_zindex) for wave in other.pathlist for item in wave]
# This keeps the wave structure of the self layer
shared_waves = []
for wave in self.pathlist:
for item in wave:
# If any item in the wave is not contained, skip it
if not item in other_flat_cells:
break
else:
shared_waves.append(wave)
if len(shared_waves)>0:
ll = shared_waves[0][0]
ur = shared_waves[-1][-1]
return [ll,ur]
return None
2020-11-03 15:29:17 +01:00