From 61f5ff6ec44de5686ea89920f088105d5a07441a Mon Sep 17 00:00:00 2001 From: FriedrichWu Date: Wed, 30 Oct 2024 13:24:53 +0100 Subject: [PATCH] updata io_pin_placer --- compiler/router/io_pin_placer.py | 393 +++++++++++++++++++++++++++++-- 1 file changed, 369 insertions(+), 24 deletions(-) diff --git a/compiler/router/io_pin_placer.py b/compiler/router/io_pin_placer.py index 48f0f722..48aaa0d0 100644 --- a/compiler/router/io_pin_placer.py +++ b/compiler/router/io_pin_placer.py @@ -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 +