Merge remote-tracking branch 'private/dev'

This commit is contained in:
mrg 2021-01-07 10:20:17 -08:00
commit c0df3ff1da
11 changed files with 137 additions and 144 deletions

View File

@ -23,10 +23,14 @@ class geometry:
A specific path, shape, or text geometry. Base class for shared
items.
"""
def __init__(self):
def __init__(self, lpp=None):
""" By default, everything has no size. """
self.width = 0
self.height = 0
if lpp:
self.lpp = lpp
self.layerNumber = lpp[0]
self.layerPurpose = lpp[1]
def __str__(self):
""" override print function output """
@ -410,10 +414,8 @@ class path(geometry):
def __init__(self, lpp, coordinates, path_width):
"""Initializes a path for the specified layer"""
super().__init__()
super().__init__(lpp)
self.name = "path"
self.layerNumber = lpp[0]
self.layerPurpose = lpp[1]
self.coordinates = map(lambda x: [x[0], x[1]], coordinates)
self.coordinates = vector(self.coordinates).snap_to_grid()
self.path_width = path_width
@ -448,11 +450,9 @@ class label(geometry):
def __init__(self, text, lpp, offset, zoom=None):
"""Initializes a text label for specified layer"""
super().__init__()
super().__init__(lpp)
self.name = "label"
self.text = text
self.layerNumber = lpp[0]
self.layerPurpose = lpp[1]
self.offset = vector(offset).snap_to_grid()
if not zoom:
@ -495,10 +495,8 @@ class rectangle(geometry):
def __init__(self, lpp, offset, width, height):
"""Initializes a rectangular shape for specified layer"""
super().__init__()
super().__init__(lpp)
self.name = "rect"
self.layerNumber = lpp[0]
self.layerPurpose = lpp[1]
self.offset = vector(offset).snap_to_grid()
self.size = vector(width, height).snap_to_grid()
self.width = round_to_grid(self.size.x)

View File

@ -473,11 +473,25 @@ class layout():
"""
self.pin_map = {}
def copy_layout_pin_shapes(self, text):
"""
Copy the shapes of the layout pins as objects.
"""
for s in self.pin_map[text]:
self.add_rect(layer=s.layer,
offset=s.ll(),
width=s.width(),
height=s.height())
def replace_layout_pin(self, text, pin):
"""
Remove the old pin and replace with a new one
"""
# Keep the shapes as they were used to connect to the router pins
self.copy_layout_pin_shapes(text)
# Remove the shapes as actual pins
self.remove_layout_pin(text)
# Add the new pin
self.add_layout_pin(text=text,
layer=pin.layer,
offset=pin.ll(),

View File

@ -108,7 +108,10 @@ class dff_buf(design.design):
well_spacing = max(well_spacing, self.pwell_to_nwell)
except AttributeError:
pass
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0))
well_spacing += self.well_extend_active
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing, 0))
# Add INV2 to the right
self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))

View File

