From 878a9cee8a6fc24649294e2c5d94b33453527674 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 15 Dec 2020 16:01:39 -0800 Subject: [PATCH 01/12] Add channel routes as flat instances to appease Magic extraction. --- compiler/base/hierarchy_layout.py | 33 ++++++++++++++++++++++++------- compiler/sram/sram_1bank.py | 7 +++++-- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 532b0f7d..4fd42635 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -236,7 +236,7 @@ class layout(): # This is commented out for runtime reasons # debug.info(4, "instance list: " + ",".join(x.name for x in self.insts)) return self.insts[-1] - + def get_inst(self, name): """ Retrieve an instance by name """ for inst in self.insts: @@ -244,6 +244,19 @@ class layout(): return inst return None + def add_flat_inst(self, name, mod, offset=[0, 0]): + """ Copies all of the items in instance into this module """ + for item in mod.objs: + item.offset += offset + self.objs.append(item) + for item in mod.insts: + item.offset += offset + self.insts.append(item) + debug.check(len(item.mod.pins) == 0, "Cannot add flat instance with subinstances.") + self.connect_inst([]) + debug.info(3, "adding flat instance {}".format(name)) + return None + def add_rect(self, layer, offset, width=None, height=None): """ Adds a rectangle on a given layer,offset with width and height @@ -1078,18 +1091,24 @@ class layout(): """ import channel_route cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self) - self.add_inst(cr.name, cr) - self.connect_inst([]) - + # This causes problem in magic since it sometimes cannot extract connectivity of isntances + # with no active devices. + # self.add_inst(cr.name, cr) + # self.connect_inst([]) + self.add_flat_inst(cr.name, cr) + def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None): """ Wrapper to create a horizontal channel route """ import channel_route cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self) - self.add_inst(cr.name, cr) - self.connect_inst([]) - + # This causes problem in magic since it sometimes cannot extract connectivity of isntances + # with no active devices. + # self.add_inst(cr.name, cr) + # self.connect_inst([]) + self.add_flat_inst(cr.name, cr) + def add_boundary(self, ll=vector(0, 0), ur=None): """ Add boundary for debugging dimensions """ if OPTS.netlist_only: diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 55ea7527..c8dd7601 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -414,8 +414,11 @@ class sram_1bank(sram_base): layer_stack=self.m1_stack, parent=self) if add_routes: - self.add_inst(cr.name, cr) - self.connect_inst([]) + # This causes problem in magic since it sometimes cannot extract connectivity of isntances + # with no active devices. + # self.add_inst(cr.name, cr) + # self.connect_inst([]) + self.add_flat_inst(cr.name, cr) else: self.col_addr_bus_size[port] = cr.height From f55b57033d467d1b04b37a5229051ca78b83a547 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 15 Dec 2020 16:37:23 -0800 Subject: [PATCH 02/12] Route col decoder address with data bits in channel --- compiler/sram/sram_1bank.py | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index c8dd7601..18b1da99 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -402,28 +402,6 @@ class sram_1bank(sram_base): bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, dff_pins))) - if port == 0: - offset = vector(self.control_logic_insts[port].rx() + self.dff.width, - - self.data_bus_size[port] + 2 * self.m1_pitch) - else: - offset = vector(0, - self.bank.height + 2 * self.m1_space) - - cr = channel_route.channel_route(netlist=route_map, - offset=offset, - layer_stack=self.m1_stack, - parent=self) - if add_routes: - # This causes problem in magic since it sometimes cannot extract connectivity of isntances - # with no active devices. - # self.add_inst(cr.name, cr) - # self.connect_inst([]) - self.add_flat_inst(cr.name, cr) - else: - self.col_addr_bus_size[port] = cr.height - - route_map = [] - # wmask dff if self.num_wmasks > 0 and port in self.write_ports: dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)] @@ -473,8 +451,11 @@ class sram_1bank(sram_base): layer_stack=layer_stack, parent=self) if add_routes: - self.add_inst(cr.name, cr) - self.connect_inst([]) + # This causes problem in magic since it sometimes cannot extract connectivity of isntances + # with no active devices. + # self.add_inst(cr.name, cr) + # self.connect_inst([]) + self.add_flat_inst(cr.name, cr) else: self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap else: @@ -485,8 +466,11 @@ class sram_1bank(sram_base): layer_stack=layer_stack, parent=self) if add_routes: - self.add_inst(cr.name, cr) - self.connect_inst([]) + # This causes problem in magic since it sometimes cannot extract connectivity of isntances + # with no active devices. + # self.add_inst(cr.name, cr) + # self.connect_inst([]) + self.add_flat_inst(cr.name, cr) else: self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap From d5ed45dadfd99c0d5d5ea961d40549ac54a65752 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Dec 2020 16:42:19 -0800 Subject: [PATCH 03/12] Make default router tree router --- compiler/router/supply_tree_router.py | 107 +++++++++++++++----------- compiler/sram/sram_base.py | 15 ++-- compiler/tests/configs/config.py | 2 +- 3 files changed, 70 insertions(+), 54 deletions(-) diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 9d98c2b0..c065feb1 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -5,20 +5,14 @@ #(acting for and on behalf of Oklahoma State University) #All rights reserved. # -import gdsMill -import tech -import math import debug -from globals import OPTS,print_time -from contact import contact -from pin_group import pin_group -from pin_layout import pin_layout -from vector3d import vector3d +from globals import print_time from router import router -from direction import direction from datetime import datetime -import grid import grid_utils +from scipy.sparse import csr_matrix +from scipy.sparse.csgraph import minimum_spanning_tree + class supply_tree_router(router): """ @@ -36,7 +30,6 @@ class supply_tree_router(router): router.__init__(self, layers, design, gds_filename, self.rail_track_width) - def create_routing_grid(self): """ Create a sprase routing grid with A* expansion functions. @@ -133,7 +126,6 @@ class supply_tree_router(router): self.set_blockages(blockage_grids,False) - def route_pins(self, pin_name): """ This will route each of the remaining pin components to the other pins. @@ -144,48 +136,73 @@ class supply_tree_router(router): debug.info(1,"Maze routing {0} with {1} pin components to connect.".format(pin_name, remaining_components)) - for index,pg in enumerate(self.pin_groups[pin_name]): - if pg.is_routed(): - continue + # Create full graph + pin_size = len(self.pin_groups[pin_name]) + adj_matrix = [[0] * pin_size for i in range(pin_size)] - debug.info(1,"Routing component {0} {1}".format(pin_name, index)) + for index1,pg1 in enumerate(self.pin_groups[pin_name]): + for index2,pg2 in enumerate(self.pin_groups[pin_name]): + if index1>=index2: + continue + dist = int(grid_utils.distance_set(list(pg1.grids)[0], pg2.grids)) + adj_matrix[index1][index2] = dist - # Clear everything in the routing grid. - self.rg.reinit() + # Find MST + X = csr_matrix(adj_matrix) + Tcsr = minimum_spanning_tree(X) + mst = Tcsr.toarray().astype(int) + connections = [] + for x in range(pin_size): + for y in range(pin_size): + if x >= y: + continue + if mst[x][y]>0: + connections.append((x, y)) + + # Route MST components + for (src, dest) in connections: + self.route_signal(pin_name, src, dest) + + #self.write_debug_gds("final.gds", True) + #return - # This is inefficient since it is non-incremental, but it was - # easier to debug. - self.prepare_blockages(pin_name) + def route_signal(self, pin_name, src_idx, dest_idx): + + debug.info(2, "Routing {0} to {1} on pin {2}".format(src_idx, dest_idx, pin_name)) + + # Clear everything in the routing grid. + self.rg.reinit() - # Add the single component of the pin as the source - # which unmarks it as a blockage too - self.add_pin_component_source(pin_name,index) + # This is inefficient since it is non-incremental, but it was + # easier to debug. + self.prepare_blockages(pin_name) - # Marks all pin components except index as target - self.add_pin_component_target_except(pin_name,index) - # Add the prevous paths as a target too - self.add_path_target(self.paths) + # Add the single component of the pin as the source + # which unmarks it as a blockage too + self.add_pin_component_source(pin_name, src_idx) - print("SOURCE: ") - for k,v in self.rg.map.items(): - if v.source: - print(k) + # Marks all pin components except index as target + self.add_pin_component_target(pin_name, dest_idx) + + # Add the prevous paths as a target too + #self.add_path_target(self.paths) - print("TARGET: ") - for k,v in self.rg.map.items(): - if v.target: - print(k) + # print("SOURCE: ") + # for k,v in self.rg.map.items(): + # if v.source: + # print(k) + + # print("TARGET: ") + # for k,v in self.rg.map.items(): + # if v.target: + # print(k) - import pdb; pdb.set_trace() - if index==1: - self.write_debug_gds("debug{}.gds".format(pin_name),False) + # Actually run the A* router + if not self.run_router(detour_scale=5): + self.write_debug_gds("debug_route.gds", True) - # Actually run the A* router - if not self.run_router(detour_scale=5): - self.write_debug_gds("debug_route.gds",True) - - #if index==3 and pin_name=="vdd": - # self.write_debug_gds("route.gds",False) + # if index==3 and pin_name=="vdd": + # self.write_debug_gds("route.gds",False) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 9fbdec64..85346037 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -229,20 +229,19 @@ class sram_base(design, verilog, lef): # Do not route the power supply (leave as must-connect pins) return - grid_stack = set() try: from tech import power_grid grid_stack = power_grid except ImportError: # if no power_grid is specified by tech we use sensible defaults - import tech - if "m4" in tech.layer: - # Route a M3/M4 grid - grid_stack = self.m3_stack - elif "m3" in tech.layer: - grid_stack =("m3",) + # Route a M3/M4 grid + grid_stack = self.m3_stack - from supply_grid_router import supply_grid_router as router + if OPTS.route_supplies == "grid": + from supply_grid_router import supply_grid_router as router + elif OPTS.route_supplies: + from supply_tree_router import supply_tree_router as router + rtr=router(grid_stack, self) rtr.route() diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py index d19ae02e..0682ef6e 100644 --- a/compiler/tests/configs/config.py +++ b/compiler/tests/configs/config.py @@ -12,6 +12,6 @@ num_words = 16 tech_name = OPTS.tech_name nominal_corner_only = True -route_supplies = True +route_supplies = "tree" check_lvsdrc = True From 2b0f8bf2637a9a4c8623d5e6f95a0e9c9f7c7a11 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Dec 2020 16:57:29 -0800 Subject: [PATCH 04/12] Don't exit with error when source is target for maze router --- compiler/router/router.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/router/router.py b/compiler/router/router.py index a6df4f0b..bd99ab2c 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -1000,8 +1000,7 @@ class router(router_tech): # Double check source and taget are not same node, if so, we are done! for k, v in self.rg.map.items(): if v.source and v.target: - debug.error("Grid cell is source and target! {}".format(k)) - return False + return True # returns the path in tracks (path, cost) = self.rg.route(detour_scale) From 11384ef926c06a01995ebaad4f41be61fa647c55 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Dec 2020 16:57:40 -0800 Subject: [PATCH 05/12] Improve output messaging of tree router --- compiler/router/supply_tree_router.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index c065feb1..d67ee9ce 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -133,8 +133,8 @@ class supply_tree_router(router): """ remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name]) - debug.info(1,"Maze routing {0} with {1} pin components to connect.".format(pin_name, - remaining_components)) + debug.info(1,"Routing {0} with {1} pin components to connect.".format(pin_name, + remaining_components)) # Create full graph pin_size = len(self.pin_groups[pin_name]) @@ -158,6 +158,8 @@ class supply_tree_router(router): continue if mst[x][y]>0: connections.append((x, y)) + + debug.info(1,"MST has {0} segments.".format(len(connections))) # Route MST components for (src, dest) in connections: From 56c4c8972041cc9923b93a288808e59c6bd37a92 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 17 Dec 2020 01:34:53 -0800 Subject: [PATCH 06/12] Adjusted error margin for period in analytical model and added check in model test. --- compiler/characterizer/delay.py | 5 +++-- compiler/tests/21_model_delay_test.py | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 867b3cdb..5d2dd09a 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -1362,8 +1362,9 @@ class delay(simulation): else: debug.error("Measurement name not recognized: {}".format(mname), 1) - # Estimate the period as double the delay with margin - period_margin = 0.1 + # Margin for error in period. Calculated by averaging required margin for a small and large + # memory. FIXME: margin is quite large, should be looked into. + period_margin = 1.85 sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin, "leakage_power": power.leakage} diff --git a/compiler/tests/21_model_delay_test.py b/compiler/tests/21_model_delay_test.py index e711c7ca..af1dfa12 100755 --- a/compiler/tests/21_model_delay_test.py +++ b/compiler/tests/21_model_delay_test.py @@ -63,7 +63,9 @@ class model_delay_test(openram_test): # Only compare the delays spice_delays = {key:value for key, value in spice_data.items() if 'delay' in key} + spice_delays['min_period'] = spice_data['min_period'] model_delays = {key:value for key, value in model_data.items() if 'delay' in key} + model_delays['min_period'] = model_data['min_period'] debug.info(1,"Spice Delays={}".format(spice_delays)) debug.info(1,"Model Delays={}".format(model_delays)) From c0ab0af201571b711eeb5fc75fbc4d908f35b36d Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 17 Dec 2020 11:39:17 -0800 Subject: [PATCH 07/12] Retry routes with expanding detour allowed. --- compiler/router/router.py | 7 ++-- compiler/router/supply_tree_router.py | 51 ++++++++++----------------- compiler/sram/sram_1bank.py | 4 +-- 3 files changed, 22 insertions(+), 40 deletions(-) diff --git a/compiler/router/router.py b/compiler/router/router.py index bd99ab2c..d63e1739 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -865,7 +865,7 @@ class router(router_tech): debug.check(index Date: Thu, 17 Dec 2020 11:48:08 -0800 Subject: [PATCH 08/12] Move supply pins for wmask and array to edge to avoid channel route congestion --- compiler/modules/write_mask_and_array.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index ae5d8550..83235613 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -138,12 +138,11 @@ class write_mask_and_array(design.design): self.add_via_stack_center(from_layer=en_pin.layer, to_layer="m3", offset=en_pos) - - for supply in ["gnd", "vdd"]: - supply_pin=self.and2_insts[i].get_pin(supply) - self.add_power_pin(supply, supply_pin.center(), start_layer=supply_pin.layer) - + for supply in ["gnd", "vdd"]: supply_pin_left = self.and2_insts[0].get_pin(supply) supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply) self.add_path(supply_pin_left.layer, [supply_pin_left.lc(), supply_pin_right.rc()]) + self.add_power_pin(supply, supply_pin_left.center(), start_layer=supply_pin_left.layer) + self.add_power_pin(supply, supply_pin_right.center(), start_layer=supply_pin_right.layer) + From bad735fd893ee52eb2c8fb3daac6f506b8629994 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 17 Dec 2020 15:24:44 -0800 Subject: [PATCH 09/12] Uncomment flatten as it is neeeded for correct extraction --- compiler/verify/magic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 9d2ace8c..b3d2d99b 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -89,7 +89,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa f.write("drc off\n") f.write("gds polygon subcell true\n") f.write("gds warning default\n") - f.write("#gds flatten true\n") + f.write("gds flatten true\n") f.write("gds readonly true\n") f.write("#gds ordering true\n") f.write("gds read {}\n".format(gds_name)) From 29880a0b5aa55dc1efc2e5adb90a8b562872f690 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 17 Dec 2020 15:25:19 -0800 Subject: [PATCH 10/12] Write mask and array supply pins on the ends --- compiler/modules/write_mask_and_array.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 83235613..77804253 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -11,7 +11,7 @@ import math from sram_factory import factory from vector import vector from globals import OPTS -from tech import layer + class write_mask_and_array(design.design): """ @@ -95,9 +95,9 @@ class write_mask_and_array(design.design): if not self.offsets: self.offsets = [] for i in range(self.columns): - self.offsets.append(i * self.driver_spacing) + self.offsets.append((i + self.write_size - 1) * self.driver_spacing) - self.width = self.offsets[-1] + self.driver_spacing + self.width = self.columns * self.bitcell.width self.height = self.and2.height write_bits = self.columns / self.num_wmasks @@ -140,9 +140,11 @@ class write_mask_and_array(design.design): offset=en_pos) for supply in ["gnd", "vdd"]: - supply_pin_left = self.and2_insts[0].get_pin(supply) - supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply) - self.add_path(supply_pin_left.layer, [supply_pin_left.lc(), supply_pin_right.rc()]) - self.add_power_pin(supply, supply_pin_left.center(), start_layer=supply_pin_left.layer) - self.add_power_pin(supply, supply_pin_right.center(), start_layer=supply_pin_right.layer) + supply_pin = self.and2_insts[0].get_pin(supply) + supply_pin_yoffset = supply_pin.cy() + left_loc = vector(0, supply_pin_yoffset) + right_loc = vector(self.width, supply_pin_yoffset) + self.add_path(supply_pin.layer, [left_loc, right_loc]) + self.add_power_pin(supply, left_loc, start_layer=supply_pin.layer) + self.add_power_pin(supply, right_loc, start_layer=supply_pin.layer) From 946ad66e7a875d9187ec013105c2c0f101aae73c Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 18 Dec 2020 09:22:10 -0800 Subject: [PATCH 11/12] Make width based on bitcell offsets, not number of columns --- compiler/modules/write_mask_and_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 77804253..f25a71c5 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -97,7 +97,7 @@ class write_mask_and_array(design.design): for i in range(self.columns): self.offsets.append((i + self.write_size - 1) * self.driver_spacing) - self.width = self.columns * self.bitcell.width + self.width = self.offsets[-1] + self.bitcell.width self.height = self.and2.height write_bits = self.columns / self.num_wmasks From 3c08dfcca5e2c3b3b28811de1c24e03c7dd43848 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 18 Dec 2020 11:09:10 -0800 Subject: [PATCH 12/12] Enable single pin for vdd/gnd after supply router --- compiler/router/router.py | 13 +++++++++++++ compiler/router/signal_grid.py | 1 - compiler/sram/sram_base.py | 16 +++++++++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/compiler/router/router.py b/compiler/router/router.py index d63e1739..e1fa72ab 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -1152,7 +1152,20 @@ class router(router_tech): width=pin.width(), height=pin.height()) + def get_pin(self, pin_name): + """ Return the lowest, leftest pin group """ + keep_pin = None + for index,pg in enumerate(self.pin_groups[pin_name]): + for pin in pg.enclosures: + if not keep_pin: + keep_pin = pin + else: + if pin.lx() <= keep_pin.lx() and pin.by() <= keep_pin.by(): + keep_pin = pin + + return keep_pin + # FIXME: This should be replaced with vector.snap_to_grid at some point def snap_to_grid(offset): """ diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py index 4cddaa5f..4d9efa3e 100644 --- a/compiler/router/signal_grid.py +++ b/compiler/router/signal_grid.py @@ -125,7 +125,6 @@ class signal_grid(grid): #else: # print("Cost bounded") - debug.warning("Unable to route path. Expand the detour_scale to allow detours.") return (None,None) def expand_dirs(self,curpath): diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 85346037..76acbf0b 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -221,9 +221,9 @@ class sram_base(design, verilog, lef): # Copy the pins to the top level # This will either be used to route or left unconnected. - for inst in self.insts: - self.copy_power_pins(inst, "vdd") - self.copy_power_pins(inst, "gnd") + for pin_name in ["vdd", "gnd"]: + for inst in self.insts: + self.copy_power_pins(inst, pin_name) if not OPTS.route_supplies: # Do not route the power supply (leave as must-connect pins) @@ -245,6 +245,16 @@ class sram_base(design, verilog, lef): rtr=router(grid_stack, self) rtr.route() + vdd_pin = rtr.get_pin("vdd") + gnd_pin = rtr.get_pin("gnd") + for pin_name, pin in [("vdd", vdd_pin), ("gnd", gnd_pin)]: + self.remove_layout_pin(pin_name) + self.add_layout_pin(pin_name, + pin.layer, + pin.ll(), + pin.width(), + pin.height()) + def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """