Updates to IO signal router.

Route signals to perimeter using maze router.
Move IO pins without perimeter pins to M3 using add_io_pin (like add_power_pin).
This commit is contained in:
mrg 2020-12-22 09:39:58 -08:00
parent 348001b1c8
commit ae1c889235
8 changed files with 160 additions and 182 deletions

View File

@ -1197,6 +1197,18 @@ class layout():
elif add_vias:
self.add_power_pin(name, pin.center(), start_layer=pin.layer)
def add_io_pin(self, instance, pin_name, new_name=""):
"""
Add a signle input or output pin up to metal 3.
"""
pin = instance.get_pin(pin_name)
if new_name == "":
new_name = pin_name
# Just use the power pin function for now to save code
self.add_power_pin(name=new_name, loc=pin.center(), start_layer=pin.layer)
def add_power_pin(self, name, loc, size=[1, 1], directions=None, start_layer="m1"):
"""
Add a single power pin from the lowest power_grid layer down to M1 (or li) at

View File

@ -5,12 +5,11 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import numpy as np
import string
import debug
from vector3d import vector3d
from grid_cell import grid_cell
class grid:
"""
A two layer routing map. Each cell can be blocked in the vertical
@ -23,7 +22,6 @@ class grid:
NONPREFERRED_COST = 4
PREFERRED_COST = 1
def __init__(self, ll, ur, track_width):
""" Initialize the map and define the costs. """
@ -33,12 +31,12 @@ class grid:
self.track_width = track_width
self.track_widths = [self.track_width, self.track_width, 1.0]
self.track_factor = [1/self.track_width, 1/self.track_width, 1.0]
self.track_factor = [1 / self.track_width, 1 / self.track_width, 1.0]
# The bounds are in grids for this
# This is really lower left bottom layer and upper right top layer in 3D.
self.ll = vector3d(ll.x,ll.y,0).scale(self.track_factor).round()
self.ur = vector3d(ur.x,ur.y,1).scale(self.track_factor).round()
self.ll = vector3d(ll.x, ll.y, 0).scale(self.track_factor).round()
self.ur = vector3d(ur.x, ur.y, 1).scale(self.track_factor).round()
# let's leave the map sparse, cells are created on demand to reduce memory
self.map={}
@ -46,18 +44,18 @@ class grid:
def add_all_grids(self):
for x in range(self.ll.x, self.ur.x, 1):
for y in range(self.ll.y, self.ur.y, 1):
self.add_map(vector3d(x,y,0))
self.add_map(vector3d(x,y,1))
self.add_map(vector3d(x, y, 0))
self.add_map(vector3d(x, y, 1))
def set_blocked(self,n,value=True):
def set_blocked(self, n, value=True):
if not isinstance(n, vector3d):
for item in n:
self.set_blocked(item,value)
self.set_blocked(item, value)
else:
self.add_map(n)
self.map[n].blocked=value
def is_blocked(self,n):
def is_blocked(self, n):
if not isinstance(n, vector3d):
for item in n:
if self.is_blocked(item):
@ -68,11 +66,10 @@ class grid:
self.add_map(n)
return self.map[n].blocked
def set_path(self,n,value=True):
if isinstance(n, (list,tuple,set,frozenset)):
def set_path(self, n, value=True):
if isinstance(n, (list, tuple, set, frozenset)):
for item in n:
self.set_path(item,value)
self.set_path(item, value)
else:
self.add_map(n)
self.map[n].path=value
@ -81,47 +78,89 @@ class grid:
for k in self.map:
self.map[k].blocked=False
def set_source(self,n,value=True):
def set_source(self, n, value=True):
if not isinstance(n, vector3d):
for item in n:
self.set_source(item,value)
self.set_source(item, value)
else:
self.add_map(n)
self.map[n].source=value
self.source.add(n)
def set_target(self,n,value=True):
def set_target(self, n, value=True):
if not isinstance(n, vector3d):
for item in n:
self.set_target(item,value)
self.set_target(item, value)
else:
self.add_map(n)
self.map[n].target=value
self.target.add(n)
def add_source(self,track_list,value=True):
debug.info(3,"Adding source list={0}".format(str(track_list)))
def add_source(self, track_list, value=True):
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)
debug.info(4, "Adding source ={0}".format(str(n)))
self.set_source(n, value)
self.set_blocked(n, False)
def add_target(self,track_list,value=True):
debug.info(3,"Adding target list={0}".format(str(track_list)))
def add_target(self, track_list, value=True):
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)
debug.info(4, "Adding target ={0}".format(str(n)))
self.set_target(n, value)
self.set_blocked(n, False)
def is_target(self,point):
def add_perimeter_target(self, side="all", value=True):
debug.info(3, "Adding perimeter target")
# 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)
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)
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)
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)
def is_target(self, point):
"""
Point is in the target set, so we are done.
"""
return point in self.target
def add_map(self,n):
def add_map(self, n):
"""
Add a point to the map if it doesn't exist.
"""
@ -132,8 +171,7 @@ class grid:
if n not in self.map:
self.map[n]=grid_cell()
def block_path(self,path):
def block_path(self, path):
"""
Mark the path in the routing grid as blocked.
Also unsets the path flag.

View File

@ -286,7 +286,7 @@ class router(router_tech):
If so, reduce the pin group grid to not include the adjacent grid.
Try to do this intelligently to keep th pins enclosed.
"""
debug.info(1,
debug.info(2,
"Comparing {0} and {1} adjacency".format(pin_name1,
pin_name2))
removed_grids = 0
@ -302,7 +302,7 @@ class router(router_tech):
adj_grids))
self.remove_adjacent_grid(pg1, pg2, adj_grids)
debug.info(1, "Removed {} adjacent grids.".format(removed_grids))
debug.info(2, "Removed {} adjacent grids.".format(removed_grids))
def remove_adjacent_grid(self, pg1, pg2, adj_grids):
"""
@ -348,6 +348,10 @@ class router(router_tech):
smaller))
smaller.grids.remove(adj)
def set_supply_rail_blocked(self, value):
# This is just a virtual function
pass
def prepare_blockages(self, pin_name):
"""
Reset and add all of the blockages in the design.
@ -363,7 +367,11 @@ class router(router_tech):
# Block all of the supply rails
# (some will be unblocked if they're a target)
self.set_supply_rail_blocked(True)
try:
self.set_supply_rail_blocked(True)
except AttributeError:
# If function doesn't exist, it isn't a supply router
pass
# Block all of the pin components
# (some will be unblocked if they're a source/target)
@ -798,7 +806,7 @@ class router(router_tech):
"""
Convert the pin groups into pin tracks and blockage tracks.
"""
debug.info(1, "Converting pins for {}.".format(pin_name))
debug.info(2, "Converting pins for {}.".format(pin_name))
for pg in self.pin_groups[pin_name]:
pg.convert_pin()
@ -809,7 +817,7 @@ class router(router_tech):
that are blocked by other shapes.
"""
for pin_name in self.pin_groups:
debug.info(1, "Enclosing pins for {}".format(pin_name))
debug.info(2, "Enclosing pins for {}".format(pin_name))
for pg in self.pin_groups[pin_name]:
pg.enclose_pin()
pg.add_enclosure(self.cell)
@ -830,6 +838,12 @@ class router(router_tech):
for i in range(self.num_pin_components(pin_name)):
self.add_pin_component_target(pin_name, i)
def add_perimeter_target(self, side="all"):
"""
This will mark all the cells on the perimeter of the original layout as a target.
"""
self.rg.add_perimeter_target(side=side)
def num_pin_components(self, pin_name):
"""
This returns how many disconnected pin components there are.
@ -902,7 +916,7 @@ class router(router_tech):
# self.write_debug_gds()
# First, simplify the path for
# debug.info(1, str(self.path))
# debug.info(3, str(self.path))
contracted_path = self.contract_path(path)
debug.info(3, "Contracted path: " + str(contracted_path))

