2018-08-23 00:56:19 +02:00
|
|
|
import gdsMill
|
|
|
|
|
import tech
|
|
|
|
|
from contact import contact
|
|
|
|
|
import math
|
|
|
|
|
import debug
|
2018-09-07 23:46:58 +02:00
|
|
|
from globals import OPTS
|
2018-08-23 00:56:19 +02:00
|
|
|
import grid
|
|
|
|
|
from pin_layout import pin_layout
|
|
|
|
|
from vector import vector
|
|
|
|
|
from vector3d import vector3d
|
|
|
|
|
from router import router
|
2018-09-07 23:46:58 +02:00
|
|
|
from direction import direction
|
2018-08-23 00:56:19 +02:00
|
|
|
|
|
|
|
|
class supply_router(router):
|
|
|
|
|
"""
|
|
|
|
|
A router class to read an obstruction map from a gds and
|
|
|
|
|
routes a grid to connect the supply on the two layers.
|
|
|
|
|
"""
|
|
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
def __init__(self, layers, design, gds_filename=None):
|
2018-09-06 20:54:14 +02:00
|
|
|
"""
|
2018-10-04 23:04:29 +02:00
|
|
|
This will route on layers in design. It will get the blockages from
|
|
|
|
|
either the gds file name or the design itself (by saving to a gds file).
|
2018-08-23 00:56:19 +02:00
|
|
|
"""
|
2018-10-04 23:04:29 +02:00
|
|
|
router.__init__(self, layers, design, gds_filename)
|
|
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
# Power rail width in grid units.
|
|
|
|
|
self.rail_track_width = 2
|
|
|
|
|
|
2018-08-23 00:56:19 +02:00
|
|
|
|
2018-09-06 20:54:14 +02:00
|
|
|
def create_routing_grid(self):
|
|
|
|
|
"""
|
|
|
|
|
Create a sprase routing grid with A* expansion functions.
|
2018-08-23 00:56:19 +02:00
|
|
|
"""
|
2018-09-06 20:54:14 +02:00
|
|
|
size = self.ur - self.ll
|
|
|
|
|
debug.info(1,"Size: {0} x {1}".format(size.x,size.y))
|
2018-08-23 00:56:19 +02:00
|
|
|
|
2018-09-06 20:54:14 +02:00
|
|
|
import supply_grid
|
|
|
|
|
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
|
2018-09-06 01:01:11 +02:00
|
|
|
|
2018-10-04 23:04:29 +02:00
|
|
|
def route(self, vdd_name="vdd", gnd_name="gnd"):
|
2018-08-23 00:56:19 +02:00
|
|
|
"""
|
2018-09-06 01:01:11 +02:00
|
|
|
Add power supply rails and connect all pins to these rails.
|
2018-08-23 00:56:19 +02:00
|
|
|
"""
|
2018-08-30 00:32:45 +02:00
|
|
|
debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, gnd_name))
|
2018-09-08 19:05:48 +02:00
|
|
|
self.vdd_name = vdd_name
|
|
|
|
|
self.gnd_name = gnd_name
|
2018-08-23 00:56:19 +02:00
|
|
|
|
|
|
|
|
# Clear the pins if we have previously routed
|
|
|
|
|
if (hasattr(self,'rg')):
|
|
|
|
|
self.clear_pins()
|
|
|
|
|
else:
|
|
|
|
|
# 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()
|
|
|
|
|
|
|
|
|
|
# Get the pin shapes
|
2018-10-04 23:04:29 +02:00
|
|
|
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
|
2018-09-21 01:00:13 +02:00
|
|
|
|
2018-09-13 18:10:29 +02:00
|
|
|
# Add the supply rails in a mesh network and connect H/V with vias
|
2018-09-18 21:57:39 +02:00
|
|
|
# Block everything
|
2018-10-11 00:15:58 +02:00
|
|
|
self.prepare_blockages(self.gnd_name)
|
2018-09-18 21:57:39 +02:00
|
|
|
# Determine the rail locations
|
2018-09-13 18:10:29 +02:00
|
|
|
self.route_supply_rails(self.gnd_name,0)
|
2018-08-23 00:56:19 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
# Block everything
|
2018-10-11 00:15:58 +02:00
|
|
|
self.prepare_blockages(self.vdd_name)
|
2018-09-18 21:57:39 +02:00
|
|
|
# Determine the rail locations
|
2018-09-13 18:10:29 +02:00
|
|
|
self.route_supply_rails(self.vdd_name,1)
|
2018-10-11 00:15:58 +02:00
|
|
|
|
2018-10-15 20:25:51 +02:00
|
|
|
#self.write_debug_gds("pre_pin_debug.gds",stop_program=True)
|
2018-10-11 00:15:58 +02:00
|
|
|
|
2018-09-13 18:10:29 +02:00
|
|
|
# Route the supply pins to the supply rails
|
2018-09-18 21:57:39 +02:00
|
|
|
self.route_pins_to_rails(gnd_name)
|
|
|
|
|
self.route_pins_to_rails(vdd_name)
|
2018-09-06 01:01:11 +02:00
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
#self.write_debug_gds("post_pin_debug.gds",stop_program=False)
|
|
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
return True
|
2018-08-23 00:56:19 +02:00
|
|
|
|
2018-09-07 23:46:58 +02:00
|
|
|
|
2018-09-13 18:10:29 +02:00
|
|
|
def connect_supply_rails(self, name):
|
2018-09-07 23:46:58 +02:00
|
|
|
"""
|
2018-09-18 21:57:39 +02:00
|
|
|
Determine which supply rails overlap and can accomodate a via.
|
2018-10-11 00:15:58 +02:00
|
|
|
Remove any supply rails that do not have a via since they are disconnected.
|
|
|
|
|
NOTE: It is still possible though unlikely that there are disconnected groups of rails.
|
2018-09-07 23:46:58 +02:00
|
|
|
"""
|
2018-09-18 21:57:39 +02:00
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
# Split into horizontal and vertical paths
|
|
|
|
|
vertical_rails = [x for x in self.supply_rails if x[0][0].z==1 and x.name==name]
|
|
|
|
|
horizontal_rails = [x for x in self.supply_rails if x[0][0].z==0 and x.name==name]
|
2018-09-18 21:57:39 +02:00
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
# Flag to see if each supply rail has at least one via (i.e. it is "connected")
|
|
|
|
|
vertical_flags = [False] * len(vertical_rails)
|
|
|
|
|
horizontal_flags = [False] * len(horizontal_rails)
|
2018-09-18 21:57:39 +02:00
|
|
|
|
|
|
|
|
# Compute a list of "shared areas" that are bigger than a via
|
|
|
|
|
via_areas = []
|
2018-10-11 00:15:58 +02:00
|
|
|
for vindex,v in enumerate(vertical_rails):
|
|
|
|
|
for hindex,h in enumerate(horizontal_rails):
|
2018-09-18 21:57:39 +02:00
|
|
|
# Compute the overlap of the two paths, None if no overlap
|
2018-09-07 23:46:58 +02:00
|
|
|
overlap = v.overlap(h)
|
|
|
|
|
if overlap:
|
2018-09-13 18:10:29 +02:00
|
|
|
(ll,ur) = overlap
|
2018-09-18 21:57:39 +02:00
|
|
|
# We can add a via only if it is a full track width in each dimension
|
2018-09-13 18:10:29 +02:00
|
|
|
if ur.x-ll.x >= self.rail_track_width-1 and ur.y-ll.y >= self.rail_track_width-1:
|
2018-10-11 00:15:58 +02:00
|
|
|
vertical_flags[vindex]=True
|
|
|
|
|
horizontal_flags[hindex]=True
|
2018-09-18 21:57:39 +02:00
|
|
|
via_areas.append(overlap)
|
2018-09-07 23:46:58 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
# Go through and add the vias at the center of the intersection
|
|
|
|
|
for (ll,ur) in via_areas:
|
2018-09-07 23:46:58 +02:00
|
|
|
center = (ll + ur).scale(0.5,0.5,0)
|
|
|
|
|
self.add_via(center,self.rail_track_width)
|
2018-09-18 21:57:39 +02:00
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
# Retrieve the original indices into supply_rails for removal
|
|
|
|
|
remove_hrails = [rail for flag,rail in zip(horizontal_flags,horizontal_rails) if not flag]
|
|
|
|
|
remove_vrails = [rail for flag,rail in zip(vertical_flags,vertical_rails) if not flag]
|
|
|
|
|
for rail in remove_hrails + remove_vrails:
|
|
|
|
|
debug.info(1,"Removing disconnected supply rail {}".format(rail))
|
|
|
|
|
self.supply_rails.remove(rail)
|
2018-09-07 23:46:58 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
def add_supply_rails(self, name):
|
|
|
|
|
"""
|
|
|
|
|
Add the shapes that represent the routed supply rails.
|
|
|
|
|
This is after the paths have been pruned and only include rails that are
|
|
|
|
|
connected with vias.
|
|
|
|
|
"""
|
|
|
|
|
for wave_path in self.supply_rails:
|
|
|
|
|
if wave_path.name == name:
|
|
|
|
|
self.add_wavepath(name, wave_path)
|
|
|
|
|
|
2018-10-15 20:25:51 +02:00
|
|
|
def compute_supply_rail_dimensions(self):
|
2018-09-06 01:01:11 +02:00
|
|
|
"""
|
2018-10-15 20:25:51 +02:00
|
|
|
Compute the supply rail dimensions including wide metal spacing rules.
|
2018-09-06 01:01:11 +02:00
|
|
|
"""
|
2018-10-15 20:25:51 +02:00
|
|
|
|
|
|
|
|
self.max_yoffset = self.rg.ur.y
|
|
|
|
|
self.max_xoffset = self.rg.ur.x
|
2018-10-15 18:59:16 +02:00
|
|
|
|
2018-10-15 20:25:51 +02:00
|
|
|
# Longest length is conservative
|
|
|
|
|
rail_length = max(self.max_yoffset,self.max_xoffset)
|
2018-10-15 18:59:16 +02:00
|
|
|
# Convert the number of tracks to dimensions to get the design rule spacing
|
|
|
|
|
rail_width = self.track_width*self.rail_track_width
|
2018-10-15 20:25:51 +02:00
|
|
|
|
|
|
|
|
# Get the conservative width and spacing of the top rails
|
|
|
|
|
(horizontal_width, horizontal_space) = self.get_layer_width_space(0, rail_width, rail_length)
|
|
|
|
|
(vertical_width, vertical_space) = self.get_layer_width_space(1, rail_width, rail_length)
|
2018-10-15 18:59:16 +02:00
|
|
|
width = max(horizontal_width, vertical_width)
|
|
|
|
|
space = max(horizontal_space, vertical_space)
|
2018-10-15 20:25:51 +02:00
|
|
|
|
2018-10-15 18:59:16 +02:00
|
|
|
# This is the supply rail pitch in terms of routing grids
|
2018-10-15 20:25:51 +02:00
|
|
|
# i.e. a rail of self.rail_track_width needs this many tracks including
|
|
|
|
|
# space
|
|
|
|
|
track_pitch = self.rail_track_width*width + space
|
|
|
|
|
|
|
|
|
|
self.supply_rail_step = math.ceil(track_pitch/self.track_width)
|
|
|
|
|
debug.info(1,"Rail step: {}".format(self.supply_rail_step))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def compute_supply_rails(self, name, start_offset):
|
|
|
|
|
"""
|
|
|
|
|
Compute the unblocked locations for the horizontal and vertical supply rails.
|
|
|
|
|
Go in a raster order from bottom to the top (for horizontal) and left to right
|
|
|
|
|
(for vertical). Start with an initial start_offset in x and y direction.
|
|
|
|
|
"""
|
2018-09-07 23:46:58 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
# Horizontal supply rails
|
2018-10-15 20:25:51 +02:00
|
|
|
for offset in range(start_offset, self.max_yoffset, self.supply_rail_step):
|
2018-09-07 23:46:58 +02:00
|
|
|
# Seed the function at the location with the given width
|
2018-10-15 20:25:51 +02:00
|
|
|
wave = [vector3d(0,offset+i,0) for i in range(self.supply_rail_step)]
|
2018-09-08 19:05:48 +02:00
|
|
|
# While we can keep expanding east in this horizontal track
|
2018-10-15 20:25:51 +02:00
|
|
|
while wave and wave[0].x < self.max_xoffset:
|
2018-09-18 21:57:39 +02:00
|
|
|
wave = self.find_supply_rail(name, wave, direction.EAST)
|
2018-09-06 23:30:59 +02:00
|
|
|
|
|
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
# Vertical supply rails
|
|
|
|
|
max_offset = self.rg.ur.x
|
2018-10-15 20:25:51 +02:00
|
|
|
for offset in range(start_offset, self.max_xoffset, self.supply_rail_step):
|
2018-09-07 23:46:58 +02:00
|
|
|
# Seed the function at the location with the given width
|
2018-10-15 20:25:51 +02:00
|
|
|
wave = [vector3d(offset+i,0,1) for i in range(self.supply_rail_step)]
|
2018-09-08 19:05:48 +02:00
|
|
|
# While we can keep expanding north in this vertical track
|
2018-10-15 20:25:51 +02:00
|
|
|
while wave and wave[0].y < self.max_yoffset:
|
2018-09-18 21:57:39 +02:00
|
|
|
wave = self.find_supply_rail(name, wave, direction.NORTH)
|
2018-09-06 23:30:59 +02:00
|
|
|
|
2018-10-15 20:25:51 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
def find_supply_rail(self, name, seed_wave, direct):
|
2018-09-06 23:30:59 +02:00
|
|
|
"""
|
2018-09-08 19:05:48 +02:00
|
|
|
This finds the first valid starting location and routes a supply rail
|
|
|
|
|
in the given direction.
|
|
|
|
|
It returns the space after the end of the rail to seed another call for multiple
|
|
|
|
|
supply rails in the same "track" when there is a blockage.
|
2018-09-06 23:30:59 +02:00
|
|
|
"""
|
2018-09-07 23:46:58 +02:00
|
|
|
|
|
|
|
|
# Sweep to find an initial unblocked valid wave
|
|
|
|
|
start_wave = self.rg.find_start_wave(seed_wave, len(seed_wave), direct)
|
2018-09-06 23:30:59 +02:00
|
|
|
if not start_wave:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# Expand the wave to the right
|
2018-09-07 23:46:58 +02:00
|
|
|
wave_path = self.rg.probe(start_wave, direct)
|
2018-09-06 23:30:59 +02:00
|
|
|
if not wave_path:
|
|
|
|
|
return None
|
|
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
# We must have at least 2 tracks to drop plus 2 tracks for a via
|
|
|
|
|
if len(wave_path)>=4*self.rail_track_width:
|
|
|
|
|
# drop the first and last steps to leave escape routing room
|
|
|
|
|
# around the blockage that stopped the probe
|
|
|
|
|
# except, don't drop the first if it is the first in a row/column
|
|
|
|
|
if (direct==direction.NORTH and seed_wave[0].y>0):
|
|
|
|
|
wave_path.trim_first()
|
|
|
|
|
elif (direct == direction.EAST and seed_wave[0].x>0):
|
|
|
|
|
wave_path.trim_first()
|
|
|
|
|
|
|
|
|
|
wave_path.trim_last()
|
2018-09-07 23:46:58 +02:00
|
|
|
wave_path.name = name
|
2018-09-18 21:57:39 +02:00
|
|
|
self.supply_rails.append(wave_path)
|
2018-09-06 23:30:59 +02:00
|
|
|
|
|
|
|
|
# seed the next start wave location
|
|
|
|
|
wave_end = wave_path[-1]
|
2018-09-07 23:46:58 +02:00
|
|
|
return wave_path.neighbor(direct)
|
2018-09-06 23:30:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-10-15 20:25:51 +02:00
|
|
|
def route_supply_rails(self, name, supply_number):
|
|
|
|
|
"""
|
|
|
|
|
Route the horizontal and vertical supply rails across the entire design.
|
|
|
|
|
Must be done with lower left at 0,0
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
# Compute the grid dimensions
|
|
|
|
|
self.compute_supply_rail_dimensions()
|
|
|
|
|
|
|
|
|
|
# Compute the grid locations of the supply rails
|
|
|
|
|
start_offset = supply_number*self.rail_track_width
|
|
|
|
|
self.compute_supply_rails(name, start_offset)
|
|
|
|
|
|
|
|
|
|
# Add the supply rail vias (and prune disconnected rails)
|
|
|
|
|
self.connect_supply_rails(name)
|
|
|
|
|
|
|
|
|
|
# Add the rails themselves
|
|
|
|
|
self.add_supply_rails(name)
|
|
|
|
|
|
|
|
|
|
|
2018-09-06 20:54:14 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
def route_pins_to_rails(self, pin_name):
|
2018-09-08 19:05:48 +02:00
|
|
|
"""
|
|
|
|
|
This will route each of the pin components to the supply rails.
|
|
|
|
|
After it is done, the cells are added to the pin blockage list.
|
|
|
|
|
"""
|
2018-09-09 03:55:36 +02:00
|
|
|
|
2018-09-18 21:57:39 +02:00
|
|
|
|
|
|
|
|
num_components = self.num_pin_components(pin_name)
|
2018-09-21 01:00:13 +02:00
|
|
|
debug.info(1,"Pin {0} has {1} components to route.".format(pin_name, num_components))
|
2018-10-05 17:39:28 +02:00
|
|
|
|
|
|
|
|
recent_paths = []
|
2018-09-09 03:55:36 +02:00
|
|
|
# For every component
|
2018-09-18 21:57:39 +02:00
|
|
|
for index in range(num_components):
|
2018-09-21 01:00:13 +02:00
|
|
|
debug.info(2,"Routing component {0} {1}".format(pin_name, index))
|
2018-09-09 03:55:36 +02:00
|
|
|
|
2018-09-13 18:10:29 +02:00
|
|
|
self.rg.reinit()
|
|
|
|
|
|
2018-10-11 00:15:58 +02:00
|
|
|
self.prepare_blockages(pin_name)
|
2018-10-06 00:57:34 +02:00
|
|
|
|
2018-09-09 03:55:36 +02:00
|
|
|
# Add the single component of the pin as the source
|
|
|
|
|
# which unmarks it as a blockage too
|
2018-09-18 21:57:39 +02:00
|
|
|
self.add_pin_component_source(pin_name,index)
|
2018-09-13 18:10:29 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
# Add all of the rails as targets
|
2018-09-09 03:55:36 +02:00
|
|
|
# Don't add the other pins, but we could?
|
2018-09-08 19:05:48 +02:00
|
|
|
self.add_supply_rail_target(pin_name)
|
2018-10-05 17:39:28 +02:00
|
|
|
|
2018-10-06 00:57:34 +02:00
|
|
|
# Add the previous paths as targets too
|
|
|
|
|
#self.add_path_target(recent_paths)
|
|
|
|
|
|
2018-10-15 22:23:31 +02:00
|
|
|
#print(self.rg.target)
|
2018-10-06 00:57:34 +02:00
|
|
|
|
2018-09-09 03:55:36 +02:00
|
|
|
# Actually run the A* router
|
2018-09-21 01:00:13 +02:00
|
|
|
if not self.run_router(detour_scale=5):
|
|
|
|
|
self.write_debug_gds()
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2018-10-05 17:39:28 +02:00
|
|
|
recent_paths.append(self.paths[-1])
|
2018-09-09 03:55:36 +02:00
|
|
|
|
2018-08-23 00:56:19 +02:00
|
|
|
|
2018-10-12 23:37:51 +02:00
|
|
|
def add_supply_rail_target(self, pin_name):
|
|
|
|
|
"""
|
|
|
|
|
Add the supply rails of given name as a routing target.
|
|
|
|
|
"""
|
|
|
|
|
debug.info(2,"Add supply rail target {}".format(pin_name))
|
|
|
|
|
for rail in self.supply_rails:
|
|
|
|
|
if rail.name != pin_name:
|
|
|
|
|
continue
|
2018-10-15 22:23:31 +02:00
|
|
|
# Set the middle track only as the target since wide metal
|
|
|
|
|
# spacings may mean the other grids are actually empty space
|
|
|
|
|
middle_index = math.floor(len(rail[0])/2)
|
2018-10-12 23:37:51 +02:00
|
|
|
for wave_index in range(len(rail)):
|
|
|
|
|
pin_in_tracks = rail[wave_index]
|
|
|
|
|
#debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks))
|
2018-10-15 22:23:31 +02:00
|
|
|
self.rg.set_target(pin_in_tracks[middle_index])
|
2018-10-12 23:37:51 +02:00
|
|
|
self.rg.set_blocked(pin_in_tracks,False)
|
2018-08-23 00:56:19 +02:00
|
|
|
|
2018-10-15 22:23:31 +02:00
|
|
|
|
2018-10-12 23:37:51 +02:00
|
|
|
def set_supply_rail_blocked(self, value=True):
|
|
|
|
|
"""
|
|
|
|
|
Add the supply rails of given name as a routing target.
|
|
|
|
|
"""
|
|
|
|
|
debug.info(3,"Blocking supply rail")
|
|
|
|
|
for rail in self.supply_rails:
|
|
|
|
|
for wave_index in range(len(rail)):
|
|
|
|
|
pin_in_tracks = rail[wave_index]
|
|
|
|
|
#debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks))
|
|
|
|
|
self.rg.set_blocked(pin_in_tracks,value)
|
|
|
|
|
|