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: elif add_vias:
self.add_power_pin(name, pin.center(), start_layer=pin.layer) 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"): 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 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) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import numpy as np
import string
import debug import debug
from vector3d import vector3d from vector3d import vector3d
from grid_cell import grid_cell from grid_cell import grid_cell
class grid: class grid:
""" """
A two layer routing map. Each cell can be blocked in the vertical A two layer routing map. Each cell can be blocked in the vertical
@ -23,7 +22,6 @@ class grid:
NONPREFERRED_COST = 4 NONPREFERRED_COST = 4
PREFERRED_COST = 1 PREFERRED_COST = 1
def __init__(self, ll, ur, track_width): def __init__(self, ll, ur, track_width):
""" Initialize the map and define the costs. """ """ Initialize the map and define the costs. """
@ -33,12 +31,12 @@ class grid:
self.track_width = track_width self.track_width = track_width
self.track_widths = [self.track_width, self.track_width, 1.0] 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 # The bounds are in grids for this
# This is really lower left bottom layer and upper right top layer in 3D. # 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.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.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 # let's leave the map sparse, cells are created on demand to reduce memory
self.map={} self.map={}
@ -46,18 +44,18 @@ class grid:
def add_all_grids(self): def add_all_grids(self):
for x in range(self.ll.x, self.ur.x, 1): for x in range(self.ll.x, self.ur.x, 1):
for y in range(self.ll.y, self.ur.y, 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, 0))
self.add_map(vector3d(x,y,1)) 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): if not isinstance(n, vector3d):
for item in n: for item in n:
self.set_blocked(item,value) self.set_blocked(item, value)
else: else:
self.add_map(n) self.add_map(n)
self.map[n].blocked=value self.map[n].blocked=value
def is_blocked(self,n): def is_blocked(self, n):
if not isinstance(n, vector3d): if not isinstance(n, vector3d):
for item in n: for item in n:
if self.is_blocked(item): if self.is_blocked(item):
@ -68,11 +66,10 @@ class grid:
self.add_map(n) self.add_map(n)
return self.map[n].blocked return self.map[n].blocked
def set_path(self, n, value=True):
def set_path(self,n,value=True): if isinstance(n, (list, tuple, set, frozenset)):
if isinstance(n, (list,tuple,set,frozenset)):
for item in n: for item in n:
self.set_path(item,value) self.set_path(item, value)
else: else:
self.add_map(n) self.add_map(n)
self.map[n].path=value self.map[n].path=value
@ -81,47 +78,89 @@ class grid:
for k in self.map: for k in self.map:
self.map[k].blocked=False self.map[k].blocked=False
def set_source(self,n,value=True): def set_source(self, n, value=True):
if not isinstance(n, vector3d): if not isinstance(n, vector3d):
for item in n: for item in n:
self.set_source(item,value) self.set_source(item, value)
else: else:
self.add_map(n) self.add_map(n)
self.map[n].source=value self.map[n].source=value
self.source.add(n) self.source.add(n)
def set_target(self,n,value=True): def set_target(self, n, value=True):
if not isinstance(n, vector3d): if not isinstance(n, vector3d):
for item in n: for item in n:
self.set_target(item,value) self.set_target(item, value)
else: else:
self.add_map(n) self.add_map(n)
self.map[n].target=value self.map[n].target=value
self.target.add(n) self.target.add(n)
def add_source(self, track_list, value=True):
def add_source(self,track_list,value=True): debug.info(3, "Adding source list={0}".format(str(track_list)))
debug.info(3,"Adding source list={0}".format(str(track_list)))
for n in track_list: for n in track_list:
debug.info(4,"Adding source ={0}".format(str(n))) debug.info(4, "Adding source ={0}".format(str(n)))
self.set_source(n,value) self.set_source(n, value)
self.set_blocked(n,False) self.set_blocked(n, False)
def add_target(self, track_list, value=True):
def add_target(self,track_list,value=True): debug.info(3, "Adding target list={0}".format(str(track_list)))
debug.info(3,"Adding target list={0}".format(str(track_list)))
for n in track_list: for n in track_list:
debug.info(4,"Adding target ={0}".format(str(n))) debug.info(4, "Adding target ={0}".format(str(n)))
self.set_target(n,value) self.set_target(n, value)
self.set_blocked(n,False) 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. Point is in the target set, so we are done.
""" """
return point in self.target 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. Add a point to the map if it doesn't exist.
""" """
@ -132,8 +171,7 @@ class grid:
if n not in self.map: if n not in self.map:
self.map[n]=grid_cell() self.map[n]=grid_cell()
def block_path(self, path):
def block_path(self,path):
""" """
Mark the path in the routing grid as blocked. Mark the path in the routing grid as blocked.
Also unsets the path flag. 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. If so, reduce the pin group grid to not include the adjacent grid.
Try to do this intelligently to keep th pins enclosed. Try to do this intelligently to keep th pins enclosed.
""" """
debug.info(1, debug.info(2,
"Comparing {0} and {1} adjacency".format(pin_name1, "Comparing {0} and {1} adjacency".format(pin_name1,
pin_name2)) pin_name2))
removed_grids = 0 removed_grids = 0
@ -302,7 +302,7 @@ class router(router_tech):
adj_grids)) adj_grids))
self.remove_adjacent_grid(pg1, pg2, 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): def remove_adjacent_grid(self, pg1, pg2, adj_grids):
""" """
@ -348,6 +348,10 @@ class router(router_tech):
smaller)) smaller))
smaller.grids.remove(adj) smaller.grids.remove(adj)
def set_supply_rail_blocked(self, value):
# This is just a virtual function
pass
def prepare_blockages(self, pin_name): def prepare_blockages(self, pin_name):
""" """
Reset and add all of the blockages in the design. Reset and add all of the blockages in the design.
@ -363,7 +367,11 @@ class router(router_tech):
# Block all of the supply rails # Block all of the supply rails
# (some will be unblocked if they're a target) # (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 # Block all of the pin components
# (some will be unblocked if they're a source/target) # (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. 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]: for pg in self.pin_groups[pin_name]:
pg.convert_pin() pg.convert_pin()
@ -809,7 +817,7 @@ class router(router_tech):
that are blocked by other shapes. that are blocked by other shapes.
""" """
for pin_name in self.pin_groups: 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]: for pg in self.pin_groups[pin_name]:
pg.enclose_pin() pg.enclose_pin()
pg.add_enclosure(self.cell) pg.add_enclosure(self.cell)
@ -830,6 +838,12 @@ class router(router_tech):
for i in range(self.num_pin_components(pin_name)): for i in range(self.num_pin_components(pin_name)):
self.add_pin_component_target(pin_name, i) 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): def num_pin_components(self, pin_name):
""" """
This returns how many disconnected pin components there are. This returns how many disconnected pin components there are.
@ -902,7 +916,7 @@ class router(router_tech):
# self.write_debug_gds() # self.write_debug_gds()
# First, simplify the path for # First, simplify the path for
# debug.info(1, str(self.path)) # debug.info(3, str(self.path))
contracted_path = self.contract_path(path) contracted_path = self.contract_path(path)
debug.info(3, "Contracted path: " + str(contracted_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. We will use an A* search, so this cost must be pessimistic.
Cost so far will be the length of the path. 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 # Counter is used to not require data comparison in Python 3.x
# Items will be returned in order they are added during cost ties # Items will be returned in order they are added during cost ties
self.counter = 0 self.counter = 0

View File

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

View File

@ -85,37 +85,6 @@ class supply_tree_router(router):
return True 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): def route_pins(self, pin_name):
""" """
This will route each of the remaining pin components to the other pins. 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,10 +249,8 @@ class sram_1bank(sram_base):
""" """
Add the top-level pins for a single bank SRAM with control. Add the top-level pins for a single bank SRAM with control.
""" """
highest_coord = self.find_highest_coords() # List of pin to new pin name
lowest_coord = self.find_lowest_coords() all_pins = []
bbox = [lowest_coord, highest_coord]
for port in self.all_ports: for port in self.all_ports:
# Depending on the port, use the bottom/top or left/right sides # Depending on the port, use the bottom/top or left/right sides
# Port 0 is left/bottom # Port 0 is left/bottom
@ -262,125 +260,62 @@ class sram_1bank(sram_base):
# Connect the control pins as inputs # Connect the control pins as inputs
for signal in self.control_logic_inputs[port]: for signal in self.control_logic_inputs[port]:
if signal == "clk":
continue
if signal.startswith("rbl"): if signal.startswith("rbl"):
continue continue
if OPTS.perimeter_pins: self.add_io_pin(self.control_logic_insts[port],
self.add_perimeter_pin(name=signal + "{}".format(port), signal,
pin=self.control_logic_insts[port].get_pin(signal), signal + "{}".format(port))
side=left_or_right, if signal=="clk":
bbox=bbox) all_pins.append(("{0}{1}".format(signal, port), bottom_or_top))
else: else:
self.copy_layout_pin(self.control_logic_insts[port], all_pins.append(("{0}{1}".format(signal, port), left_or_right))
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 = []
if port in self.write_ports: if port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols): for bit in range(self.word_size + self.num_spare_cols):
if OPTS.perimeter_pins: self.add_io_pin(self.data_dff_insts[port],
p = self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit), "din_{}".format(bit),
pin=self.data_dff_insts[port].get_pin("din_{0}".format(bit)), "din{0}[{1}]".format(port, bit))
side=bottom_or_top, all_pins.append(("din{0}[{1}]".format(port, bit), 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))
# Data output pins go to BOTTOM/TOP if port in self.readwrite_ports or port in self.read_ports:
if port in self.readwrite_ports:
for bit in range(self.word_size + self.num_spare_cols): for bit in range(self.word_size + self.num_spare_cols):
if OPTS.perimeter_pins: self.add_io_pin(self.bank_inst,
# This should be routed next to the din pin "dout{0}_{1}".format(port, bit),
p = din_ports[bit] "dout{0}[{1}]".format(port, bit))
self.add_layout_pin_rect_center(text="dout{0}[{1}]".format(port, bit), all_pins.append(("dout{0}[{1}]".format(port, bit), bottom_or_top))
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))
# Lower address bits go to BOTTOM/TOP
for bit in range(self.col_addr_size): for bit in range(self.col_addr_size):
if OPTS.perimeter_pins: self.add_io_pin(self.col_addr_dff_insts[port],
self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit), "din_{}".format(bit),
pin=self.col_addr_dff_insts[port].get_pin("din_{}".format(bit)), "addr{0}[{1}]".format(port, bit))
side=bottom_or_top,
bbox=bbox) all_pins.append(("addr{0}[{1}]".format(port, bit), bottom_or_top))
else:
self.copy_layout_pin(self.col_addr_dff_insts[port],
"din_{}".format(bit),
"addr{0}[{1}]".format(port, bit))
# Upper address bits go to LEFT/RIGHT
for bit in range(self.row_addr_size): for bit in range(self.row_addr_size):
if OPTS.perimeter_pins: self.add_io_pin(self.row_addr_dff_insts[port],
self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit + self.col_addr_size), "din_{}".format(bit),
pin=self.row_addr_dff_insts[port].get_pin("din_{}".format(bit)), "addr{0}[{1}]".format(port, bit + self.col_addr_size))
side=left_or_right, all_pins.append(("addr{0}[{1}]".format(port, bit + self.col_addr_size), 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))
# Write mask pins go to BOTTOM/TOP
if port in self.write_ports: if port in self.write_ports:
if self.write_size: if self.write_size:
for bit in range(self.num_wmasks): for bit in range(self.num_wmasks):
if OPTS.perimeter_pins: self.add_io_pin(self.wmask_dff_insts[port],
self.add_perimeter_pin(name="wmask{0}[{1}]".format(port, bit), "din_{}".format(bit),
pin=self.wmask_dff_insts[port].get_pin("din_{}".format(bit)), "wmask{0}[{1}]".format(port, bit))
side=bottom_or_top, all_pins.append(("wmask{0}[{1}]".format(port, bit), bottom_or_top))
bbox=bbox)
else:
self.copy_layout_pin(self.wmask_dff_insts[port],
"din_{}".format(bit),
"wmask{0}[{1}]".format(port, bit))
# Spare wen pins go to BOTTOM/TOP
if port in self.write_ports: if port in self.write_ports:
for bit in range(self.num_spare_cols): for bit in range(self.num_spare_cols):
if OPTS.perimeter_pins: self.add_io_pin(self.spare_wen_dff_insts[port],
self.add_perimeter_pin(name="spare_wen{0}[{1}]".format(port, bit), "din_{}".format(bit),
pin=self.spare_wen_dff_insts[port].get_pin("din_{}".format(bit)), "spare_wen{0}[{1}]".format(port, bit))
side=left_or_right, all_pins.append(("spare_wen{0}[{1}]".format(port, bit), bottom_or_top))
bbox=bbox)
else: if OPTS.perimeter_pins:
self.copy_layout_pin(self.spare_wen_dff_insts[port], from signal_exit_router import signal_exit_router as router
"din_{}".format(bit), rtr=router(self.m3_stack, self)
"spare_wen{0}[{1}]".format(port, bit)) rtr.route(all_pins)
def route_layout(self): def route_layout(self):
""" Route a single bank SRAM """ """ Route a single bank SRAM """

View File

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