OpenRAM/compiler/router/supply_placer.py

845 lines
39 KiB
Python

# See LICENSE for licensing information.
#
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
# All rights reserved.
#
from openram import debug
from openram.base.vector import vector
from openram import OPTS
from .graph import graph
from .graph_shape import graph_shape
from .router import router
class supply_placer(router):
def __init__(self, layers, design, bbox=None, pin_type=None, ext_vdd_name="vccd1", ext_gnd_name="vssd1", moat_pins=None):
# `router` is the base router class
router.__init__(self, layers, design, bbox)
# Side supply pin type
# (can be "top", "bottom", "right", "left", and "ring")
self.pin_type = pin_type
# New pins are the side supply pins
self.new_pins = {}
# external power name of the whole macro
self.ext_vdd_name = ext_vdd_name
self.ext_gnd_name = ext_gnd_name
# instances
self.insts = self.design.insts
# moat pins
self.moat_pins = moat_pins
# store a graphshape of intermediate points(if shift)/source points(if no shift) when connecting moat_pins to outside
# trick: since in the creation of dnwell, these pins are "ordered" added, so they'are also ordered here
# order inside list: left -> right or bottom -> up
self.moat_pins_left = []
self.moat_pins_right = []
self.moat_pins_top = []
self.moat_pins_bottom = []
# io pins
self.io_pins_left = []
self.io_pins_right = []
self.io_pins_top = []
self.io_pins_bottom = []
def route_outside(self, vdd_name="vdd", gnd_name="gnd", io_pin_names=None):
# only connect supply with inside submodules, not connecting to the power ring
debug.info(1, "Running router for {} and {}...".format(vdd_name, gnd_name))
# Save pin names
self.vdd_name = vdd_name
self.gnd_name = gnd_name
# Prepare gdsMill to find pins and blockages
self.prepare_gds_reader()
# Find vdd/gnd pins of bank, to be routed
self.find_pins_inside(vdd_name)
self.find_pins_inside(gnd_name)
self.route_moat(io_pin_names)
# Find blockages and vias
self.find_blockages()
self.find_vias()
# Convert blockages and vias if they overlap a pin
self.convert_vias()
self.convert_blockages()
# Add vdd and gnd pins as blockages as well
# NOTE: This is done to make vdd and gnd pins DRC-safe
for pin in self.all_pins:
self.blockages.append(self.inflate_shape(pin))
# Prepare the selected moat pins
selected_moat_pins = self.prepare_selected_moat_pins()
# Route vdd and gnd
routed_count = 0
routed_max = len(self.pins[vdd_name]) + len(self.pins[gnd_name])
for pin_name in [vdd_name, gnd_name]:
if pin_name == gnd_name: # otherwise will not recognaize the moat blocakge
self.prepare_gds_reader()
# Find blockages and vias
self.find_blockages()
self.find_vias()
# Convert blockages and vias if they overlap a pin
self.convert_vias()
self.convert_blockages()
pins = self.pins[pin_name]
# Route closest pins according to the minimum spanning tree
for source, target in self.get_mst_with_ring(list(pins), selected_moat_pins, pin_name):
# Create the graph
g = graph(self)
g.create_graph(source, target)
# Find the shortest path from source to target
path = g.find_shortest_path()
# If no path is found, throw an error
if path is None:
self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target)
debug.error("Couldn't route from {} to {}.".format(source, target), -1)
# Create the path shapes on layout
new_wires, new_vias = self.add_path(path)
# Find the recently added shapes
self.find_blockages(pin_name, new_wires)
self.find_vias(new_vias)
# Report routed count
routed_count += 1
debug.info(2, "Routed {} of {} supply pins".format(routed_count, routed_max))
# finsih
self.replace_layout_pins()
def route_inside(self, vdd_name="vdd", gnd_name="gnd"):
# only connect supply with inside submodules, not connecting to the power ring
debug.info(1, "Running router for {} and {}...".format(vdd_name, gnd_name))
# Save pin names
self.vdd_name = vdd_name
self.gnd_name = gnd_name
# Prepare gdsMill to find pins and blockages
self.prepare_gds_reader()
# Find vdd/gnd pins of bank, to be routed
self.find_pins_inside(vdd_name)
self.find_pins_inside(gnd_name)
# Find blockages and vias
self.find_blockages()
self.find_vias()
# Convert blockages and vias if they overlap a pin
self.convert_vias()
self.convert_blockages()
# Add vdd and gnd pins as blockages as well
# NOTE: This is done to make vdd and gnd pins DRC-safe
for pin in self.all_pins:
self.blockages.append(self.inflate_shape(pin))
# Route vdd and gnd
routed_count = 0
routed_max = len(self.pins[vdd_name]) + len(self.pins[gnd_name])
for pin_name in [vdd_name, gnd_name]:
pins = self.pins[pin_name]
# Route closest pins according to the minimum spanning tree
for source, target in self.get_mst_pairs(list(pins)):
# Create the graph
g = graph(self)
g.create_graph(source, target)
# Find the shortest path from source to target
path = g.find_shortest_path()
# If no path is found, throw an error
if path is None:
self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target)
debug.error("Couldn't route from {} to {}.".format(source, target), -1)
# Create the path shapes on layout
new_wires, new_vias = self.add_path(path)
# Find the recently added shapes
self.find_blockages(pin_name, new_wires)
self.find_vias(new_vias)
# Report routed count
routed_count += 1
debug.info(2, "Routed {} of {} supply pins".format(routed_count, routed_max))
# finsih
self.replace_layout_pins()
def route_moat(self, io_pin_names):
# route the vdd pins at the moat
# io_pin_names is a list
# the moat vdd shape will also be created and stored in the list
for moat_pin in self.moat_pins:
self.check_overlap(moat_pin, io_pin_names)
def replace_layout_pins(self):
# clear all the inside "vdd " "gnd" at sram module level
# Copy the pin shape(s) to rectangles
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles
for pin in self.design.get_pins(pin_name):
self.design.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pin shape(s)
self.design.remove_layout_pin(pin_name)
# Get new pins, change the name of ring to extern supply name
# vccd1 ring
pins = self.get_new_pins("vdd")
for pin in pins:
self.design.add_layout_pin(self.ext_vdd_name,
pin.layer,
pin.ll(),
pin.width(),
pin.height())
# vssd1 ring
pins = self.get_new_pins("gnd")
for pin in pins:
self.design.add_layout_pin(self.ext_gnd_name,
pin.layer,
pin.ll(),
pin.width(),
pin.height())
def prepare_selected_moat_pins(self):
""" Selcet the possibe moat pins, feed into the MST, where will decide which of these pin should be connected to which pin """
if len(self.design.all_ports) > 1:
# in order to save runtime
# top -> moat pins all
# bottom -> moat pins all
# left -> moat pins near control logic
# right -> moat pins near control logic
# expected connection for control logic
# for port 0 -> left
start_y = self.design.control_logic_insts[0].by()# bottom edge y value
end_y = self.design.control_logic_insts[0].uy()# up edge y value
# filter the pin in the range
filtered_moat_pins_left = [pin for pin in self.moat_pins_left if start_y <= pin.center().y <= end_y]
# for port 1 -> right
start_y = self.design.control_logic_insts[1].by()# bottom edge y value
end_y = self.design.control_logic_insts[1].uy()# up edge y value
# filter the pin in the range
filtered_moat_pins_right = [pin for pin in self.moat_pins_right if start_y <= pin.center().y <= end_y]
# return the selected moat pins
selected_moat_pins = []
selected_moat_pins.extend(filtered_moat_pins_left)
selected_moat_pins.extend(self.moat_pins_bottom)
selected_moat_pins.extend(filtered_moat_pins_right)
selected_moat_pins.extend(self.moat_pins_top)
return selected_moat_pins
else: # only 1 port
# in order to save runtime
# top -> moat pins all
# bottom -> moat pins all
# left -> moat pins near control logic
# right -> moat pins all
start_y = self.design.control_logic_insts[0].by()# bottom edge y value
end_y = self.design.control_logic_insts[0].uy()# up edge y value
# filter the pin in the range
filtered_moat_pins_left = [pin for pin in self.moat_pins_left if start_y <= pin.center().y <= end_y]
# return the selected moat pins
selected_moat_pins = []
selected_moat_pins.extend(filtered_moat_pins_left)
selected_moat_pins.extend(self.moat_pins_bottom)
selected_moat_pins.extend(self.moat_pins_right)
selected_moat_pins.extend(self.moat_pins_top)
return selected_moat_pins
def prepare_escape_pins(self):
# clear all the inside "vdd " "gnd" at sram module level
# Copy the pin shape(s) to rectangles
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles
for pin in self.design.get_pins(pin_name):
self.design.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pin shape(s)
self.design.remove_layout_pin(pin_name)
# prepare pins for every instances
for pin_name in ["vdd"]:
count =0
for pin in self.design.control_logic_insts[0].get_pins(pin_name):
debug.warning("vdd pin shape -> ll:{0} ur:{1}".format(pin.ll(), pin.ur()))
"""
count = count + 1
new_pin = graph_shape("gnd", pin.rect, pin.layer)
source = new_pin
source_center_x = pin.center().x
target_ur_y = self.new_pins[self.ext_gnd_name][1].center().y + 0.5 * self.new_pins[self.ext_gnd_name][1].height()
ll = vector(source_center_x - 2.6 - 0.5 * self.track_wire, pin.center().y - 0.5 * self.track_wire)#target_ur_y - self.track_wire)
ur = vector(source_center_x - 2.6 + 0.5 * self.track_wire, pin.center().y + 0.5 * self.track_wire)#target_ur_y)
target = graph_shape("fake", [ll,ur], pin.layer)
# Create the graph
g = graph(self)
g.create_graph(source, target)
# Find the shortest path from source to target
path = g.find_shortest_path()
# If no path is found, throw an error
if path is None:
self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target)
debug.error("Couldn't route from {} to {}.".format(source, target), -1)
# Create the path shapes on layout
new_wires, new_vias = self.add_path(path)
# Find the recently added shapes
self.find_blockages(pin_name, new_wires)
self.find_vias(new_vias)
"""
debug.warning("vdd of wmask number -> {0}".format(count))
debug.warning("instance postion -> {0} {1}".format(self.design.control_logic_insts[0].lx(), self.design.control_logic_insts[0].by()))
# print pins_all
for pin in self.all_pins:
debug.warning("all_pins -> {0}".format(pin))
def check_overlap(self, moat_pin, io_pin_names):
# use all the IO pins(at correspoding edge) to check overlap, check 1 moat vdd pin, give the corresponding target/source position as list, and connect them
add_distance = 0
direction = 1
self.prepare_io_pins(io_pin_names)
# judge the edge of moat vdd
edge = self.get_closest_edge(moat_pin)
source_center = moat_pin.center()
if edge == "bottom":
add_distance = self.via2_via3_pitch # if shift, need to fulfill via2-via3 spacing, top/bottom only
pin_too_close = any(abs(io_pin.center().x - source_center.x) < self.track_width for io_pin in self.io_pins_bottom)
tmp_center = vector(source_center.x, source_center.y)
while pin_too_close:
tmp_center = vector(source_center.x, source_center.y)
add_distance = add_distance + 0.1
if direction == 1: # right shift
tmp_center = vector((tmp_center.x + add_distance), tmp_center.y)
else: # left shift
tmp_center = vector((tmp_center.x - add_distance), tmp_center.y)
pin_too_close = any(abs(io_pin.center().x - tmp_center.x) < self.track_width for io_pin in self.io_pins_bottom)
direction = - direction
# the nearst vdd ring
vdd_ring = self.new_pins["vdd"][1] # order in list -> "top", "bottom", "right", "left"]
# bottom ring's y position at it's top
target_egde_y = vdd_ring.center().y + 0.5 * vdd_ring.height()
if tmp_center == source_center: # no overlap
# no jog, direct return the source/target center position
# the target center position, should consider enought space for via
target_point = vector(tmp_center.x, (target_egde_y - 0.5 * self.track_wire))
source_point = vector(tmp_center.x, tmp_center.y)
point_list = [source_point, target_point]
self.add_wire(point_list, vertical=True)
# store the shape of moat pins, need for route later
ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire)
ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m4")
self.moat_pins_bottom.append(moat_pin_route)
else: # need jog
# shift the center
# add rectangle at same layer (original)
intermediate_point = vector(tmp_center.x, tmp_center.y)
source_point = vector(source_center.x, source_center.y)
target_point = vector(tmp_center.x, (target_egde_y - 0.5 * self.track_wire))
point_list = [source_point, intermediate_point, target_point]
self.add_wire(point_list, vertical=True)
# store the shape of moat pins, need for route later
ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire)
ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m4")
self.moat_pins_bottom.append(moat_pin_route)
elif edge == "top":
add_distance = self.via2_via3_pitch # if shift, need to fulfill via2-via3 spacing, top/bottom only
pin_too_close = any(abs(io_pin.center().x - source_center.x) < self.track_width for io_pin in self.io_pins_top)
tmp_center = vector(source_center.x, source_center.y)
while pin_too_close:
tmp_center = vector(source_center.x, source_center.y)
add_distance = add_distance + 0.1
if direction == 1: # right shift
tmp_center = vector((tmp_center.x + add_distance), tmp_center.y)
else: # left shift
tmp_center = vector((tmp_center.x - add_distance), tmp_center.y)
pin_too_close = any(abs(io_pin.center().x - tmp_center.x) < self.track_width for io_pin in self.io_pins_top)
direction = - direction
# the nearst vdd ring
vdd_ring = self.new_pins["vdd"][0] # order in list -> "top", "bottom", "right", "left"]
# top ring's y position at it's bottom
target_egde_y = vdd_ring.center().y - 0.5 * vdd_ring.height()
if tmp_center == source_center: # no overlap
# no jog, direct return the source/target center position
# the target center position, should consider enought space for via
target_point = vector(tmp_center.x, (target_egde_y + 0.5 * self.track_wire))
source_point = vector(tmp_center.x, tmp_center.y)
point_list = [source_point, target_point]
self.add_wire(point_list, vertical=True)
# store the shape of moat pins, need for route later
ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire)
ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m4")
self.moat_pins_top.append(moat_pin_route)
else: # need jog
# shift the center
# add rectangle at same layer (original)
intermediate_point = vector(tmp_center.x, tmp_center.y)
source_point = vector(source_center.x, source_center.y)
target_point = vector(tmp_center.x, (target_egde_y + 0.5 * self.track_wire))
point_list = [source_point, intermediate_point, target_point]
self.add_wire(point_list, vertical=True)
# store the shape of moat pins, need for route later
ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire)
ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m4")
self.moat_pins_top.append(moat_pin_route)
elif edge == "left":
pin_too_close = any(abs(io_pin.center().y - source_center.y) < self.track_width for io_pin in self.io_pins_left)
tmp_center = vector(source_center.x, source_center.y)
while pin_too_close:
tmp_center = vector(source_center.x, source_center.y)
add_distance = add_distance + 0.1
if direction == 1: # up shift
tmp_center = vector(tmp_center.x, (tmp_center.y + add_distance))
else: # down shift
tmp_center = vector(tmp_center.x, (tmp_center.y - add_distance))
pin_too_close = any(abs(io_pin.center().y - tmp_center.y) < self.track_width for io_pin in self.io_pins_left)
direction = - direction
# the nearst vdd ring
vdd_ring = self.new_pins["vdd"][3] # order in list -> "top", "bottom", "right", "left"]
# left ring's x position at it's right
target_egde_x = vdd_ring.center().x + 0.5 * vdd_ring.width()
if tmp_center == source_center: # no overlap
# no jog, direct return the source/target center position
# the target center position, should consider enought space for via
target_point = vector((target_egde_x - 0.5 * self.track_wire), tmp_center.y)
source_point = vector(tmp_center.x, tmp_center.y)
point_list = [source_point, target_point]
self.add_wire(point_list, vertical=False)
# store the shape of moat pins, need for route later
ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire)
ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m3")
self.moat_pins_left.append(moat_pin_route)
else: # need jog
# shift the center
# add rectangle at same layer (original)
intermediate_point = vector(tmp_center.x, tmp_center.y)
source_point = vector(source_center.x, source_center.y)
target_point = vector((target_egde_x - 0.5 * self.track_wire), tmp_center.y)
point_list = [source_point, intermediate_point, target_point]
self.add_wire(point_list, vertical=False)
# store the shape of moat pins, need for route later
ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire)
ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m3")
self.moat_pins_left.append(moat_pin_route)
else: #right
pin_too_close = any(abs(io_pin.center().y - source_center.y) < self.track_width for io_pin in self.io_pins_right)
tmp_center = vector(source_center.x, source_center.y)
while pin_too_close:
tmp_center = vector(source_center.x, source_center.y)
add_distance = add_distance + 0.1
if direction == 1: # up shift
tmp_center = vector(tmp_center.x, (tmp_center.y + add_distance))
else: # down shift
tmp_center = vector(tmp_center.x, (tmp_center.y - add_distance))
pin_too_close = any(abs(io_pin.center().y - tmp_center.y) < self.track_width for io_pin in self.io_pins_right)
direction = - direction
# the nearst vdd ring
vdd_ring = self.new_pins["vdd"][2] # order in list -> "top", "bottom", "right", "left"]
# right ring's y position at it's left
target_egde_x = vdd_ring.center().x - 0.5 * vdd_ring.width()
if tmp_center == source_center: # no overlap
# no jog, direct return the source/target center position
# the target center position, should consider enought space for via
target_point = vector((target_egde_x + 0.5 * self.track_wire), tmp_center.y)
source_point = vector(tmp_center.x, tmp_center.y)
point_list = [source_point, target_point]
self.add_wire(point_list, vertical=False)
# store the shape of moat pins, need for route later
ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire)
ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m3")
self.moat_pins_right.append(moat_pin_route)
else: # need jog
# shift the center
# add rectangle at same layer (original)
intermediate_point = vector(tmp_center.x, tmp_center.y)
source_point = vector(source_center.x, source_center.y)
target_point = vector((target_egde_x + 0.5 * self.track_wire) ,tmp_center.y)
point_list = [source_point, intermediate_point, target_point]
self.add_wire(point_list, vertical=False)
# store the shape of moat pins, need for route later
ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire)
ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m3")
self.moat_pins_right.append(moat_pin_route)
def add_wire(self, point_list, vertical=False):
if vertical == True: # m4 line, need start via3(m3 -> m4), end via3(m3 -> m4)
if len(point_list) == 2: # direct connect
# start via
self.add_via(point=point_list[0],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[0],
from_layer="m4",
to_layer="m4") # shape
# connection
self.add_line(point_1=point_list[0],
point_2=point_list[1],
layer="m4")
# end via
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[1],
from_layer="m4",
to_layer="m4") # shape
elif len(point_list) == 3: # need intermediate point
# jog
self.add_line(point_1=point_list[0],
point_2=point_list[1],
layer="m3")
# start_via
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m3") # shape
# connection
self.add_line(point_1=point_list[1],
point_2=point_list[2],
layer="m4")
# end via
self.add_via(point=point_list[2],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[2],
from_layer="m4",
to_layer="m4") # shape
else: # m3 line, need start via2(m2 -> m3), end via3(m3 -> m4)
if len(point_list) == 2: # direct connect
# start via
self.add_via(point=point_list[0],
from_layer="m2",
to_layer="m3")
self.add_via(point=point_list[0],
from_layer="m3",
to_layer="m3") # shape
# connection
self.add_line(point_1=point_list[0],
point_2=point_list[1],
layer="m3")
# end via
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m3") # shape
elif len(point_list) == 3: # need intermediate point
# jog
self.add_line(point_1=point_list[0],
point_2=point_list[1],
layer="m2")
# start_via
self.add_via(point=point_list[1],
from_layer="m2",
to_layer="m3")
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m3") # shape
# connection
self.add_line(point_1=point_list[1],
point_2=point_list[2],
layer="m3")
# end via
self.add_via(point=point_list[2],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[2],
from_layer="m3",
to_layer="m3") # shape
def add_line(self, point_1, point_2, layer="m3"): # "m2", "m3", "m4"
self.design.add_path(layer, [point_1, point_2], self.track_wire)
def add_via(self, point, from_layer="m3", to_layer="m4"):
# via could be via2(m2 -> m3), via3(m3 -> m4)
# or a shape at same layer
if from_layer == to_layer:
self.design.add_rect_center(layer=from_layer,
offset=point,
width=self.track_wire,
height=self.track_wire)
else:
self.design.add_via_stack_center(from_layer=from_layer,
to_layer=to_layer,
offset=point)
def prepare_io_pins(self, io_pin_names):
# io_pin_names is a list
# find all the io pins
for pin_name in io_pin_names:
self.find_pins(pin_name)# pin now in self.pins
io_pin = next(iter(self.pins[pin_name]))
self.find_closest_edge(io_pin)
def get_closest_edge(self, pin):
""" Return a point's the closest edge and the edge's axis direction. Here we use to find the edge of moat vdd """
ll, ur = self.bbox
point = pin.center()
debug.warning("moat pin center -> {0}".format(point))
# Snap the pin to the perimeter and break the iteration
ll_diff_x = abs(point.x - ll.x)
ll_diff_y = abs(point.y - ll.y)
ur_diff_x = abs(point.x - ur.x)
ur_diff_y = abs(point.y - ur.y)
min_diff = min(ll_diff_x, ll_diff_y, ur_diff_x, ur_diff_y)
if min_diff == ll_diff_x:
return "left"
if min_diff == ll_diff_y:
return "bottom"
if min_diff == ur_diff_x:
return "right"
return "top"
def find_closest_edge(self, pin):
""" Use to find the edge, where the io pin locats """
ll, ur = self.bbox
#debug.warning("pin -> {0}".format(pin))
point = pin.center()
# Snap the pin to the perimeter and break the iteration
ll_diff_x = abs(point.x - ll.x)
ll_diff_y = abs(point.y - ll.y)
ur_diff_x = abs(point.x - ur.x)
ur_diff_y = abs(point.y - ur.y)
min_diff = min(ll_diff_x, ll_diff_y, ur_diff_x, ur_diff_y)
if min_diff == ll_diff_x:
self.io_pins_left.append(pin)
elif min_diff == ll_diff_y:
self.io_pins_bottom.append(pin)
elif min_diff == ur_diff_x:
self.io_pins_right.append(pin)
else:
self.io_pins_top.append(pin)
def add_side_pin(self, pin_name, side, num_vias=3, num_fake_pins=4):
""" Add supply pin to one side of the layout. """
ll, ur = self.bbox
vertical = side in ["left", "right"]
inner = pin_name == "vdd"
# Calculate wires' wideness
wideness = self.track_wire * num_vias + self.track_space * (num_vias - 1)
# Calculate the offset for the inner ring
if inner:
margin = wideness * 2
else:
margin = 0
# Calculate the lower left coordinate
if side == "top":
offset = vector(ll.x + margin, ur.y - wideness - margin)
elif side == "bottom":
offset = vector(ll.x + margin, ll.y + margin)
elif side == "left":
offset = vector(ll.x + margin, ll.y + margin)
elif side == "right":
offset = vector(ur.x - wideness - margin, ll.y + margin)
# Calculate width and height
shape = ur - ll
if vertical:
shape_width = wideness
shape_height = shape.y
else:
shape_width = shape.x
shape_height = wideness
if inner:
if vertical:
shape_height -= margin * 2
else:
shape_width -= margin * 2
# Add this new pin
layer = self.get_layer(int(vertical))
pin = self.design.add_layout_pin(text=pin_name,
layer=layer,
offset=offset,
width=shape_width,
height=shape_height)
return pin
def add_ring_pin(self, pin_name, num_vias=3, num_fake_pins=4):
""" Add the supply ring to the layout. """
# Add side pins
new_pins = []
for side in ["top", "bottom", "right", "left"]:
new_shape = self.add_side_pin(pin_name, side, num_vias, num_fake_pins)
ll, ur = new_shape.rect
rect = [ll, ur]
layer = self.get_layer(side in ["left", "right"])
new_pin = graph_shape(name=pin_name,
rect=rect,
layer_name_pp=layer)
new_pins.append(new_pin)
# Add vias to the corners
shift = self.track_wire + self.track_space
half_wide = self.track_wire / 2
for i in range(4):
ll, ur = new_pins[i].rect
if i % 2:
top_left = vector(ur.x - (num_vias - 1) * shift - half_wide, ll.y + (num_vias - 1) * shift + half_wide)
else:
top_left = vector(ll.x + half_wide, ur.y - half_wide)
for j in range(num_vias):
for k in range(num_vias):
offset = vector(top_left.x + j * shift, top_left.y - k * shift)
self.design.add_via_center(layers=self.layers,
offset=offset)
# Save side pins for routing
self.new_pins[pin_name] = new_pins
for pin in new_pins:
self.blockages.append(self.inflate_shape(pin))
def get_mst_pairs(self, pins):
"""
Return the pin pairs from the minimum spanning tree in a graph that
connects all pins together.
"""
pin_count = len(pins)
# Create an adjacency matrix that connects all pins
edges = [[0] * pin_count for i in range(pin_count)]
for i in range(pin_count):
for j in range(pin_count):
# Skip if they're the same pin
if i == j:
continue
# Skip if both pins are fake
if pins[i] in self.fake_pins and pins[j] in self.fake_pins:
continue
edges[i][j] = pins[i].distance(pins[j])
pin_connected = [False] * pin_count
pin_connected[0] = True
# Add the minimum cost edge in each iteration (Prim's)
mst_pairs = []
for i in range(pin_count - 1):
min_cost = float("inf")
s = 0
t = 0
# Iterate over already connected pins
for m in range(pin_count):
# Skip if not connected
if not pin_connected[m]:
continue
# Iterate over this pin's neighbors
for n in range(pin_count):
# Skip if already connected or isn't a neighbor
if pin_connected[n] or edges[m][n] == 0:
continue
# Choose this edge if it's better the the current one
if edges[m][n] < min_cost:
min_cost = edges[m][n]
s = m
t = n
pin_connected[t] = True
mst_pairs.append((pins[s], pins[t]))
return mst_pairs
def get_mst_with_ring(self, pins, ring_pins, pin_name="vdd"):
"""
Extend the MST logic to connect internal pins to the nearest external ring pins.
"""
# Prepare the pins that are allowed to connect to the moat pins.
# Specical handle gnd ring
candidate_pins = []
max_distance = 13
if pin_name == "gnd":
ring_pins = []
ring_pins = self.new_pins[pin_name]
for pin in pins:
for ring_pin in ring_pins:
dist = pin.distance(ring_pin)
if max_distance is None or dist <= max_distance:
candidate_pins.append(pin)
break
# Compute the MST for internal pins
mst_pairs = self.get_mst_pairs(pins)
# Connect each internal pin to the nearest external ring pin
used_ring_pins = set()
internal_to_ring_pairs = []
for pin in candidate_pins:
min_distance = float("inf")
nearest_ring_pin = None
for ring_pin in ring_pins:
if pin_name == "vdd" and ring_pin in used_ring_pins:
continue
dist = pin.distance(ring_pin)
if dist < min_distance:
min_distance = dist
nearest_ring_pin = ring_pin
# Add the connection to the nearest ring pin
if nearest_ring_pin:
internal_to_ring_pairs.append((pin, nearest_ring_pin))
# Mark the ring pin as used if the pin is VDD
if pin_name == "vdd":
used_ring_pins.add(nearest_ring_pin)
# Combine internal MST pairs and external connections
full_connections = mst_pairs + internal_to_ring_pairs
return full_connections
def get_new_pins(self, name):
""" Return the new supply pins added by this router. """
return self.new_pins[name]