View File

@ -49,8 +49,6 @@ class signal_grid(grid):
We will use an A* search, so this cost must be pessimistic.
Cost so far will be the length of the path.
"""
#debug.info(3, "Initializing queue.")
# 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.counter = 0

View File

@ -74,6 +74,7 @@ class supply_grid_router(router):
start_time = datetime.now()
# Block everything
self.prepare_blockages(self.gnd_name)
# Determine the rail locations
self.route_supply_rails(self.gnd_name, 0)

View File

@ -85,37 +85,6 @@ class supply_tree_router(router):
return True
def prepare_blockages(self, pin_name):
"""
Reset and add all of the blockages in the design.
Names is a list of pins to add as a blockage.
"""
debug.info(3,"Preparing blockages.")
# 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 pin components (some will be unblocked if they're a source/target)
# Also block the previous routes
for name in self.pin_groups:
blockage_grids = {y for x in self.pin_groups[name] for y in x.grids}
self.set_blockages(blockage_grids,True)
blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages}
self.set_blockages(blockage_grids,True)
# FIXME: These duplicate a bit of work
# These are the paths that have already been routed.
self.set_blockages(self.path_blockages)
# 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)
def route_pins(self, pin_name):
"""
This will route each of the remaining pin components to the other pins.
@ -187,3 +156,14 @@ class supply_tree_router(router):
def add_io_pin(self, instance, pin_name, new_name=""):
"""
Add a signle input or output pin up to metal 3.
"""
pin = instance.get_pins(pin_name)
if new_name == "":
new_name = pin_name
# Just use the power pin function for now to save code
self.add_power_pin(name=new_name, loc=pin.center(), start_layer=pin.layer)

