updata io_pin_placer

This commit is contained in:
FriedrichWu 2024-10-30 13:24:53 +01:00
parent ee6be23cfa
commit 61f5ff6ec4
1 changed files with 369 additions and 24 deletions

View File

@ -13,9 +13,6 @@ from .router import router
import re
class io_pin_placer(router):
"""
This is the signal escape router that uses the Hanan grid graph method.
"""
def __init__(self, layers, design, bbox=None):
@ -30,6 +27,9 @@ class io_pin_placer(router):
self.io_pins_added_right = []
self.io_pins_added_up = []
self.io_pins_added_down = []
# fake_pins, use for rename
self.io_pins_fake =[]
def get_closest_edge(self, point):
@ -51,8 +51,162 @@ class io_pin_placer(router):
if min_diff == ur_diff_x:
return "right", True
return "top", False
def initial_position(self, pins): # pins is list []
""" Set the IO pin center at the perimeter """
pattern_clk = r'^clk'
pattern_addr0 = r'^addr0'
pattern_addr1 = r'^addr1'
pattern_dout0 = r'^dout0'
pattern_dout1 = r'^dout1'
pattern_din = r'^din'
pattern_wmask = r'^wmask'
pattern_spare_wen = r'^spare_wen'
pattern_web = r'^web'
pattern_csb = r'^csb'
for pin in pins:
c = pin.center()
# Find the closest edge
edge, vertical = self.get_closest_edge(c)
if re.match(pattern_clk, pin.name):# clk, should be placed at horizontal edge
if edge == "bottom" or edge == "left":
edge = "bottom"
elif edge == "top" or edge == "right":
edge = "top"
self.store_position(pin, edge, vertical)
if re.match(pattern_addr0, pin.name): # all the addr0[] should be placed at left edge
if edge == "top" or edge == "left":
edge = "left"
vertical = True
elif edge == "bottom": # but for big sram, addr0[] may have pins at bottom, which is allowed
vertical = False
self.store_position(pin, edge, vertical)
if re.match(pattern_addr1, pin.name): # all the addr1[] should be placed at right edge
if edge == "bottom" or edge == "right":
edge = "right"
vertical = True
elif edge == "top": # but for big sram, addr1[] may have pins at top, which is allowed
vertical = False
self.store_position(pin, edge, vertical)
if re.match(pattern_din, pin.name): # din
self.store_position(pin, edge, vertical)
if re.match(pattern_wmask, pin.name): # wmask
self.store_position(pin, edge, vertical)
if re.match(pattern_spare_wen, pin.name): # spare_wen
self.store_position(pin, edge, vertical)
if re.match(pattern_csb, pin.name):# csb
self.store_position(pin, edge, vertical)
if re.match(pattern_web, pin.name): # web
self.store_position(pin, edge, vertical)
# special handle the dout pins, if r/rw ports
for pin in pins:
if re.match(pattern_dout0, pin.name):
edge = "bottom"
vertical = False
self.store_dout_position(pin, edge, vertical)
if re.match(pattern_dout1, pin.name):
edge = "top"
vertical = False
self.store_dout_position(pin, edge, vertical)
def check_overlap(self, pin_position, edge):
""" Return the suggested position to aviod overlap """
ll, ur = self.bbox
c = pin_position # original source pin center
offset = 0.95 + 0.19 # FIX: this is the magic number to overcome the ovetflow problem at the boundary, may need a method
add_distance = 0
if edge == "bottom":
fake_center = vector(c.x, ll.y - self.track_wire * 2 + offset)
pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down)
via_to_close = False
# if cannot direct place below the source pin, need move towards right, and ensure the min. distance between vias
while pin_to_close or via_to_close:
debug.warning("overlap, changing position")
add_distance = add_distance + 0.1
fake_center = vector(c.x + add_distance, ll.y - self.track_wire * 2 + offset)
pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down)
via_to_close = abs(fake_center.x - c.x) < 0.6
if edge == "top":
fake_center = vector(c.x, ur.y + self.track_wire * 2 - offset)
pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up)
via_to_close = False
# if cannot direct place below the source pin, need move towards right, and ensure the min. distance between vias
while pin_to_close or via_to_close:
debug.warning("overlap, changing position")
add_distance = add_distance + 0.1
fake_center = vector(c.x + add_distance, ur.y + self.track_wire * 2 - offset)
pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up)
via_to_close = abs(fake_center.x - c.x) < 0.6
return fake_center
def store_dout_position(self, pin, edge, vertical):
pin_position = pin.center()
pin_position = self.check_overlap(pin_position, edge)
# store the center position, rect, layer of fake pin, here make sure the pin in the gds will be big enough
layer = self.get_layer(int(not vertical))
half_wire_vector = vector([self.half_wire] * 2)
nll = pin_position - half_wire_vector - half_wire_vector
nur = pin_position + half_wire_vector + half_wire_vector
rect = [nll, nur]
#fake_pin = [pin.name() + "_" + "fake", pin_position, rect, layer]
fake_pin = graph_shape(name=pin.name + "_" + "fake",
rect=rect,
layer_name_pp=layer)
if edge == "left":
self.io_pins_added_left.append(fake_pin)
elif edge == "bottom":
self.io_pins_added_down.append(fake_pin)
elif edge == "right":
self.io_pins_added_right.append(fake_pin)
elif edge == "top":
self.io_pins_added_up.append(fake_pin)
self.io_pins_fake.append(fake_pin)
debug.warning("pin added: {0}".format(fake_pin))
def store_position(self, pin, edge, vertical): # also need to store the source pin
ll, ur = self.bbox
c = pin.center()
offset = 0.95 + 0.19 # FIX: this is the magic number to overcome the ovetflow problem at the boundary, may need a method
if edge == "left":
fake_center = vector(ll.x - self.track_wire * 2 + offset, c.y)
if edge == "bottom":
fake_center = vector(c.x, ll.y - self.track_wire * 2 + offset)
if edge == "right":
fake_center = vector(ur.x + self.track_wire * 2 - offset, c.y)
if edge == "top":
fake_center = vector(c.x, ur.y + self.track_wire * 2 - offset)
# store the center position, rect, layer of fake pin, here make sure the pin in the gds will be big enough
layer = self.get_layer(int(not vertical))
half_wire_vector = vector([self.half_wire] * 2)
nll = fake_center - half_wire_vector - half_wire_vector
nur = fake_center + half_wire_vector + half_wire_vector
rect = [nll, nur]
#fake_pin = [pin.name() + "_" + "fake", fake_center, rect, layer]
fake_pin = graph_shape(name=pin.name + "_" + "fake",
rect=rect,
layer_name_pp=layer)
if edge == "left":
self.io_pins_added_left.append(fake_pin)
elif edge == "bottom":
self.io_pins_added_down.append(fake_pin)
elif edge == "right":
self.io_pins_added_right.append(fake_pin)
elif edge == "top":
self.io_pins_added_up.append(fake_pin)
self.io_pins_fake.append(fake_pin)
debug.warning("pin added: {0}".format(fake_pin))
def create_fake_pin(self, pin):
""" Create a fake pin on the perimeter orthogonal to the given pin. """
@ -63,51 +217,70 @@ class io_pin_placer(router):
print(pin.name)
# Find the closest edge
edge, vertical = self.get_closest_edge(c)
# Relocate the pin position of addr/dout
pattern_addr = r'^addr'
pattern_dout = r'^dout'
if re.match(pattern_addr, pin.name):# all the addr[] should be placed at vertical edge
if edge == "top" or edge == "left":
edge = "left"
vertical = True
elif edge == "bottom" or edge == "right":
edge = "right"
vertical = True
if re.match(pattern_dout, pin.name):# all the dout[] should be placed at horizontal edge
if edge == "bottom" or edge == "right":
edge = "bottom"
vertical = False
elif edge == "top" or edge == "left":
edge = "top"
vertical = False
offset = 0.95 + 0.19 # FIX: this is the magic number to overcome the ovetflow problem at the boundary, may need a method
add_distance = 0
# Keep the fake pin out of the SRAM layout are so that they won't be
# blocked by previous signals if they're on the same orthogonal line
if edge == "left":
fake_center = vector(ll.x - self.track_wire * 2 + offset, c.y)
is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.8 + self.half_wire * 4)for pin_added in self.io_pins_added_left)
is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_left)
while is_too_close:
debug.warning("overlap, changing position")
add_distance = add_distance + 0.8 + self.half_wire * 4
add_distance = add_distance + 0.1#+ 0.4 + self.half_wire * 4
fake_center = vector(ll.x - self.track_wire * 2 + offset, c.y + add_distance)
is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.8 + self.half_wire * 4)for pin_added in self.io_pins_added_left)
is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_left)
if edge == "bottom":
fake_center = vector(c.x, ll.y - self.track_wire * 2 + offset)
is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.8 + self.half_wire * 4)for pin_added in self.io_pins_added_down)
is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down)
while is_too_close:
debug.warning("overlap, changing position")
add_distance = add_distance + 0.8 + self.half_wire * 4
add_distance = add_distance + 0.1#0.4 + self.half_wire * 4
fake_center = vector(c.x + add_distance, ll.y - self.track_wire * 2 + offset)
is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.8 + self.half_wire * 4)for pin_added in self.io_pins_added_down)
is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down)
if edge == "right":
fake_center = vector(ur.x + self.track_wire * 2 - offset, c.y)
is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.8 + self.half_wire * 4)for pin_added in self.io_pins_added_right)
is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_right)
while is_too_close:
debug.warning("overlap, changing position")
add_distance = add_distance + 0.8 + self.half_wire * 4
add_distance = add_distance + 0.1#0.4 + self.half_wire * 4
fake_center = vector(ur.x + self.track_wire * 2 - offset, c.y + add_distance)
# debug
for pin_added in self.io_pins_added_right:
dis = abs(pin_added.center().y - fake_center.y)
debug.warning("current position is {0}".format(fake_center))
debug.warning("distance from {0} is {1}".format(pin_added, dis))
debug.warning("must disrance is {0}".format(0.8 + self.half_wire * 4))
is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.8 + self.half_wire * 4)for pin_added in self.io_pins_added_right)
debug.warning("must disrance is {0}".format(0.4 + self.half_wire * 4))
is_too_close = any(abs(pin_added.center().y - fake_center.y) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_right)
if edge == "top":
fake_center = vector(c.x, ur.y + self.track_wire * 2 - offset)
is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.8 + self.half_wire * 4)for pin_added in self.io_pins_added_up)
is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up)
while is_too_close:
debug.warning("overlap, changing position")
add_distance = add_distance + 0.8 + self.half_wire * 4
add_distance = add_distance + 0.1#0.4 + self.half_wire * 4
fake_center = vector(c.x + add_distance, ur.y + self.track_wire * 2 - offset)
is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.8 + self.half_wire * 4)for pin_added in self.io_pins_added_up)
is_too_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up)
# Create the fake pin shape, here make sure the pin in the gds will be big enough
layer = self.get_layer(int(not vertical))
@ -133,11 +306,11 @@ class io_pin_placer(router):
self.io_pins_added_up.append(pin)
debug.warning("pin added: {0}".format(pin))
return pin
return pin, vertical, add_distance
def add_io_pins(self, pin_names):
""" Add IO pins on the edges without routing them. """
""" Add IO pins on the edges WITHOUT routing them. """
debug.info(1, "Adding IO pins on the perimeter...")
# Prepare GDS reader (if necessary for pin/blockage identification)
@ -147,16 +320,188 @@ class io_pin_placer(router):
for name in pin_names:
self.find_pins(name)# this will add the pins to the self.pins
# Replace layout pins with those on the perimeter
# inital position
pin_list = []
for name in self.pins:
pin = next(iter(self.pins[name]))
fake_pin = self.create_fake_pin(pin)
self.design.replace_layout_pin(name, fake_pin) # if do not have replace_layout_pin, final gds won't have io pins
pin_list.append(pin)
self.initial_position(pin_list)
# Change IO pin names, which means "internal name" will be used, and internal io pins will not have label such as "dout0[0]"
self.replace_layout_pins(pin_names)
def add_io_pins_connected(self, pin_names):
""" Add IO pins on the edges WITH routing them. """
debug.info(1, "Adding IO pins on the perimeter...")
# Prepare GDS reader (if necessary for pin/blockage identification)
self.prepare_gds_reader()
# Find pins to be added (without routing)
for name in pin_names:
self.find_pins(name)# this will add the pins to the self.pins
debug.warning("the pins in pin_name -> {0}".format(name))
# inital position
pin_list = []
for name in self.pins:
pin = next(iter(self.pins[name]))
pin_list.append(pin)
debug.warning("the pins in self.pins -> {0}".format(name))
self.initial_position(pin_list)
# add fake io pins at the perimeter, which will be used for routing
for fake_pin in self.io_pins_fake:
self.design.add_layout_pin(text=fake_pin.name,
layer=fake_pin.layer,
offset=fake_pin.ll(),
width=fake_pin.width(),
height=fake_pin.height())
# connect the source_pin and io_pin(target)
self.connect_pins(pin_names)
# remove the fake pin before change the name, in order to avoid possible problem
for fake_pin in self.io_pins_fake:
self.remove_io_pins(fake_pin.name)
# Change IO pin names, which means "internal name" will be used, and internal io pins will not have label such as "dout0[0]"
self.replace_layout_pins(pin_names)
def connect_pins(self, pin_names): # pin_names should be a list
""" Add IO pins on the edges, and connect them to the internal one, not-graph like process """
debug.info(1, "connecting to io pins...")
pattern_dout = r'^dout'
for pin_name in pin_names:
# get pin pairs ready
source_pin = next(iter(self.pins[pin_name]))
for fake_pin in self.io_pins_fake:
if pin_name + "_" + "fake" == fake_pin.name:
target_pin = fake_pin
break
# special hanlde dout pins
if re.match(pattern_dout, pin_name):
number_str = re.findall(r'\[(\d+)\]', pin_name)
if number_str:
number = int(number_str[0])
if number % 2 == 0:
is_up = True
else:
is_up = False
point_list = self.decide_point(source_pin, target_pin, is_up)
self.add_wire(point_list)
# other pins
else:
debug.warning("source{0}".format(source_pin.center()))
debug.warning("target{0}".format(target_pin.center()))
point_list = self.decide_point(source_pin, target_pin)
debug.warning("point_list->{0}".format(point_list))
self.add_wire(point_list)
def add_wire(self, point_list):
if len(point_list) == 2:
# direct connect
self.add_line(point_list[0], point_list[1])
elif len(point_list) == 4:
# intermediate points
self.add_line(point_list[0], point_list[1])
self.add_via(point_list[1])
self.add_line(point_list[1], point_list[2])
self.add_via(point_list[2])
self.add_line(point_list[2], point_list[3])
def add_line(self, point_1, point_2):
if round(point_1.y, 3) == round(point_2.y, 3):
# horizontal m3
self.design.add_path(self.get_layer(False), [point_1, point_2])
else:
# vertical m4
self.design.add_path(self.get_layer(True), [point_1, point_2])
def add_via(self, point):
# currently only m3-m4 vias are supported in this method
# usd in order to make "z" shape routing only
self.design.add_via_stack_center(from_layer=self.get_layer(False),
to_layer=self.get_layer(True),
offset=point)
def add_start_via(self, point):
# currently only m3-m4 vias are supported in this method
# used in source_pin only
self.design.add_via_stack_center(from_layer=self.get_layer(False),
to_layer=self.get_layer(True),
offset=point)
def decide_point(self, source_pin, target_pin, is_up=False):
ll, ur = self.bbox
offset = 0.95 + 0.19 # FIX: this is the magic number to overcome the ovetflow problem at the boundary, may need a method
# internal -> left
if round(target_pin.center().x, 3) == round(ll.x - self.track_wire * 2 + offset, 3):
# direct connect possible
return [source_pin.rc(), target_pin.lc()] # FIX: not sure if shape overlap in met3 allowed, but seems OK
# internal -> right
if round(target_pin.center().x, 3) == round(ur.x + self.track_wire * 2 - offset, 3):
# direct connect possible
return [source_pin.lc(), target_pin.rc()]
# internal -> top, need to add start_via m3->m4
if round(target_pin.center().y, 3) == round(ur.y + self.track_wire * 2 - offset, 3):
self.add_start_via(source_pin.center())
if round(target_pin.center().x, 3) == round(source_pin.center().x, 3):
# direct connect possible
return [source_pin.bc(), target_pin.uc()]
else:
# need intermediate point
via_basic_y = self.design.bank.height + 3 # 3 is magic number, make sure out of bank area
is_up = not is_up# Be attention, for channel at the top, the is_up should be inverted! Otherwise will cause overlap!
if is_up:
via_basic_y = via_basic_y + 0.5
else:
via_basic_y = via_basic_y - 0.5
point_1 = vector(source_pin.center().x, via_basic_y)
point_2 = vector(target_pin.center().x, via_basic_y)
return [source_pin.bc(), point_1, point_2, target_pin.uc()]
# internal -> bottom, need to add start_via m3->m4
if round(target_pin.center().y, 3) == round(ll.y - self.track_wire * 2 + offset, 3):
self.add_start_via(source_pin.center())
if round(target_pin.center().x, 3) == round(source_pin.center().x, 3):
# direct connect possible
return [source_pin.uc(), target_pin.bc()]
else:
# need intermediate point
via_basic_y = ll.y + 21 # 21 is magic number, make sure out of dff area
if is_up:
via_basic_y = via_basic_y + 0.5
else:
via_basic_y = via_basic_y - 0.5
point_1 = vector(source_pin.center().x, via_basic_y)
point_2 = vector(target_pin.center().x, via_basic_y)
return [source_pin.uc(), point_1, point_2, target_pin.bc()]
def remove_io_pins(self, pin_name):
# remove io pin in gds, so we could reroute
self.design.remove_layout_pin(pin_name)
def replace_layout_pins(self, pin_names):
""" Change the IO pin names with new ones around the perimeter. """
for pin_name in pin_names:
perimeter_pin_name = pin_name + "_" + "fake"
for pin in self.io_pins_fake:
if pin.name == perimeter_pin_name:
perimeter_pin = pin
self.design.replace_layout_pin(pin_name, perimeter_pin)
break