mirror of https://github.com/VLSIDA/OpenRAM.git
Refactor channel route to be a design.
This commit is contained in:
parent
7220b23402
commit
f11afaa63d
|
|
@ -0,0 +1,306 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
||||||
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||||
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
import collections
|
||||||
|
import debug
|
||||||
|
from tech import drc
|
||||||
|
from vector import vector
|
||||||
|
import design
|
||||||
|
|
||||||
|
|
||||||
|
class channel_route(design.design):
|
||||||
|
|
||||||
|
unique_id = 0
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
netlist,
|
||||||
|
offset,
|
||||||
|
layer_stack,
|
||||||
|
directions=None,
|
||||||
|
vertical=False,
|
||||||
|
add_routes=True):
|
||||||
|
"""
|
||||||
|
The net list is a list of the nets with each net being a list of pins
|
||||||
|
to be connected. The offset is the lower-left of where the
|
||||||
|
routing channel will start. This does NOT try to minimize the
|
||||||
|
number of tracks -- instead, it picks an order to avoid the
|
||||||
|
vertical conflicts between pins. The track size must be the number of
|
||||||
|
nets times the *nonpreferred* routing of the non-track layer pitch.
|
||||||
|
|
||||||
|
"""
|
||||||
|
name = "cr_{0}".format(channel_route.unique_id)
|
||||||
|
channel_route.unique_id += 1
|
||||||
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
self.netlist = netlist
|
||||||
|
self.offset = offset
|
||||||
|
self.layer_stack = layer_stack
|
||||||
|
self.directions = directions
|
||||||
|
self.vertical = vertical
|
||||||
|
self.add_routes = add_routes
|
||||||
|
|
||||||
|
if not directions or directions == "pref":
|
||||||
|
# Use the preferred layer directions
|
||||||
|
if self.get_preferred_direction(layer_stack[0]) == "V":
|
||||||
|
self.vertical_layer = layer_stack[0]
|
||||||
|
self.horizontal_layer = layer_stack[2]
|
||||||
|
else:
|
||||||
|
self.vertical_layer = layer_stack[2]
|
||||||
|
self.horizontal_layer = layer_stack[0]
|
||||||
|
elif directions == "nonpref":
|
||||||
|
# Use the preferred layer directions
|
||||||
|
if self.get_preferred_direction(layer_stack[0]) == "V":
|
||||||
|
self.vertical_layer = layer_stack[2]
|
||||||
|
self.horizontal_layer = layer_stack[0]
|
||||||
|
else:
|
||||||
|
self.vertical_layer = layer_stack[0]
|
||||||
|
self.horizontal_layer = layer_stack[2]
|
||||||
|
else:
|
||||||
|
# Use the layer directions specified to the router rather than
|
||||||
|
# the preferred directions
|
||||||
|
debug.check(directions[0] != directions[1], "Must have unique layer directions.")
|
||||||
|
if directions[0] == "V":
|
||||||
|
self.vertical_layer = layer_stack[0]
|
||||||
|
self.horizontal_layer = layer_stack[2]
|
||||||
|
else:
|
||||||
|
self.horizontal_layer = layer_stack[0]
|
||||||
|
self.vertical_layer = layer_stack[2]
|
||||||
|
|
||||||
|
layer_stuff = self.get_layer_pitch(self.vertical_layer)
|
||||||
|
(self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
|
||||||
|
|
||||||
|
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
|
||||||
|
(self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
|
||||||
|
|
||||||
|
self.route()
|
||||||
|
|
||||||
|
def remove_net_from_graph(self, pin, g):
|
||||||
|
"""
|
||||||
|
Remove the pin from the graph and all conflicts
|
||||||
|
"""
|
||||||
|
g.pop(pin, None)
|
||||||
|
|
||||||
|
# Remove the pin from all conflicts
|
||||||
|
# FIXME: This is O(n^2), so maybe optimize it.
|
||||||
|
for other_pin, conflicts in g.items():
|
||||||
|
if pin in conflicts:
|
||||||
|
conflicts.remove(pin)
|
||||||
|
g[other_pin]=conflicts
|
||||||
|
return g
|
||||||
|
|
||||||
|
def vcg_nets_overlap(self, net1, net2):
|
||||||
|
"""
|
||||||
|
Check all the pin pairs on two nets and return a pin
|
||||||
|
overlap if any pin overlaps.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.vertical:
|
||||||
|
pitch = self.horizontal_nonpref_pitch
|
||||||
|
else:
|
||||||
|
pitch = self.vertical_nonpref_pitch
|
||||||
|
|
||||||
|
for pin1 in net1:
|
||||||
|
for pin2 in net2:
|
||||||
|
if self.vcg_pin_overlap(pin1, pin2, pitch):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def route(self):
|
||||||
|
# FIXME: Must extend this to a horizontal conflict graph
|
||||||
|
# too if we want to minimize the
|
||||||
|
# number of tracks!
|
||||||
|
# hcg = {}
|
||||||
|
|
||||||
|
# Initialize the vertical conflict graph (vcg)
|
||||||
|
# and make a list of all pins
|
||||||
|
vcg = collections.OrderedDict()
|
||||||
|
|
||||||
|
# Create names for the nets for the graphs
|
||||||
|
nets = collections.OrderedDict()
|
||||||
|
index = 0
|
||||||
|
# print(netlist)
|
||||||
|
for pin_list in self.netlist:
|
||||||
|
net_name = "n{}".format(index)
|
||||||
|
index += 1
|
||||||
|
nets[net_name] = pin_list
|
||||||
|
|
||||||
|
# print("Nets:")
|
||||||
|
# for net_name in nets:
|
||||||
|
# print(net_name, [x.name for x in nets[net_name]])
|
||||||
|
|
||||||
|
# Find the vertical pin conflicts
|
||||||
|
# FIXME: O(n^2) but who cares for now
|
||||||
|
for net_name1 in nets:
|
||||||
|
if net_name1 not in vcg.keys():
|
||||||
|
vcg[net_name1] = []
|
||||||
|
for net_name2 in nets:
|
||||||
|
if net_name2 not in vcg.keys():
|
||||||
|
vcg[net_name2] = []
|
||||||
|
# Skip yourself
|
||||||
|
if net_name1 == net_name2:
|
||||||
|
continue
|
||||||
|
if self.vcg_nets_overlap(nets[net_name1],
|
||||||
|
nets[net_name2]):
|
||||||
|
vcg[net_name2].append(net_name1)
|
||||||
|
|
||||||
|
current_offset = self.offset
|
||||||
|
|
||||||
|
# list of routes to do
|
||||||
|
while vcg:
|
||||||
|
# from pprint import pformat
|
||||||
|
# print("VCG:\n", pformat(vcg))
|
||||||
|
# get a route from conflict graph with empty fanout set
|
||||||
|
net_name = None
|
||||||
|
for net_name, conflicts in vcg.items():
|
||||||
|
if len(conflicts) == 0:
|
||||||
|
vcg = self.remove_net_from_graph(net_name, vcg)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# FIXME: We don't support cyclic VCGs right now.
|
||||||
|
debug.error("Cyclic VCG in channel router.", -1)
|
||||||
|
|
||||||
|
# These are the pins we'll have to connect
|
||||||
|
pin_list = nets[net_name]
|
||||||
|
# print("Routing:", net_name, [x.name for x in pin_list])
|
||||||
|
|
||||||
|
# Remove the net from other constriants in the VCG
|
||||||
|
vcg = self.remove_net_from_graph(net_name, vcg)
|
||||||
|
|
||||||
|
# Add the trunk routes from the bottom up for
|
||||||
|
# horizontal or the left to right for vertical
|
||||||
|
if self.vertical:
|
||||||
|
if self.add_routes:
|
||||||
|
self.add_vertical_trunk_route(pin_list,
|
||||||
|
current_offset,
|
||||||
|
self.vertical_nonpref_pitch)
|
||||||
|
# This accounts for the via-to-via spacings
|
||||||
|
current_offset += vector(self.horizontal_nonpref_pitch, 0)
|
||||||
|
else:
|
||||||
|
if self.add_routes:
|
||||||
|
self.add_horizontal_trunk_route(pin_list,
|
||||||
|
current_offset,
|
||||||
|
self.horizontal_nonpref_pitch)
|
||||||
|
# This accounts for the via-to-via spacings
|
||||||
|
current_offset += vector(0, self.vertical_nonpref_pitch)
|
||||||
|
|
||||||
|
# Return the size of the channel
|
||||||
|
if self.vertical:
|
||||||
|
self.width = 0
|
||||||
|
self.height = current_offset.y
|
||||||
|
return current_offset.y + self.vertical_nonpref_pitch - self.offset.y
|
||||||
|
else:
|
||||||
|
self.width = current_offset.x
|
||||||
|
self.height = 0
|
||||||
|
return current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
|
||||||
|
|
||||||
|
def get_layer_pitch(self, layer):
|
||||||
|
""" Return the track pitch on a given layer """
|
||||||
|
try:
|
||||||
|
# FIXME: Using non-pref pitch here due to overlap bug in VCG constraints.
|
||||||
|
# It should just result in inefficient channel width but will work.
|
||||||
|
pitch = getattr(self, "{}_pitch".format(layer))
|
||||||
|
nonpref_pitch = getattr(self, "{}_nonpref_pitch".format(layer))
|
||||||
|
space = getattr(self, "{}_space".format(layer))
|
||||||
|
except AttributeError:
|
||||||
|
debug.error("Cannot find layer pitch.", -1)
|
||||||
|
return (nonpref_pitch, pitch, pitch - space, space)
|
||||||
|
|
||||||
|
def add_horizontal_trunk_route(self,
|
||||||
|
pins,
|
||||||
|
trunk_offset,
|
||||||
|
pitch):
|
||||||
|
"""
|
||||||
|
Create a trunk route for all pins with
|
||||||
|
the trunk located at the given y offset.
|
||||||
|
"""
|
||||||
|
max_x = max([pin.center().x for pin in pins])
|
||||||
|
min_x = min([pin.center().x for pin in pins])
|
||||||
|
|
||||||
|
# if we are less than a pitch, just create a non-preferred layer jog
|
||||||
|
if max_x - min_x <= pitch:
|
||||||
|
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
|
||||||
|
|
||||||
|
# Add the horizontal trunk on the vertical layer!
|
||||||
|
self.add_path(self.vertical_layer,
|
||||||
|
[vector(min_x - half_layer_width, trunk_offset.y),
|
||||||
|
vector(max_x + half_layer_width, trunk_offset.y)])
|
||||||
|
|
||||||
|
# Route each pin to the trunk
|
||||||
|
for pin in pins:
|
||||||
|
# No bend needed here
|
||||||
|
mid = vector(pin.center().x, trunk_offset.y)
|
||||||
|
self.add_path(self.vertical_layer, [pin.center(), mid])
|
||||||
|
else:
|
||||||
|
# Add the horizontal trunk
|
||||||
|
self.add_path(self.horizontal_layer,
|
||||||
|
[vector(min_x, trunk_offset.y),
|
||||||
|
vector(max_x, trunk_offset.y)])
|
||||||
|
|
||||||
|
# Route each pin to the trunk
|
||||||
|
for pin in pins:
|
||||||
|
mid = vector(pin.center().x, trunk_offset.y)
|
||||||
|
self.add_path(self.vertical_layer, [pin.center(), mid])
|
||||||
|
self.add_via_center(layers=self.layer_stack,
|
||||||
|
offset=mid,
|
||||||
|
directions=self.directions)
|
||||||
|
|
||||||
|
def add_vertical_trunk_route(self,
|
||||||
|
pins,
|
||||||
|
trunk_offset,
|
||||||
|
pitch):
|
||||||
|
"""
|
||||||
|
Create a trunk route for all pins with the
|
||||||
|
trunk located at the given x offset.
|
||||||
|
"""
|
||||||
|
max_y = max([pin.center().y for pin in pins])
|
||||||
|
min_y = min([pin.center().y for pin in pins])
|
||||||
|
|
||||||
|
# if we are less than a pitch, just create a non-preferred layer jog
|
||||||
|
if max_y - min_y <= pitch:
|
||||||
|
|
||||||
|
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
|
||||||
|
|
||||||
|
# Add the vertical trunk on the horizontal layer!
|
||||||
|
self.add_path(self.horizontal_layer,
|
||||||
|
[vector(trunk_offset.x, min_y - half_layer_width),
|
||||||
|
vector(trunk_offset.x, max_y + half_layer_width)])
|
||||||
|
|
||||||
|
# Route each pin to the trunk
|
||||||
|
for pin in pins:
|
||||||
|
# No bend needed here
|
||||||
|
mid = vector(trunk_offset.x, pin.center().y)
|
||||||
|
self.add_path(self.horizontal_layer, [pin.center(), mid])
|
||||||
|
else:
|
||||||
|
# Add the vertical trunk
|
||||||
|
self.add_path(self.vertical_layer,
|
||||||
|
[vector(trunk_offset.x, min_y),
|
||||||
|
vector(trunk_offset.x, max_y)])
|
||||||
|
|
||||||
|
# Route each pin to the trunk
|
||||||
|
for pin in pins:
|
||||||
|
mid = vector(trunk_offset.x, pin.center().y)
|
||||||
|
self.add_path(self.horizontal_layer, [pin.center(), mid])
|
||||||
|
self.add_via_center(layers=self.layer_stack,
|
||||||
|
offset=mid,
|
||||||
|
directions=self.directions)
|
||||||
|
|
||||||
|
def vcg_pin_overlap(self, pin1, pin2, pitch):
|
||||||
|
""" Check for vertical or horizontal overlap of the two pins """
|
||||||
|
|
||||||
|
# FIXME: If the pins are not in a row, this may break.
|
||||||
|
# However, a top pin shouldn't overlap another top pin,
|
||||||
|
# for example, so the extra comparison *shouldn't* matter.
|
||||||
|
|
||||||
|
# Pin 1 must be in the "BOTTOM" set
|
||||||
|
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
|
||||||
|
|
||||||
|
# Pin 1 must be in the "LEFT" set
|
||||||
|
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch
|
||||||
|
overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap)
|
||||||
|
return overlaps
|
||||||
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
# (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 collections
|
|
||||||
import geometry
|
import geometry
|
||||||
import gdsMill
|
import gdsMill
|
||||||
import debug
|
import debug
|
||||||
|
|
@ -1019,282 +1018,24 @@ class layout():
|
||||||
to_layer=dest_pin.layer,
|
to_layer=dest_pin.layer,
|
||||||
offset=out_pos)
|
offset=out_pos)
|
||||||
|
|
||||||
def get_layer_pitch(self, layer):
|
|
||||||
""" Return the track pitch on a given layer """
|
|
||||||
try:
|
|
||||||
# FIXME: Using non-pref pitch here due to overlap bug in VCG constraints.
|
|
||||||
# It should just result in inefficient channel width but will work.
|
|
||||||
pitch = getattr(self, "{}_pitch".format(layer))
|
|
||||||
nonpref_pitch = getattr(self, "{}_nonpref_pitch".format(layer))
|
|
||||||
space = getattr(self, "{}_space".format(layer))
|
|
||||||
except AttributeError:
|
|
||||||
debug.error("Cannot find layer pitch.", -1)
|
|
||||||
return (nonpref_pitch, pitch, pitch - space, space)
|
|
||||||
|
|
||||||
def add_horizontal_trunk_route(self,
|
|
||||||
pins,
|
|
||||||
trunk_offset,
|
|
||||||
layer_stack,
|
|
||||||
pitch):
|
|
||||||
"""
|
|
||||||
Create a trunk route for all pins with
|
|
||||||
the trunk located at the given y offset.
|
|
||||||
"""
|
|
||||||
max_x = max([pin.center().x for pin in pins])
|
|
||||||
min_x = min([pin.center().x for pin in pins])
|
|
||||||
|
|
||||||
# if we are less than a pitch, just create a non-preferred layer jog
|
|
||||||
if max_x - min_x <= pitch:
|
|
||||||
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
|
|
||||||
|
|
||||||
# Add the horizontal trunk on the vertical layer!
|
|
||||||
self.add_path(self.vertical_layer,
|
|
||||||
[vector(min_x - half_layer_width, trunk_offset.y),
|
|
||||||
vector(max_x + half_layer_width, trunk_offset.y)])
|
|
||||||
|
|
||||||
# Route each pin to the trunk
|
|
||||||
for pin in pins:
|
|
||||||
# No bend needed here
|
|
||||||
mid = vector(pin.center().x, trunk_offset.y)
|
|
||||||
self.add_path(self.vertical_layer, [pin.center(), mid])
|
|
||||||
else:
|
|
||||||
# Add the horizontal trunk
|
|
||||||
self.add_path(self.horizontal_layer,
|
|
||||||
[vector(min_x, trunk_offset.y),
|
|
||||||
vector(max_x, trunk_offset.y)])
|
|
||||||
|
|
||||||
# Route each pin to the trunk
|
|
||||||
for pin in pins:
|
|
||||||
mid = vector(pin.center().x, trunk_offset.y)
|
|
||||||
self.add_path(self.vertical_layer, [pin.center(), mid])
|
|
||||||
self.add_via_center(layers=layer_stack,
|
|
||||||
offset=mid,
|
|
||||||
directions=self.directions)
|
|
||||||
|
|
||||||
def add_vertical_trunk_route(self,
|
|
||||||
pins,
|
|
||||||
trunk_offset,
|
|
||||||
layer_stack,
|
|
||||||
pitch):
|
|
||||||
"""
|
|
||||||
Create a trunk route for all pins with the
|
|
||||||
trunk located at the given x offset.
|
|
||||||
"""
|
|
||||||
max_y = max([pin.center().y for pin in pins])
|
|
||||||
min_y = min([pin.center().y for pin in pins])
|
|
||||||
|
|
||||||
# if we are less than a pitch, just create a non-preferred layer jog
|
|
||||||
if max_y - min_y <= pitch:
|
|
||||||
|
|
||||||
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
|
|
||||||
|
|
||||||
# Add the vertical trunk on the horizontal layer!
|
|
||||||
self.add_path(self.horizontal_layer,
|
|
||||||
[vector(trunk_offset.x, min_y - half_layer_width),
|
|
||||||
vector(trunk_offset.x, max_y + half_layer_width)])
|
|
||||||
|
|
||||||
# Route each pin to the trunk
|
|
||||||
for pin in pins:
|
|
||||||
# No bend needed here
|
|
||||||
mid = vector(trunk_offset.x, pin.center().y)
|
|
||||||
self.add_path(self.horizontal_layer, [pin.center(), mid])
|
|
||||||
else:
|
|
||||||
# Add the vertical trunk
|
|
||||||
self.add_path(self.vertical_layer,
|
|
||||||
[vector(trunk_offset.x, min_y),
|
|
||||||
vector(trunk_offset.x, max_y)])
|
|
||||||
|
|
||||||
# Route each pin to the trunk
|
|
||||||
for pin in pins:
|
|
||||||
mid = vector(trunk_offset.x, pin.center().y)
|
|
||||||
self.add_path(self.horizontal_layer, [pin.center(), mid])
|
|
||||||
self.add_via_center(layers=layer_stack,
|
|
||||||
offset=mid,
|
|
||||||
directions=self.directions)
|
|
||||||
|
|
||||||
def create_channel_route(self, netlist,
|
|
||||||
offset,
|
|
||||||
layer_stack,
|
|
||||||
directions=None,
|
|
||||||
vertical=False):
|
|
||||||
"""
|
|
||||||
The net list is a list of the nets with each net being a list of pins
|
|
||||||
to be connected. The offset is the lower-left of where the
|
|
||||||
routing channel will start. This does NOT try to minimize the
|
|
||||||
number of tracks -- instead, it picks an order to avoid the
|
|
||||||
vertical conflicts between pins. The track size must be the number of
|
|
||||||
nets times the *nonpreferred* routing of the non-track layer pitch.
|
|
||||||
|
|
||||||
"""
|
|
||||||
def remove_net_from_graph(pin, g):
|
|
||||||
"""
|
|
||||||
Remove the pin from the graph and all conflicts
|
|
||||||
"""
|
|
||||||
g.pop(pin, None)
|
|
||||||
|
|
||||||
# Remove the pin from all conflicts
|
|
||||||
# FIXME: This is O(n^2), so maybe optimize it.
|
|
||||||
for other_pin, conflicts in g.items():
|
|
||||||
if pin in conflicts:
|
|
||||||
conflicts.remove(pin)
|
|
||||||
g[other_pin]=conflicts
|
|
||||||
return g
|
|
||||||
|
|
||||||
def vcg_nets_overlap(net1, net2, vertical):
|
|
||||||
"""
|
|
||||||
Check all the pin pairs on two nets and return a pin
|
|
||||||
overlap if any pin overlaps.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if vertical:
|
|
||||||
pitch = self.horizontal_nonpref_pitch
|
|
||||||
else:
|
|
||||||
pitch = self.vertical_nonpref_pitch
|
|
||||||
|
|
||||||
for pin1 in net1:
|
|
||||||
for pin2 in net2:
|
|
||||||
if vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
|
||||||
""" Check for vertical or horizontal overlap of the two pins """
|
|
||||||
|
|
||||||
# FIXME: If the pins are not in a row, this may break.
|
|
||||||
# However, a top pin shouldn't overlap another top pin,
|
|
||||||
# for example, so the extra comparison *shouldn't* matter.
|
|
||||||
|
|
||||||
# Pin 1 must be in the "BOTTOM" set
|
|
||||||
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
|
|
||||||
|
|
||||||
# Pin 1 must be in the "LEFT" set
|
|
||||||
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch
|
|
||||||
overlaps = (not vertical and x_overlap) or (vertical and y_overlap)
|
|
||||||
return overlaps
|
|
||||||
|
|
||||||
self.directions = directions
|
|
||||||
if not directions or directions == "pref":
|
|
||||||
# Use the preferred layer directions
|
|
||||||
if self.get_preferred_direction(layer_stack[0]) == "V":
|
|
||||||
self.vertical_layer = layer_stack[0]
|
|
||||||
self.horizontal_layer = layer_stack[2]
|
|
||||||
else:
|
|
||||||
self.vertical_layer = layer_stack[2]
|
|
||||||
self.horizontal_layer = layer_stack[0]
|
|
||||||
elif directions == "nonpref":
|
|
||||||
# Use the preferred layer directions
|
|
||||||
if self.get_preferred_direction(layer_stack[0]) == "V":
|
|
||||||
self.vertical_layer = layer_stack[2]
|
|
||||||
self.horizontal_layer = layer_stack[0]
|
|
||||||
else:
|
|
||||||
self.vertical_layer = layer_stack[0]
|
|
||||||
self.horizontal_layer = layer_stack[2]
|
|
||||||
else:
|
|
||||||
# Use the layer directions specified to the router rather than
|
|
||||||
# the preferred directions
|
|
||||||
debug.check(directions[0] != directions[1], "Must have unique layer directions.")
|
|
||||||
if directions[0] == "V":
|
|
||||||
self.vertical_layer = layer_stack[0]
|
|
||||||
self.horizontal_layer = layer_stack[2]
|
|
||||||
else:
|
|
||||||
self.horizontal_layer = layer_stack[0]
|
|
||||||
self.vertical_layer = layer_stack[2]
|
|
||||||
|
|
||||||
layer_stuff = self.get_layer_pitch(self.vertical_layer)
|
|
||||||
(self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
|
|
||||||
|
|
||||||
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
|
|
||||||
(self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
|
|
||||||
|
|
||||||
# FIXME: Must extend this to a horizontal conflict graph
|
|
||||||
# too if we want to minimize the
|
|
||||||
# number of tracks!
|
|
||||||
# hcg = {}
|
|
||||||
|
|
||||||
# Initialize the vertical conflict graph (vcg)
|
|
||||||
# and make a list of all pins
|
|
||||||
vcg = collections.OrderedDict()
|
|
||||||
|
|
||||||
# Create names for the nets for the graphs
|
|
||||||
nets = collections.OrderedDict()
|
|
||||||
index = 0
|
|
||||||
# print(netlist)
|
|
||||||
for pin_list in netlist:
|
|
||||||
net_name = "n{}".format(index)
|
|
||||||
index += 1
|
|
||||||
nets[net_name] = pin_list
|
|
||||||
|
|
||||||
# print("Nets:")
|
|
||||||
# for net_name in nets:
|
|
||||||
# print(net_name, [x.name for x in nets[net_name]])
|
|
||||||
|
|
||||||
# Find the vertical pin conflicts
|
|
||||||
# FIXME: O(n^2) but who cares for now
|
|
||||||
for net_name1 in nets:
|
|
||||||
if net_name1 not in vcg.keys():
|
|
||||||
vcg[net_name1] = []
|
|
||||||
for net_name2 in nets:
|
|
||||||
if net_name2 not in vcg.keys():
|
|
||||||
vcg[net_name2] = []
|
|
||||||
# Skip yourself
|
|
||||||
if net_name1 == net_name2:
|
|
||||||
continue
|
|
||||||
if vcg_nets_overlap(nets[net_name1],
|
|
||||||
nets[net_name2],
|
|
||||||
vertical):
|
|
||||||
vcg[net_name2].append(net_name1)
|
|
||||||
|
|
||||||
# list of routes to do
|
|
||||||
while vcg:
|
|
||||||
# from pprint import pformat
|
|
||||||
# print("VCG:\n", pformat(vcg))
|
|
||||||
# get a route from conflict graph with empty fanout set
|
|
||||||
net_name = None
|
|
||||||
for net_name, conflicts in vcg.items():
|
|
||||||
if len(conflicts) == 0:
|
|
||||||
vcg = remove_net_from_graph(net_name, vcg)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# FIXME: We don't support cyclic VCGs right now.
|
|
||||||
debug.error("Cyclic VCG in channel router.", -1)
|
|
||||||
|
|
||||||
# These are the pins we'll have to connect
|
|
||||||
pin_list = nets[net_name]
|
|
||||||
# print("Routing:", net_name, [x.name for x in pin_list])
|
|
||||||
|
|
||||||
# Remove the net from other constriants in the VCG
|
|
||||||
vcg = remove_net_from_graph(net_name, vcg)
|
|
||||||
|
|
||||||
# Add the trunk routes from the bottom up for
|
|
||||||
# horizontal or the left to right for vertical
|
|
||||||
if vertical:
|
|
||||||
self.add_vertical_trunk_route(pin_list,
|
|
||||||
offset,
|
|
||||||
layer_stack,
|
|
||||||
self.vertical_nonpref_pitch)
|
|
||||||
# This accounts for the via-to-via spacings
|
|
||||||
offset += vector(self.horizontal_nonpref_pitch, 0)
|
|
||||||
else:
|
|
||||||
self.add_horizontal_trunk_route(pin_list,
|
|
||||||
offset,
|
|
||||||
layer_stack,
|
|
||||||
self.horizontal_nonpref_pitch)
|
|
||||||
# This accounts for the via-to-via spacings
|
|
||||||
offset += vector(0, self.vertical_nonpref_pitch)
|
|
||||||
|
|
||||||
def create_vertical_channel_route(self, netlist, offset, layer_stack, directions=None):
|
def create_vertical_channel_route(self, netlist, offset, layer_stack, directions=None):
|
||||||
"""
|
"""
|
||||||
Wrapper to create a vertical channel route
|
Wrapper to create a vertical channel route
|
||||||
"""
|
"""
|
||||||
self.create_channel_route(netlist, offset, layer_stack, directions, vertical=True)
|
import channel_route
|
||||||
|
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True)
|
||||||
|
self.add_inst("vc", cr)
|
||||||
|
self.connect_inst([])
|
||||||
|
|
||||||
def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None):
|
def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None):
|
||||||
"""
|
"""
|
||||||
Wrapper to create a horizontal channel route
|
Wrapper to create a horizontal channel route
|
||||||
"""
|
"""
|
||||||
self.create_channel_route(netlist, offset, layer_stack, directions, vertical=False)
|
import channel_route
|
||||||
|
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False)
|
||||||
|
self.add_inst("hc", cr)
|
||||||
|
self.connect_inst([])
|
||||||
|
|
||||||
def add_boundary(self, ll=vector(0, 0), ur=None):
|
def add_boundary(self, ll=vector(0, 0), ur=None):
|
||||||
""" Add boundary for debugging dimensions """
|
""" Add boundary for debugging dimensions """
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue