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-08-30 00:32:45 +02:00
|
|
|
def __init__(self, gds_name=None, module=None):
|
2018-09-06 20:54:14 +02:00
|
|
|
"""
|
|
|
|
|
Use the gds file for the blockages with the top module topName and
|
2018-08-23 00:56:19 +02:00
|
|
|
layers for the layers to route on
|
|
|
|
|
"""
|
2018-08-30 00:32:45 +02:00
|
|
|
router.__init__(self, gds_name, module)
|
2018-09-06 23:30:59 +02:00
|
|
|
|
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-08-23 00:56:19 +02:00
|
|
|
def route(self, cell, layers, vdd_name="vdd", gnd_name="gnd"):
|
|
|
|
|
"""
|
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-08-23 00:56:19 +02:00
|
|
|
self.cell = cell
|
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:
|
|
|
|
|
# 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 get all shapes as blockages
|
|
|
|
|
self.find_blockages()
|
|
|
|
|
|
|
|
|
|
# Get the pin shapes
|
2018-09-08 19:05:48 +02:00
|
|
|
self.find_pins(vdd_name)
|
|
|
|
|
self.find_pins(gnd_name)
|
2018-08-23 00:56:19 +02:00
|
|
|
|
|
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
self.add_blockages()
|
|
|
|
|
self.add_pin_blockages(vdd_name)
|
|
|
|
|
self.route_supply_rails(gnd_name,0)
|
|
|
|
|
self.connect_supply_rail(gnd_name)
|
|
|
|
|
self.route_pins_to_rails(gnd_name)
|
|
|
|
|
|
2018-09-09 03:55:36 +02:00
|
|
|
# Start fresh. Not the best for run-time, but simpler.
|
2018-09-08 19:05:48 +02:00
|
|
|
self.clear_blockages()
|
2018-09-09 03:55:36 +02:00
|
|
|
|
|
|
|
|
self.add_blockages()
|
2018-09-08 19:05:48 +02:00
|
|
|
self.add_pin_blockages(gnd_name)
|
|
|
|
|
self.route_supply_rails(vdd_name,1)
|
|
|
|
|
self.connect_supply_rail(vdd_name)
|
|
|
|
|
self.route_pins_to_rails(vdd_name)
|
2018-09-06 01:01:11 +02:00
|
|
|
|
2018-08-28 19:41:19 +02:00
|
|
|
self.write_debug_gds()
|
2018-08-23 00:56:19 +02:00
|
|
|
return False
|
|
|
|
|
|
2018-09-07 23:46:58 +02:00
|
|
|
|
|
|
|
|
def connect_supply_rail(self, name):
|
|
|
|
|
"""
|
|
|
|
|
Add vias between overlapping supply rails.
|
|
|
|
|
"""
|
|
|
|
|
paths = [x for x in self.paths if x.name == name]
|
|
|
|
|
|
|
|
|
|
# Split into horizontal and vertical
|
|
|
|
|
vertical_paths = [x for x in paths if x[0][0].z==1]
|
|
|
|
|
horizontal_paths = [x for x in paths if x[0][0].z==0]
|
|
|
|
|
|
|
|
|
|
shared_areas = []
|
|
|
|
|
for v in vertical_paths:
|
|
|
|
|
for h in horizontal_paths:
|
|
|
|
|
overlap = v.overlap(h)
|
|
|
|
|
if overlap:
|
|
|
|
|
shared_areas.append(overlap)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (ll,ur) in shared_areas:
|
|
|
|
|
center = (ll + ur).scale(0.5,0.5,0)
|
|
|
|
|
self.add_via(center,self.rail_track_width)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def route_supply_rails(self, name, supply_number):
|
2018-09-06 01:01:11 +02:00
|
|
|
"""
|
2018-09-08 19:05:48 +02:00
|
|
|
Route the horizontal and vertical supply rails across the entire design.
|
2018-09-06 01:01:11 +02:00
|
|
|
"""
|
2018-09-06 23:30:59 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
start_offset = supply_number*self.rail_track_width
|
|
|
|
|
max_yoffset = self.rg.ur.y
|
|
|
|
|
max_xoffset = self.rg.ur.x
|
|
|
|
|
step_offset = 2*self.rail_track_width
|
2018-09-07 23:46:58 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
# Horizontal supply rails
|
|
|
|
|
for offset in range(start_offset, max_yoffset, step_offset):
|
2018-09-07 23:46:58 +02:00
|
|
|
# Seed the function at the location with the given width
|
|
|
|
|
wave = [vector3d(0,offset+i,0) for i in range(self.rail_track_width)]
|
2018-09-08 19:05:48 +02:00
|
|
|
# While we can keep expanding east in this horizontal track
|
|
|
|
|
while wave and wave[0].x < max_xoffset:
|
|
|
|
|
wave = self.route_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
|
|
|
|
|
for offset in range(start_offset, max_xoffset, step_offset):
|
2018-09-07 23:46:58 +02:00
|
|
|
# Seed the function at the location with the given width
|
|
|
|
|
wave = [vector3d(offset+i,0,1) for i in range(self.rail_track_width)]
|
2018-09-08 19:05:48 +02:00
|
|
|
# While we can keep expanding north in this vertical track
|
|
|
|
|
while wave and wave[0].y < max_yoffset:
|
|
|
|
|
wave = self.route_supply_rail(name, wave, direction.NORTH)
|
2018-09-06 23:30:59 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
# Remember index of path size which is how many rails we had at the start
|
|
|
|
|
self.num_rails = len(self.paths)
|
2018-09-06 23:30:59 +02:00
|
|
|
|
2018-09-07 23:46:58 +02:00
|
|
|
def route_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-09-07 23:46:58 +02:00
|
|
|
# Filter any path that won't span 2 rails
|
|
|
|
|
# so that we can guarantee it is connected
|
|
|
|
|
if len(wave_path)>=2*self.rail_track_width:
|
|
|
|
|
self.add_wavepath(name, wave_path)
|
|
|
|
|
wave_path.name = name
|
|
|
|
|
self.paths.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-09-06 20:54:14 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
def route_pins_to_rails(self,pin_name):
|
|
|
|
|
"""
|
|
|
|
|
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
|
|
|
|
|
|
|
|
# For every component
|
2018-09-08 19:05:48 +02:00
|
|
|
for index in range(self.num_pin_components(pin_name)):
|
2018-09-09 03:55:36 +02:00
|
|
|
|
|
|
|
|
# Block all the pin components first
|
2018-09-08 19:05:48 +02:00
|
|
|
self.add_component_blockages(pin_name)
|
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-08 19:05:48 +02:00
|
|
|
self.add_pin_component(pin_name,index,is_source=True)
|
2018-09-09 03:55:36 +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-09-06 01:01:11 +02:00
|
|
|
|
2018-09-09 03:55:36 +02:00
|
|
|
# Actually run the A* router
|
|
|
|
|
self.run_router(detour_scale=5)
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2018-09-09 03:55:36 +02:00
|
|
|
|
2018-09-08 19:05:48 +02:00
|
|
|
|
2018-09-09 03:55:36 +02:00
|
|
|
|
|
|
|
|
|
2018-09-06 01:01:11 +02:00
|
|
|
|
2018-08-23 00:56:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|