2016-11-12 17:57:26 +01:00
|
|
|
import gdsMill
|
|
|
|
|
import tech
|
2016-11-17 00:02:07 +01:00
|
|
|
from contact import contact
|
2016-11-12 17:57:26 +01:00
|
|
|
import math
|
|
|
|
|
import debug
|
2016-11-17 00:02:07 +01:00
|
|
|
import grid
|
2017-04-19 21:41:13 +02:00
|
|
|
from vector import vector
|
|
|
|
|
from vector3d import vector3d
|
2016-11-17 01:47:31 +01:00
|
|
|
|
2016-11-12 17:57:26 +01:00
|
|
|
|
|
|
|
|
class router:
|
|
|
|
|
"""A router class to read an obstruction map from a gds and plan a
|
2016-11-17 00:02:07 +01:00
|
|
|
route on a given layer. This is limited to two layer routes.
|
2016-11-12 17:57:26 +01:00
|
|
|
"""
|
2017-04-12 19:59:04 +02:00
|
|
|
|
2016-11-17 00:02:07 +01:00
|
|
|
def __init__(self, gds_name):
|
2016-11-12 17:57:26 +01:00
|
|
|
"""Use the gds file for the blockages with the top module topName and
|
|
|
|
|
layers for the layers to route on
|
|
|
|
|
"""
|
2017-04-12 19:59:04 +02:00
|
|
|
# Load the gds file and read in all the shapes
|
2016-11-17 00:02:07 +01:00
|
|
|
self.gds_name = gds_name
|
2016-11-17 20:24:17 +01:00
|
|
|
self.layout = gdsMill.VlsiLayout(units=tech.GDS["unit"])
|
2016-11-12 17:57:26 +01:00
|
|
|
self.reader = gdsMill.Gds2reader(self.layout)
|
2016-11-17 00:02:07 +01:00
|
|
|
self.reader.loadFromFile(gds_name)
|
|
|
|
|
self.top_name = self.layout.rootStructureName
|
|
|
|
|
|
2017-04-12 19:59:04 +02:00
|
|
|
# A list of pin names for source and dest
|
2016-11-17 00:02:07 +01:00
|
|
|
self.pin_names = []
|
2017-04-12 19:59:04 +02:00
|
|
|
# The map of pin names to list of all pin shapes for a pin.
|
2016-11-17 00:02:07 +01:00
|
|
|
self.pin_shapes = {}
|
2017-04-12 19:59:04 +02:00
|
|
|
# The corresponding layers of the above pin shapes
|
2016-11-17 00:02:07 +01:00
|
|
|
self.pin_layers = {}
|
2017-04-12 19:59:04 +02:00
|
|
|
|
|
|
|
|
# The boundary will determine the limits to the size of the routing grid
|
2016-11-17 00:02:07 +01:00
|
|
|
self.boundary = self.layout.measureBoundary(self.top_name)
|
|
|
|
|
self.ll = vector(self.boundary[0])
|
|
|
|
|
self.ur = vector(self.boundary[1])
|
2016-11-17 20:24:17 +01:00
|
|
|
|
2016-11-17 00:02:07 +01:00
|
|
|
|
|
|
|
|
def set_top(self,top_name):
|
|
|
|
|
""" If we want to route something besides the top-level cell."""
|
|
|
|
|
self.top_name = top_name
|
|
|
|
|
|
|
|
|
|
def set_layers(self, layers):
|
|
|
|
|
""" Allows us to change the layers that we are routing on. """
|
2016-11-12 17:57:26 +01:00
|
|
|
self.layers = layers
|
2016-11-17 00:02:07 +01:00
|
|
|
(horiz_layer, via_layer, vert_layer) = self.layers
|
|
|
|
|
|
|
|
|
|
self.vert_layer_name = vert_layer
|
|
|
|
|
self.vert_layer_width = tech.drc["minwidth_{0}".format(vert_layer)]
|
|
|
|
|
self.vert_layer_number = tech.layer[vert_layer]
|
|
|
|
|
|
|
|
|
|
self.horiz_layer_name = horiz_layer
|
|
|
|
|
self.horiz_layer_width = tech.drc["minwidth_{0}".format(horiz_layer)]
|
|
|
|
|
self.horiz_layer_number = tech.layer[horiz_layer]
|
|
|
|
|
|
2017-04-12 19:59:04 +02:00
|
|
|
# Contacted track spacing.
|
2016-11-17 00:02:07 +01:00
|
|
|
via_connect = contact(self.layers, (1, 1))
|
2016-11-19 00:18:36 +01:00
|
|
|
max_via_size = max(via_connect.width,via_connect.height)
|
|
|
|
|
horiz_layer_spacing = tech.drc[str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)]
|
|
|
|
|
vert_layer_spacing = tech.drc[str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name)]
|
|
|
|
|
self.horiz_track_width = max_via_size + horiz_layer_spacing
|
|
|
|
|
self.vert_track_width = max_via_size + vert_layer_spacing
|
2016-11-17 00:02:07 +01:00
|
|
|
|
2017-04-12 19:59:04 +02:00
|
|
|
# We'll keep horizontal and vertical tracks the same for simplicity.
|
2016-11-17 00:02:07 +01:00
|
|
|
self.track_width = max(self.horiz_track_width,self.vert_track_width)
|
2017-04-12 19:59:04 +02:00
|
|
|
debug.info(1,"Track width: "+str(self.track_width))
|
|
|
|
|
|
|
|
|
|
self.track_widths = [self.track_width] * 2
|
|
|
|
|
self.track_factor = [1/self.track_width] * 2
|
|
|
|
|
debug.info(1,"Track factor: {0}".format(self.track_factor))
|
2016-11-17 00:02:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_routing_grid(self):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Create a routing grid that spans given area. Wires cannot exist outside region.
|
|
|
|
|
"""
|
2016-11-17 20:24:17 +01:00
|
|
|
# We will add a halo around the boundary
|
|
|
|
|
# of this many tracks
|
2017-04-12 19:59:04 +02:00
|
|
|
size = self.ur - self.ll
|
|
|
|
|
debug.info(1,"Size: {0} x {1}".format(size.x,size.y))
|
2016-11-17 00:02:07 +01:00
|
|
|
|
2017-04-14 22:56:09 +02:00
|
|
|
self.rg = grid.grid()
|
2016-11-17 00:02:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def find_pin(self,pin):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Finds the pin shapes and converts to tracks
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# Returns all the shapes that enclose a pin on a given layer
|
|
|
|
|
(pin_name,pin_layer,pin_shapes) = self.layout.readAllPinShapes(str(pin))
|
2016-11-18 17:55:34 +01:00
|
|
|
|
|
|
|
|
self.pin_shapes[str(pin)]=[]
|
2016-11-17 00:02:07 +01:00
|
|
|
self.pin_names.append(pin_name)
|
2017-04-19 21:41:13 +02:00
|
|
|
self.pin_layers[str(pin)] = pin_layer
|
2016-11-18 17:55:34 +01:00
|
|
|
|
|
|
|
|
for pin_shape in pin_shapes:
|
2016-11-19 01:16:19 +01:00
|
|
|
debug.info(2,"Find pin {0} layer {1} shape {2}".format(pin_name,str(pin_layer),str(pin_shape)))
|
2016-11-18 17:55:34 +01:00
|
|
|
# repack the shape as a pair of vectors rather than four values
|
|
|
|
|
shape=[vector(pin_shape[0],pin_shape[1]),vector(pin_shape[2],pin_shape[3])]
|
2017-04-12 19:59:04 +02:00
|
|
|
# convert the pin coordinates to tracks and round the sizes down
|
|
|
|
|
self.pin_shapes[str(pin)].append(shape)
|
2016-11-18 17:55:34 +01:00
|
|
|
|
|
|
|
|
return self.pin_shapes[str(pin)]
|
2016-11-12 17:57:26 +01:00
|
|
|
|
|
|
|
|
def find_blockages(self):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Iterate through all the layers and write the obstacles to the routing grid.
|
2017-04-24 20:28:36 +02:00
|
|
|
This doesn't consider whether the obstacles will be pins or not. They get reset later
|
|
|
|
|
if they are not actually a blockage.
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
2016-11-17 00:02:07 +01:00
|
|
|
for layer in self.layers:
|
|
|
|
|
self.write_obstacle(self.top_name)
|
2016-11-12 17:57:26 +01:00
|
|
|
|
2017-04-24 19:27:04 +02:00
|
|
|
|
2017-04-16 17:04:06 +02:00
|
|
|
def clear_pins(self):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Reset the source and destination pins to start a new routing.
|
2017-04-24 20:28:36 +02:00
|
|
|
Convert the source/dest pins to blockages.
|
|
|
|
|
Convert the routed path to blockages.
|
|
|
|
|
Keep the other blockages unchanged.
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
2017-04-24 19:27:04 +02:00
|
|
|
|
|
|
|
|
self.pin_names = []
|
|
|
|
|
self.pin_shapes = {}
|
|
|
|
|
self.pin_layers = {}
|
|
|
|
|
|
|
|
|
|
self.rg.reinit()
|
2017-04-15 16:49:05 +02:00
|
|
|
|
2016-11-18 01:46:41 +01:00
|
|
|
|
2017-04-24 20:28:36 +02:00
|
|
|
def route(self, layers, src, dest, cost_factor=1):
|
2016-11-18 01:46:41 +01:00
|
|
|
"""
|
|
|
|
|
Route a single source-destination net and return
|
2017-04-24 20:28:36 +02:00
|
|
|
the simplified rectilinear path. Cost factor is how sub-optimal to explore for a feasible route.
|
|
|
|
|
This is used to speed up the routing when there is not much detouring needed.
|
2016-11-18 01:46:41 +01:00
|
|
|
"""
|
2017-04-12 19:59:04 +02:00
|
|
|
# Clear the pins if we have previously routed
|
2017-04-24 19:27:04 +02:00
|
|
|
if (hasattr(self,'rg')):
|
|
|
|
|
self.clear_pins()
|
|
|
|
|
else:
|
2017-04-24 20:28:36 +02:00
|
|
|
# Set up layers and track sizes
|
|
|
|
|
self.set_layers(layers)
|
|
|
|
|
# Creat a routing grid over the entire area
|
|
|
|
|
# FIXME: This could be created only over the routing region,
|
|
|
|
|
# but this is simplest for now.
|
|
|
|
|
self.create_routing_grid()
|
|
|
|
|
# This will write all shapes as blockages, but setting pins will
|
|
|
|
|
# clear the blockage attribute
|
|
|
|
|
self.find_blockages()
|
2017-04-12 19:59:04 +02:00
|
|
|
|
2016-11-17 20:24:17 +01:00
|
|
|
self.set_source(src)
|
2017-04-12 19:59:04 +02:00
|
|
|
|
2016-11-17 20:24:17 +01:00
|
|
|
self.set_target(dest)
|
2017-04-12 19:59:04 +02:00
|
|
|
|
2017-04-24 21:13:01 +02:00
|
|
|
# View the initial route pins and blockages for debugging
|
|
|
|
|
#self.rg.view()
|
2017-04-24 19:27:04 +02:00
|
|
|
|
2016-11-17 22:26:03 +01:00
|
|
|
# returns the path in tracks
|
2017-04-24 20:28:36 +02:00
|
|
|
(self.path,cost) = self.rg.route(cost_factor)
|
2017-04-14 22:18:35 +02:00
|
|
|
debug.info(1,"Found path: cost={0} ".format(cost))
|
2017-04-12 19:59:04 +02:00
|
|
|
debug.info(2,str(self.path))
|
|
|
|
|
self.set_path(self.path)
|
2017-04-24 21:13:01 +02:00
|
|
|
# View the final route for debugging
|
2017-04-24 21:14:19 +02:00
|
|
|
#self.rg.view()
|
2017-04-12 19:59:04 +02:00
|
|
|
|
|
|
|
|
return
|
2016-11-19 01:16:19 +01:00
|
|
|
|
2017-04-12 19:59:04 +02:00
|
|
|
def add_route(self,cell):
|
|
|
|
|
"""
|
|
|
|
|
Add the current wire route to the given design instance.
|
|
|
|
|
"""
|
2017-04-14 22:18:35 +02:00
|
|
|
# First, simplify the path for
|
2017-04-19 21:41:13 +02:00
|
|
|
#debug.info(1,str(self.path))
|
2017-04-12 19:59:04 +02:00
|
|
|
contracted_path = self.contract_path(self.path)
|
|
|
|
|
debug.info(1,str(contracted_path))
|
|
|
|
|
|
2016-11-19 01:16:19 +01:00
|
|
|
# Make sure there's a pin enclosure on the source and dest
|
|
|
|
|
src_shape = self.convert_track_to_shape(contracted_path[0])
|
2017-04-24 22:47:56 +02:00
|
|
|
cell.add_rect(layer=self.layers[2*contracted_path[0].z],
|
2017-04-12 19:59:04 +02:00
|
|
|
offset=src_shape[0],
|
|
|
|
|
width=src_shape[1].x-src_shape[0].x,
|
|
|
|
|
height=src_shape[1].y-src_shape[0].y)
|
|
|
|
|
|
2016-11-19 01:16:19 +01:00
|
|
|
dest_shape = self.convert_track_to_shape(contracted_path[-1])
|
2017-04-24 22:47:56 +02:00
|
|
|
cell.add_rect(layer=self.layers[2*contracted_path[-1].z],
|
2017-04-12 19:59:04 +02:00
|
|
|
offset=dest_shape[0],
|
|
|
|
|
width=dest_shape[1].x-dest_shape[0].x,
|
|
|
|
|
height=dest_shape[1].y-dest_shape[0].y)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# convert the path back to absolute units from tracks
|
|
|
|
|
abs_path = map(self.convert_point_to_units,contracted_path)
|
2017-04-14 22:18:35 +02:00
|
|
|
debug.info(1,str(abs_path))
|
2017-04-19 21:41:13 +02:00
|
|
|
cell.add_route(self.layers,abs_path)
|
2017-04-14 22:18:35 +02:00
|
|
|
|
|
|
|
|
# Check if a via is needed at the start point
|
2017-04-19 21:41:13 +02:00
|
|
|
if (contracted_path[0].z!=self.source_pin_layer):
|
2017-04-14 22:18:35 +02:00
|
|
|
# offset this by 1/2 the via size
|
|
|
|
|
c=contact(self.layers, (1, 1))
|
|
|
|
|
via_offset = vector(-0.5*c.width,-0.5*c.height)
|
|
|
|
|
cell.add_via(self.layers,abs_path[0]+via_offset)
|
2017-04-24 19:27:04 +02:00
|
|
|
|
2017-04-14 22:18:35 +02:00
|
|
|
# Check if a via is needed at the end point
|
2017-04-19 21:41:13 +02:00
|
|
|
if (contracted_path[-1].z!=self.target_pin_layer):
|
2017-04-14 22:18:35 +02:00
|
|
|
# offset this by 1/2 the via size
|
|
|
|
|
c=contact(self.layers, (1, 1))
|
|
|
|
|
via_offset = vector(-0.5*c.width,-0.5*c.height)
|
|
|
|
|
cell.add_via(self.layers,abs_path[-1]+via_offset)
|
|
|
|
|
|
2016-11-19 01:16:19 +01:00
|
|
|
|
2016-11-12 17:57:26 +01:00
|
|
|
|
|
|
|
|
def create_steiner_routes(self,pins):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Find a set of steiner points and then return the list of
|
|
|
|
|
point-to-point routes.
|
|
|
|
|
"""
|
2016-11-12 17:57:26 +01:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def find_steiner_points(self,pins):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Find the set of steiner points and return them.
|
|
|
|
|
"""
|
2016-11-12 17:57:26 +01:00
|
|
|
pass
|
|
|
|
|
|
2016-11-17 00:02:07 +01:00
|
|
|
def translate_coordinates(self, coord, mirr, angle, xyShift):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Calculate coordinates after flip, rotate, and shift
|
|
|
|
|
"""
|
2016-11-17 00:02:07 +01:00
|
|
|
coordinate = []
|
|
|
|
|
for item in coord:
|
|
|
|
|
x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0])
|
|
|
|
|
y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1])
|
|
|
|
|
coordinate += [(x, y)]
|
|
|
|
|
return coordinate
|
|
|
|
|
|
2016-11-17 20:24:17 +01:00
|
|
|
def convert_shape_to_units(self, shape):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Scale a shape (two vector list) to user units
|
|
|
|
|
"""
|
2016-11-17 20:24:17 +01:00
|
|
|
unit_factor = [tech.GDS["unit"][0]] * 2
|
|
|
|
|
ll=shape[0].scale(unit_factor)
|
|
|
|
|
ur=shape[1].scale(unit_factor)
|
|
|
|
|
return [ll,ur]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def min_max_coord(self, coord):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Find the lowest and highest corner of a Rectangle
|
|
|
|
|
"""
|
2016-11-17 00:02:07 +01:00
|
|
|
coordinate = []
|
2016-11-17 20:24:17 +01:00
|
|
|
minx = min(coord[0][0], coord[1][0], coord[2][0], coord[3][0])
|
|
|
|
|
maxx = max(coord[0][0], coord[1][0], coord[2][0], coord[3][0])
|
|
|
|
|
miny = min(coord[0][1], coord[1][1], coord[2][1], coord[3][1])
|
|
|
|
|
maxy = max(coord[0][1], coord[1][1], coord[2][1], coord[3][1])
|
2016-11-17 00:02:07 +01:00
|
|
|
coordinate += [vector(minx, miny)]
|
|
|
|
|
coordinate += [vector(maxx, maxy)]
|
|
|
|
|
return coordinate
|
|
|
|
|
|
2016-11-17 22:26:03 +01:00
|
|
|
def get_inertia(self,p0,p1):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Sets the direction based on the previous direction we came from.
|
|
|
|
|
"""
|
2016-11-17 22:26:03 +01:00
|
|
|
# direction (index) of movement
|
2017-04-14 22:18:35 +02:00
|
|
|
if p0.x!=p1.x:
|
2017-04-12 19:59:04 +02:00
|
|
|
return 0
|
2017-04-14 22:18:35 +02:00
|
|
|
elif p0.y!=p1.y:
|
|
|
|
|
return 1
|
2016-11-17 22:26:03 +01:00
|
|
|
else:
|
2017-04-12 19:59:04 +02:00
|
|
|
# z direction
|
|
|
|
|
return 2
|
|
|
|
|
|
2016-11-17 22:26:03 +01:00
|
|
|
|
|
|
|
|
def contract_path(self,path):
|
|
|
|
|
"""
|
2017-04-12 19:59:04 +02:00
|
|
|
Remove intermediate points in a rectilinear path.
|
2016-11-17 22:26:03 +01:00
|
|
|
"""
|
|
|
|
|
newpath = [path[0]]
|
2017-04-12 19:59:04 +02:00
|
|
|
for i in range(1,len(path)-1):
|
2016-11-17 22:26:03 +01:00
|
|
|
prev_inertia=self.get_inertia(path[i-1],path[i])
|
|
|
|
|
next_inertia=self.get_inertia(path[i],path[i+1])
|
2017-04-12 19:59:04 +02:00
|
|
|
# if we switch directions, add the point, otherwise don't
|
2016-11-17 22:26:03 +01:00
|
|
|
if prev_inertia!=next_inertia:
|
|
|
|
|
newpath.append(path[i])
|
|
|
|
|
|
2017-04-12 19:59:04 +02:00
|
|
|
# always add the last path
|
2016-11-17 22:26:03 +01:00
|
|
|
newpath.append(path[-1])
|
|
|
|
|
return newpath
|
|
|
|
|
|
|
|
|
|
def set_path(self,path):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Mark the path in the routing grid.
|
|
|
|
|
"""
|
2016-11-17 22:48:27 +01:00
|
|
|
debug.info(3,"Set path: " + str(path))
|
2016-11-17 22:26:03 +01:00
|
|
|
self.rg.set_path(path)
|
|
|
|
|
|
2016-11-17 00:02:07 +01:00
|
|
|
def set_source(self,name):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Mark the grids that are in the pin rectangle ranges to have the source property.
|
|
|
|
|
"""
|
2017-04-19 21:41:13 +02:00
|
|
|
self.source_pin_name = name
|
2016-11-18 17:55:34 +01:00
|
|
|
shapes = self.find_pin(name)
|
2016-11-17 00:02:07 +01:00
|
|
|
zindex = 0 if self.pin_layers[name]==self.horiz_layer_number else 1
|
2017-04-19 21:41:13 +02:00
|
|
|
self.source_pin_layer = zindex
|
2016-11-18 17:55:34 +01:00
|
|
|
for shape in shapes:
|
2017-04-12 19:59:04 +02:00
|
|
|
shape_in_tracks=self.convert_shape_to_tracks(shape)
|
|
|
|
|
debug.info(1,"Set source: " + str(name) + " " + str(shape_in_tracks) + " z=" + str(zindex))
|
|
|
|
|
self.rg.set_source(shape_in_tracks[0],shape_in_tracks[1],zindex)
|
2016-11-17 00:02:07 +01:00
|
|
|
|
2016-11-17 01:47:31 +01:00
|
|
|
|
2016-11-17 00:02:07 +01:00
|
|
|
def set_target(self,name):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Mark the grids that are in the pin rectangle ranges to have the target property.
|
|
|
|
|
"""
|
2017-04-19 21:41:13 +02:00
|
|
|
self.target_pin_name = name
|
2016-11-18 17:55:34 +01:00
|
|
|
shapes = self.find_pin(name)
|
|
|
|
|
zindex = 0 if self.pin_layers[name]==self.horiz_layer_number else 1
|
2017-04-19 21:41:13 +02:00
|
|
|
self.target_pin_layer = zindex
|
2016-11-18 17:55:34 +01:00
|
|
|
for shape in shapes:
|
2017-04-12 19:59:04 +02:00
|
|
|
shape_in_tracks=self.convert_shape_to_tracks(shape)
|
|
|
|
|
debug.info(1,"Set target: " + str(name) + " " + str(shape_in_tracks) + " z=" + str(zindex))
|
|
|
|
|
self.rg.set_target(shape_in_tracks[0],shape_in_tracks[1],zindex)
|
2016-11-12 17:57:26 +01:00
|
|
|
|
2016-11-17 00:02:07 +01:00
|
|
|
def write_obstacle(self, sref, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0)):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
|
|
|
|
Recursive write boundaries as blockages to the routing grid.
|
|
|
|
|
Recurses for each Structure in GDS.
|
|
|
|
|
"""
|
2016-11-17 00:02:07 +01:00
|
|
|
for boundary in self.layout.structures[sref].boundaries:
|
2016-11-17 20:24:17 +01:00
|
|
|
coord_trans = self.translate_coordinates(boundary.coordinates, mirr, angle, xyShift)
|
|
|
|
|
shape_coords = self.min_max_coord(coord_trans)
|
|
|
|
|
shape = self.convert_shape_to_units(shape_coords)
|
2016-11-17 00:02:07 +01:00
|
|
|
|
2017-04-12 19:59:04 +02:00
|
|
|
# only consider the two layers that we are routing on
|
2016-11-17 00:02:07 +01:00
|
|
|
if boundary.drawingLayer in [self.vert_layer_number,self.horiz_layer_number]:
|
2017-04-12 19:59:04 +02:00
|
|
|
zlayer = 0 if boundary.drawingLayer==self.horiz_layer_number else 1
|
2017-04-24 20:28:36 +02:00
|
|
|
[ll,ur]=self.convert_shape_to_tracks(shape)
|
|
|
|
|
self.rg.add_blockage(ll,ur,zlayer)
|
2016-11-17 00:02:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# recurse given the mirror, angle, etc.
|
|
|
|
|
for cur_sref in self.layout.structures[sref].srefs:
|
2016-11-12 17:57:26 +01:00
|
|
|
sMirr = 1
|
2016-11-17 20:24:17 +01:00
|
|
|
if cur_sref.transFlags[0] == True:
|
2016-11-12 17:57:26 +01:00
|
|
|
sMirr = -1
|
|
|
|
|
sAngle = math.radians(float(0))
|
2016-11-17 20:24:17 +01:00
|
|
|
if cur_sref.rotateAngle:
|
2016-11-17 00:02:07 +01:00
|
|
|
sAngle = math.radians(float(cur_sref.rotateAngle))
|
2016-11-12 17:57:26 +01:00
|
|
|
sAngle += angle
|
2016-11-17 00:02:07 +01:00
|
|
|
x = cur_sref.coordinates[0]
|
|
|
|
|
y = cur_sref.coordinates[1]
|
2016-11-12 17:57:26 +01:00
|
|
|
newX = (x)*math.cos(angle) - mirr*(y)*math.sin(angle) + xyShift[0]
|
|
|
|
|
newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1]
|
|
|
|
|
sxyShift = (newX, newY)
|
2016-11-17 00:02:07 +01:00
|
|
|
|
2016-11-17 20:24:17 +01:00
|
|
|
self.write_obstacle(cur_sref.sName, sMirr, sAngle, sxyShift)
|
2016-11-17 00:02:07 +01:00
|
|
|
|
2016-11-18 21:57:07 +01:00
|
|
|
def convert_point_to_units(self,p):
|
2016-11-17 22:26:03 +01:00
|
|
|
"""
|
|
|
|
|
Convert a path set of tracks to center line path.
|
|
|
|
|
"""
|
2017-04-19 21:41:13 +02:00
|
|
|
pt = vector3d(p)
|
|
|
|
|
pt=pt.scale(self.track_widths[0],self.track_widths[1],1)
|
2017-04-12 19:59:04 +02:00
|
|
|
return pt
|
2016-11-17 22:26:03 +01:00
|
|
|
|
2017-04-12 19:59:04 +02:00
|
|
|
def convert_shape_to_tracks(self,shape,round_bigger=False):
|
2016-11-17 00:02:07 +01:00
|
|
|
"""
|
|
|
|
|
Convert a rectangular shape into track units.
|
|
|
|
|
"""
|
|
|
|
|
[ll,ur] = shape
|
2016-11-20 17:41:49 +01:00
|
|
|
ll = snap_to_grid(ll)
|
|
|
|
|
ur = snap_to_grid(ur)
|
2016-11-17 00:02:07 +01:00
|
|
|
|
2016-11-17 22:26:03 +01:00
|
|
|
# to scale coordinates to tracks
|
2017-04-14 22:56:09 +02:00
|
|
|
#debug.info(1,"Converting [ {0} , {1} ]".format(ll,ur))
|
2017-04-12 19:59:04 +02:00
|
|
|
ll=ll.scale(self.track_factor)
|
|
|
|
|
ur=ur.scale(self.track_factor)
|
|
|
|
|
ll = ll.floor() if round_bigger else ll.round()
|
|
|
|
|
ur = ur.ceil() if round_bigger else ur.round()
|
|
|
|
|
#debug.info(1,"Converted [ {0} , {1} ]".format(ll,ur))
|
2016-11-17 00:02:07 +01:00
|
|
|
return [ll,ur]
|
2016-11-19 01:16:19 +01:00
|
|
|
|
|
|
|
|
|
2017-04-12 19:59:04 +02:00
|
|
|
def convert_track_to_shape(self,track):
|
2016-11-19 01:16:19 +01:00
|
|
|
"""
|
2017-04-12 19:59:04 +02:00
|
|
|
Convert a grid point into a rectangle shape that occupies the centered
|
|
|
|
|
track.
|
2016-11-19 01:16:19 +01:00
|
|
|
"""
|
|
|
|
|
# to scale coordinates to tracks
|
2017-04-12 19:59:04 +02:00
|
|
|
# FIXME: should be the metal width no the track width?
|
|
|
|
|
x = track.x*self.track_width - 0.5*self.track_width
|
|
|
|
|
y = track.y*self.track_width - 0.5*self.track_width
|
2016-11-19 01:16:19 +01:00
|
|
|
# offset lowest corner object to to (-track halo,-track halo)
|
2016-11-20 17:41:49 +01:00
|
|
|
ll = snap_to_grid(vector(x,y))
|
2017-04-12 19:59:04 +02:00
|
|
|
ur = snap_to_grid(ll + vector(self.track_width,self.track_width))
|
2016-11-19 01:16:19 +01:00
|
|
|
|
|
|
|
|
return [ll,ur]
|
2016-11-17 00:02:07 +01:00
|
|
|
|
|
|
|
|
# FIXME: This should be replaced with vector.snap_to_grid at some point
|
2016-11-18 21:57:07 +01:00
|
|
|
|
2016-11-17 00:02:07 +01:00
|
|
|
def snap_to_grid(offset):
|
|
|
|
|
"""
|
|
|
|
|
Changes the coodrinate to match the grid settings
|
|
|
|
|
"""
|
|
|
|
|
grid = tech.drc["grid"]
|
|
|
|
|
x = offset[0]
|
|
|
|
|
y = offset[1]
|
|
|
|
|
# this gets the nearest integer value
|
|
|
|
|
xgrid = int(round(round((x / grid), 2), 0))
|
|
|
|
|
ygrid = int(round(round((y / grid), 2), 0))
|
|
|
|
|
xoff = xgrid * grid
|
|
|
|
|
yoff = ygrid * grid
|
|
|
|
|
return vector(xoff, yoff)
|
2016-11-18 21:57:07 +01:00
|
|
|
|