@ -78,81 +78,80 @@ class grid:
for k in self.map:
self.map[k].blocked=False
def set_source(self, n, value=True):
def clear_source(self):
for k in self.map:
self.map[k].source=False
self.source = set()
def set_source(self, n):
if not isinstance(n, vector3d):
for item in n:
self.set_source(item, value)
self.set_source(item)
else:
self.add_map(n)
self.map[n].source=value
self.map[n].source=True
self.map[n].blocked=False
self.source.add(n)
def set_target(self, n, value=True):
def clear_target(self):
for k in self.map:
self.map[k].target=False
self.target = set()
def set_target(self, n):
if not isinstance(n, vector3d):
for item in n:
self.set_target(item, value)
self.set_target(item)
else:
self.add_map(n)
self.map[n].target=value
self.map[n].target=True
self.map[n].blocked=False
self.target.add(n)
def add_source(self, track_list, value=True):
def add_source(self, track_list):
debug.info(3, "Adding source list={0}".format(str(track_list)))
for n in track_list:
debug.info(4, "Adding source ={0}".format(str(n)))
self.set_source(n, value)
self.set_blocked(n, False)
self.set_source(n)
# self.set_blocked(n, False)
def add_target(self, track_list, value=True):
def add_target(self, track_list):
debug.info(3, "Adding target list={0}".format(str(track_list)))
for n in track_list:
debug.info(4, "Adding target ={0}".format(str(n)))
self.set_target(n, value)
self.set_blocked(n, False)
self.set_target(n)
# self.set_blocked(n, False)
def add_perimeter_target(self, side="all", value=True):
def add_perimeter_target(self, side="all"):
debug.info(3, "Adding perimeter target")
perimeter_list = []
# Add the left/right columns
if side=="all" or side=="left":
x = self.ll.x
for y in range(self.ll.y, self.ur.y, 1):
n = vector3d(x, y, 0)
self.set_target(n, value)
self.set_blocked(n, False)
n = vector3d(x, y, 1)
self.set_target(n, value)
self.set_blocked(n, False)
perimeter_list.append(vector3d(x, y, 0))
perimeter_list.append(vector3d(x, y, 1))
if side=="all" or side=="right":
x = self.ur.x
for y in range(self.ll.y, self.ur.y, 1):
n = vector3d(x, y, 0)
self.set_target(n, value)
self.set_blocked(n, False)
n = vector3d(x, y, 1)
self.set_target(n, value)
self.set_blocked(n, False)
perimeter_list.append(vector3d(x, y, 0))
perimeter_list.append(vector3d(x, y, 1))
if side=="all" or side=="bottom":
y = self.ll.y
for x in range(self.ll.x, self.ur.x, 1):
n = vector3d(x, y, 0)
self.set_target(n, value)
self.set_blocked(n, False)
n = vector3d(x, y, 1)
self.set_target(n, value)
self.set_blocked(n, False)
perimeter_list.append(vector3d(x, y, 0))
perimeter_list.append(vector3d(x, y, 1))
if side=="all" or side=="top":
y = self.ur.y
for x in range(self.ll.x, self.ur.x, 1):
n = vector3d(x, y, 0)
self.set_target(n, value)
self.set_blocked(n, False)
n = vector3d(x, y, 1)
self.set_target(n, value)
self.set_blocked(n, False)
perimeter_list.append(vector3d(x, y, 0))
perimeter_list.append(vector3d(x, y, 1))
self.set_target(perimeter_list)
def is_target(self, point):
"""

View File

@ -34,7 +34,6 @@ class grid_cell:
if self.min_cost > 0:
return self.min_cost
def get_type(self):
type_string = ""
@ -50,7 +49,5 @@ class grid_cell:
if self.path:
type_string += "P"
if type_string != "":
return type_string
return type_string
return None

View File

@ -6,10 +6,11 @@
# All rights reserved.
#
import itertools
import math
import gdsMill
from tech import drc, GDS
from tech import layer as techlayer
import math
import debug
from router_tech import router_tech
from pin_layout import pin_layout
@ -189,7 +190,6 @@ class router(router_tech):
# self.combine_adjacent_pins(pin)
# print_time("Combining adjacent pins",datetime.now(), start_time, 4)
# Separate any adjacent grids of differing net names
# that overlap
# Must be done before enclosing pins
@ -266,18 +266,10 @@ class router(router_tech):
This will try to separate all grid pins by the supplied
number of separation tracks (default is to prevent adjacency).
"""
# Commented out to debug with SCMOS
# if separation==0:
# return
pin_names = self.pin_groups.keys()
for i, pin_name1 in enumerate(pin_names):
for j, pin_name2 in enumerate(pin_names):
if i == j:
continue
if i > j:
return
self.separate_adjacent_pin(pin_name1, pin_name2, separation)
for (pin_name1, pin_name2) in itertools.combinations(pin_names, 2):
self.separate_adjacent_pin(pin_name1, pin_name2, separation)
def separate_adjacent_pin(self, pin_name1, pin_name2, separation):
"""
@ -290,6 +282,7 @@ class router(router_tech):
"Comparing {0} and {1} adjacency".format(pin_name1,
pin_name2))
removed_grids = 0
for index1, pg1 in enumerate(self.pin_groups[pin_name1]):
for index2, pg2 in enumerate(self.pin_groups[pin_name2]):
adj_grids = pg1.adjacent_grids(pg2, separation)
@ -362,7 +355,7 @@ class router(router_tech):
# 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)
# print("BLOCKING:", self.blocked_grids)
self.set_blockages(self.blocked_grids, True)
# Block all of the supply rails
@ -389,8 +382,9 @@ class router(router_tech):
# 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
blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids}
self.set_blockages(blockage_grids, False)
# 1/6/21: This would cause things that looked like loops in the supply tree router
# blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids}
# self.set_blockages(blockage_grids, False)
def convert_shape_to_units(self, shape):
"""
@ -1039,18 +1033,18 @@ class router(router_tech):
(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).rect
self.cell.add_rect(layer="boundary",
offset=ll,
width=ur[0]-ll[0],
height=ur[1]-ll[1])
width=ur[0] - ll[0],
height=ur[1] - ll[1])
# (ll, ur) = self.convert_track_to_pin(coord).rect
# 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])
width=ur[0] - ll[0],
height=ur[1] - ll[1])
def write_debug_gds(self, gds_name="debug_route.gds", stop_program=True):
"""
@ -1058,9 +1052,9 @@ class router(router_tech):
search information annotated on it.
"""
debug.info(0, "Writing annotated router gds file to {}".format(gds_name))
self.del_router_info()
self.add_router_info()
self.cell.gds_write(gds_name)
self.del_router_info()
if stop_program:
import sys
@ -1071,17 +1065,17 @@ class router(router_tech):
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)
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)
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 t != None:
off = vector((shape[1].x + shape[0].x) / 2,
(shape[1].y + shape[0].y) / 2)
if t:
if g[2] == 1:
# Upper layer is upper right label
type_off = off + partial_track
@ -1107,16 +1101,15 @@ class router(router_tech):
self.cell.add_label(text="{0},{1}".format(g[0], g[1]),
layer="text",
offset=shape[0],
zoom=0.05)
offset=shape[0])
def del_router_info(self):
"""
Erase all of the comments on the current level.
"""
debug.info(0, "Erasing router info")
layer_num = techlayer["text"]
self.cell.objs = [x for x in self.cell.objs if x.layerNumber != layer_num]
lpp = techlayer["text"]
self.cell.objs = [x for x in self.cell.objs if x.lpp != lpp]
def add_router_info(self):
"""
@ -1132,7 +1125,6 @@ class router(router_tech):
show_all_grids = True
if show_all_grids:
# self.rg.add_all_grids()
for g in self.rg.map:
self.annotate_grid(g)
@ -1143,8 +1135,8 @@ class router(router_tech):
(ll, ur) = blockage.inflate()
self.cell.add_rect(layer="text",
offset=ll,
width=ur.x-ll.x,
height=ur.y-ll.y)
width=ur.x - ll.x,
height=ur.y - ll.y)
if show_blockage_grids:
self.set_blockages(self.blocked_grids, True)
for g in self.rg.map:

View File

@ -9,7 +9,7 @@ import debug
from globals import print_time
from router import router
from datetime import datetime
from supply_grid import supply_grid
from signal_grid import signal_grid
class signal_escape_router(router):
@ -30,31 +30,38 @@ class signal_escape_router(router):
"""
size = self.ur - self.ll
debug.info(1,"Size: {0} x {1}".format(size.x, size.y))
self.rg = supply_grid(self.ll, self.ur, self.track_width)
self.rg = signal_grid(self.ll, self.ur, self.track_width)
def escape_route(self, pin_list):
def perimeter_dist(self, pin_name):
"""
Return the shortest Manhattan distance to the bounding box perimeter.
"""
loc = self.cell.get_pin(pin_name).center()
x_dist = min(loc.x - self.ll.x, self.ur.x - loc.x)
y_dist = min(loc.y - self.ll.y, self.ur.y - loc.y)
return min(x_dist, y_dist)
def escape_route(self, pin_names):
"""
Takes a list of tuples (name, side) and routes them. After routing,
it removes the old pin and places a new one on the perimeter.
"""
pin_names = [x[0] for x in pin_list]
# Clear the pins if we have previously routed
if (hasattr(self,'rg')):
self.clear_pins()
else:
self.create_routing_grid()
self.create_routing_grid()
# Get the pin shapes
start_time = datetime.now()
self.find_pins_and_blockages(pin_names)
print_time("Finding pins and blockages",datetime.now(), start_time, 3)
# Order the routes by closest to the perimeter first
# This prevents some pins near the perimeter from being blocked by other pins
ordered_pin_names = sorted(pin_names, key=lambda x: self.perimeter_dist(x))
# Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter
start_time = datetime.now()
for pin_name, side in pin_list:
self.route_signal(pin_name, side)
for pin_name in ordered_pin_names:
self.route_signal(pin_name)
print_time("Maze routing pins",datetime.now(), start_time, 3)
@ -62,10 +69,10 @@ class signal_escape_router(router):
return True
def route_signal(self, pin_name, side):
def route_signal(self, pin_name, side="all"):
for detour_scale in [5 * pow(2, x) for x in range(5)]:
debug.info(1, "Exit routing {0} with scale {1}".format(pin_name, detour_scale))
debug.info(1, "Escape routing {0} with scale {1}".format(pin_name, detour_scale))
# Clear everything in the routing grid.
self.rg.reinit()

