OpenRAM/compiler/path.py

184 lines
7.1 KiB
Python
Raw Normal View History

2016-11-08 18:57:35 +01:00
from tech import drc
from tech import layer as techlayer
import debug
import design
from vector import vector
from utils import snap_to_grid
class path(design.design):
"""
Object metal path; given the layer type
Add a path of minimium metal width between a set of points.
The points should be rectilinear to control the bend points. If
not, it will always go down first. The points are the center of the path.
If width is not given, it uses minimum layer width.
"""
unique_path_id = 1
def __init__(self, layer, position_list, width=None):
name = "path_{0}".format(path.unique_path_id)
path.unique_path_id += 1
design.design.__init__(self, name)
debug.info(2, "create path obj {0}".format(name))
self.name = name
self.layer_name = layer
self.layer_id = techlayer[layer]
if width==None:
self.layer_width = drc["minwidth_{0}".format(layer)]
else:
self.layer_width = width
self.position_list = position_list
self.pins = [] # used for matching parm lengths
self.switch_pos_list = []
self.create_layout()
def create_layout(self):
self.create_rectilinear_route()
self.connect_corner()
self.create_rectangles()
# wires and paths should not be offset to (0,0)
def create_rectilinear_route(self):
""" Add intermediate nodes if it isn't rectilinear. Also skip
repeated nodes. Also, convert to vector if the aren't."""
pl = self.position_list
self.position_list = []
for index in range(len(pl) - 1):
if pl[index] != pl[index + 1]:
self.position_list.append(vector(pl[index]))
if (pl[index][0] != pl[index + 1][0]) and (pl[index][1] != pl[index + 1][1]):
self.position_list.append(vector(pl[index][0], pl[index + 1][1]))
self.position_list.append(vector(pl[-1]))
def pairwise(self, iterable):
"""s -> (s0,s1), (s1,s2), (s2, s3), ..."""
from itertools import tee, izip
a, b = tee(iterable)
next(b, None)
temp = []
for v in izip(a, b):
temp.append(list(v))
return temp
def connect_corner(self):
""" Add a corner square at every corner of the path."""
pl = self.pairwise(self.position_list)
from itertools import izip
orient = None # orientation toggler
offset = [0, 0]
for (v, w), index in izip(pl, range(len(pl))):
if index != 0:
if pl[index][1] == pl[index - 1][0]:
if v[0] != w[0]:
offset = [(offset[0] + (w[0] - v[0])), offset[1]]
else:
offset = [offset[0], (offset[1] + w[1] - v[1])]
orient = not orient
continue
if v[0] != w[0]:
if (orient == None):
orient = True
if not orient:
orient = not orient
temp_offset = offset
self.switch_pos_list.append(temp_offset)
via_offset = self.switch_pos_list[-1]
corner_offset = [via_offset[0] - 0.5 * self.layer_width,
via_offset[1] - 0.5 * self.layer_width]
self.draw_corner_wire(corner_offset)
offset = [(offset[0] + (w[0] - v[0])), offset[1]]
elif v[1] != w[1]:
if (orient == None):
orient = False
if orient:
orient = not orient
temp_offset = offset
self.switch_pos_list.append(temp_offset)
via_offset = self.switch_pos_list[-1]
corner_offset = [via_offset[0] - 0.5 * self.layer_width,
via_offset[1] - 0.5 * self.layer_width]
self.draw_corner_wire(corner_offset)
offset = [offset[0], (offset[1] + w[1] - v[1])]
def draw_corner_wire(self, offset):
""" This function adds the corner squares since the center
line convention only draws to the center of the corner."""
self.add_rect(layer=self.layer_name,
offset=offset,
width=self.layer_width,
height=self.layer_width)
def create_rectangles(self):
""" Create the actual rectangles on teh appropriate layers
using the position list of the corners. """
offset = [0, 0]
# FIXME: These should not be hard coded limits.
xval = [1000000, -1000000]
yval = [1000000, -1000000]
pl = self.position_list # position list
for index in range(len(pl) - 1):
temp_offset = offset
if temp_offset[0] < xval[0]:
xval[0] = temp_offset[0]
if temp_offset[0] > xval[1]:
xval[1] = temp_offset[0]
if temp_offset[1] < yval[0]:
yval[0] = temp_offset[1]
if temp_offset[1] > yval[1]:
yval[1] = temp_offset[1]
if pl[index][0] != pl[index + 1][0]:
line_length = pl[index + 1][0] - pl[index][0]
temp_offset = [temp_offset[0],
temp_offset[1] - 0.5 * self.layer_width]
if line_length < 0:
temp_offset = [temp_offset[0] + line_length,
temp_offset[1]]
self.add_line(layer_name=self.layer_name,
length=abs(line_length),
offset=temp_offset,
orientation="horizontal")
offset = [offset[0] + line_length,
offset[1]]
elif pl[index][1] != pl[index + 1][1]:
line_length = pl[index + 1][1] - pl[index][1]
temp_offset = [temp_offset[0] - 0.5 * self.layer_width,
temp_offset[1]]
if line_length < 0:
temp_offset = [temp_offset[0],
temp_offset[1] + line_length]
self.add_line(layer_name=self.layer_name,
length=abs(line_length),
offset=temp_offset,
orientation="vertical")
offset = [offset[0],
offset[1] + line_length]
self.width = abs(xval[0] - xval[1])
self.height = abs(yval[0] - yval[1])
def add_line(self, layer_name, length, offset, orientation):
"""
straight line object with layer_minwidth
(orientation: "vertical" or "horizontal") default is vertical
"""
layer_width = drc["minwidth_{0}".format(layer_name)]
width = layer_width
height = length
if orientation == "horizontal":
width = length
height = layer_width
self.add_rect(layer=layer_name,
offset=offset,
width=width,
height=height)