2018-09-21 01:00:13 +02:00
|
|
|
import sys
|
2016-11-12 17:57:26 +01:00
|
|
|
import gdsMill
|
2018-10-12 23:37:51 +02:00
|
|
|
from tech import drc,GDS,layer
|
2016-11-17 00:02:07 +01:00
|
|
|
from contact import contact
|
2016-11-12 17:57:26 +01:00
|
|
|
import math
|
|
|
|
|
import debug
|
2018-08-22 20:37:24 +02:00
|
|
|
from pin_layout import pin_layout
|
2017-04-19 21:41:13 +02:00
|
|
|
from vector import vector
|
|
|
|
|
from vector3d import vector3d
|
2017-06-07 19:10:18 +02:00
|
|
|
from globals import OPTS
|
2018-09-08 19:05:48 +02:00
|
|
|
from pprint import pformat
|
2016-11-12 17:57:26 +01:00
|
|
|
|
|
|
|
|
class router:
|
2018-08-23 00:56:19 +02:00
|
|
|
"""
|
|
|
|
|
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.
|
2018-08-23 00:56:19 +02:00
|
|
|
It populates blockages on a grid class.
|
2016-11-12 17:57:26 +01:00
|
|
|
"""
|
2017-04-12 19:59:04 +02:00
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
def __init__(self, layers, design, gds_filename=None):
|
2016-11-12 17:57:26 +01:00
|
|
|
"""
|
2018-10-04 23:04:29 +02:00
|
|
|
This will instantiate a copy of the gds file or the module at (0,0) and
|
|
|
|
|
route on top of this. The blockages from the gds/module will be considered.
|
|
|
|
|
"""
|
|
|
|
|
self.cell = design
|
2018-08-30 00:32:45 +02:00
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
# If didn't specify a gds blockage file, write it out to read the gds
|
2018-08-30 00:32:45 +02:00
|
|
|
# This isn't efficient, but easy for now
|
2018-10-04 23:04:29 +02:00
|
|
|
if not gds_filename:
|
|
|
|
|
gds_filename = OPTS.openram_temp+"temp.gds"
|
|
|
|
|
self.cell.gds_write(gds_filename)
|
2018-08-30 00:32:45 +02:00
|
|
|
|
|
|
|
|
# Load the gds file and read in all the shapes
|
2018-10-12 23:37:51 +02:00
|
|
|
self.layout = gdsMill.VlsiLayout(units=GDS["unit"])
|
2016-11-12 17:57:26 +01:00
|
|
|
self.reader = gdsMill.Gds2reader(self.layout)
|
2018-10-04 23:04:29 +02:00
|
|
|
self.reader.loadFromFile(gds_filename)
|
2016-11-17 00:02:07 +01:00
|
|
|
self.top_name = self.layout.rootStructureName
|
|
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
# Set up layers and track sizes
|
|
|
|
|
self.set_layers(layers)
|
|
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
### The pin data structures
|
2018-09-06 20:54:14 +02:00
|
|
|
# A map of pin names to pin structures
|
2018-08-23 00:56:19 +02:00
|
|
|
self.pins = {}
|
2018-09-21 01:00:13 +02:00
|
|
|
# This is a set of all pins so that we don't create blockages for these shapes.
|
|
|
|
|
self.all_pins = set()
|
2018-10-11 00:15:58 +02:00
|
|
|
|
|
|
|
|
# This is a set of pin groups. Each group consists of overlapping pin shapes on the same layer.
|
2018-09-08 19:05:48 +02:00
|
|
|
self.pin_groups = {}
|
2018-10-11 00:15:58 +02:00
|
|
|
# These are the corresponding pin grids for each pin group.
|
|
|
|
|
self.pin_grids = {}
|
|
|
|
|
# The corresponding set of partially blocked grids for each pin group.
|
|
|
|
|
# These are blockages for other nets but unblocked for this component.
|
|
|
|
|
self.pin_blockages = {}
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
### The blockage data structures
|
2018-10-11 00:15:58 +02:00
|
|
|
# A list of metal shapes (using the same pin_layout structure) that are not pins but blockages.
|
2018-08-28 19:41:19 +02:00
|
|
|
self.blockages=[]
|
2018-10-11 00:15:58 +02:00
|
|
|
# The corresponding set of blocked grids for above pin shapes
|
2018-09-21 01:00:13 +02:00
|
|
|
self.blocked_grids = set()
|
2018-09-06 20:54:14 +02:00
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
### The routed data structures
|
2018-09-18 21:57:39 +02:00
|
|
|
# A list of paths that have been "routed"
|
2017-06-05 23:42:56 +02:00
|
|
|
self.paths = []
|
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)
|
2018-09-06 20:54:14 +02:00
|
|
|
# These must be un-indexed to get rid of the matrix type
|
|
|
|
|
self.ll = vector(self.boundary[0][0], self.boundary[0][1])
|
|
|
|
|
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
|
2016-11-17 20:24:17 +01:00
|
|
|
|
2018-09-06 20:54:14 +02:00
|
|
|
def clear_pins(self):
|
|
|
|
|
"""
|
|
|
|
|
Convert the routed path to blockages.
|
|
|
|
|
Keep the other blockages unchanged.
|
|
|
|
|
"""
|
|
|
|
|
self.pins = {}
|
2018-09-21 01:00:13 +02:00
|
|
|
self.all_pins = set()
|
2018-09-08 19:05:48 +02:00
|
|
|
self.pin_groups = {}
|
|
|
|
|
self.pin_grids = {}
|
2018-10-11 00:15:58 +02:00
|
|
|
self.pin_blockages = {}
|
2018-09-21 01:00:13 +02:00
|
|
|
# DO NOT clear the blockages as these don't change
|
|
|
|
|
self.rg.reinit()
|
|
|
|
|
|
2018-09-06 20:54:14 +02: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
|
|
|
|
|
|
2018-09-05 01:35:40 +02:00
|
|
|
def get_zindex(self,layer_num):
|
|
|
|
|
if layer_num==self.horiz_layer_number:
|
|
|
|
|
return 0
|
|
|
|
|
else:
|
|
|
|
|
return 1
|
|
|
|
|
|
2018-09-06 20:54:14 +02:00
|
|
|
def get_layer(self, zindex):
|
|
|
|
|
if zindex==1:
|
|
|
|
|
return self.vert_layer_name
|
|
|
|
|
elif zindex==0:
|
|
|
|
|
return self.horiz_layer_name
|
|
|
|
|
else:
|
2018-09-07 23:46:58 +02:00
|
|
|
debug.error("Invalid zindex {}".format(zindex),-1)
|
2018-09-06 20:54:14 +02:00
|
|
|
|
|
|
|
|
def is_wave(self,path):
|
|
|
|
|
"""
|
2018-09-07 23:46:58 +02:00
|
|
|
Determines if this is a multi-track width wave (True) or a normal route (False)
|
2018-09-06 20:54:14 +02:00
|
|
|
"""
|
2018-09-07 23:46:58 +02:00
|
|
|
return len(path[0])>1
|
2018-09-06 20:54:14 +02:00
|
|
|
|
2016-11-17 00:02:07 +01:00
|
|
|
def set_layers(self, layers):
|
2018-09-07 23:46:58 +02:00
|
|
|
"""
|
|
|
|
|
Allows us to change the layers that we are routing on. First layer
|
2017-05-25 00:17:49 +02:00
|
|
|
is always horizontal, middle is via, and last is always
|
|
|
|
|
vertical.
|
|
|
|
|
"""
|
2016-11-12 17:57:26 +01:00
|
|
|
self.layers = layers
|
2018-09-18 23:55:36 +02:00
|
|
|
(self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers
|
2016-11-17 00:02:07 +01:00
|
|
|
|
2018-10-12 23:37:51 +02:00
|
|
|
# This is the minimum routed track spacing
|
2016-11-17 00:02:07 +01:00
|
|
|
via_connect = contact(self.layers, (1, 1))
|
2017-05-25 00:17:49 +02:00
|
|
|
self.max_via_size = max(via_connect.width,via_connect.height)
|
2018-10-12 23:37:51 +02:00
|
|
|
|
|
|
|
|
self.vert_layer_minwidth = drc("minwidth_{0}".format(self.vert_layer_name))
|
|
|
|
|
self.vert_layer_spacing = drc(str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name))
|
|
|
|
|
self.vert_layer_number = layer[self.vert_layer_name]
|
|
|
|
|
|
|
|
|
|
self.horiz_layer_minwidth = drc("minwidth_{0}".format(self.horiz_layer_name))
|
|
|
|
|
self.horiz_layer_spacing = drc(str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name))
|
|
|
|
|
self.horiz_layer_number = layer[self.horiz_layer_name]
|
|
|
|
|
|
2017-05-25 00:17:49 +02:00
|
|
|
self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing
|
|
|
|
|
self.vert_track_width = self.max_via_size + self.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
|
|
|
|
2018-09-18 23:55:36 +02:00
|
|
|
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
|
|
|
|
|
self.layer_widths = [self.track_width - self.horiz_layer_spacing, 1, self.track_width - self.vert_layer_spacing]
|
2016-11-17 00:02:07 +01:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def retrieve_pins(self,pin_name):
|
|
|
|
|
"""
|
|
|
|
|
Retrieve the pin shapes from the layout.
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
2018-09-06 01:01:11 +02:00
|
|
|
shape_list=self.layout.getAllPinShapesByLabel(str(pin_name))
|
2018-09-18 21:57:39 +02:00
|
|
|
pin_set = set()
|
2018-09-05 20:06:17 +02:00
|
|
|
for shape in shape_list:
|
|
|
|
|
(name,layer,boundary)=shape
|
2018-08-22 20:37:24 +02:00
|
|
|
rect = [vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])]
|
2018-09-05 20:06:17 +02:00
|
|
|
pin = pin_layout(pin_name, rect, layer)
|
2018-09-18 21:57:39 +02:00
|
|
|
pin_set.add(pin)
|
2018-08-22 20:37:24 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
debug.check(len(pin_set)>0,"Did not find any pin shapes for {0}.".format(str(pin_name)))
|
|
|
|
|
self.pins[pin_name] = pin_set
|
2018-09-21 01:00:13 +02:00
|
|
|
self.all_pins.update(pin_set)
|
2018-08-22 20:37:24 +02:00
|
|
|
|
2018-10-06 00:57:34 +02:00
|
|
|
for pin in self.pins[pin_name]:
|
2018-10-11 00:15:58 +02:00
|
|
|
debug.info(2,"Retrieved pin {}".format(str(pin)))
|
2018-10-06 00:57:34 +02:00
|
|
|
|
|
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def find_pins(self,pin_name):
|
|
|
|
|
"""
|
|
|
|
|
Finds the pin shapes and converts to tracks.
|
|
|
|
|
Pin can either be a label or a location,layer pair: [[x,y],layer].
|
|
|
|
|
"""
|
|
|
|
|
self.retrieve_pins(pin_name)
|
|
|
|
|
self.analyze_pins(pin_name)
|
2016-11-18 17:55:34 +01:00
|
|
|
|
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
|
|
|
"""
|
2018-09-07 23:46:58 +02:00
|
|
|
for layer in [self.vert_layer_number,self.horiz_layer_number]:
|
2018-09-08 19:05:48 +02:00
|
|
|
self.retrieve_blockages(layer)
|
|
|
|
|
|
2017-04-24 19:27:04 +02:00
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
# # def reinit(self):
|
|
|
|
|
# # """
|
|
|
|
|
# # Reset the source and destination pins to start a new routing.
|
|
|
|
|
# # Convert the source/dest pins to blockages.
|
|
|
|
|
# # Convert the routed path to blockages.
|
|
|
|
|
# # Keep the other blockages unchanged.
|
|
|
|
|
# # """
|
|
|
|
|
# # self.clear_pins()
|
|
|
|
|
# # # DO NOT clear the blockages as these don't change
|
|
|
|
|
# # self.rg.reinit()
|
2017-04-15 16:49:05 +02:00
|
|
|
|
2016-11-18 01:46:41 +01:00
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
def find_pins_and_blockages(self, pin_list):
|
|
|
|
|
"""
|
|
|
|
|
Find the pins and blockages in the design
|
|
|
|
|
"""
|
|
|
|
|
# This finds the pin shapes and sorts them into "groups" that are connected
|
2018-10-06 00:57:34 +02:00
|
|
|
# This must come before the blockages, so we can ignore metal shapes that are blockages.
|
2018-10-04 23:04:29 +02:00
|
|
|
for pin in pin_list:
|
|
|
|
|
self.find_pins(pin)
|
|
|
|
|
|
|
|
|
|
# This will get all shapes as blockages and convert to grid units
|
|
|
|
|
# This ignores shapes that were pins
|
|
|
|
|
self.find_blockages()
|
2018-10-11 00:15:58 +02:00
|
|
|
|
|
|
|
|
# Convert the blockages to grid units
|
|
|
|
|
self.convert_blockages()
|
2018-10-04 23:04:29 +02:00
|
|
|
|
|
|
|
|
# This will convert the pins to grid units
|
|
|
|
|
# It must be done after blockages to ensure no DRCs between expanded pins and blocked grids
|
|
|
|
|
for pin in pin_list:
|
|
|
|
|
self.convert_pins(pin)
|
|
|
|
|
|
|
|
|
|
# Enclose the continguous grid units in a metal rectangle to fix some DRCs
|
|
|
|
|
self.enclose_pins()
|
|
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
def prepare_blockages(self, pin_name):
|
2018-10-04 23:04:29 +02:00
|
|
|
"""
|
|
|
|
|
Reset and add all of the blockages in the design.
|
|
|
|
|
Names is a list of pins to add as a blockage.
|
|
|
|
|
"""
|
2018-10-11 00:15:58 +02:00
|
|
|
debug.info(3,"Preparing blockages.")
|
2018-10-04 23:04:29 +02:00
|
|
|
|
|
|
|
|
# Start fresh. Not the best for run-time, but simpler.
|
|
|
|
|
self.clear_blockages()
|
|
|
|
|
# This adds the initial blockges of the design
|
|
|
|
|
#print("BLOCKING:",self.blocked_grids)
|
|
|
|
|
self.set_blockages(self.blocked_grids,True)
|
|
|
|
|
|
|
|
|
|
# Block all of the supply rails (some will be unblocked if they're a target)
|
|
|
|
|
self.set_supply_rail_blocked(True)
|
|
|
|
|
|
|
|
|
|
# Block all of the pin components (some will be unblocked if they're a source/target)
|
2018-10-11 00:15:58 +02:00
|
|
|
for name in self.pin_grids.keys():
|
|
|
|
|
self.set_blockages(self.pin_grids[name],True)
|
|
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
# Don't mark the other components as targets since we want to route
|
|
|
|
|
# directly to a rail, but unblock all the source components so we can
|
|
|
|
|
# route over them
|
|
|
|
|
self.set_blockages(self.pin_grids[pin_name],False)
|
|
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
# These are the paths that have already been routed.
|
|
|
|
|
self.set_path_blockages()
|
|
|
|
|
|
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
|
|
|
|
|
"""
|
2018-10-12 23:37:51 +02:00
|
|
|
unit_factor = [GDS["unit"][0]] * 2
|
2016-11-17 20:24:17 +01:00
|
|
|
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
|
|
|
|
2017-06-05 23:42:56 +02:00
|
|
|
def add_path_blockages(self):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
2017-06-05 23:42:56 +02:00
|
|
|
Go through all of the past paths and add them as blockages.
|
|
|
|
|
This is so we don't have to write/reload the GDS.
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
2017-06-05 23:42:56 +02:00
|
|
|
for path in self.paths:
|
|
|
|
|
for grid in path:
|
|
|
|
|
self.rg.set_blocked(grid)
|
2016-11-17 22:26:03 +01:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def clear_blockages(self):
|
|
|
|
|
"""
|
|
|
|
|
Clear all blockages on the grid.
|
|
|
|
|
"""
|
2018-10-11 00:15:58 +02:00
|
|
|
debug.info(3,"Clearing all blockages")
|
2018-09-08 19:05:48 +02:00
|
|
|
self.rg.clear_blockages()
|
|
|
|
|
|
2018-09-13 18:10:29 +02:00
|
|
|
def set_blockages(self, blockages, value=True):
|
2018-09-08 19:05:48 +02:00
|
|
|
""" Flag the blockages in the grid """
|
2018-09-13 18:10:29 +02:00
|
|
|
self.rg.set_blocked(blockages, value)
|
|
|
|
|
|
|
|
|
|
def set_path_blockages(self,value=True):
|
|
|
|
|
""" Flag the paths as blockages """
|
|
|
|
|
# These are the paths that have already been routed.
|
|
|
|
|
# This adds the initial blockges of the design
|
|
|
|
|
for p in self.paths:
|
|
|
|
|
p.set_blocked(value)
|
|
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def get_blockage_tracks(self, ll, ur, z):
|
2018-09-13 18:10:29 +02:00
|
|
|
debug.info(4,"Converting blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
|
2018-09-08 19:05:48 +02:00
|
|
|
|
|
|
|
|
block_list = []
|
|
|
|
|
for x in range(int(ll[0]),int(ur[0])+1):
|
|
|
|
|
for y in range(int(ll[1]),int(ur[1])+1):
|
|
|
|
|
block_list.append(vector3d(x,y,z))
|
2018-09-05 20:06:17 +02:00
|
|
|
|
2018-09-13 18:10:29 +02:00
|
|
|
return set(block_list)
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2018-09-13 18:10:29 +02:00
|
|
|
def convert_blockage(self, blockage):
|
|
|
|
|
"""
|
|
|
|
|
Convert a pin layout blockage shape to routing grid tracks.
|
|
|
|
|
"""
|
2018-09-21 01:00:13 +02:00
|
|
|
# Inflate the blockage by half a spacing rule
|
2018-09-13 18:10:29 +02:00
|
|
|
[ll,ur]=self.convert_blockage_to_tracks(blockage.inflate())
|
|
|
|
|
zlayer = self.get_zindex(blockage.layer_num)
|
2018-09-21 01:00:13 +02:00
|
|
|
blockage_tracks = self.get_blockage_tracks(ll, ur, zlayer)
|
2018-09-13 18:10:29 +02:00
|
|
|
return blockage_tracks
|
2018-09-08 19:05:48 +02:00
|
|
|
|
|
|
|
|
def convert_blockages(self):
|
|
|
|
|
""" Convert blockages to grid tracks. """
|
|
|
|
|
|
2017-06-05 23:42:56 +02:00
|
|
|
for blockage in self.blockages:
|
2018-09-13 18:10:29 +02:00
|
|
|
debug.info(3,"Converting blockage {}".format(str(blockage)))
|
|
|
|
|
blockage_list = self.convert_blockage(blockage)
|
|
|
|
|
self.blocked_grids.update(blockage_list)
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2016-11-12 17:57:26 +01:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def retrieve_blockages(self, layer_num):
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
2017-06-05 23:42:56 +02:00
|
|
|
Recursive find boundaries as blockages to the routing grid.
|
2017-04-12 19:59:04 +02:00
|
|
|
"""
|
2018-08-28 19:41:19 +02:00
|
|
|
|
|
|
|
|
shapes = self.layout.getAllShapesInStructureList(layer_num)
|
|
|
|
|
for boundary in shapes:
|
2018-08-30 00:32:45 +02:00
|
|
|
ll = vector(boundary[0],boundary[1])
|
|
|
|
|
ur = vector(boundary[2],boundary[3])
|
|
|
|
|
rect = [ll,ur]
|
|
|
|
|
new_pin = pin_layout("blockage{}".format(len(self.blockages)),rect,layer_num)
|
2018-10-06 00:57:34 +02:00
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
# If there is a rectangle that is the same in the pins, it isn't a blockage!
|
|
|
|
|
if new_pin not in self.all_pins:
|
|
|
|
|
self.blockages.append(new_pin)
|
2018-08-28 19:41:19 +02:00
|
|
|
|
2016-11-17 00:02:07 +01:00
|
|
|
|
2018-09-08 19:05:48 +02: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)
|
2018-09-06 20:54:14 +02:00
|
|
|
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
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def convert_wave_to_units(self, wave):
|
2018-09-06 20:54:14 +02:00
|
|
|
"""
|
|
|
|
|
Convert a wave to a set of center points
|
|
|
|
|
"""
|
|
|
|
|
return [self.convert_point_to_units(i) for i in wave]
|
|
|
|
|
|
|
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def convert_blockage_to_tracks(self, shape):
|
2016-11-17 00:02:07 +01:00
|
|
|
"""
|
2017-05-25 00:36:30 +02:00
|
|
|
Convert a rectangular blockage shape into track units.
|
|
|
|
|
"""
|
2018-08-28 19:41:19 +02:00
|
|
|
(ll,ur) = shape
|
2018-09-21 01:00:13 +02:00
|
|
|
ll = snap_to_grid(ll)
|
|
|
|
|
ur = snap_to_grid(ur)
|
2017-05-25 00:36:30 +02:00
|
|
|
|
|
|
|
|
# to scale coordinates to tracks
|
2018-08-28 19:41:19 +02:00
|
|
|
debug.info(3,"Converting [ {0} , {1} ]".format(ll,ur))
|
|
|
|
|
old_ll = ll
|
|
|
|
|
old_ur = ur
|
2017-05-25 00:36:30 +02:00
|
|
|
ll=ll.scale(self.track_factor)
|
|
|
|
|
ur=ur.scale(self.track_factor)
|
2018-09-05 20:06:17 +02:00
|
|
|
# We can round since we are using inflated shapes
|
|
|
|
|
# and the track points are at the center
|
|
|
|
|
ll = ll.round()
|
|
|
|
|
ur = ur.round()
|
|
|
|
|
# if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5:
|
|
|
|
|
# debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur))
|
|
|
|
|
# debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur))
|
|
|
|
|
# pin=self.convert_track_to_shape(ll)
|
|
|
|
|
# debug.info(0,"Pin {}".format(pin))
|
2017-05-25 00:36:30 +02:00
|
|
|
return [ll,ur]
|
|
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
def convert_pin_to_tracks(self, pin_name, pin):
|
2017-05-25 00:36:30 +02:00
|
|
|
"""
|
2017-05-25 19:37:24 +02:00
|
|
|
Convert a rectangular pin shape into a list of track locations,layers.
|
2018-09-21 01:00:13 +02:00
|
|
|
If no pins are "on-grid" (i.e. sufficient overlap) it makes the one with most overlap if it is not blocked.
|
2016-11-17 00:02:07 +01:00
|
|
|
"""
|
2018-08-28 19:41:19 +02:00
|
|
|
(ll,ur) = pin.rect
|
2018-09-13 18:10:29 +02:00
|
|
|
debug.info(3,"Converting pin [ {0} , {1} ]".format(ll,ur))
|
2017-05-25 19:37:24 +02:00
|
|
|
|
|
|
|
|
# scale the size bigger to include neaby tracks
|
|
|
|
|
ll=ll.scale(self.track_factor).floor()
|
|
|
|
|
ur=ur.scale(self.track_factor).ceil()
|
|
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
# Keep tabs on tracks with sufficient and insufficient overlap
|
|
|
|
|
sufficient_list = set()
|
|
|
|
|
insufficient_list = set()
|
|
|
|
|
|
2018-09-05 01:35:40 +02:00
|
|
|
zindex=self.get_zindex(pin.layer_num)
|
2018-09-13 18:10:29 +02:00
|
|
|
for x in range(int(ll[0]),int(ur[0])+1):
|
|
|
|
|
for y in range(int(ll[1]),int(ur[1])+1):
|
|
|
|
|
debug.info(4,"Converting [ {0} , {1} ]".format(x,y))
|
2018-09-21 01:00:13 +02:00
|
|
|
(full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex))
|
|
|
|
|
if full_overlap:
|
|
|
|
|
sufficient_list.update([full_overlap])
|
|
|
|
|
if partial_overlap:
|
|
|
|
|
insufficient_list.update([partial_overlap])
|
|
|
|
|
|
|
|
|
|
if len(sufficient_list)>0:
|
|
|
|
|
return sufficient_list
|
|
|
|
|
elif len(insufficient_list)>0:
|
|
|
|
|
# If there wasn't a sufficient grid, find the best and patch it to be on grid.
|
|
|
|
|
return self.get_best_offgrid_pin(pin, insufficient_list)
|
|
|
|
|
else:
|
|
|
|
|
debug.error("Unable to find any overlapping grids.", -1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_best_offgrid_pin(self, pin, insufficient_list):
|
|
|
|
|
"""
|
|
|
|
|
Given a pin and a list of partial overlap grids:
|
|
|
|
|
1) Find the unblocked grids.
|
|
|
|
|
2) If one, use it.
|
|
|
|
|
3) If not, find the greatest overlap.
|
|
|
|
|
4) Add a pin with the most overlap to make it "on grid"
|
|
|
|
|
that is not blocked.
|
|
|
|
|
"""
|
|
|
|
|
#print("INSUFFICIENT LIST",insufficient_list)
|
|
|
|
|
# Find the coordinate with the most overlap
|
|
|
|
|
best_coord = None
|
|
|
|
|
best_overlap = -math.inf
|
|
|
|
|
for coord in insufficient_list:
|
|
|
|
|
full_rect = self.convert_track_to_pin(coord)
|
|
|
|
|
# Compute the overlap with that rectangle
|
|
|
|
|
overlap_rect=self.compute_overlap(pin.rect,full_rect)
|
|
|
|
|
# Determine the min x or y overlap
|
|
|
|
|
min_overlap = min(overlap_rect)
|
|
|
|
|
if min_overlap>best_overlap:
|
|
|
|
|
best_overlap=min_overlap
|
|
|
|
|
best_coord=coord
|
|
|
|
|
|
|
|
|
|
return set([best_coord])
|
|
|
|
|
|
|
|
|
|
|
2018-10-12 23:37:51 +02:00
|
|
|
def get_layer_width_space(self, zindex, width=0, length=0):
|
2018-09-21 01:00:13 +02:00
|
|
|
"""
|
2018-10-12 23:37:51 +02:00
|
|
|
Return the width and spacing of a given layer
|
|
|
|
|
and wire of a given width and length.
|
2018-09-21 01:00:13 +02:00
|
|
|
"""
|
|
|
|
|
if zindex==1:
|
2018-10-12 23:37:51 +02:00
|
|
|
layer_name = self.vert_layer_name
|
2018-09-21 01:00:13 +02:00
|
|
|
elif zindex==0:
|
2018-10-12 23:37:51 +02:00
|
|
|
layer_name = self.horiz_layer_name
|
2018-09-21 01:00:13 +02:00
|
|
|
else:
|
|
|
|
|
debug.error("Invalid zindex for track", -1)
|
|
|
|
|
|
2018-10-15 18:59:16 +02:00
|
|
|
min_width = drc("minwidth_{0}".format(layer_name), width, length)
|
|
|
|
|
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), width, length)
|
2018-10-12 23:37:51 +02:00
|
|
|
|
2018-10-15 18:59:16 +02:00
|
|
|
return (min_width,min_spacing)
|
2018-09-21 01:00:13 +02:00
|
|
|
|
|
|
|
|
def convert_pin_coord_to_tracks(self, pin, coord):
|
|
|
|
|
"""
|
|
|
|
|
Given a pin and a track coordinate, determine if the pin overlaps enough.
|
|
|
|
|
If it does, add additional metal to make the pin "on grid".
|
|
|
|
|
If it doesn't, add it to the blocked grid list.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
(width, spacing) = self.get_layer_width_space(coord.z)
|
|
|
|
|
|
|
|
|
|
# This is the rectangle if we put a pin in the center of the track
|
|
|
|
|
track_rect = self.convert_track_to_pin(coord)
|
|
|
|
|
overlap_width = self.compute_overlap_width(pin.rect, track_rect)
|
|
|
|
|
|
|
|
|
|
debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_rect, overlap_width))
|
|
|
|
|
# If it overlaps by more than the min width DRC, we can just use the track
|
|
|
|
|
if overlap_width==math.inf or snap_val_to_grid(overlap_width) >= snap_val_to_grid(width):
|
|
|
|
|
debug.info(3," Overlap: {0} >? {1}".format(overlap_width,spacing))
|
|
|
|
|
return (coord, None)
|
|
|
|
|
# Otherwise, keep track of the partial overlap grids in case we need to patch it later.
|
|
|
|
|
else:
|
|
|
|
|
debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_width,spacing))
|
|
|
|
|
return (None, coord)
|
|
|
|
|
|
2017-06-05 23:42:56 +02:00
|
|
|
|
2017-05-25 19:37:24 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def compute_overlap(self, r1, r2):
|
2017-05-25 19:37:24 +02:00
|
|
|
""" Calculate the rectangular overlap of two rectangles. """
|
|
|
|
|
(r1_ll,r1_ur) = r1
|
|
|
|
|
(r2_ll,r2_ur) = r2
|
|
|
|
|
|
|
|
|
|
#ov_ur = vector(min(r1_ur.x,r2_ur.x),min(r1_ur.y,r2_ur.y))
|
|
|
|
|
#ov_ll = vector(max(r1_ll.x,r2_ll.x),max(r1_ll.y,r2_ll.y))
|
2016-11-19 01:16:19 +01:00
|
|
|
|
2017-05-25 19:37:24 +02:00
|
|
|
dy = min(r1_ur.y,r2_ur.y)-max(r1_ll.y,r2_ll.y)
|
|
|
|
|
dx = min(r1_ur.x,r2_ur.x)-max(r1_ll.x,r2_ll.x)
|
|
|
|
|
|
|
|
|
|
if dx>0 and dy>0:
|
|
|
|
|
return [dx,dy]
|
|
|
|
|
else:
|
|
|
|
|
return [0,0]
|
2018-09-21 01:00:13 +02:00
|
|
|
|
|
|
|
|
def compute_overlap_width(self, r1, r2):
|
|
|
|
|
"""
|
|
|
|
|
Calculate the intersection segment and determine its width.
|
|
|
|
|
"""
|
|
|
|
|
intersections = self.compute_overlap_segment(r1,r2)
|
|
|
|
|
|
|
|
|
|
if len(intersections)==2:
|
|
|
|
|
(p1,p2) = intersections
|
|
|
|
|
return math.sqrt(pow(p1[0]-p2[0],2) + pow(p1[1]-p2[1],2))
|
|
|
|
|
else:
|
|
|
|
|
# we either have no overlap or complete overlap
|
|
|
|
|
# Compute the width of the overlap of the two rectangles
|
|
|
|
|
overlap_rect=self.compute_overlap(r1, r2)
|
|
|
|
|
# Determine the min x or y overlap
|
|
|
|
|
min_overlap = min(overlap_rect)
|
|
|
|
|
if min_overlap>0:
|
|
|
|
|
return math.inf
|
|
|
|
|
else:
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def compute_overlap_segment(self, r1, r2):
|
|
|
|
|
"""
|
|
|
|
|
Calculate the intersection segment of two rectangles
|
|
|
|
|
(if any)
|
|
|
|
|
"""
|
|
|
|
|
(r1_ll,r1_ur) = r1
|
|
|
|
|
(r2_ll,r2_ur) = r2
|
|
|
|
|
|
|
|
|
|
# The other corners besides ll and ur
|
|
|
|
|
r1_ul = vector(r1_ll.x, r1_ur.y)
|
|
|
|
|
r1_lr = vector(r1_ur.x, r1_ll.y)
|
|
|
|
|
r2_ul = vector(r2_ll.x, r2_ur.y)
|
|
|
|
|
r2_lr = vector(r2_ur.x, r2_ll.y)
|
|
|
|
|
|
|
|
|
|
from itertools import tee
|
|
|
|
|
def pairwise(iterable):
|
|
|
|
|
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
|
|
|
|
|
a, b = tee(iterable)
|
|
|
|
|
next(b, None)
|
|
|
|
|
return zip(a, b)
|
|
|
|
|
|
|
|
|
|
# R1 edges CW
|
|
|
|
|
r1_cw_points = [r1_ll, r1_ul, r1_ur, r1_lr, r1_ll]
|
|
|
|
|
r1_edges = []
|
|
|
|
|
for (p,q) in pairwise(r1_cw_points):
|
|
|
|
|
r1_edges.append([p,q])
|
|
|
|
|
|
|
|
|
|
# R2 edges CW
|
|
|
|
|
r2_cw_points = [r2_ll, r2_ul, r2_ur, r2_lr, r2_ll]
|
|
|
|
|
r2_edges = []
|
|
|
|
|
for (p,q) in pairwise(r2_cw_points):
|
|
|
|
|
r2_edges.append([p,q])
|
|
|
|
|
|
|
|
|
|
# There are 4 edges on each rectangle
|
|
|
|
|
# so just brute force check intersection of each
|
|
|
|
|
# Two pairs of them should intersect
|
|
|
|
|
intersections = []
|
|
|
|
|
for r1e in r1_edges:
|
|
|
|
|
for r2e in r2_edges:
|
|
|
|
|
i = self.segment_intersection(r1e, r2e)
|
|
|
|
|
if i:
|
|
|
|
|
intersections.append(i)
|
|
|
|
|
|
|
|
|
|
return intersections
|
|
|
|
|
|
|
|
|
|
def on_segment(self, p, q, r):
|
|
|
|
|
"""
|
|
|
|
|
Given three co-linear points, determine if q lies on segment pr
|
|
|
|
|
"""
|
|
|
|
|
if q[0] <= max(p[0], r[0]) and \
|
|
|
|
|
q[0] >= min(p[0], r[0]) and \
|
|
|
|
|
q[1] <= max(p[1], r[1]) and \
|
|
|
|
|
q[1] >= min(p[1], r[1]):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def segment_intersection(self, s1, s2):
|
|
|
|
|
"""
|
|
|
|
|
Determine the intersection point of two segments
|
|
|
|
|
Return the a segment if they overlap.
|
|
|
|
|
Return None if they don't.
|
|
|
|
|
"""
|
|
|
|
|
(a,b) = s1
|
|
|
|
|
(c,d) = s2
|
|
|
|
|
# Line AB represented as a1x + b1y = c1
|
|
|
|
|
a1 = b.y - a.y
|
|
|
|
|
b1 = a.x - b.x
|
|
|
|
|
c1 = a1*a.x + b1*a.y
|
|
|
|
|
|
|
|
|
|
# Line CD represented as a2x + b2y = c2
|
|
|
|
|
a2 = d.y - c.y
|
|
|
|
|
b2 = c.x - d.x
|
|
|
|
|
c2 = a2*c.x + b2*c.y
|
|
|
|
|
|
|
|
|
|
determinant = a1*b2 - a2*b1
|
|
|
|
|
|
|
|
|
|
if determinant!=0:
|
|
|
|
|
x = (b2*c1 - b1*c2)/determinant
|
|
|
|
|
y = (a1*c2 - a2*c1)/determinant
|
2017-05-25 19:37:24 +02:00
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
r = [x,y]
|
|
|
|
|
if self.on_segment(a, r, b) and self.on_segment(c, r, d):
|
|
|
|
|
return [x, y]
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
2016-11-19 01:16:19 +01:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def convert_track_to_pin(self, track):
|
2016-11-19 01:16:19 +01:00
|
|
|
"""
|
2017-05-25 00:17:49 +02:00
|
|
|
Convert a grid point into a rectangle shape that is centered
|
|
|
|
|
track in the track and leaves half a DRC space in each direction.
|
|
|
|
|
"""
|
|
|
|
|
# space depends on which layer it is
|
2018-09-18 21:57:39 +02:00
|
|
|
if self.get_layer(track[2])==self.horiz_layer_name:
|
2017-05-25 00:36:30 +02:00
|
|
|
space = 0.5*self.horiz_layer_spacing
|
2017-05-25 00:17:49 +02:00
|
|
|
else:
|
2017-05-25 00:36:30 +02:00
|
|
|
space = 0.5*self.vert_layer_spacing
|
|
|
|
|
|
2017-05-25 00:17:49 +02:00
|
|
|
# calculate lower left
|
|
|
|
|
x = track.x*self.track_width - 0.5*self.track_width + space
|
|
|
|
|
y = track.y*self.track_width - 0.5*self.track_width + space
|
|
|
|
|
ll = snap_to_grid(vector(x,y))
|
|
|
|
|
|
|
|
|
|
# calculate upper right
|
|
|
|
|
x = track.x*self.track_width + 0.5*self.track_width - space
|
|
|
|
|
y = track.y*self.track_width + 0.5*self.track_width - space
|
|
|
|
|
ur = snap_to_grid(vector(x,y))
|
|
|
|
|
|
|
|
|
|
return [ll,ur]
|
|
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def convert_track_to_shape(self, track):
|
2017-05-25 00:17:49 +02:00
|
|
|
"""
|
|
|
|
|
Convert a grid point into a rectangle shape that occupies the entire centered
|
2017-04-12 19:59:04 +02:00
|
|
|
track.
|
2016-11-19 01:16:19 +01:00
|
|
|
"""
|
|
|
|
|
# to scale coordinates to tracks
|
2018-09-13 18:10:29 +02:00
|
|
|
x = track[0]*self.track_width - 0.5*self.track_width
|
|
|
|
|
y = track[1]*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]
|
2017-05-25 00:17:49 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def analyze_pins(self, pin_name):
|
2018-08-23 00:56:19 +02:00
|
|
|
"""
|
2018-09-08 19:05:48 +02:00
|
|
|
Analyze the shapes of a pin and combine them into groups which are connected.
|
2018-08-23 00:56:19 +02:00
|
|
|
"""
|
2018-09-18 21:57:39 +02:00
|
|
|
pin_set = self.pins[pin_name]
|
|
|
|
|
local_debug=False
|
2018-09-08 19:05:48 +02:00
|
|
|
# Put each pin in an equivalence class of it's own
|
2018-09-18 21:57:39 +02:00
|
|
|
equiv_classes = [set([x]) for x in pin_set]
|
|
|
|
|
if local_debug:
|
2018-10-15 20:25:51 +02:00
|
|
|
debug.info(0,"INITIAL\n",equiv_classes)
|
2018-09-08 19:05:48 +02:00
|
|
|
|
|
|
|
|
def compare_classes(class1, class2):
|
|
|
|
|
"""
|
|
|
|
|
Determine if two classes should be combined and if so return
|
|
|
|
|
the combined set. Otherwise, return None.
|
|
|
|
|
"""
|
2018-09-18 21:57:39 +02:00
|
|
|
if local_debug:
|
2018-10-15 20:25:51 +02:00
|
|
|
debug.info(0,"CLASS1:\n",class1)
|
|
|
|
|
debug.info(0,"CLASS2:\n",class2)
|
2018-09-08 19:05:48 +02:00
|
|
|
# Compare each pin in each class,
|
|
|
|
|
# and if any overlap, return the combined the class
|
|
|
|
|
for p1 in class1:
|
|
|
|
|
for p2 in class2:
|
|
|
|
|
if p1.overlaps(p2):
|
2018-09-18 21:57:39 +02:00
|
|
|
combined_class = class1 | class2
|
|
|
|
|
if local_debug:
|
2018-10-15 20:25:51 +02:00
|
|
|
debug.info(0,"COMBINE:",pformat(combined_class))
|
2018-09-08 19:05:48 +02:00
|
|
|
return combined_class
|
2018-09-18 21:57:39 +02:00
|
|
|
|
|
|
|
|
if local_debug:
|
2018-10-15 20:25:51 +02:00
|
|
|
debug.info(0,"NO COMBINE")
|
2018-09-08 19:05:48 +02:00
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def combine_classes(equiv_classes):
|
|
|
|
|
""" Recursive function to combine classes. """
|
2018-09-18 21:57:39 +02:00
|
|
|
if local_debug:
|
2018-10-15 20:25:51 +02:00
|
|
|
debug.info(0,"\nRECURSE:\n",pformat(equiv_classes))
|
2018-09-08 19:05:48 +02:00
|
|
|
if len(equiv_classes)==1:
|
|
|
|
|
return(equiv_classes)
|
|
|
|
|
|
|
|
|
|
for class1 in equiv_classes:
|
|
|
|
|
for class2 in equiv_classes:
|
|
|
|
|
if class1 == class2:
|
|
|
|
|
continue
|
|
|
|
|
class3 = compare_classes(class1, class2)
|
|
|
|
|
if class3:
|
|
|
|
|
new_classes = equiv_classes
|
|
|
|
|
new_classes.remove(class1)
|
|
|
|
|
new_classes.remove(class2)
|
|
|
|
|
new_classes.append(class3)
|
|
|
|
|
return(combine_classes(new_classes))
|
|
|
|
|
else:
|
|
|
|
|
return(equiv_classes)
|
2018-08-23 00:56:19 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
reduced_classes = combine_classes(equiv_classes)
|
2018-09-18 21:57:39 +02:00
|
|
|
if local_debug:
|
2018-10-15 20:25:51 +02:00
|
|
|
debug.info(0,"FINAL ",reduced_classes)
|
2018-09-18 21:57:39 +02:00
|
|
|
self.pin_groups[pin_name]=reduced_classes
|
2018-09-08 19:05:48 +02:00
|
|
|
|
|
|
|
|
def convert_pins(self, pin_name):
|
2018-08-23 00:56:19 +02:00
|
|
|
"""
|
2018-10-11 00:15:58 +02:00
|
|
|
Convert the pin groups into pin tracks and blockage tracks.
|
2018-08-23 00:56:19 +02:00
|
|
|
"""
|
2018-09-13 18:10:29 +02:00
|
|
|
try:
|
2018-10-11 00:15:58 +02:00
|
|
|
self.pin_grids[pin_name]
|
2018-09-13 18:10:29 +02:00
|
|
|
except:
|
2018-10-11 00:15:58 +02:00
|
|
|
self.pin_grids[pin_name] = []
|
2018-09-21 01:00:13 +02:00
|
|
|
|
2018-08-23 00:56:19 +02:00
|
|
|
found_pin = False
|
2018-09-08 19:05:48 +02:00
|
|
|
for pg in self.pin_groups[pin_name]:
|
2018-09-21 01:00:13 +02:00
|
|
|
#print("PG ",pg)
|
2018-09-13 18:10:29 +02:00
|
|
|
# Keep the same groups for each pin
|
|
|
|
|
pin_set = set()
|
2018-09-18 21:57:39 +02:00
|
|
|
blockage_set = set()
|
2018-09-08 19:05:48 +02:00
|
|
|
for pin in pg:
|
2018-09-18 21:57:39 +02:00
|
|
|
debug.info(2," Converting {0}".format(pin))
|
2018-09-21 01:00:13 +02:00
|
|
|
# Determine which tracks the pin overlaps
|
|
|
|
|
pin_in_tracks=self.convert_pin_to_tracks(pin_name, pin)
|
2018-09-13 18:10:29 +02:00
|
|
|
pin_set.update(pin_in_tracks)
|
2018-09-21 01:00:13 +02:00
|
|
|
# Blockages will be a super-set of pins since it uses the inflated pin shape.
|
|
|
|
|
blockage_in_tracks = self.convert_blockage(pin)
|
2018-09-18 21:57:39 +02:00
|
|
|
blockage_set.update(blockage_in_tracks)
|
2018-09-13 18:10:29 +02:00
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
# If we have a blockage, we must remove the grids
|
|
|
|
|
# Remember, this excludes the pin blockages already
|
|
|
|
|
shared_set = pin_set & self.blocked_grids
|
|
|
|
|
if shared_set:
|
|
|
|
|
debug.info(2,"Removing pins {}".format(shared_set))
|
|
|
|
|
shared_set = blockage_set & self.blocked_grids
|
|
|
|
|
if shared_set:
|
|
|
|
|
debug.info(2,"Removing blocks {}".format(shared_set))
|
|
|
|
|
pin_set.difference_update(self.blocked_grids)
|
|
|
|
|
blockage_set.difference_update(self.blocked_grids)
|
2018-09-18 21:57:39 +02:00
|
|
|
debug.info(2," pins {}".format(pin_set))
|
|
|
|
|
debug.info(2," blocks {}".format(blockage_set))
|
2018-09-13 18:10:29 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
# At least one of the groups must have some valid tracks
|
2018-10-11 00:15:58 +02:00
|
|
|
if (len(pin_set)==0 and len(blockage_set)==0):
|
2018-09-18 21:57:39 +02:00
|
|
|
self.write_debug_gds()
|
2018-10-11 00:15:58 +02:00
|
|
|
debug.error("Unable to find unblocked pin on grid.")
|
2018-10-05 17:39:28 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
# We need to route each of the components, so don't combine the groups
|
2018-10-11 00:15:58 +02:00
|
|
|
self.pin_grids[pin_name].append(pin_set | blockage_set)
|
2018-10-06 00:57:34 +02:00
|
|
|
|
|
|
|
|
# Add all of the partial blocked grids to the set for the design
|
|
|
|
|
# if they are not blocked by other metal
|
2018-10-11 00:15:58 +02:00
|
|
|
#partial_set = blockage_set - pin_set
|
|
|
|
|
#self.pin_blockages[pin_name].append(partial_set)
|
2018-09-18 21:57:39 +02:00
|
|
|
|
2018-10-06 00:57:34 +02:00
|
|
|
# We should not have added the pins to the blockages,
|
|
|
|
|
# but remove them just in case
|
|
|
|
|
# Partial set may still be in the blockages if there were
|
|
|
|
|
# other shapes disconnected from the pins that were also overlapping
|
2018-10-11 00:15:58 +02:00
|
|
|
#self.blocked_grids.difference_update(pin_set)
|
2018-09-13 18:10:29 +02:00
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
|
2018-10-06 00:57:34 +02:00
|
|
|
def enclose_pin_grids(self, grids, seed):
|
2018-09-21 01:00:13 +02:00
|
|
|
"""
|
2018-10-06 00:57:34 +02:00
|
|
|
This encloses a single pin component with a rectangle
|
|
|
|
|
starting with the seed and expanding right until blocked
|
|
|
|
|
and then up until blocked.
|
2018-09-21 01:00:13 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# We may have started with an empty set
|
|
|
|
|
if not grids:
|
2018-10-11 00:15:58 +02:00
|
|
|
return None
|
2018-09-21 01:00:13 +02:00
|
|
|
|
2018-10-06 00:57:34 +02:00
|
|
|
# Start with the seed
|
|
|
|
|
ll = seed
|
|
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
# Start with the ll and make the widest row
|
|
|
|
|
row = [ll]
|
|
|
|
|
# Move right while we can
|
|
|
|
|
while True:
|
|
|
|
|
right = row[-1] + vector3d(1,0,0)
|
2018-10-06 00:57:34 +02:00
|
|
|
# Can't move if not in the pin shape
|
2018-10-11 00:15:58 +02:00
|
|
|
if right in grids and right not in self.blocked_grids:
|
2018-09-21 01:00:13 +02:00
|
|
|
row.append(right)
|
|
|
|
|
else:
|
|
|
|
|
break
|
|
|
|
|
# Move up while we can
|
|
|
|
|
while True:
|
|
|
|
|
next_row = [x+vector3d(0,1,0) for x in row]
|
|
|
|
|
for cell in next_row:
|
2018-10-06 00:57:34 +02:00
|
|
|
# Can't move if any cell is not in the pin shape
|
2018-10-11 00:15:58 +02:00
|
|
|
if cell not in grids or cell in self.blocked_grids:
|
2018-09-21 01:00:13 +02:00
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
row = next_row
|
|
|
|
|
# Skips the second break
|
|
|
|
|
continue
|
|
|
|
|
# Breaks from the nested break
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# Add a shape from ll to ur
|
|
|
|
|
ur = row[-1]
|
2018-10-19 23:21:03 +02:00
|
|
|
return self.compute_pin_enclosure(ll, ur, ll.z)
|
2018-10-11 00:15:58 +02:00
|
|
|
|
2018-10-19 23:21:03 +02:00
|
|
|
def remove_redundant_shapes(self, pin_list):
|
2018-10-11 00:15:58 +02:00
|
|
|
"""
|
2018-10-19 23:21:03 +02:00
|
|
|
Remove any pin layout that is contained within another.
|
2018-10-11 00:15:58 +02:00
|
|
|
"""
|
2018-10-19 23:21:03 +02:00
|
|
|
|
|
|
|
|
print("INITIAL:",pin_list)
|
|
|
|
|
|
|
|
|
|
# Make a copy of the list to start
|
|
|
|
|
new_pin_list = pin_list.copy()
|
|
|
|
|
|
|
|
|
|
# This is n^2, but the number is small
|
2018-10-11 00:15:58 +02:00
|
|
|
for pin1 in pin_list:
|
|
|
|
|
for pin2 in pin_list:
|
2018-10-19 23:21:03 +02:00
|
|
|
# Can't contain yourself
|
2018-10-11 00:15:58 +02:00
|
|
|
if pin1 == pin2:
|
|
|
|
|
continue
|
|
|
|
|
if pin2.contains(pin1):
|
2018-10-19 23:21:03 +02:00
|
|
|
# It may have already been removed by being enclosed in another pin
|
|
|
|
|
if pin1 in new_pin_list:
|
2018-10-11 00:15:58 +02:00
|
|
|
new_pin_list.remove(pin1)
|
2018-10-19 23:21:03 +02:00
|
|
|
|
|
|
|
|
print("FINAL :",new_pin_list)
|
2018-10-11 00:15:58 +02:00
|
|
|
return new_pin_list
|
|
|
|
|
|
2018-10-19 23:21:03 +02:00
|
|
|
def compute_enclosures(self, tracks):
|
|
|
|
|
"""
|
|
|
|
|
Find the minimum rectangle enclosures of the given tracks.
|
|
|
|
|
"""
|
|
|
|
|
pin_list = []
|
|
|
|
|
for seed in tracks:
|
|
|
|
|
pin_list.append(self.enclose_pin_grids(tracks, seed))
|
|
|
|
|
|
|
|
|
|
return self.remove_redundant_shapes(pin_list)
|
|
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
def overlap_any_shape(self, pin_list, shape_list):
|
|
|
|
|
"""
|
|
|
|
|
Does the given pin overlap any of the shapes in the pin list.
|
|
|
|
|
"""
|
|
|
|
|
for pin in pin_list:
|
|
|
|
|
for other in shape_list:
|
|
|
|
|
if pin.overlaps(other):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def max_pin_layout(self, pin_list):
|
|
|
|
|
"""
|
|
|
|
|
Return the max area pin_layout
|
|
|
|
|
"""
|
|
|
|
|
biggest = pin_list[0]
|
|
|
|
|
for pin in pin_list:
|
|
|
|
|
if pin.area() > biggest.area():
|
|
|
|
|
biggest = pin
|
|
|
|
|
|
|
|
|
|
return pin
|
|
|
|
|
|
|
|
|
|
def find_smallest_connector(self, pin_list, enclosure_list):
|
|
|
|
|
"""
|
|
|
|
|
Compute all of the connectors between non-overlapping pins and enclosures.
|
|
|
|
|
Return the smallest.
|
|
|
|
|
"""
|
|
|
|
|
smallest = None
|
|
|
|
|
for pin in pin_list:
|
|
|
|
|
for enclosure in enclosure_list:
|
|
|
|
|
new_enclosure = self.compute_enclosure(pin, enclosure)
|
|
|
|
|
if smallest == None or new_enclosure.area()<smallest.area():
|
|
|
|
|
smallest = new_enclosure
|
|
|
|
|
|
|
|
|
|
return smallest
|
2018-09-21 01:00:13 +02:00
|
|
|
|
|
|
|
|
def enclose_pins(self):
|
|
|
|
|
"""
|
|
|
|
|
This will find the biggest rectangle enclosing some grid squares and
|
|
|
|
|
put a rectangle over it. It does not enclose grid squares that are blocked
|
|
|
|
|
by other shapes.
|
|
|
|
|
"""
|
2018-10-19 23:21:03 +02:00
|
|
|
# These are used for debugging
|
|
|
|
|
self.connector_enclosure = []
|
|
|
|
|
self.enclosures = []
|
|
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
for pin_name in self.pin_grids.keys():
|
|
|
|
|
debug.info(1,"Enclosing pins for {}".format(pin_name))
|
|
|
|
|
debug.check(len(self.pin_groups[pin_name])==len(self.pin_grids[pin_name]),"Unequal pin_group and pin_grid")
|
2018-10-19 23:21:03 +02:00
|
|
|
for pin_group,pin_grid_set in zip(self.pin_groups[pin_name],self.pin_grids[pin_name]):
|
2018-10-11 00:15:58 +02:00
|
|
|
|
|
|
|
|
# Compute the enclosure pin_layout list of the set of tracks
|
2018-10-19 23:21:03 +02:00
|
|
|
enclosure_list = self.compute_enclosures(pin_grid_set)
|
2018-10-11 00:15:58 +02:00
|
|
|
for pin in enclosure_list:
|
|
|
|
|
debug.info(2,"Adding enclosure {0} {1}".format(pin_name, pin))
|
|
|
|
|
self.cell.add_rect(layer=pin.layer,
|
|
|
|
|
offset=pin.ll(),
|
|
|
|
|
width=pin.width(),
|
|
|
|
|
height=pin.height())
|
2018-10-19 23:21:03 +02:00
|
|
|
self.enclosures.append(pin)
|
2018-10-11 00:15:58 +02:00
|
|
|
|
|
|
|
|
# Check if a pin shape overlaps any enclosure.
|
|
|
|
|
# If so, we are done.
|
|
|
|
|
# FIXME: Check if by more than a DRC width
|
|
|
|
|
if self.overlap_any_shape(pin_group, enclosure_list):
|
|
|
|
|
debug.info(2,"Pin overlaps enclosure {0}".format(pin_name))
|
|
|
|
|
else:
|
|
|
|
|
new_enclosure = self.find_smallest_connector(pin_group, enclosure_list)
|
|
|
|
|
debug.info(2,"Adding connector enclosure {0} {1}".format(pin_name, new_enclosure))
|
|
|
|
|
self.cell.add_rect(layer=new_enclosure.layer,
|
|
|
|
|
offset=new_enclosure.ll(),
|
|
|
|
|
width=new_enclosure.width(),
|
|
|
|
|
height=new_enclosure.height())
|
2018-10-19 23:21:03 +02:00
|
|
|
self.connector_enclosure.append(new_enclosure)
|
2018-10-11 00:15:58 +02:00
|
|
|
|
|
|
|
|
|
2018-10-06 00:57:34 +02:00
|
|
|
|
|
|
|
|
#self.write_debug_gds("pin_debug.gds", True)
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
def compute_enclosure(self, pin, enclosure):
|
|
|
|
|
"""
|
|
|
|
|
Compute an enclosure to connect the pin to the enclosure shape.
|
|
|
|
|
This assumes the shape will be the dimension of the pin.
|
|
|
|
|
"""
|
|
|
|
|
if pin.xoverlaps(enclosure):
|
|
|
|
|
# Is it vertical overlap, extend pin shape to enclosure
|
|
|
|
|
plc = pin.lc()
|
|
|
|
|
prc = pin.rc()
|
|
|
|
|
elc = enclosure.lc()
|
|
|
|
|
erc = enclosure.rc()
|
|
|
|
|
ymin = min(plc.y,elc.y)
|
|
|
|
|
ymax = max(plc.y,elc.y)
|
|
|
|
|
ll = vector(plc.x, ymin)
|
|
|
|
|
ur = vector(prc.x, ymax)
|
|
|
|
|
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
|
|
|
|
elif pin.yoverlaps(enclosure):
|
|
|
|
|
# Is it horizontal overlap, extend pin shape to enclosure
|
|
|
|
|
pbc = pin.bc()
|
|
|
|
|
puc = pin.uc()
|
|
|
|
|
ebc = enclosure.bc()
|
|
|
|
|
euc = enclosure.uc()
|
|
|
|
|
xmin = min(pbc.x,ebc.x)
|
|
|
|
|
xmax = max(pbc.x,ebc.x)
|
|
|
|
|
ll = vector(xmin, pbc.y)
|
2018-10-19 23:21:03 +02:00
|
|
|
ur = vector(xmax, puc.y)
|
2018-10-11 00:15:58 +02:00
|
|
|
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
|
|
|
|
else:
|
|
|
|
|
# Neither, so we must do a corner-to corner
|
|
|
|
|
pc = pin.center()
|
|
|
|
|
ec = enclosure.center()
|
|
|
|
|
xmin = min(pc.x, ec.x)
|
|
|
|
|
xmax = max(pc.x, ec.x)
|
|
|
|
|
ymin = min(pc.y, ec.y)
|
|
|
|
|
ymax = max(pc.y, ec.y)
|
|
|
|
|
ll = vector(xmin, ymin)
|
|
|
|
|
ur = vector(xmax, ymax)
|
|
|
|
|
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
|
|
|
|
|
|
|
|
|
return p
|
|
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
def add_source(self, pin_name):
|
2018-09-08 19:05:48 +02:00
|
|
|
"""
|
2018-09-18 21:57:39 +02:00
|
|
|
This will mark the grids for all pin components as a source.
|
2018-09-08 19:05:48 +02:00
|
|
|
Marking as source or target also clears blockage status.
|
|
|
|
|
"""
|
2018-10-11 00:15:58 +02:00
|
|
|
for i in range(self.num_pin_grids(pin_name)):
|
2018-09-18 21:57:39 +02:00
|
|
|
self.add_pin_component_source(pin_name, i)
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
def add_target(self, pin_name):
|
|
|
|
|
"""
|
|
|
|
|
This will mark the grids for all pin components as a target.
|
|
|
|
|
Marking as source or target also clears blockage status.
|
|
|
|
|
"""
|
2018-10-11 00:15:58 +02:00
|
|
|
for i in range(self.num_pin_grids(pin_name)):
|
2018-09-18 21:57:39 +02:00
|
|
|
self.add_pin_component_target(pin_name, i)
|
|
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def num_pin_components(self, pin_name):
|
|
|
|
|
"""
|
|
|
|
|
This returns how many disconnected pin components there are.
|
|
|
|
|
"""
|
2018-10-11 00:15:58 +02:00
|
|
|
return len(self.pin_grids[pin_name])
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
def add_pin_component_source(self, pin_name, index):
|
2018-09-08 19:05:48 +02:00
|
|
|
"""
|
2018-09-18 21:57:39 +02:00
|
|
|
This will mark only the pin tracks from the indexed pin component as a source.
|
2018-09-08 19:05:48 +02:00
|
|
|
It also unsets it as a blockage.
|
|
|
|
|
"""
|
|
|
|
|
debug.check(index<self.num_pin_components(pin_name),"Pin component index too large.")
|
|
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
pin_in_tracks = self.pin_grids[pin_name][index]
|
2018-09-18 21:57:39 +02:00
|
|
|
debug.info(1,"Set source: " + str(pin_name) + " " + str(pin_in_tracks))
|
|
|
|
|
self.rg.add_source(pin_in_tracks)
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2018-10-05 17:39:28 +02:00
|
|
|
def add_path_target(self, paths):
|
|
|
|
|
"""
|
|
|
|
|
Set all of the paths as a target too.
|
|
|
|
|
"""
|
|
|
|
|
for p in paths:
|
|
|
|
|
self.rg.set_target(p)
|
|
|
|
|
self.rg.set_blocked(p,False)
|
2018-09-13 18:10:29 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
def add_pin_component_target(self, pin_name, index):
|
|
|
|
|
"""
|
|
|
|
|
This will mark only the pin tracks from the indexed pin component as a target.
|
|
|
|
|
It also unsets it as a blockage.
|
|
|
|
|
"""
|
2018-10-11 00:15:58 +02:00
|
|
|
debug.check(index<self.num_pin_grids(pin_name),"Pin component index too large.")
|
2018-09-18 21:57:39 +02:00
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
pin_in_tracks = self.pin_grids[pin_name][index]
|
2018-09-18 21:57:39 +02:00
|
|
|
debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks))
|
|
|
|
|
self.rg.add_target(pin_in_tracks)
|
|
|
|
|
|
|
|
|
|
|
2018-09-13 18:10:29 +02:00
|
|
|
def set_component_blockages(self, pin_name, value=True):
|
2018-09-08 19:05:48 +02:00
|
|
|
"""
|
|
|
|
|
Block all of the pin components.
|
|
|
|
|
"""
|
2018-10-04 23:04:29 +02:00
|
|
|
debug.info(2,"Setting blockages {0} {1}".format(pin_name,value))
|
2018-10-11 00:15:58 +02:00
|
|
|
for component in self.pin_grids[pin_name]:
|
2018-09-13 18:10:29 +02:00
|
|
|
self.set_blockages(component, value)
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2018-08-23 00:56:19 +02:00
|
|
|
|
2018-09-06 20:54:14 +02:00
|
|
|
def prepare_path(self,path):
|
2018-09-06 01:01:11 +02:00
|
|
|
"""
|
2018-09-13 18:10:29 +02:00
|
|
|
Prepare a path or wave for routing ebedding.
|
|
|
|
|
This tracks the path, simplifies the path and marks it as a path for debug output.
|
2018-09-06 01:01:11 +02:00
|
|
|
"""
|
2018-09-13 18:10:29 +02:00
|
|
|
debug.info(4,"Set path: " + str(path))
|
2018-09-06 01:01:11 +02:00
|
|
|
|
|
|
|
|
# Keep track of path for future blockages
|
2018-09-18 21:57:39 +02:00
|
|
|
#path.set_blocked()
|
2018-09-06 01:01:11 +02:00
|
|
|
|
|
|
|
|
# This is marked for debug
|
2018-09-07 23:46:58 +02:00
|
|
|
path.set_path()
|
2018-09-06 01:01:11 +02:00
|
|
|
|
|
|
|
|
# For debugging... if the path failed to route.
|
|
|
|
|
if False or path==None:
|
|
|
|
|
self.write_debug_gds()
|
|
|
|
|
|
|
|
|
|
# First, simplify the path for
|
|
|
|
|
#debug.info(1,str(self.path))
|
|
|
|
|
contracted_path = self.contract_path(path)
|
2018-09-13 18:10:29 +02:00
|
|
|
debug.info(3,"Contracted path: " + str(contracted_path))
|
2018-09-06 20:54:14 +02:00
|
|
|
|
|
|
|
|
return contracted_path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_route(self,path):
|
|
|
|
|
"""
|
|
|
|
|
Add the current wire route to the given design instance.
|
|
|
|
|
"""
|
|
|
|
|
path=self.prepare_path(path)
|
|
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
debug.info(1,"Adding route: {}".format(str(path)))
|
2018-09-18 23:55:36 +02:00
|
|
|
# If it is only a square, add an enclosure to the track
|
|
|
|
|
if len(path)==1:
|
2018-10-04 23:04:29 +02:00
|
|
|
self.add_single_enclosure(path[0][0])
|
2018-09-18 23:55:36 +02:00
|
|
|
else:
|
2018-10-04 23:04:29 +02:00
|
|
|
# convert the path back to absolute units from tracks
|
|
|
|
|
# This assumes 1-track wide again
|
|
|
|
|
abs_path = [self.convert_point_to_units(x[0]) for x in path]
|
2018-09-18 23:55:36 +02:00
|
|
|
# Otherwise, add the route which includes enclosures
|
|
|
|
|
self.cell.add_route(layers=self.layers,
|
|
|
|
|
coordinates=abs_path,
|
|
|
|
|
layer_widths=self.layer_widths)
|
|
|
|
|
|
2018-10-04 23:44:25 +02:00
|
|
|
def add_single_enclosure(self, track):
|
2018-09-18 21:57:39 +02:00
|
|
|
"""
|
|
|
|
|
Add a metal enclosure that is the size of the routing grid minus a spacing on each side.
|
|
|
|
|
"""
|
2018-10-04 23:44:25 +02:00
|
|
|
(ll,ur) = self.convert_track_to_pin(track)
|
|
|
|
|
self.cell.add_rect(layer=self.get_layer(track.z),
|
|
|
|
|
offset=ll,
|
|
|
|
|
width=ur.x-ll.x,
|
|
|
|
|
height=ur.y-ll.y)
|
2018-09-18 21:57:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-09-07 23:46:58 +02:00
|
|
|
def add_via(self,loc,size=1):
|
|
|
|
|
"""
|
|
|
|
|
Add a via centered at the current location
|
|
|
|
|
"""
|
|
|
|
|
loc = self.convert_point_to_units(vector3d(loc[0],loc[1],0))
|
|
|
|
|
self.cell.add_via_center(layers=self.layers,
|
|
|
|
|
offset=vector(loc.x,loc.y),
|
|
|
|
|
size=(size,size))
|
|
|
|
|
|
|
|
|
|
|
2018-09-06 01:01:11 +02:00
|
|
|
|
2018-09-07 23:46:58 +02:00
|
|
|
def add_wavepath(self, name, path):
|
2018-09-06 20:54:14 +02:00
|
|
|
"""
|
|
|
|
|
Add the current wave to the given design instance.
|
2018-10-12 23:37:51 +02:00
|
|
|
This is a single rectangle that is multiple tracks wide.
|
|
|
|
|
It must pay attention to wide metal spacing rules.
|
2018-09-06 20:54:14 +02:00
|
|
|
"""
|
|
|
|
|
path=self.prepare_path(path)
|
2018-09-07 23:46:58 +02:00
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
ll = path[0][0]
|
|
|
|
|
ur = path[-1][-1]
|
|
|
|
|
z = ll.z
|
2018-10-19 23:21:03 +02:00
|
|
|
pin = self.compute_wide_enclosure(ll, ur, z, name)
|
2018-10-15 22:23:31 +02:00
|
|
|
#print(ll, ur, ll.z, "->",pin)
|
2018-10-11 00:15:58 +02:00
|
|
|
self.cell.add_layout_pin(text=name,
|
|
|
|
|
layer=pin.layer,
|
|
|
|
|
offset=pin.ll(),
|
|
|
|
|
width=pin.width(),
|
|
|
|
|
height=pin.height())
|
2018-09-21 01:00:13 +02:00
|
|
|
|
|
|
|
|
return pin
|
|
|
|
|
|
2018-10-19 23:21:03 +02:00
|
|
|
def compute_pin_enclosure(self, ll, ur, zindex, name=""):
|
|
|
|
|
"""
|
|
|
|
|
Enclose the tracks from ll to ur in a single rectangle that meets
|
|
|
|
|
the track DRC rules. If name is supplied, it is added as a pin and
|
|
|
|
|
not just a rectangle.
|
|
|
|
|
"""
|
|
|
|
|
# Get the layer information
|
|
|
|
|
(width, space) = self.get_layer_width_space(zindex)
|
|
|
|
|
layer = self.get_layer(zindex)
|
|
|
|
|
|
|
|
|
|
# This finds the pin shape enclosed by the track with DRC spacing on the sides
|
|
|
|
|
(abs_ll,unused) = self.convert_track_to_pin(ll)
|
|
|
|
|
(unused,abs_ur) = self.convert_track_to_pin(ur)
|
|
|
|
|
#print("enclose ll={0} ur={1}".format(ll,ur))
|
|
|
|
|
#print("enclose ll={0} ur={1}".format(abs_ll,abs_ur))
|
|
|
|
|
|
|
|
|
|
pin = pin_layout(name, [abs_ll, abs_ur], layer)
|
|
|
|
|
|
|
|
|
|
return pin
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def compute_wide_enclosure(self, ll, ur, zindex, name=""):
|
2018-09-21 01:00:13 +02:00
|
|
|
"""
|
|
|
|
|
Enclose the tracks from ll to ur in a single rectangle that meets the track DRC rules.
|
|
|
|
|
If name is supplied, it is added as a pin and not just a rectangle.
|
|
|
|
|
|
|
|
|
|
"""
|
2018-10-12 23:37:51 +02:00
|
|
|
|
|
|
|
|
# Find the pin enclosure of the whole track shape (ignoring DRCs)
|
|
|
|
|
(abs_ll,unused) = self.convert_track_to_shape(ll)
|
|
|
|
|
(unused,abs_ur) = self.convert_track_to_shape(ur)
|
2018-10-15 20:25:51 +02:00
|
|
|
|
|
|
|
|
# Get the layer information
|
|
|
|
|
x_distance = abs(abs_ll.x-abs_ur.x)
|
|
|
|
|
y_distance = abs(abs_ll.y-abs_ur.y)
|
|
|
|
|
shape_width = min(x_distance, y_distance)
|
|
|
|
|
shape_length = max(x_distance, y_distance)
|
2018-10-12 23:37:51 +02:00
|
|
|
|
|
|
|
|
# Get the DRC rule for the grid dimensions
|
2018-10-15 20:25:51 +02:00
|
|
|
(width, space) = self.get_layer_width_space(zindex, shape_width, shape_length)
|
2018-10-12 23:37:51 +02:00
|
|
|
layer = self.get_layer(zindex)
|
2018-10-19 23:21:03 +02:00
|
|
|
|
|
|
|
|
if zindex==0:
|
|
|
|
|
spacing = vector(0.5*self.track_width, 0.5*space)
|
|
|
|
|
else:
|
|
|
|
|
spacing = vector(0.5*space, 0.5*self.track_width)
|
2018-10-15 20:25:51 +02:00
|
|
|
# Compute the shape offsets with correct spacing
|
2018-10-19 23:21:03 +02:00
|
|
|
new_ll = abs_ll + spacing
|
|
|
|
|
new_ur = abs_ur - spacing
|
2018-10-15 20:25:51 +02:00
|
|
|
pin = pin_layout(name, [new_ll, new_ur], layer)
|
2018-09-21 01:00:13 +02:00
|
|
|
|
2018-09-06 23:30:59 +02:00
|
|
|
return pin
|
2018-10-19 23:21:03 +02:00
|
|
|
|
2018-09-06 20:54:14 +02:00
|
|
|
|
2018-09-06 01:01:11 +02:00
|
|
|
def get_inertia(self,p0,p1):
|
|
|
|
|
"""
|
|
|
|
|
Sets the direction based on the previous direction we came from.
|
|
|
|
|
"""
|
|
|
|
|
# direction (index) of movement
|
|
|
|
|
if p0.x!=p1.x:
|
|
|
|
|
return 0
|
|
|
|
|
elif p0.y!=p1.y:
|
|
|
|
|
return 1
|
|
|
|
|
else:
|
|
|
|
|
# z direction
|
|
|
|
|
return 2
|
|
|
|
|
|
|
|
|
|
def contract_path(self,path):
|
|
|
|
|
"""
|
2018-09-06 20:54:14 +02:00
|
|
|
Remove intermediate points in a rectilinear path or a wave.
|
2018-09-06 01:01:11 +02:00
|
|
|
"""
|
2018-09-06 20:54:14 +02:00
|
|
|
# Waves are always linear, so just return the first and last.
|
|
|
|
|
if self.is_wave(path):
|
|
|
|
|
return [path[0],path[-1]]
|
|
|
|
|
|
|
|
|
|
# Make a list only of points that change inertia of the path
|
2018-09-06 01:01:11 +02:00
|
|
|
newpath = [path[0]]
|
|
|
|
|
for i in range(1,len(path)-1):
|
2018-09-07 23:46:58 +02:00
|
|
|
prev_inertia=self.get_inertia(path[i-1][0],path[i][0])
|
|
|
|
|
next_inertia=self.get_inertia(path[i][0],path[i+1][0])
|
2018-09-06 01:01:11 +02:00
|
|
|
# if we switch directions, add the point, otherwise don't
|
|
|
|
|
if prev_inertia!=next_inertia:
|
|
|
|
|
newpath.append(path[i])
|
|
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
# always add the last path unless it was a single point
|
|
|
|
|
if len(path)>1:
|
|
|
|
|
newpath.append(path[-1])
|
2018-09-06 01:01:11 +02:00
|
|
|
return newpath
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add_path_blockages(self):
|
|
|
|
|
"""
|
|
|
|
|
Go through all of the past paths and add them as blockages.
|
|
|
|
|
This is so we don't have to write/reload the GDS.
|
|
|
|
|
"""
|
|
|
|
|
for path in self.paths:
|
|
|
|
|
self.rg.block_path(path)
|
|
|
|
|
|
2018-09-09 03:55:36 +02:00
|
|
|
def run_router(self, detour_scale):
|
|
|
|
|
"""
|
|
|
|
|
This assumes the blockages, source, and target are all set up.
|
|
|
|
|
"""
|
|
|
|
|
# returns the path in tracks
|
|
|
|
|
(path,cost) = self.rg.route(detour_scale)
|
|
|
|
|
if path:
|
2018-10-11 00:15:58 +02:00
|
|
|
debug.info(2,"Found path: cost={0} ".format(cost))
|
|
|
|
|
debug.info(3,str(path))
|
2018-09-18 21:57:39 +02:00
|
|
|
self.paths.append(path)
|
2018-09-09 03:55:36 +02:00
|
|
|
self.add_route(path)
|
|
|
|
|
else:
|
2018-10-11 00:15:58 +02:00
|
|
|
self.write_debug_gds("failed_route.gds")
|
2018-09-09 03:55:36 +02:00
|
|
|
# clean up so we can try a reroute
|
2018-10-11 00:15:58 +02:00
|
|
|
self.rg.reinit()
|
2018-09-09 03:55:36 +02:00
|
|
|
return False
|
|
|
|
|
return True
|
2018-09-21 01:00:13 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def annotate_pin_and_tracks(self, pin, tracks):
|
|
|
|
|
""""
|
|
|
|
|
Annotate some shapes for debug purposes
|
|
|
|
|
"""
|
|
|
|
|
debug.info(0,"Annotating\n pin {0}\n tracks {1}".format(pin,tracks))
|
|
|
|
|
for coord in tracks:
|
|
|
|
|
(ll,ur) = self.convert_track_to_shape(coord)
|
|
|
|
|
self.cell.add_rect(layer="text",
|
|
|
|
|
offset=ll,
|
|
|
|
|
width=ur[0]-ll[0],
|
|
|
|
|
height=ur[1]-ll[1])
|
|
|
|
|
(ll,ur) = self.convert_track_to_pin(coord)
|
|
|
|
|
self.cell.add_rect(layer="boundary",
|
|
|
|
|
offset=ll,
|
|
|
|
|
width=ur[0]-ll[0],
|
|
|
|
|
height=ur[1]-ll[1])
|
|
|
|
|
(ll,ur) = pin.rect
|
|
|
|
|
self.cell.add_rect(layer="text",
|
|
|
|
|
offset=ll,
|
|
|
|
|
width=ur[0]-ll[0],
|
|
|
|
|
height=ur[1]-ll[1])
|
|
|
|
|
|
|
|
|
|
def write_debug_gds(self, gds_name="debug_route.gds", stop_program=True):
|
|
|
|
|
"""
|
|
|
|
|
Write out a GDS file with the routing grid and search information annotated on it.
|
|
|
|
|
"""
|
|
|
|
|
self.add_router_info()
|
|
|
|
|
self.cell.gds_write(gds_name)
|
|
|
|
|
|
|
|
|
|
if stop_program:
|
|
|
|
|
import sys
|
|
|
|
|
sys.exit(1)
|
2018-10-19 23:21:03 +02:00
|
|
|
|
|
|
|
|
def annotate_grid(self, g):
|
|
|
|
|
"""
|
|
|
|
|
Display grid information in the GDS file for a single grid cell.
|
|
|
|
|
"""
|
|
|
|
|
shape = self.convert_track_to_shape(g)
|
|
|
|
|
partial_track=vector(0,self.track_width/6.0)
|
|
|
|
|
self.cell.add_rect(layer="text",
|
|
|
|
|
offset=shape[0],
|
|
|
|
|
width=shape[1].x-shape[0].x,
|
|
|
|
|
height=shape[1].y-shape[0].y)
|
|
|
|
|
t=self.rg.map[g].get_type()
|
|
|
|
|
|
|
|
|
|
# midpoint offset
|
|
|
|
|
off=vector((shape[1].x+shape[0].x)/2,
|
|
|
|
|
(shape[1].y+shape[0].y)/2)
|
|
|
|
|
if g[2]==1:
|
|
|
|
|
# Upper layer is upper right label
|
|
|
|
|
type_off=off+partial_track
|
|
|
|
|
else:
|
|
|
|
|
# Lower layer is lower left label
|
|
|
|
|
type_off=off-partial_track
|
|
|
|
|
if t!=None:
|
|
|
|
|
self.cell.add_label(text=str(t),
|
|
|
|
|
layer="text",
|
|
|
|
|
offset=type_off)
|
|
|
|
|
self.cell.add_label(text="{0},{1}".format(g[0],g[1]),
|
|
|
|
|
layer="text",
|
|
|
|
|
offset=shape[0],
|
|
|
|
|
zoom=0.05)
|
|
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
def add_router_info(self):
|
|
|
|
|
"""
|
|
|
|
|
Write the routing grid and router cost, blockage, pins on
|
|
|
|
|
the boundary layer for debugging purposes. This can only be
|
|
|
|
|
called once or the labels will overlap.
|
|
|
|
|
"""
|
|
|
|
|
debug.info(0,"Adding router info")
|
|
|
|
|
|
2018-10-19 23:21:03 +02:00
|
|
|
show_blockages = False
|
|
|
|
|
show_blockage_grids = False
|
|
|
|
|
show_enclosures = False
|
|
|
|
|
show_connectors = False
|
|
|
|
|
show_all_grids = True
|
|
|
|
|
|
|
|
|
|
if show_all_grids:
|
|
|
|
|
self.rg.add_all_grids()
|
|
|
|
|
for g in self.rg.map.keys():
|
|
|
|
|
self.annotate_grid(g)
|
|
|
|
|
|
|
|
|
|
if show_blockages:
|
2018-09-21 01:00:13 +02:00
|
|
|
# Display the inflated blockage
|
|
|
|
|
for blockage in self.blockages:
|
|
|
|
|
debug.info(1,"Adding {}".format(blockage))
|
|
|
|
|
(ll,ur) = blockage.inflate()
|
|
|
|
|
self.cell.add_rect(layer="text",
|
|
|
|
|
offset=ll,
|
|
|
|
|
width=ur.x-ll.x,
|
|
|
|
|
height=ur.y-ll.y)
|
2018-10-19 23:21:03 +02:00
|
|
|
if show_blockage_grids:
|
2018-10-11 00:15:58 +02:00
|
|
|
self.set_blockages(self.blocked_grids,True)
|
2018-09-21 01:00:13 +02:00
|
|
|
grid_keys=self.rg.map.keys()
|
|
|
|
|
for g in grid_keys:
|
2018-10-19 23:21:03 +02:00
|
|
|
self.annotate_grid(g)
|
2018-09-21 01:00:13 +02:00
|
|
|
|
2018-10-19 23:21:03 +02:00
|
|
|
if show_connectors:
|
|
|
|
|
for pin in self.connector_enclosure:
|
|
|
|
|
#print("connector: ",str(pin))
|
|
|
|
|
self.cell.add_rect(layer="text",
|
|
|
|
|
offset=pin.ll(),
|
|
|
|
|
width=pin.width(),
|
|
|
|
|
height=pin.height())
|
|
|
|
|
if show_enclosures:
|
|
|
|
|
for pin in self.enclosures:
|
|
|
|
|
#print("enclosure: ",pin.name,pin.ll(),pin.width(),pin.height())
|
|
|
|
|
self.cell.add_rect(layer="text",
|
|
|
|
|
offset=pin.ll(),
|
|
|
|
|
width=pin.width(),
|
|
|
|
|
height=pin.height())
|
2018-09-21 01:00:13 +02:00
|
|
|
|
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
|
|
|
|
|
"""
|
2018-09-21 01:00:13 +02:00
|
|
|
xoff = snap_val_to_grid(offset[0])
|
|
|
|
|
yoff = snap_val_to_grid(offset[1])
|
2016-11-17 00:02:07 +01:00
|
|
|
return vector(xoff, yoff)
|
2016-11-18 21:57:07 +01:00
|
|
|
|
2018-09-21 01:00:13 +02:00
|
|
|
def snap_val_to_grid(x):
|
2018-10-12 23:37:51 +02:00
|
|
|
grid = drc("grid")
|
2018-09-21 01:00:13 +02:00
|
|
|
xgrid = int(round(round((x / grid), 2), 0))
|
|
|
|
|
xoff = xgrid * grid
|
|
|
|
|
return xoff
|