mirror of https://github.com/VLSIDA/OpenRAM.git
450 lines
20 KiB
Python
450 lines
20 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.base.vector3d import vector3d
|
|
from openram import OPTS
|
|
from .graph import graph
|
|
from .graph_shape import graph_shape
|
|
from .router import router
|
|
import re
|
|
|
|
class io_pin_placer(router):
|
|
|
|
def __init__(self, layers, design, bbox=None):
|
|
|
|
# `router` is the base router class
|
|
router.__init__(self, layers, design, bbox)
|
|
|
|
# New pins are the side supply pins
|
|
self.new_pins = {}
|
|
|
|
# added_io_pins
|
|
self.io_pins_added_left = []
|
|
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):
|
|
""" Return a point's the closest edge and the edge's axis direction. """
|
|
|
|
ll, ur = self.bbox
|
|
|
|
# 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", True
|
|
if min_diff == ll_diff_y:
|
|
return "bottom", False
|
|
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 vertical edge
|
|
if edge == "bottom" or edge == "left":#clk0
|
|
edge = "left"
|
|
elif edge == "top" or edge == "right":#clk1
|
|
edge = "right"
|
|
vertical = True
|
|
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()
|
|
pattern_clk = r'^clk'
|
|
pattern_csb = r'^csb'
|
|
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 re.match(pattern_clk, pin.name): # clk0 need to be higher at left edge, 0.32 is magic number
|
|
fake_center = vector(ll.x - self.track_wire * 2 + offset, c.y + 0.32)
|
|
if re.match(pattern_csb, pin.name): # csb0 need to be lower at left edge, 0.32 is magic number
|
|
fake_center = vector(ll.x - self.track_wire * 2 + offset, c.y - 0.32)
|
|
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 re.match(pattern_clk, pin.name): # clk1 need to be lower at right edge, 0.32 is magic number
|
|
fake_center = vector(ur.x + self.track_wire * 2 - offset, c.y - 0.32)
|
|
if re.match(pattern_csb, pin.name): # csb0 need to be higher at right edge, 0.32 is magic number
|
|
fake_center = vector(ur.x + self.track_wire * 2 - offset, c.y + 0.32)
|
|
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 add_io_pins(self, pin_names):
|
|
""" 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)
|
|
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
|
|
|
|
# inital position
|
|
pin_list = []
|
|
for name in self.pins:
|
|
pin = next(iter(self.pins[name]))
|
|
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 add_big_plate(self, layer, offset, width, height):
|
|
# add rectagle at internal source pin, which avoid jog/non-preferred routing, but could implement shift-routing
|
|
# used in source_pin only
|
|
# offset->vertor(...), it is bottom-left position
|
|
self.design.add_rect(layer=layer,
|
|
offset=offset,
|
|
width=width,
|
|
height=height)
|
|
|
|
|
|
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
|
|
pattern_clk = r'^clk'
|
|
pattern_csb = r'^csb'
|
|
# internal -> left
|
|
if round(target_pin.center().x, 3) == round(ll.x - self.track_wire * 2 + offset, 3):
|
|
# special handle clk0
|
|
if re.match(pattern_clk, source_pin.name):
|
|
return [vector(source_pin.rc().x, source_pin.rc().y + 0.32), target_pin.lc()]# 0.32 should be same in initial_position
|
|
# special handel csb0
|
|
elif re.match(pattern_csb, source_pin.name):
|
|
return [vector(source_pin.rc().x, source_pin.rc().y - 0.32), target_pin.lc()]# 0.32 should be same in initial_position
|
|
else:
|
|
# 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):
|
|
# special handel clk1
|
|
if re.match(pattern_clk, source_pin.name):
|
|
return [vector(source_pin.lc().x, source_pin.lc().y - 0.32), target_pin.rc()]# 0.32 should be same in initial_position
|
|
# special handle csb0
|
|
elif re.match(pattern_csb, source_pin.name):
|
|
return [vector(source_pin.lc().x, source_pin.lc().y + 0.32), target_pin.rc()]# 0.32 should be same in initial_position
|
|
else:
|
|
# 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
|
|
via_basic_y = self.design.bank_inst.uy() + 3 * self.design.m3_pitch
|
|
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
|
|
via_basic_y = via_basic_y + self.design.m3_pitch
|
|
else:
|
|
#via_basic_y = via_basic_y - 0.5
|
|
via_basic_y = via_basic_y - self.design.m3_pitch
|
|
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 + 22 # 22 is magic number, make sure out of dff area
|
|
via_basic_y = self.design.data_dff_insts[0].uy() + 3 * self.design.m3_pitch
|
|
if is_up:
|
|
#via_basic_y = via_basic_y + 0.5
|
|
via_basic_y = via_basic_y + self.design.m3_pitch
|
|
else:
|
|
#via_basic_y = via_basic_y - 0.5
|
|
via_basic_y = via_basic_y - self.design.m3_pitch
|
|
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
|
|
|
|
|
|
|
|
|
|
|