First draft of naive channel route in hierarchy_layout. It doesn't implement horizontal conflicts or try to minimize the number of channels.

This commit is contained in:
Matt Guthaus 2018-07-25 11:13:30 -07:00
parent 48d3b25b74
commit f7a2766c29
2 changed files with 180 additions and 14 deletions

View File

@ -636,6 +636,181 @@ class layout(lef.lef):
offset=bus_pos,
rotate=90)
def add_horizontal_trunk_route(self, pins, trunk_offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Create a trunk route for all pins with the the trunk located at the given y offset.
"""
if not pitch:
pitch = self.m1_pitch
max_x = max([pin.center().x for pin in pins])
min_x = min([pin.center().x for pin in pins])
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[0])]
# if we are less than a pitch, just create a non-preferred layer jog
if max_x-min_x < pitch:
# Add the horizontal trunk on the vertical layer!
self.add_path(layer_stack[2],[vector(min_x-half_minwidth,trunk_offset.y), vector(max_x+half_minwidth,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(layer_stack[2], [pin.center(), mid])
else:
# Add the horizontal trunk
self.add_path(layer_stack[0],[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)])
trunk_mid = vector(0.5*(max_x+min_x),trunk_offset.y)
# Route each pin to the trunk
for pin in pins:
# Bend to the center of the trunk so it adds a via automatically
mid = vector(pin.center().x, trunk_offset.y)
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid])
def add_vertical_trunk_route(self, pins, trunk_offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Create a trunk route for all pins with the the trunk located at the given x offset.
"""
if not pitch:
pitch = self.m2_pitch
max_y = max([pin.center().y for pin in pins])
min_y = min([pin.center().y for pin in pins])
# Add the vertical trunk
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])]
# if we are less than a pitch, just create a non-preferred layer jog
if max_y-min_y < pitch:
# Add the horizontal trunk on the vertical layer!
self.add_path(layer_stack[0],[vector(trunk_offset.x,min_y-half_minwidth), vector(trunk_offset.x,max_y+half_minwidth)])
# 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(layer_stack[0], [pin.center(), mid])
else:
# Add the vertical trunk
self.add_path(layer_stack[2],[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)])
trunk_mid = vector(trunk_offset.x,0.5*(max_y+min_y),)
# Route each pin to the trunk
for pin in pins:
# Bend to the center of the trunk so it adds a via automatically
mid = vector(trunk_offset.x, pin.center().y)
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid])
def create_channel_route(self, route_map, bottom_inst, top_inst, offset,
layer_stack=("metal1", "via1", "metal2"), pitch=None,
vertical=False):
"""
This is a simple channel route for one-to-one connections that
will jog the top route whenever there is a conflict. It does NOT
try to minimize the number of tracks -- instead, it picks an order to avoid the vertical
conflicts between pins.
"""
if not pitch and vertical:
pitch = self.m2_pitch
elif not pitch and not vertical:
pitch = self.m1_pitch
# FIXME: Must extend this to a horizontal conflict graph too if we want to minimize the
# number of tracks!
# Initialize the vertical conflict graph (vcg) and make a list of all pins
vcg = {}
all_pins = {}
for (top_name, bot_name) in route_map:
vcg[top_name] = []
vcg[bot_name] = []
top_pin = top_inst.get_pin(top_name)
bot_pin = bottom_inst.get_pin(bot_name)
all_pins[top_name]=top_pin
all_pins[bot_name]=bot_pin
# Find the vertical pin conflicts
for (top_name, bot_name) in route_map:
top_pin = all_pins[top_name]
bot_pin = all_pins[bot_name]
if abs(top_pin.center().x-bot_pin.center().x) < pitch:
# The edges only go from top to bottom
# since we will order tracks bottom up
vcg[top_name].append(bot_name)
# This is the starting offset of the first trunk
if vertical:
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])]
offset = offset + vector(half_minwidth,0)
else:
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[0])]
offset = offset + vector(0,half_minwidth)
# list of routes to do
while vcg:
# get a route from conflict graph with empty fanout set
route_pin=None
for route_pin,conflicts in vcg.items():
if len(conflicts)==0:
# Remove the pin from the keys
vcg.pop(route_pin,None)
# Remove the pin from all conflicts
# This is O(n^2), so maybe optimize it.
for pin,conflicts in vcg.items():
if pin in conflicts:
conflicts.remove(route_pin)
vcg[pin]=conflicts
break
#print("Routing:",route_pin)
# Get the connected pins from the routing map
for pin_connections in route_map:
if route_pin in pin_connections:
break
#print("Routing:",pin_connections)
# Remove the other pins from the conflict graph too
for pin in pin_connections:
vcg.pop(pin,None)
# Create a list of the pins rather than a list of the names
pin_list = [all_pins[pin_name] for pin_name in pin_connections]
# Add the trunk route and move up to next track
if vertical:
self.add_vertical_trunk_route(pin_list, offset, layer_stack, pitch)
offset += vector(pitch,0)
else:
self.add_horizontal_trunk_route(pin_list, offset, layer_stack, pitch)
offset += vector(0,pitch)
def create_vertical_channel_route(self, route_map, left_inst, right_inst, offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Wrapper to create a vertical channel route
"""
self.create_channel_route(route_map, left_inst, right_inst, offset,
layer_stack, pitch, vertical=True)
def create_horizontal_channel_route(self, route_map, top_inst, bottom_inst, offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Wrapper to create a horizontal channel route
"""
self.create_channel_route(route_map, top_inst, bottom_inst, offset,
layer_stack, pitch, vertical=False)
def add_enclosure(self, insts, layer="nwell"):
""" Add a layer that surrounds the given instances. Useful
for creating wells, for example. Doesn't check for minimum widths or

View File

@ -38,7 +38,7 @@ class sram_1bank(sram_base):
control_pos.y + self.control_logic.height + self.m1_pitch)
self.add_row_addr_dff(row_addr_pos)
data_gap = -self.m2_pitch*(self.word_size+1)
data_gap = -self.m1_pitch*(self.word_size+1)
# Add the column address below the bank under the control
# Keep it aligned with the data flops
@ -174,23 +174,14 @@ class sram_1bank(sram_base):
def route_data_dff(self):
""" Connect the output of the data flops to the write driver """
# Create a horizontal bus
bus_names = ["data[{}]".format(x) for x in range(self.word_size)]
data_bus_offsets = self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch,
offset=self.data_dff_inst.ul() + vector(0, self.m1_pitch),
names=bus_names,
length=self.data_dff_inst.width)
# This is where the channel will start (y-dimension at least)
offset = self.data_dff_inst.ul() + vector(0, self.m1_pitch)
dff_names = ["dout[{}]".format(x) for x in range(self.word_size)]
data_dff_map = zip(dff_names, bus_names)
self.connect_horizontal_bus(data_dff_map, self.data_dff_inst, data_bus_offsets)
bank_names = ["bank_din[{}]".format(x) for x in range(self.word_size)]
data_bank_map = zip(bank_names, bus_names)
self.connect_horizontal_bus(data_bank_map, self.bank_inst, data_bus_offsets)
route_map = list(zip(bank_names, dff_names))
self.create_horizontal_channel_route(route_map, self.data_dff_inst, self.bank_inst, offset)