View File

@ -23,9 +23,6 @@ class signal_grid(grid):
""" Create a routing map of width x height cells and 2 in the z-axis. """
grid.__init__(self, ll, ur, track_factor)
# priority queue for the maze routing
self.q = []
def reinit(self):
""" Reinitialize everything for a new route. """
@ -33,14 +30,8 @@ class signal_grid(grid):
for p in self.map.values():
p.reset()
# clear source and target pins
self.source = set()
self.target = set()
# Clear the queue
while len(self.q) > 0:
heappop(self.q)
self.counter = 0
self.clear_source()
self.clear_target()
def init_queue(self):
"""
@ -51,12 +42,13 @@ class signal_grid(grid):
"""
# Counter is used to not require data comparison in Python 3.x
# Items will be returned in order they are added during cost ties
self.q = []
self.counter = 0
for s in self.source:
cost = self.cost_to_target(s)
debug.info(3, "Init: cost=" + str(cost) + " " + str([s]))
heappush(self.q, (cost, self.counter, grid_path([vector3d(s)])))
self.counter+=1
self.counter += 1
def route(self, detour_scale):
"""
@ -72,11 +64,7 @@ class signal_grid(grid):
# Check if something in the queue is already a source and a target!
for s in self.source:
if self.is_target(s):
return((grid_path([vector3d(s)]),0))
# Make sure the queue is empty if we run another route
while len(self.q)>0:
heappop(self.q)
return((grid_path([vector3d(s)]), 0))
# Put the source items into the queue
self.init_queue()

View File

@ -144,15 +144,13 @@ class supply_tree_router(router):
# Marks all pin components except index as target
self.add_pin_component_target(pin_name, dest_idx)
# Actually run the A* router
if self.run_router(detour_scale=detour_scale):
return
self.write_debug_gds("debug_route.gds", True)
def add_io_pin(self, instance, pin_name, new_name=""):
"""
Add a signle input or output pin up to metal 3.

