Vdd/gnd via stacks now use perferred directions, added cell property to override

This commit is contained in:
Joey Kunzler 2020-03-04 17:05:19 -08:00
parent 1617840ed3
commit d7529ce526
2 changed files with 73 additions and 61 deletions

View File

@ -37,7 +37,7 @@ class layout():
self.objs = [] # Holds all other objects (labels, geometries, etc) self.objs = [] # Holds all other objects (labels, geometries, etc)
self.pin_map = {} # Holds name->pin_layout map for all pins self.pin_map = {} # Holds name->pin_layout map for all pins
self.visited = [] # List of modules we have already visited self.visited = [] # List of modules we have already visited
self.is_library_cell = False # Flag for library cells self.is_library_cell = False # Flag for library cells
self.gds_read() self.gds_read()
try: try:
from tech import power_grid from tech import power_grid
@ -71,7 +71,7 @@ class layout():
(inv_num + 1) * height - \ (inv_num + 1) * height - \
(inv_num % 2) * drc["minwidth_m1"]) (inv_num % 2) * drc["minwidth_m1"])
y_dir = -1 y_dir = -1
return (base_offset, y_dir) return (base_offset, y_dir)
def find_lowest_coords(self): def find_lowest_coords(self):
@ -90,7 +90,7 @@ class layout():
lowesty2 = min(inst.by() for inst in self.insts) lowesty2 = min(inst.by() for inst in self.insts)
else: else:
lowestx2 = lowesty2 = None lowestx2 = lowesty2 = None
if lowestx1 == None and lowestx2 == None: if lowestx1 == None and lowestx2 == None:
return None return None
elif lowestx1 == None: elif lowestx1 == None:
@ -146,7 +146,7 @@ class layout():
subcoord = inst.mod.find_highest_layer_coords(layer) + inst.offset subcoord = inst.mod.find_highest_layer_coords(layer) + inst.offset
highestx = max(highestx, subcoord.x) highestx = max(highestx, subcoord.x)
highesty = max(highesty, subcoord.y) highesty = max(highesty, subcoord.y)
return vector(highestx, highesty) return vector(highestx, highesty)
def find_lowest_layer_coords(self, layer): def find_lowest_layer_coords(self, layer):
@ -172,7 +172,7 @@ class layout():
lowesty = min(lowesty, subcoord.y) lowesty = min(lowesty, subcoord.y)
return vector(lowestx, lowesty) return vector(lowestx, lowesty)
def translate_all(self, offset): def translate_all(self, offset):
""" """
Translates all objects, instances, and pins by the given (x,y) offset Translates all objects, instances, and pins by the given (x,y) offset
@ -189,7 +189,7 @@ class layout():
pin_list = self.pin_map[pin_name] pin_list = self.pin_map[pin_name]
for pin in pin_list: for pin in pin_list:
pin.rect = [pin.ll() - offset, pin.ur() - offset] pin.rect = [pin.ll() - offset, pin.ur() - offset]
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0): def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
""" Adds an instance of a mod to this module """ """ Adds an instance of a mod to this module """
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
@ -204,7 +204,7 @@ class layout():
if inst.name == name: if inst.name == name:
return inst return inst
return None return None
def add_rect(self, layer, offset, width=None, height=None): def add_rect(self, layer, offset, width=None, height=None):
""" """
Adds a rectangle on a given layer,offset with width and height Adds a rectangle on a given layer,offset with width and height
@ -260,7 +260,7 @@ class layout():
start-offset, start-offset,
minwidth_layer, minwidth_layer,
end.y-start.y) end.y-start.y)
def get_pin(self, text): def get_pin(self, text):
""" """
Return the pin or list of pins Return the pin or list of pins
@ -275,7 +275,7 @@ class layout():
except Exception: except Exception:
self.gds_write("missing_pin.gds") self.gds_write("missing_pin.gds")
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text,self.name),-1) debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text,self.name),-1)
def get_pins(self, text): def get_pins(self, text):
""" """
Return a pin list (instead of a single pin) Return a pin list (instead of a single pin)
@ -290,17 +290,17 @@ class layout():
Return a pin list of all pins Return a pin list of all pins
""" """
return self.pin_map.keys() return self.pin_map.keys()
def copy_layout_pin(self, instance, pin_name, new_name=""): def copy_layout_pin(self, instance, pin_name, new_name=""):
""" """
Create a copied version of the layout pin at the current level. Create a copied version of the layout pin at the current level.
You can optionally rename the pin to a new name. You can optionally rename the pin to a new name.
""" """
pins = instance.get_pins(pin_name) pins = instance.get_pins(pin_name)
debug.check(len(pins) > 0, debug.check(len(pins) > 0,
"Could not find pin {}".format(pin_name)) "Could not find pin {}".format(pin_name))
for pin in pins: for pin in pins:
if new_name == "": if new_name == "":
new_name = pin.name new_name = pin.name
@ -317,7 +317,7 @@ class layout():
""" """
for pin_name in self.pin_map.keys(): for pin_name in self.pin_map.keys():
self.copy_layout_pin(instance, pin_name, prefix+pin_name) self.copy_layout_pin(instance, pin_name, prefix+pin_name)
def add_layout_pin_segment_center(self, text, layer, start, end): def add_layout_pin_segment_center(self, text, layer, start, end):
""" """
Creates a path like pin with center-line convention Creates a path like pin with center-line convention
@ -325,9 +325,9 @@ class layout():
debug.check(start.x == end.x or start.y == end.y, debug.check(start.x == end.x or start.y == end.y,
"Cannot have a non-manhatten layout pin.") "Cannot have a non-manhatten layout pin.")
minwidth_layer = drc["minwidth_{}".format(layer)] minwidth_layer = drc["minwidth_{}".format(layer)]
# one of these will be zero # one of these will be zero
width = max(start.x, end.x) - min(start.x, end.x) width = max(start.x, end.x) - min(start.x, end.x)
height = max(start.y, end.y) - min(start.y, end.y) height = max(start.y, end.y) - min(start.y, end.y)
@ -341,7 +341,7 @@ class layout():
# This makes sure it is long enough, but also it is not 0 width! # This makes sure it is long enough, but also it is not 0 width!
height = max(minwidth_layer, height) height = max(minwidth_layer, height)
width = max(minwidth_layer, width) width = max(minwidth_layer, width)
return self.add_layout_pin(text, return self.add_layout_pin(text,
layer, layer,
ll_offset, ll_offset,
@ -358,13 +358,13 @@ class layout():
ll_offset = offset - vector(0.5 * width, 0.5 * height) ll_offset = offset - vector(0.5 * width, 0.5 * height)
return self.add_layout_pin(text, layer, ll_offset, width, height) return self.add_layout_pin(text, layer, ll_offset, width, height)
def remove_layout_pin(self, text): def remove_layout_pin(self, text):
""" """
Delete a labeled pin (or all pins of the same name) Delete a labeled pin (or all pins of the same name)
""" """
self.pin_map[text] = set() self.pin_map[text] = set()
def add_layout_pin(self, text, layer, offset, width=None, height=None): def add_layout_pin(self, text, layer, offset, width=None, height=None):
""" """
Create a labeled pin Create a labeled pin
@ -408,7 +408,7 @@ class layout():
layer=layer, layer=layer,
offset=offset + vector(0.5 * width, offset=offset + vector(0.5 * width,
0.5 * height)) 0.5 * height))
def add_label(self, text, layer, offset=[0, 0], zoom=-1): def add_label(self, text, layer, offset=[0, 0], zoom=-1):
"""Adds a text label on the given layer,offset, and zoom level""" """Adds a text label on the given layer,offset, and zoom level"""
# negative layers indicate "unused" layers in a given technology # negative layers indicate "unused" layers in a given technology
@ -447,7 +447,7 @@ class layout():
layer_stack=layers, layer_stack=layers,
path=coordinates, path=coordinates,
layer_widths=layer_widths) layer_widths=layer_widths)
def add_wire(self, layers, coordinates): def add_wire(self, layers, coordinates):
"""Connects a routing path on given layer,coordinates,width. """Connects a routing path on given layer,coordinates,width.
The layers are the (horizontal, via, vertical). """ The layers are the (horizontal, via, vertical). """
@ -462,14 +462,14 @@ class layout():
""" Return the preferred routing directions """ """ Return the preferred routing directions """
from tech import preferred_directions from tech import preferred_directions
return preferred_directions[layer] return preferred_directions[layer]
def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None): def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None):
""" Add a three layer via structure. """ """ Add a three layer via structure. """
if not directions: if not directions:
directions = (self.get_preferred_direction(layers[0]), directions = (self.get_preferred_direction(layers[0]),
self.get_preferred_direction(layers[2])) self.get_preferred_direction(layers[2]))
from sram_factory import factory from sram_factory import factory
via = factory.create(module_type="contact", via = factory.create(module_type="contact",
layer_stack=layers, layer_stack=layers,
@ -494,7 +494,7 @@ class layout():
if not directions: if not directions:
directions = (self.get_preferred_direction(layers[0]), directions = (self.get_preferred_direction(layers[0]),
self.get_preferred_direction(layers[2])) self.get_preferred_direction(layers[2]))
from sram_factory import factory from sram_factory import factory
via = factory.create(module_type="contact", via = factory.create(module_type="contact",
layer_stack=layers, layer_stack=layers,
@ -504,7 +504,7 @@ class layout():
well_type=well_type) well_type=well_type)
height = via.height height = via.height
width = via.width width = via.width
corrected_offset = offset + vector(-0.5 * width, corrected_offset = offset + vector(-0.5 * width,
-0.5 * height) -0.5 * height)
@ -580,7 +580,7 @@ class layout():
last_via=via, last_via=via,
size=size) size=size)
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
"""Adds a ptx module to the design.""" """Adds a ptx module to the design."""
import ptx import ptx
@ -602,11 +602,11 @@ class layout():
# This must be done for netlist only mode too # This must be done for netlist only mode too
if os.path.isfile(self.gds_file): if os.path.isfile(self.gds_file):
self.is_library_cell = True self.is_library_cell = True
if OPTS.netlist_only: if OPTS.netlist_only:
self.gds = None self.gds = None
return return
# open the gds file if it exists or else create a blank layout # open the gds file if it exists or else create a blank layout
if os.path.isfile(self.gds_file): if os.path.isfile(self.gds_file):
debug.info(3, "opening {}".format(self.gds_file)) debug.info(3, "opening {}".format(self.gds_file))
@ -662,7 +662,7 @@ class layout():
height=height, height=height,
center=False) center=False)
debug.info(2, "Adding {0} boundary {1}".format(self.name, boundary)) debug.info(2, "Adding {0} boundary {1}".format(self.name, boundary))
self.visited.append(self.name) self.visited.append(self.name)
def gds_write(self, gds_name): def gds_write(self, gds_name):
@ -681,10 +681,10 @@ class layout():
# which may have been previously processed! # which may have been previously processed!
# MRG: 10/4/18 We need to clear if we make changes and write a second GDS! # MRG: 10/4/18 We need to clear if we make changes and write a second GDS!
self.clear_visited() self.clear_visited()
# recursively create all the remaining objects # recursively create all the remaining objects
self.gds_write_file(self.gds) self.gds_write_file(self.gds)
# populates the xyTree data structure for gds # populates the xyTree data structure for gds
# self.gds.prepareForWrite() # self.gds.prepareForWrite()
writer.writeToFile(gds_name) writer.writeToFile(gds_name)
@ -706,7 +706,7 @@ class layout():
lpp = techlayer[layer] lpp = techlayer[layer]
else: else:
lpp = layer lpp = layer
blockages = [] blockages = []
for i in self.objs: for i in self.objs:
blockages += i.get_blockages(lpp) blockages += i.get_blockages(lpp)
@ -724,7 +724,7 @@ class layout():
pin_names = copy.deepcopy(self.pins) pin_names = copy.deepcopy(self.pins)
if self.name.startswith("pmos") or self.name.startswith("nmos"): if self.name.startswith("pmos") or self.name.startswith("nmos"):
pin_names.remove("B") pin_names.remove("B")
blockages = [] blockages = []
for pin_name in pin_names: for pin_name in pin_names:
pin_list = self.get_pins(pin_name) pin_list = self.get_pins(pin_name)
@ -784,7 +784,7 @@ class layout():
# half minwidth so we can return the center line offsets # half minwidth so we can return the center line offsets
half_minwidth = 0.5 * drc["minwidth_{}".format(layer)] half_minwidth = 0.5 * drc["minwidth_{}".format(layer)]
line_positions = {} line_positions = {}
if vertical: if vertical:
for i in range(len(names)): for i in range(len(names)):
@ -829,7 +829,7 @@ class layout():
layer_stack=("m1", "via1", "m2")): layer_stack=("m1", "via1", "m2")):
""" Vertical version of connect_bus. """ """ Vertical version of connect_bus. """
self.connect_bus(mapping, inst, bus_offsets, layer_stack, False) self.connect_bus(mapping, inst, bus_offsets, layer_stack, False)
def connect_bus(self, mapping, inst, bus_offsets, layer_stack, horizontal): def connect_bus(self, mapping, inst, bus_offsets, layer_stack, horizontal):
""" """
Connect a mapping of pin -> name for a bus. This could be Connect a mapping of pin -> name for a bus. This could be
@ -842,7 +842,7 @@ class layout():
route_layer = vertical_layer route_layer = vertical_layer
else: else:
route_layer = horizontal_layer route_layer = horizontal_layer
for (pin_name, bus_name) in mapping: for (pin_name, bus_name) in mapping:
pin = inst.get_pin(pin_name) pin = inst.get_pin(pin_name)
pin_pos = pin.center() pin_pos = pin.center()
@ -854,7 +854,7 @@ class layout():
else: else:
# left/right then up/down # left/right then up/down
mid_pos = vector(bus_pos.x, pin_pos.y) mid_pos = vector(bus_pos.x, pin_pos.y)
self.add_wire(layer_stack, self.add_wire(layer_stack,
[bus_pos, mid_pos, pin_pos]) [bus_pos, mid_pos, pin_pos])
@ -865,7 +865,7 @@ class layout():
offset=pin_pos) offset=pin_pos)
# FIXME: output pins tend to not be rotate, # FIXME: output pins tend to not be rotate,
# but supply pins are. Make consistent? # but supply pins are. Make consistent?
# We only need a via if they happened to align perfectly # We only need a via if they happened to align perfectly
# so the add_wire didn't add a via # so the add_wire didn't add a via
if (horizontal and bus_pos.y == pin_pos.y) or (not horizontal and bus_pos.x == pin_pos.x): if (horizontal and bus_pos.y == pin_pos.y) or (not horizontal and bus_pos.x == pin_pos.x):
@ -899,7 +899,7 @@ class layout():
self.m3_space) self.m3_space)
else: else:
debug.error("Cannot find layer pitch.") debug.error("Cannot find layer pitch.")
def add_horizontal_trunk_route(self, def add_horizontal_trunk_route(self,
pins, pins,
trunk_offset, trunk_offset,
@ -915,7 +915,7 @@ class layout():
# if we are less than a pitch, just create a non-preferred layer jog # if we are less than a pitch, just create a non-preferred layer jog
if max_x-min_x <= pitch: if max_x-min_x <= pitch:
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
# Add the horizontal trunk on the vertical layer! # Add the horizontal trunk on the vertical layer!
self.add_path(self.vertical_layer, self.add_path(self.vertical_layer,
[vector(min_x - half_layer_width, trunk_offset.y), [vector(min_x - half_layer_width, trunk_offset.y),
@ -955,7 +955,7 @@ class layout():
if max_y-min_y <= pitch: if max_y-min_y <= pitch:
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
# Add the vertical trunk on the horizontal layer! # Add the vertical trunk on the horizontal layer!
self.add_path(self.horizontal_layer, self.add_path(self.horizontal_layer,
[vector(trunk_offset.x, min_y - half_layer_width), [vector(trunk_offset.x, min_y - half_layer_width),
@ -978,7 +978,7 @@ class layout():
self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_path(self.horizontal_layer, [pin.center(), mid])
self.add_via_center(layers=layer_stack, self.add_via_center(layers=layer_stack,
offset=mid) offset=mid)
def create_channel_route(self, netlist, def create_channel_route(self, netlist,
offset, offset,
layer_stack, layer_stack,
@ -996,7 +996,7 @@ class layout():
Remove the pin from the graph and all conflicts Remove the pin from the graph and all conflicts
""" """
g.pop(pin, None) g.pop(pin, None)
# Remove the pin from all conflicts # Remove the pin from all conflicts
# FIXME: This is O(n^2), so maybe optimize it. # FIXME: This is O(n^2), so maybe optimize it.
for other_pin,conflicts in g.items(): for other_pin,conflicts in g.items():
@ -1010,21 +1010,21 @@ class layout():
Check all the pin pairs on two nets and return a pin Check all the pin pairs on two nets and return a pin
overlap if any pin overlaps. overlap if any pin overlaps.
""" """
for pin1 in net1: for pin1 in net1:
for pin2 in net2: for pin2 in net2:
if vcg_pin_overlap(pin1, pin2, vertical, pitch): if vcg_pin_overlap(pin1, pin2, vertical, pitch):
return True return True
return False return False
def vcg_pin_overlap(pin1, pin2, vertical, pitch): def vcg_pin_overlap(pin1, pin2, vertical, pitch):
""" Check for vertical or horizontal overlap of the two pins """ """ Check for vertical or horizontal overlap of the two pins """
# FIXME: If the pins are not in a row, this may break. # FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin, # However, a top pin shouldn't overlap another top pin,
# for example, so the # for example, so the
# extra comparison *shouldn't* matter. # extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set # Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x)<pitch x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x)<pitch
@ -1049,7 +1049,7 @@ class layout():
# too if we want to minimize the # too if we want to minimize the
# number of tracks! # number of tracks!
# hcg = {} # hcg = {}
# Initialize the vertical conflict graph (vcg) # Initialize the vertical conflict graph (vcg)
# and make a list of all pins # and make a list of all pins
vcg = collections.OrderedDict() vcg = collections.OrderedDict()
@ -1084,7 +1084,7 @@ class layout():
vertical, vertical,
self.horizontal_pitch): self.horizontal_pitch):
vcg[net_name2].append(net_name1) vcg[net_name2].append(net_name1)
# list of routes to do # list of routes to do
while vcg: while vcg:
# from pprint import pformat # from pprint import pformat
@ -1105,7 +1105,7 @@ class layout():
# Remove the net from other constriants in the VCG # Remove the net from other constriants in the VCG
vcg = remove_net_from_graph(net_name, vcg) vcg = remove_net_from_graph(net_name, vcg)
# Add the trunk routes from the bottom up for # Add the trunk routes from the bottom up for
# horizontal or the left to right for vertical # horizontal or the left to right for vertical
if vertical: if vertical:
@ -1137,7 +1137,7 @@ class layout():
""" Add boundary for debugging dimensions """ """ Add boundary for debugging dimensions """
if OPTS.netlist_only: if OPTS.netlist_only:
return return
if "stdc" in techlayer.keys(): if "stdc" in techlayer.keys():
boundary_layer = "stdc" boundary_layer = "stdc"
else: else:
@ -1152,7 +1152,7 @@ class layout():
offset=ll, offset=ll,
height=ur.y-ll.y, height=ur.y-ll.y,
width=ur.x-ll.x) width=ur.x-ll.x)
def add_enclosure(self, insts, layer="nwell"): def add_enclosure(self, insts, layer="nwell"):
""" Add a layer that surrounds the given instances. Useful """ Add a layer that surrounds the given instances. Useful
for creating wells, for example. Doesn't check for minimum widths or for creating wells, for example. Doesn't check for minimum widths or
@ -1193,19 +1193,25 @@ class layout():
"supply router." "supply router."
.format(name,inst.name,self.pwr_grid_layer)) .format(name,inst.name,self.pwr_grid_layer))
def add_power_pin(self, name, loc, size=[1, 1], vertical=False, start_layer="m1"): def add_power_pin(self, name, loc, size=[1, 1], vertical=False, start_layer="m1"):
""" """
Add a single power pin from the lowest power_grid layer down to M1 at Add a single power pin from the lowest power_grid layer down to M1 at
the given center location. The starting layer is specified to determine the given center location. The starting layer is specified to determine
which vias are needed. which vias are needed.
""" """
# Force vdd/gnd via stack to be vertically or horizontally oriented
# Default: None, uses prefered metal directions
if vertical: if vertical:
direction = ("V", "V") direction = ("V", "V")
else: elif not vertical and vertical is not None:
direction = ("H", "H") direction = ("H", "H")
else:
direction = None
via = self.add_via_stack_center(from_layer=start_layer, via = self.add_via_stack_center(from_layer=start_layer,
to_layer=self.pwr_grid_layer, to_layer=self.pwr_grid_layer,
size=size, size=size,
@ -1222,7 +1228,7 @@ class layout():
offset=loc, offset=loc,
width=via.width, width=via.width,
height=via.height) height=via.height)
def add_power_ring(self, bbox): def add_power_ring(self, bbox):
""" """
Create vdd and gnd power rings around an area of the bounding box Create vdd and gnd power rings around an area of the bounding box
@ -1287,7 +1293,7 @@ class layout():
offset=offset, offset=offset,
width=width, width=width,
height=self.supply_rail_width) height=self.supply_rail_width)
# TOP horizontal rails # TOP horizontal rails
offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch, offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch,
0) 0)
@ -1328,7 +1334,7 @@ class layout():
else: else:
self.supply_vias -= 1 self.supply_vias -= 1
break break
via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center), via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center),
vector(self.left_gnd_x_center, self.top_gnd_y_center), vector(self.left_gnd_x_center, self.top_gnd_y_center),
vector(self.right_gnd_x_center, self.bottom_gnd_y_center), vector(self.right_gnd_x_center, self.bottom_gnd_y_center),
@ -1337,7 +1343,7 @@ class layout():
vector(self.left_vdd_x_center, self.top_vdd_y_center), vector(self.left_vdd_x_center, self.top_vdd_y_center),
vector(self.right_vdd_x_center, self.bottom_vdd_y_center), vector(self.right_vdd_x_center, self.bottom_vdd_y_center),
vector(self.right_vdd_x_center, self.top_vdd_y_center)] vector(self.right_vdd_x_center, self.top_vdd_y_center)]
for pt in via_points: for pt in via_points:
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=pt, offset=pt,
@ -1390,4 +1396,3 @@ class layout():
debug.info(0, "name={0} : mod={1} : offset={2}".format(inst.name, debug.info(0, "name={0} : mod={1} : offset={2}".format(inst.name,
inst.mod.name, inst.mod.name,
inst.offset)) inst.offset))

View File

@ -101,13 +101,20 @@ class bitcell_base_array(design.design):
width=self.width, width=self.width,
height=wl_pin.height()) height=wl_pin.height())
# For every second row and column, add a via for gnd and vdd # For non-square via stacks, vertical/horizontal direction refers to the stack orientation in 2d space
# Default uses prefered directions for each layer; this cell property is only currently used by s8 tech (03/20)
try:
force_power_pins_vertical = cell_properties.bitcell_force_power_pins_vertical
except AttributeError:
force_power_pins_vertical = None
# Add vdd/gnd via stacks
for row in range(self.row_size): for row in range(self.row_size):
for col in range(self.column_size): for col in range(self.column_size):
inst = self.cell_inst[row,col] inst = self.cell_inst[row,col]
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name): for pin in inst.get_pins(pin_name):
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) self.add_power_pin(name=pin_name, loc=pin.center(), vertical=force_power_pins_vertical, start_layer=pin.layer)
def _adjust_x_offset(self, xoffset, col, col_offset): def _adjust_x_offset(self, xoffset, col, col_offset):
tempx = xoffset tempx = xoffset