From 6493d1a7f4ec489eb2ff0713906ceb19c3bafc25 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 25 May 2021 13:25:48 -0700 Subject: [PATCH] Add dnwell --- compiler/base/hierarchy_layout.py | 145 +++++++++++++++++++++++++++++- compiler/router/vector3d.py | 41 ++++----- 2 files changed, 162 insertions(+), 24 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 1e2add8d..e65dcfa3 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1161,6 +1161,8 @@ class layout(): height=ur.y - ll.y, width=ur.x - ll.x) + self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()] + def add_enclosure(self, insts, layer="nwell", extend=0, leftx=None, rightx=None, topy=None, boty=None): """ Add a layer that surrounds the given instances. Useful @@ -1341,7 +1343,146 @@ class layout(): layer=layer, offset=peri_pin_loc) - def add_power_ring(self, bbox): + def add_dnwell(self, bbox=None, inflate=1): + """ Create a dnwell, along with nwell moat at border. """ + + if "dnwell" not in techlayer: + return + + if not bbox: + bbox = [self.find_lowest_coords(), + self.find_highest_coords()] + + # Find the corners + [ll, ur] = bbox + + # Possibly inflate the bbox + nwell_offset = vector(self.nwell_width, self.nwell_width) + ll -= nwell_offset.scale(inflate, inflate) + ur += nwell_offset.scale(inflate, inflate) + + # Other corners + ul = vector(ll.x, ur.y) + lr = vector(ur.x, ll.y) + + # Add the dnwell + self.add_rect("dnwell", + offset=ll, + height=ur.y - ll.y, + width=ur.x - ll.x) + + # Add the moat + self.add_path("nwell", [ll, lr, ur, ul, ll - vector(0, 0.5 * self.nwell_width)]) + + # Add the taps + layer_stack = self.active_stack + tap_spacing = 2 + nwell_offset = vector(self.nwell_width, self.nwell_width) + loc = ll + nwell_offset.scale(tap_spacing, 0) + end_loc = lr - nwell_offset.scale(tap_spacing, 0) + while loc.x < end_loc.x: + self.add_via_center(layers=layer_stack, + offset=loc, + implant_type="n", + well_type="n") + self.add_via_stack_center(from_layer="li", + to_layer="m1", + offset=loc) + loc += nwell_offset.scale(tap_spacing, 0) + + loc = ul + nwell_offset.scale(tap_spacing, 0) + end_loc = ur - nwell_offset.scale(tap_spacing, 0) + while loc.x < end_loc.x: + self.add_via_center(layers=layer_stack, + offset=loc, + implant_type="n", + well_type="n") + self.add_via_stack_center(from_layer="li", + to_layer="m2", + offset=loc) + loc += nwell_offset.scale(tap_spacing, 0) + + loc = ll + nwell_offset.scale(0, tap_spacing) + end_loc = ul - nwell_offset.scale(0, tap_spacing) + while loc.y < end_loc.y: + self.add_via_center(layers=layer_stack, + offset=loc, + implant_type="n", + well_type="n") + self.add_via_stack_center(from_layer="li", + to_layer="m2", + offset=loc) + loc += nwell_offset.scale(0, tap_spacing) + + loc = lr + nwell_offset.scale(0, tap_spacing) + end_loc = ur - nwell_offset.scale(0, tap_spacing) + while loc.y < end_loc.y: + self.add_via_center(layers=layer_stack, + offset=loc, + implant_type="n", + well_type="n") + self.add_via_stack_center(from_layer="li", + to_layer="m2", + offset=loc) + loc += nwell_offset.scale(0, tap_spacing) + + # Add the gnd ring + self.add_ring([ll, ur]) + + def add_ring(self, bbox=None, width_mult=8, offset=0): + """ + Add a ring around the bbox + """ + # Ring size/space/pitch + wire_width = self.m2_width * width_mult + half_width = 0.5 * wire_width + wire_space = self.m2_space + wire_pitch = wire_width + wire_space + + # Find the corners + if not bbox: + bbox = [self.find_lowest_coords(), + self.find_highest_coords()] + + [ll, ur] = bbox + ul = vector(ll.x, ur.y) + lr = vector(ur.x, ll.y) + ll += vector(-offset * wire_pitch, + -offset * wire_pitch) + lr += vector(offset * wire_pitch, + -offset * wire_pitch) + ur += vector(offset * wire_pitch, + offset * wire_pitch) + ul += vector(-offset * wire_pitch, + offset * wire_pitch) + + half_offset = vector(half_width, half_width) + self.add_path("m1", [ll - half_offset.scale(1, 0), lr + half_offset.scale(1, 0)], width=wire_width) + self.add_path("m1", [ul - half_offset.scale(1, 0), ur + half_offset.scale(1, 0)], width=wire_width) + self.add_path("m2", [ll - half_offset.scale(0, 1), ul + half_offset.scale(0, 1)], width=wire_width) + self.add_path("m2", [lr - half_offset.scale(0, 1), ur + half_offset.scale(0, 1)], width=wire_width) + + # Find the number of vias for this pitch + supply_vias = 1 + from sram_factory import factory + while True: + c = factory.create(module_type="contact", + layer_stack=self.m1_stack, + dimensions=(supply_vias, supply_vias)) + if c.second_layer_width < wire_width and c.second_layer_height < wire_width: + supply_vias += 1 + else: + supply_vias -= 1 + break + + via_points = [ll, lr, ur, ul] + for pt in via_points: + self.add_via_center(layers=self.m1_stack, + offset=pt, + size=(supply_vias, + supply_vias)) + + def add_power_ring(self): """ Create vdd and gnd power rings around an area of the bounding box argument. Must have a supply_rail_width and supply_rail_pitch @@ -1350,7 +1491,7 @@ class layout(): modules.. """ - [ll, ur] = bbox + [ll, ur] = self.bbox supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width height = (ur.y - ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing diff --git a/compiler/router/vector3d.py b/compiler/router/vector3d.py index 8830fc36..71709837 100644 --- a/compiler/router/vector3d.py +++ b/compiler/router/vector3d.py @@ -5,9 +5,9 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import debug import math + class vector3d(): """ This is the vector3d class to represent a 3D coordinate. @@ -22,20 +22,20 @@ class vector3d(): self.x = x[0] self.y = x[1] self.z = x[2] - #will take inputs as the values of a coordinate + # will take inputs as the values of a coordinate else: self.x = x self.y = y self.z = z - self._hash = hash((self.x,self.y,self.z)) + self._hash = hash((self.x, self.y, self.z)) def __str__(self): """ override print function output """ - return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" + return "v3d[" + str(self.x) + ", " + str(self.y) + ", " + str(self.z) + "]" def __repr__(self): """ override print function output """ - return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" + return "v3d[" + str(self.x) + ", " + str(self.y) + ", " + str(self.z) + "]" def __setitem__(self, index, value): """ @@ -74,7 +74,6 @@ class vector3d(): """ return vector3d(self.x + other[0], self.y + other[1], self.z + other[2]) - def __radd__(self, other): """ Override + function (right add) @@ -98,7 +97,6 @@ class vector3d(): """ return self._hash - def __rsub__(self, other): """ Override - function (right) @@ -107,7 +105,7 @@ class vector3d(): def rotate(self): """ pass a copy of rotated vector3d, without altering the vector3d! """ - return vector3d(self.y,self.x,self.z) + return vector3d(self.y, self.x, self.z) def scale(self, x_factor, y_factor=None,z_factor=None): """ pass a copy of scaled vector3d, without altering the vector3d! """ @@ -115,7 +113,7 @@ class vector3d(): z_factor=x_factor[2] y_factor=x_factor[1] x_factor=x_factor[0] - return vector3d(self.x*x_factor,self.y*y_factor,self.z*z_factor) + return vector3d(self.x * x_factor, self.y * y_factor, self.z * z_factor) def rotate_scale(self, x_factor, y_factor=None, z_factor=None): """ pass a copy of scaled vector3d, without altering the vector3d! """ @@ -123,25 +121,25 @@ class vector3d(): z_factor=x_factor[2] y_factor=x_factor[1] x_factor=x_factor[0] - return vector3d(self.y*x_factor,self.x*y_factor,self.z*z_factor) + return vector3d(self.y * x_factor, self.x * y_factor, self.z * z_factor) def floor(self): """ Override floor function """ - return vector3d(int(math.floor(self.x)),int(math.floor(self.y)), self.z) + return vector3d(int(math.floor(self.x)), int(math.floor(self.y)), self.z) def ceil(self): """ Override ceil function """ - return vector3d(int(math.ceil(self.x)),int(math.ceil(self.y)), self.z) + return vector3d(int(math.ceil(self.x)), int(math.ceil(self.y)), self.z) def round(self): """ Override round function """ - return vector3d(int(round(self.x)),int(round(self.y)), self.z) + return vector3d(int(round(self.x)), int(round(self.y)), self.z) def __eq__(self, other): """Override the default Equals behavior""" @@ -164,30 +162,29 @@ class vector3d(): def max(self, other): """ Max of both values """ - return vector3d(max(self.x,other.x),max(self.y,other.y),max(self.z,other.z)) + return vector3d(max(self.x, other.x), max(self.y, other.y), max(self.z, other.z)) def min(self, other): """ Min of both values """ - return vector3d(min(self.x,other.x),min(self.y,other.y),min(self.z,other.z)) + return vector3d(min(self.x, other.x), min(self.y, other.y), min(self.z, other.z)) def distance(self, other): """ Return the manhattan distance between two values """ - return abs(self.x-other.x)+abs(self.y-other.y) + return abs(self.x - other.x) + abs(self.y - other.y) def euclidean_distance(self, other): """ Return the euclidean distance between two values """ - return math.sqrt((self.x-other.x)**2+(self.y-other.y)**2) - + return math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2) def adjacent(self, other): """ Is the one grid adjacent in any planar direction to the other """ - if self == other + vector3d(1,0,0): + if self == other + vector3d(1, 0, 0): return True - elif self == other + vector3d(-1,0,0): + elif self == other + vector3d(-1, 0, 0): return True - elif self == other + vector3d(0,1,0): + elif self == other + vector3d(0, 1, 0): return True - elif self == other + vector3d(0,-1,0): + elif self == other + vector3d(0, -1, 0): return True else: return False