mirror of https://github.com/VLSIDA/OpenRAM.git
Starting to implement minimizing channel router (not done)
This commit is contained in:
parent
c7bc01c3a9
commit
ba3d32fa0c
|
|
@ -12,6 +12,69 @@ from vector import vector
|
|||
import design
|
||||
|
||||
|
||||
class channel_net():
|
||||
def __init__(self, net_name, pins, vertical):
|
||||
self.name = net_name
|
||||
self.pins = pins
|
||||
self.vertical = vertical
|
||||
|
||||
# Keep track of the internval
|
||||
if vertical:
|
||||
self.min_value = min(i.by() for i in pins)
|
||||
self.max_value = max(i.uy() for i in pins)
|
||||
else:
|
||||
self.min_value = min(i.lx() for i in pins)
|
||||
self.max_value = max(i.rx() for i in pins)
|
||||
|
||||
# Keep track of the conflicts
|
||||
self.conflicts = []
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.min_value < other.min_value
|
||||
|
||||
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
|
||||
|
||||
def vcg_nets_overlap(self, other, pitch):
|
||||
"""
|
||||
Check all the pin pairs on two nets and return a pin
|
||||
overlap if any pin overlaps.
|
||||
"""
|
||||
|
||||
for pin1 in self.pins:
|
||||
for pin2 in other.pins:
|
||||
if self.vcg_pin_overlap(pin1, pin2, pitch):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def hcg_nets_overlap(self, other):
|
||||
"""
|
||||
Check if the horizontal span of the two nets overlaps eachother.
|
||||
"""
|
||||
min_overlap = self.min_value >= other.min_value and self.min_value <= other.max_value
|
||||
max_overlap = self.max_value >= other.min_value and self.max_value <= other.max_value
|
||||
return min_overlap or max_overlap
|
||||
|
||||
|
||||
class channel_route(design.design):
|
||||
|
||||
unique_id = 0
|
||||
|
|
@ -86,104 +149,112 @@ class channel_route(design.design):
|
|||
# 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
|
||||
g[other_pin].remove(pin)
|
||||
return g
|
||||
|
||||
def route(self):
|
||||
# Create names for the nets for the graphs
|
||||
nets = []
|
||||
index = 0
|
||||
# print(self.netlist)
|
||||
for pin_list in self.netlist:
|
||||
nets.append(channel_net("n{}".format(index), pin_list, self.vertical))
|
||||
index += 1
|
||||
|
||||
def vcg_nets_overlap(self, net1, net2):
|
||||
"""
|
||||
Check all the pin pairs on two nets and return a pin
|
||||
overlap if any pin overlaps.
|
||||
"""
|
||||
# Create the (undirected) horizontal constraint graph
|
||||
hcg = collections.OrderedDict()
|
||||
for net1 in nets:
|
||||
for net2 in nets:
|
||||
if net1.name == net2.name:
|
||||
continue
|
||||
if net1.hcg_nets_overlap(net2):
|
||||
try:
|
||||
hcg[net1.name].append(net2.name)
|
||||
except KeyError:
|
||||
hcg[net1.name] = [net2.name]
|
||||
try:
|
||||
hcg[net2.name].append(net1.name)
|
||||
except KeyError:
|
||||
hcg[net2.name] = [net1.name]
|
||||
|
||||
|
||||
# Initialize the vertical conflict graph (vcg)
|
||||
# and make a list of all pins
|
||||
vcg = collections.OrderedDict()
|
||||
|
||||
# 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
|
||||
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
|
||||
for net in nets:
|
||||
vcg[net.name] = []
|
||||
|
||||
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] = []
|
||||
for net1 in nets:
|
||||
for net2 in nets:
|
||||
# Skip yourself
|
||||
if net_name1 == net_name2:
|
||||
if net1.name == net2.name:
|
||||
continue
|
||||
if self.vcg_nets_overlap(nets[net_name1],
|
||||
nets[net_name2]):
|
||||
vcg[net_name2].append(net_name1)
|
||||
|
||||
if net1.vcg_nets_overlap(net2, pitch):
|
||||
vcg[net2.name].append(net1.name)
|
||||
|
||||
current_offset = self.offset
|
||||
|
||||
# list of routes to do
|
||||
while vcg:
|
||||
|
||||
# Sort nets by left edge value
|
||||
nets.sort()
|
||||
while len(nets) > 0:
|
||||
|
||||
current_offset_value = current_offset.y if self.vertical else current_offset.x
|
||||
|
||||
# from pprint import pformat
|
||||
# print("VCG:\n", pformat(vcg))
|
||||
# for name,net in vcg.items():
|
||||
# print(name, net.min_value, net.max_value, net.conflicts)
|
||||
# print(current_offset)
|
||||
# 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)
|
||||
for net in nets:
|
||||
# If it has no conflicts and the interval is to the right of the current offset in the track
|
||||
if net.min_value > current_offset_value and len(vcg[net.name]) == 0:
|
||||
# print("Routing {}".format(net.name))
|
||||
# Add the trunk routes from the bottom up for
|
||||
# horizontal or the left to right for vertical
|
||||
if self.vertical:
|
||||
self.add_vertical_trunk_route(net.pins,
|
||||
current_offset,
|
||||
self.vertical_nonpref_pitch)
|
||||
current_offset = vector(current_offset.x, net.max_value + self.horizontal_nonpref_pitch)
|
||||
else:
|
||||
self.add_horizontal_trunk_route(net.pins,
|
||||
current_offset,
|
||||
self.horizontal_nonpref_pitch)
|
||||
current_offset = vector(net.max_value + self.vertical_nonpref_pitch, current_offset.y)
|
||||
|
||||
# Remove the net from other constriants in the VCG
|
||||
vcg = self.remove_net_from_graph(net.name, vcg)
|
||||
nets.remove(net)
|
||||
|
||||
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:
|
||||
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:
|
||||
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)
|
||||
# If we made a full pass and the offset didn't change...
|
||||
current_offset_value = current_offset.y if self.vertical else current_offset.x
|
||||
initial_offset_value = self.offset.y if self.vertical else self.offset.x
|
||||
if current_offset_value == initial_offset_value:
|
||||
# FIXME: We don't support cyclic VCGs right now.
|
||||
debug.error("Cyclic VCG in channel router.", -1)
|
||||
|
||||
# Increment the track and reset the offset to the start (like a typewriter)
|
||||
if self.vertical:
|
||||
current_offset = vector(current_offset.x + self.horizontal_nonpref_pitch, self.offset.y)
|
||||
else:
|
||||
current_offset = vector(self.offset.x, current_offset.y + self.vertical_nonpref_pitch)
|
||||
|
||||
# Return the size of the channel
|
||||
if self.vertical:
|
||||
self.width = 0
|
||||
|
|
@ -226,6 +297,17 @@ class channel_route(design.design):
|
|||
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:
|
||||
if pin.cy() < trunk_offset.y:
|
||||
pin_pos = pin.uc()
|
||||
else:
|
||||
pin_pos = pin.bc()
|
||||
|
||||
# No bend needed here
|
||||
mid = vector(pin_pos.x, trunk_offset.y)
|
||||
self.add_path(self.vertical_layer, [pin_pos, mid])
|
||||
else:
|
||||
# Add the horizontal trunk
|
||||
self.add_path(self.horizontal_layer,
|
||||
|
|
@ -269,6 +351,17 @@ class channel_route(design.design):
|
|||
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:
|
||||
# Find the correct side of the pin
|
||||
if pin.cx() < trunk_offset.x:
|
||||
pin_pos = pin.rc()
|
||||
else:
|
||||
pin_pos = pin.lc()
|
||||
# No bend needed here
|
||||
mid = vector(trunk_offset.x, pin_pos.y)
|
||||
self.add_path(self.horizontal_layer, [pin_pos, mid])
|
||||
else:
|
||||
# Add the vertical trunk
|
||||
self.add_path(self.vertical_layer,
|
||||
|
|
@ -292,18 +385,4 @@ class channel_route(design.design):
|
|||
to_layer=self.horizontal_layer,
|
||||
offset=pin_pos)
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -384,6 +384,14 @@ class sram_1bank(sram_base):
|
|||
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||
route_map.extend(list(zip(bank_pins, sram_pins)))
|
||||
|
||||
# spare wen dff
|
||||
if self.num_spare_cols > 0 and port in self.write_ports:
|
||||
dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)]
|
||||
dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names]
|
||||
bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)]
|
||||
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||
route_map.extend(list(zip(bank_pins, dff_pins)))
|
||||
|
||||
if self.num_wmasks > 0 and port in self.write_ports:
|
||||
layer_stack = self.m3_stack
|
||||
else:
|
||||
|
|
@ -401,17 +409,17 @@ class sram_1bank(sram_base):
|
|||
offset=offset,
|
||||
layer_stack=layer_stack)
|
||||
|
||||
# Route these separately because sometimes the pin pitch on the write driver is too narrow for M3 (FreePDK45)
|
||||
# spare wen dff
|
||||
if self.num_spare_cols > 0 and port in self.write_ports:
|
||||
dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)]
|
||||
dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names]
|
||||
bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)]
|
||||
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||
route_map = zip(bank_pins, dff_pins)
|
||||
self.create_horizontal_channel_route(netlist=route_map,
|
||||
offset=offset,
|
||||
layer_stack=self.m1_stack)
|
||||
# # Route these separately because sometimes the pin pitch on the write driver is too narrow for M3 (FreePDK45)
|
||||
# # spare wen dff
|
||||
# if self.num_spare_cols > 0 and port in self.write_ports:
|
||||
# dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)]
|
||||
# dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names]
|
||||
# bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)]
|
||||
# bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||
# route_map = zip(bank_pins, dff_pins)
|
||||
# self.create_horizontal_channel_route(netlist=route_map,
|
||||
# offset=offset,
|
||||
# layer_stack=self.m1_stack)
|
||||
|
||||
def route_clk(self):
|
||||
""" Route the clock network """
|
||||
|
|
|
|||
Loading…
Reference in New Issue