View File

@ -254,43 +254,37 @@ class sram_1bank(sram_base):
# List of pin to new pin name
pins_to_route = []
for port in self.all_ports:
# Depending on the port, use the bottom/top or left/right sides
# Port 0 is left/bottom
# Port 1 is right/top
bottom_or_top = "bottom" if port==0 else "top"
left_or_right = "left" if port==0 else "right"
# Connect the control pins as inputs
for signal in self.control_logic_inputs[port]:
if signal.startswith("rbl"):
continue
if signal=="clk":
pins_to_route.append(("{0}{1}".format(signal, port), bottom_or_top))
pins_to_route.append("{0}{1}".format(signal, port))
else:
pins_to_route.append(("{0}{1}".format(signal, port), left_or_right))
pins_to_route.append("{0}{1}".format(signal, port))
if port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
pins_to_route.append(("din{0}[{1}]".format(port, bit), bottom_or_top))
pins_to_route.append("din{0}[{1}]".format(port, bit))
if port in self.readwrite_ports or port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
pins_to_route.append(("dout{0}[{1}]".format(port, bit), bottom_or_top))
pins_to_route.append("dout{0}[{1}]".format(port, bit))
for bit in range(self.col_addr_size):
pins_to_route.append(("addr{0}[{1}]".format(port, bit), bottom_or_top))
pins_to_route.append("addr{0}[{1}]".format(port, bit))
for bit in range(self.row_addr_size):
pins_to_route.append(("addr{0}[{1}]".format(port, bit + self.col_addr_size), left_or_right))
pins_to_route.append("addr{0}[{1}]".format(port, bit + self.col_addr_size))
if port in self.write_ports:
if self.write_size:
for bit in range(self.num_wmasks):
pins_to_route.append(("wmask{0}[{1}]".format(port, bit), bottom_or_top))
pins_to_route.append("wmask{0}[{1}]".format(port, bit))
if port in self.write_ports:
for bit in range(self.num_spare_cols):
pins_to_route.append(("spare_wen{0}[{1}]".format(port, bit), bottom_or_top))
pins_to_route.append("spare_wen{0}[{1}]".format(port, bit))
rtr=router(self.m3_stack, self)
rtr.escape_route(pins_to_route)

View File

@ -88,9 +88,12 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
f.write("drc off\n")
f.write("gds polygon subcell true\n")
f.write("gds warning default\n")
f.write("gds flatten true\n")
f.write("gds readonly true\n")
# These two options are temporarily disabled until Tim fixes a bug in magic related
# to flattening channel routes and vias (hierarchy with no devices in it). Otherwise,
# they appear to be disconnected.
f.write("#gds flatten true\n")
f.write("#gds ordering true\n")
f.write("gds readonly true\n")
f.write("gds read {}\n".format(gds_name))
f.write('puts "Finished reading gds {}"\n'.format(gds_name))
f.write("load {}\n".format(cell_name))