View File

@ -249,138 +249,73 @@ class sram_1bank(sram_base):
"""
Add the top-level pins for a single bank SRAM with control.
"""
highest_coord = self.find_highest_coords()
lowest_coord = self.find_lowest_coords()
bbox = [lowest_coord, highest_coord]
# List of pin to new pin name
all_pins = []
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 == "clk":
continue
if signal.startswith("rbl"):
continue
if OPTS.perimeter_pins:
self.add_perimeter_pin(name=signal + "{}".format(port),
pin=self.control_logic_insts[port].get_pin(signal),
side=left_or_right,
bbox=bbox)
self.add_io_pin(self.control_logic_insts[port],
signal,
signal + "{}".format(port))
if signal=="clk":
all_pins.append(("{0}{1}".format(signal, port), bottom_or_top))
else:
self.copy_layout_pin(self.control_logic_insts[port],
signal,
signal + "{}".format(port))
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="clk{}".format(port),
pin=self.control_logic_insts[port].get_pin("clk"),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.control_logic_insts[port],
"clk",
"clk{}".format(port))
# Data input pins go to BOTTOM/TOP
din_ports = []
all_pins.append(("{0}{1}".format(signal, port), left_or_right))
if port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
if OPTS.perimeter_pins:
p = self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit),
pin=self.data_dff_insts[port].get_pin("din_{0}".format(bit)),
side=bottom_or_top,
bbox=bbox)
din_ports.append(p)
else:
self.copy_layout_pin(self.data_dff_insts[port],
"din_{}".format(bit),
"din{0}[{1}]".format(port, bit))
self.add_io_pin(self.data_dff_insts[port],
"din_{}".format(bit),
"din{0}[{1}]".format(port, bit))
all_pins.append(("din{0}[{1}]".format(port, bit), bottom_or_top))
# Data output pins go to BOTTOM/TOP
if port in self.readwrite_ports:
if port in self.readwrite_ports or port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
if OPTS.perimeter_pins:
# This should be routed next to the din pin
p = din_ports[bit]
self.add_layout_pin_rect_center(text="dout{0}[{1}]".format(port, bit),
layer=p.layer,
offset=p.center() + vector(self.m3_pitch, 0),
width=p.width(),
height=p.height())
else:
self.copy_layout_pin(self.bank_inst,
"dout{0}_{1}".format(port, bit),
"dout{0}[{1}]".format(port, bit))
elif port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
if OPTS.perimeter_pins:
# This should have a clear route to the perimeter if there are no din routes
self.add_perimeter_pin(name="dout{0}[{1}]".format(port, bit),
pin=self.bank_inst.get_pin("dout{0}_{1}".format(port, bit)),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.bank_inst,
"dout{0}_{1}".format(port, bit),
"dout{0}[{1}]".format(port, bit))
self.add_io_pin(self.bank_inst,
"dout{0}_{1}".format(port, bit),
"dout{0}[{1}]".format(port, bit))
all_pins.append(("dout{0}[{1}]".format(port, bit), bottom_or_top))
# Lower address bits go to BOTTOM/TOP
for bit in range(self.col_addr_size):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit),
pin=self.col_addr_dff_insts[port].get_pin("din_{}".format(bit)),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.col_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit))
self.add_io_pin(self.col_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit))
all_pins.append(("addr{0}[{1}]".format(port, bit), bottom_or_top))
# Upper address bits go to LEFT/RIGHT
for bit in range(self.row_addr_size):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit + self.col_addr_size),
pin=self.row_addr_dff_insts[port].get_pin("din_{}".format(bit)),
side=left_or_right,
bbox=bbox)
else:
self.copy_layout_pin(self.row_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit + self.col_addr_size))
self.add_io_pin(self.row_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit + self.col_addr_size))
all_pins.append(("addr{0}[{1}]".format(port, bit + self.col_addr_size), left_or_right))
# Write mask pins go to BOTTOM/TOP
if port in self.write_ports:
if self.write_size:
for bit in range(self.num_wmasks):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="wmask{0}[{1}]".format(port, bit),
pin=self.wmask_dff_insts[port].get_pin("din_{}".format(bit)),
side=bottom_or_top,
bbox=bbox)
else:
self.copy_layout_pin(self.wmask_dff_insts[port],
"din_{}".format(bit),
"wmask{0}[{1}]".format(port, bit))
self.add_io_pin(self.wmask_dff_insts[port],
"din_{}".format(bit),
"wmask{0}[{1}]".format(port, bit))
all_pins.append(("wmask{0}[{1}]".format(port, bit), bottom_or_top))
# Spare wen pins go to BOTTOM/TOP
if port in self.write_ports:
for bit in range(self.num_spare_cols):
if OPTS.perimeter_pins:
self.add_perimeter_pin(name="spare_wen{0}[{1}]".format(port, bit),
pin=self.spare_wen_dff_insts[port].get_pin("din_{}".format(bit)),
side=left_or_right,
bbox=bbox)
else:
self.copy_layout_pin(self.spare_wen_dff_insts[port],
"din_{}".format(bit),
"spare_wen{0}[{1}]".format(port, bit))
self.add_io_pin(self.spare_wen_dff_insts[port],
"din_{}".format(bit),
"spare_wen{0}[{1}]".format(port, bit))
all_pins.append(("spare_wen{0}[{1}]".format(port, bit), bottom_or_top))
if OPTS.perimeter_pins:
from signal_exit_router import signal_exit_router as router
rtr=router(self.m3_stack, self)
rtr.route(all_pins)
def route_layout(self):
""" Route a single bank SRAM """

View File

@ -11,9 +11,9 @@ num_words = 16
tech_name = OPTS.tech_name
# perimeter_pins = True
perimeter_pins = True
nominal_corner_only = True
route_supplies = "grid"
route_supplies = "tree"
check_lvsdrc = True