From 928b02aa1fd3929919542564b3648e4b79d8d421 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Wed, 11 Dec 2019 10:37:17 +0100 Subject: [PATCH 001/103] gdsMill: Fix reader/writer ignoring the 'DATATYPE' of 'PATH' records most readers (like KLayout/Calibre) will not open gds files omiting the DATATYPE. Signed-off-by: Bastian Koppelmann --- compiler/gdsMill/gdsMill/gds2reader.py | 5 +++++ compiler/gdsMill/gdsMill/gds2writer.py | 4 ++++ compiler/gdsMill/gdsMill/gdsPrimitives.py | 1 + 3 files changed, 10 insertions(+) diff --git a/compiler/gdsMill/gdsMill/gds2reader.py b/compiler/gdsMill/gdsMill/gds2reader.py index 7e519c36..56dc4bef 100644 --- a/compiler/gdsMill/gdsMill/gds2reader.py +++ b/compiler/gdsMill/gdsMill/gds2reader.py @@ -251,6 +251,11 @@ class Gds2reader: thisPath.pathType=pathType if(self.debugToTerminal==1): print("\t\t\tPath Type: "+str(pathType)) + elif(idBits==b'\x0E\x02'): #Data type + dataType = struct.unpack(">h",record[2:4])[0] + thisPath.dataType=dataType + if(self.debugToTerminal==1): + print("\t\t\tData Type: "+str(dataType)) elif(idBits==b'\x0F\x03'): #Path width pathWidth = struct.unpack(">i",record[2:6])[0] thisPath.pathWidth=pathWidth diff --git a/compiler/gdsMill/gdsMill/gds2writer.py b/compiler/gdsMill/gdsMill/gds2writer.py index 402416cd..059c156d 100644 --- a/compiler/gdsMill/gdsMill/gds2writer.py +++ b/compiler/gdsMill/gdsMill/gds2writer.py @@ -238,6 +238,10 @@ class Gds2writer: idBits=b'\x16\x02' #purpose layer purposeLayer = struct.pack(">h",thisPath.purposeLayer) self.writeRecord(idBits+purposeLayer) + if(thisPath.dataType is not None): + idBits=b'\x0E\x02' #Data type + dataType = struct.pack(">h",thisPath.dataType) + self.writeRecord(idBits+dataType) if(thisPath.pathType): idBits=b'\x21\x02' #Path type pathType = struct.pack(">h",thisPath.pathType) diff --git a/compiler/gdsMill/gdsMill/gdsPrimitives.py b/compiler/gdsMill/gdsMill/gdsPrimitives.py index dc43fea6..ef87cec8 100644 --- a/compiler/gdsMill/gdsMill/gdsPrimitives.py +++ b/compiler/gdsMill/gdsMill/gdsPrimitives.py @@ -33,6 +33,7 @@ class GdsPath: self.drawingLayer="" self.purposeLayer = None self.pathType="" + self.dataType=None self.pathWidth="" self.coordinates="" From f9a66e86b4103182e0b3df528d4bcc8ace5b6763 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 11 Dec 2019 09:09:59 -0800 Subject: [PATCH 002/103] Add npc option to contact --- compiler/base/contact.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 9e947082..acfcc265 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -56,6 +56,7 @@ class contact(hierarchy_design.hierarchy_design): self.create_contact_array() self.create_first_layer_enclosure() self.create_second_layer_enclosure() + self.create_nitride_cut_enclosure() self.height = max(obj.offset.y + obj.height for obj in self.objs) self.width = max(obj.offset.x + obj.width for obj in self.objs) @@ -72,6 +73,7 @@ class contact(hierarchy_design.hierarchy_design): (first_layer, via_layer, second_layer) = self.layer_stack self.first_layer_name = first_layer self.second_layer_name = second_layer + # Contacts will have unique per first layer if via_layer == "contact": if first_layer in ("active", "poly"): @@ -81,6 +83,7 @@ class contact(hierarchy_design.hierarchy_design): else: self.via_layer_name = via_layer + def setup_layout_constants(self): """ Determine the design rules for the enclosure layers """ @@ -150,6 +153,25 @@ class contact(hierarchy_design.hierarchy_design): height=self.contact_width) offset = offset + vector(self.contact_pitch, 0) + def create_nitride_cut_enclosure(self): + """ Special layer that encloses poly contacts in some processes """ + # Check if there is a special poly nitride cut layer + if "npc" not in layer.keys(): + return + + # Only add for poly layers + if self.first_layer_name == "poly": + self.add_rect(layer="npc", + offset=self.first_layer_position, + width=self.first_layer_width, + height=self.first_layer_height) + elif self.second_layer_name == "poly": + self.add_rect(layer="npc", + offset=self.second_layer_position, + width=self.second_layer_width, + height=self.second_layer_height) + + def create_first_layer_enclosure(self): # this is if the first and second layers are different self.first_layer_position = vector( From e048ada23c256dabc090f38a737c010b51f4ff15 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 11 Dec 2019 17:56:55 -0800 Subject: [PATCH 003/103] Abstract basic DRC checks --- compiler/base/contact.py | 32 ++-- compiler/base/design.py | 9 +- compiler/drc/design_rules.py | 16 +- compiler/pgates/ptx.py | 8 +- compiler/tests/03_contact_test.py | 8 +- technology/freepdk45/tech/tech.py | 243 +++++++++++++++-------------- technology/scn4m_subm/tech/tech.py | 216 +++++++++++++------------ 7 files changed, 297 insertions(+), 235 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index acfcc265..26a59d31 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -7,7 +7,7 @@ # import hierarchy_design import debug -from tech import drc, layer +from tech import * from vector import vector @@ -75,13 +75,18 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_name = second_layer # Contacts will have unique per first layer - if via_layer == "contact": + if via_layer in layer.keys(): + self.via_layer_name = via_layer + elif via_layer == "contact": if first_layer in ("active", "poly"): self.via_layer_name = first_layer + "_" + via_layer + elif second_layer in ("active", "poly"): + self.via_layer_name = second_layer + "_" + via_layer else: - self.via_layer_name = second_layer + "_" + via_layer + debug.error("Invalid via layer {}".format(via_layer), -1) else: - self.via_layer_name = via_layer + debug.error("Invalid via layer {}".format(via_layer), -1) + def setup_layout_constants(self): @@ -224,23 +229,30 @@ from sram_factory import factory # This is not instantiated and used for calculations only. # These are static 1x1 contacts to reuse in all the design modules. well = factory.create(module_type="contact", - layer_stack=("active", "contact", "metal1"), + layer_stack=active_stack, directions=("H", "V")) active = factory.create(module_type="contact", - layer_stack=("active", "contact", "metal1"), + layer_stack=active_stack, directions=("H", "V")) poly = factory.create(module_type="contact", - layer_stack=("poly", "contact", "metal1"), + layer_stack=poly_stack, directions=("V", "H")) +if "li" in layer.keys(): + lim1 = factory.create(module_type="contact", + layer_stack=li_stack, + directions=("V", "H")) +else: + lim1 = None + m1m2 = factory.create(module_type="contact", - layer_stack=("metal1", "via1", "metal2"), + layer_stack=metal1_stack, directions=("H", "V")) m2m3 = factory.create(module_type="contact", - layer_stack=("metal2", "via2", "metal3"), + layer_stack=metal2_stack, directions=("V", "H")) if "metal4" in layer.keys(): m3m4 = factory.create(module_type="contact", - layer_stack=("metal3", "via3", "metal4"), + layer_stack=metal3_stack, directions=("H", "V")) else: m3m4 = None diff --git a/compiler/base/design.py b/compiler/base/design.py index c6385243..0ecfbbd4 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -48,12 +48,15 @@ class design(hierarchy_design): self.m4_space = drc("metal4_to_metal4") self.active_width = drc("minwidth_active") self.active_space = drc("active_to_body_active") - self.contact_width = drc("minwidth_active_contact") + if "contact" in layer: + self.contact_width = drc("minwidth_contact") + else: + self.contact_width = drc("minwidth_active_contact") self.poly_to_active = drc("poly_to_active") self.poly_extend_active = drc("poly_extend_active") - self.poly_to_poly_contact = drc("poly_to_poly_contact") - self.active_contact_to_gate = drc("active_contact_to_gate") + self.poly_to_contact = drc("poly_to_contact") + self.contact_to_gate = drc("contact_to_gate") self.well_enclose_active = drc("well_enclosure_active") self.implant_enclose_active = drc("implant_enclosure_active") self.implant_space = drc("implant_to_implant") diff --git a/compiler/drc/design_rules.py b/compiler/drc/design_rules.py index 1017aca6..cf54bdb3 100644 --- a/compiler/drc/design_rules.py +++ b/compiler/drc/design_rules.py @@ -43,6 +43,20 @@ class design_rules(): else: debug.error("Must call complex DRC rule {} with arguments.".format(b),-1) + + def add_layer(self, name, width, spacing, area=0): + # Minimum width + self.add("minwidth_{}".format(name), width) + # Minimum spacing (could be a table too) + self.add("{0}_to_{0}".format(name), spacing) + # Minimum area + self.add("minarea_{}".format(name), area) - + def add_enclosure(self, name, layer, enclosure, extension=None): + self.add("{0}_enclosure_{1}".format(name, layer), enclosure) + # Reserved for asymmetric enclosures + if extension: + self.add("{0}_extend_{1}".format(name, layer), extension) + else: + self.add("{0}_extend_{1}".format(name, layer), enclosure) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index a1606ac3..588152c0 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -130,18 +130,18 @@ class ptx(design.design): # The contacted poly pitch (or uncontacted in an odd technology) - self.poly_pitch = max(2 * self.active_contact_to_gate + self.contact_width + self.poly_width, + self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width, self.poly_space) # The contacted poly pitch (or uncontacted in an odd technology) - self.contact_pitch = 2 * self.active_contact_to_gate + self.contact_width + self.poly_width + self.contact_pitch = 2 * self.contact_to_gate + self.contact_width + self.poly_width # The enclosure of an active contact. Not sure about second term. - active_enclose_contact = max(drc("active_enclosure_active_contact"), + active_enclose_contact = max(drc("active_enclosure_contact"), (self.active_width - self.contact_width) / 2) # This is the distance from the edge of poly to the contacted end of active - self.end_to_poly = active_enclose_contact + self.contact_width + self.active_contact_to_gate + self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate # Active width is determined by enclosure on both ends and contacted pitch, diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index 2e3f4f0f..60d8cd6b 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -21,7 +21,9 @@ class contact_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - for layer_stack in [("metal1", "via1", "metal2"), ("poly", "contact", "metal1")]: + from tech import poly_stack, beol_stacks + + for layer_stack in [poly_stack] + beol_stacks: stack_name = ":".join(map(str, layer_stack)) # Check single 1 x 1 contact" @@ -43,6 +45,10 @@ class contact_test(openram_test): debug.info(2, "1 x 1 {} test".format(stack_name)) c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1), directions=("V","V")) self.local_drc_check(c) + + # Only do multiple contacts for BEOL + for layer_stack in beol_stacks: + stack_name = ":".join(map(str, layer_stack)) # check vertical array with one in the middle and two ends debug.info(2, "1 x 3 {} test".format(stack_name)) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index a8b9d263..f8dc7d06 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -29,7 +29,28 @@ GDS["unit"] = (0.0005,1e-9) GDS["zoom"] = 0.05 ################################################### -##GDS Layer Map +# Interconnect stacks +################################################### + +poly_stack = ("poly", "contact", "metal1") +active_stack = ("active", "contact", "metal1") +metal1_stack = ("metal1", "via1", "metal2") +metal2_stack = ("metal2", "via2", "metal3") +metal3_stack = ("metal3", "via3", "metal4") + +# The FEOL stacks get us up to metal1 +feol_stacks = [poly_stack, + active_stack] + +# The BEOL stacks are metal1 and up +beol_stacks = [metal1_stack, + metal2_stack, + metal3_stack] + +layer_stacks = feol_stacks + beol_stacks + +################################################### +# GDS Layer Map ################################################### # create the GDS layer map @@ -44,8 +65,7 @@ layer["vtg"] = (6, 0) layer["vth"] = (7, 0) layer["thkox"] = (8, 0) layer["poly"] = (9, 0) -layer["active_contact"] = (10, 0) -layer["poly_contact"] = (10, 0) +layer["contact"] = (10, 0) layer["metal1"] = (11, 0) layer["via1"] = (12, 0) layer["metal2"] = (13, 0) @@ -69,11 +89,7 @@ layer["text"] = (239, 0) layer["boundary"]= (239, 0) ################################################### -##END GDS Layer Map -################################################### - -################################################### -##DRC/LVS Rules Setup +# DRC/LVS Rules Setup ################################################### #technology parameter @@ -105,18 +121,21 @@ drc["minlength_channel"] = 0.05 # WELL.2 Minimum spacing of nwell/pwell at different potential drc["pwell_to_nwell"] = 0.225 # WELL.3 Minimum spacing of nwell/pwell at the same potential -drc["well_to_well"] = 0.135 # WELL.4 Minimum width of nwell/pwell -drc["minwidth_well"] = 0.2 +drc.add_layer("well", + width = 0.2, + spacing = 0.135) # POLY.1 Minimum width of poly -drc["minwidth_poly"] = 0.05 # POLY.2 Minimum spacing of poly AND active -drc["poly_to_poly"] = 0.14 +drc.add_layer("poly", + width = 0.05, + spacing = 0.14) + # POLY.3 Minimum poly extension beyond active drc["poly_extend_active"] = 0.055 # Not a rule -drc["poly_to_poly_contact"] = 0.075 +drc["poly_to_contact"] = 0.075 # POLY.4 Minimum enclosure of active around gate drc["active_enclosure_gate"] = 0.07 # POLY.5 Minimum spacing of field poly to active @@ -129,162 +148,164 @@ drc["minarea_poly"] = 0.0 # ACTIVE.2 Minimum spacing of active drc["active_to_body_active"] = 0.08 # ACTIVE.1 Minimum width of active -drc["minwidth_active"] = 0.09 -# Not a rule -drc["active_to_active"] = 0 +drc.add_layer("active", + width = 0.09, + spacing = 0) # ACTIVE.3 Minimum enclosure/spacing of nwell/pwell to active -drc["well_enclosure_active"] = 0.055 -# Reserved for asymmetric enclosures -drc["well_extend_active"] = 0.055 -# Not a rule -drc["minarea_active"] = 0 +drc.add_enclosure("well", + layer = "active", + enclosure = 0.055) # IMPLANT.1 Minimum spacing of nimplant/ pimplant to channel drc["implant_to_channel"] = 0.07 # Not a rule -drc["implant_enclosure_active"] = 0 +drc.add_enclosure("implant", + layer = "active", + enclosure = 0) # Not a rule -drc["implant_enclosure_contact"] = 0 +drc.add_enclosure("implant", + layer = "contact", + enclosure = 0) # IMPLANT.2 Minimum spacing of nimplant/ pimplant to contact drc["implant_to_contact"] = 0.025 # IMPLANT.3 Minimum width/ spacing of nimplant/ pimplant -drc["implant_to_implant"] = 0.045 # IMPLANT.4 Minimum width/ spacing of nimplant/ pimplant -drc["minwidth_implant"] = 0.045 +drc.add_layer("implant", + width = 0.045, + spacing = 0.045) # CONTACT.1 Minimum width of contact -drc["minwidth_active_contact"] = 0.065 # CONTACT.2 Minimum spacing of contact -drc["active_contact_to_active_contact"] = 0.075 +drc.add_layer("contact", + width = 0.065, + spacing = 0.075) # CONTACT.4 Minimum enclosure of active around contact -drc["active_enclosure_active_contact"] = 0.005 -# Reserved for asymmetric enclosures -drc["active_extend_active_contact"] = 0.005 +drc.add_enclosure("active", + layer = "contact", + enclosure = 0.005) + # CONTACT.6 Minimum spacing of contact and gate -drc["active_contact_to_gate"] = 0.0375 #changed from 0.035 +drc["contact_to_gate"] = 0.0375 #changed from 0.035 # CONTACT.7 Minimum spacing of contact and poly -drc["active_contact_to_poly"] = 0.090 +drc["contact_to_poly"] = 0.090 # CONTACT.1 Minimum width of contact -drc["minwidth_poly_contact"] = 0.065 # CONTACT.2 Minimum spacing of contact -drc["poly_contact_to_poly_contact"] = 0.075 -# Reserved for asymmetric enclosures -drc["poly_extend_contact"] = 0.005 +drc.add_layer("contact", + width = 0.065, + spacing = 0.075) # CONTACT.5 Minimum enclosure of poly around contact -drc["poly_enclosure_poly_contact"] = 0.005 -# Reserved for asymmetric enclosures -drc["poly_extend_poly_contact"] = 0.005 +drc.add_enclosure("poly", + layer = "contact", + enclosure = 0.005) # CONTACT.6 Minimum spacing of contact and gate -drc["poly_contact_to_gate"] = 0.0375 #changed from 0.035 +drc["contact_to_gate"] = 0.0375 #changed from 0.035 # CONTACT.7 Minimum spacing of contact and poly -drc["poly_contact_to_poly"] = 0.090 +drc["contact_to_poly"] = 0.090 # METAL1.1 Minimum width of metal1 -drc["minwidth_metal1"] = 0.065 # METAL1.2 Minimum spacing of metal1 -drc["metal1_to_metal1"] = 0.065 +drc.add_layer("metal1", + width = 0.065, + spacing = 0.065) + # METAL1.3 Minimum enclosure around contact on two opposite sides -drc["metal1_enclosure_active_contact"] = 0 -# Reserved for asymmetric enclosures -drc["metal1_extend_active_contact"] = 0.035 -# METAL1.3 Minimum enclosure around contact on two opposite sides -drc["metal1_enclosure_poly_contact"] = 0 -# Reserved for asymmetric enclosures -drc["metal1_extend_poly_contact"] = 0.035 +drc.add_enclosure("metal1", + layer = "contact", + enclosure = 0, + extension = 0.035) # METAL1.4 inimum enclosure around via1 on two opposite sides -drc["metal1_extend_via1"] = 0.035 -# Reserved for asymmetric enclosures -drc["metal1_enclosure_via1"] = 0 -# Not a rule -drc["minarea_metal1"] = 0 +drc.add_enclosure("metal1", + layer = "via1", + enclosure = 0, + extension = 0.035) # VIA1.1 Minimum width of via1 -drc["minwidth_via1"] = 0.065 # VIA1.2 Minimum spacing of via1 -drc["via1_to_via1"] = 0.075 +drc.add_layer("via1", + width = 0.065, + spacing = 0.075) + # METALINT.1 Minimum width of intermediate metal -drc["minwidth_metal2"] = 0.07 # METALINT.2 Minimum spacing of intermediate metal -drc["metal2_to_metal2"] = 0.07 +drc.add_layer("metal2", + width = 0.07, + spacing = 0.07) + # METALINT.3 Minimum enclosure around via1 on two opposite sides -drc["metal2_extend_via1"] = 0.035 -# Reserved for asymmetric enclosures -drc["metal2_enclosure_via1"] = 0 +drc.add_enclosure("metal2", + layer = "via1", + enclosure = 0, + extension = 0.035) + # METALINT.4 Minimum enclosure around via[2-3] on two opposite sides -drc["metal2_extend_via2"] = 0.035 -# Reserved for asymmetric enclosures -drc["metal2_enclosure_via2"] = 0 -# Not a rule -drc["minarea_metal2"] = 0 +drc.add_enclosure("metal2", + layer = "via2", + enclosure = 0, + extension = 0.035) # VIA2-3.1 Minimum width of Via[2-3] -drc["minwidth_via2"] = 0.065 # VIA2-3.2 Minimum spacing of Via[2-3] -drc["via2_to_via2"] = 0.075 +drc.add_layer("via2", + width = 0.065, + spacing = 0.075) # METALINT.1 Minimum width of intermediate metal -drc["minwidth_metal3"] = 0.07 # METALINT.2 Minimum spacing of intermediate metal -#drc["metal3_to_metal3"] = 0.07 # Minimum spacing of metal3 wider than 0.09 & longer than 0.3 = 0.09 # Minimum spacing of metal3 wider than 0.27 & longer than 0.9 = 0.27 # Minimum spacing of metal3 wider than 0.5 & longer than 1.8 = 0.5 # Minimum spacing of metal3 wider than 0.9 & longer than 2.7 = 0.9 # Minimum spacing of metal3 wider than 1.5 & longer than 4.0 = 1.5 -drc["metal3_to_metal3"] = drc_lut({(0.00, 0.0) : 0.07, - (0.09, 0.3) : 0.09, - (0.27, 0.9) : 0.27, - (0.50, 1.8) : 0.5, - (0.90, 2.7) : 0.9, - (1.50, 4.0) : 1.5}) +drc.add_layer("metal3", + width = 0.07, + spacing = drc_lut({(0.00, 0.0) : 0.07, + (0.09, 0.3) : 0.09, + (0.27, 0.9) : 0.27, + (0.50, 1.8) : 0.5, + (0.90, 2.7) : 0.9, + (1.50, 4.0) : 1.5})) # METALINT.3 Minimum enclosure around via1 on two opposite sides -drc["metal3_extend_via2"] = 0.035 -# Reserved for asymmetric enclosures -drc["metal3_enclosure_via2"] = 0 +drc.add_enclosure("metal3", + layer = "via2", + enclosure = 0, + extension = 0.035) + # METALINT.4 Minimum enclosure around via[2-3] on two opposite sides -drc["metal3_extend_via3"]=0.035 -# Reserved for asymmetric enclosures -drc["metal3_enclosure_via3"] = 0 -# Not a rule -drc["minarea_metal3"] = 0 +drc.add_enclosure("metal3", + layer = "via3", + enclosure = 0, + extension = 0.035) # VIA2-3.1 Minimum width of Via[2-3] -drc["minwidth_via3"] = 0.07 # VIA2-3.2 Minimum spacing of Via[2-3] -drc["via3_to_via3"] = 0.085 +drc.add_layer("via3", + width = 0.07, + spacing = 0.085) # METALSMG.1 Minimum width of semi-global metal -drc["minwidth_metal4"] = 0.14 # METALSMG.2 Minimum spacing of semi-global metal -#drc["metal4_to_metal4"] = 0.14 # Minimum spacing of metal4 wider than 0.27 & longer than 0.9 = 0.27 # Minimum spacing of metal4 wider than 0.5 & longer than 1.8 = 0.5 # Minimum spacing of metal4 wider than 0.9 & longer than 2.7 = 0.9 # Minimum spacing of metal4 wider than 1.5 & longer than 4.0 = 1.5 -drc["metal4_to_metal4"] = drc_lut({(0.00, 0.0) : 0.14, - (0.27, 0.9) : 0.27, - (0.50, 1.8) : 0.5, - (0.90, 2.7) : 0.9, - (1.50, 4.0) : 1.5}) +drc.add_layer("metal4", + width = 0.14, + spacing = drc_lut({(0.00, 0.0) : 0.14, + (0.27, 0.9) : 0.27, + (0.50, 1.8) : 0.5, + (0.90, 2.7) : 0.9, + (1.50, 4.0) : 1.5})) # METALSMG.3 Minimum enclosure around via[3-6] on two opposite sides -drc["metal4_extend_via3"] = 0.0025 -# Reserved for asymmetric enclosure -drc["metal4_enclosure_via3"] = 0.0025 -# Not a rule -drc["minarea_metal4"] = 0 +drc.add_enclosure("metal4", + layer = "via3", + enclosure = 0.0025) # Metal 5-10 are ommitted - - ################################################### -##END DRC/LVS Rules -################################################### - -################################################### -##Spice Simulation Parameters +# Spice Simulation Parameters ################################################### #spice info @@ -347,11 +368,7 @@ parameter["sa_inv_nmos_size"] = 0.27 #micro-meters parameter["bitcell_drain_cap"] = 0.1 #In Femto-Farad, approximation of drain capacitance ################################################### -##END Spice Simulation Parameters -################################################### - -################################################### -##BEGIN Technology Tool Preferences +# Technology Tool Preferences ################################################### drc_name = "calibre" @@ -359,7 +376,3 @@ lvs_name = "calibre" pex_name = "calibre" blackbox_bitcell = False - -################################################### -##END Technology Tool Preferences -################################################### diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 8e189efd..23a0d9fe 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -28,6 +28,27 @@ GDS["unit"]=(0.001,1e-6) # default label zoom GDS["zoom"] = 0.5 +################################################### +# Interconnect stacks +################################################### + +poly_stack = ("poly", "poly_contact", "metal1") +active_stack = ("active", "active_contact", "metal1") +metal1_stack = ("metal1", "via1", "metal2") +metal2_stack = ("metal2", "via2", "metal3") +metal3_stack = ("metal3", "via3", "metal4") + +# The FEOL stacks get us up to metal1 +feol_stacks = [poly_stack, + active_stack] + +# The BEOL stacks are metal1 and up +beol_stacks = [metal1_stack, + metal2_stack, + metal3_stack] + +layer_stacks = feol_stacks + beol_stacks + ################################################### ##GDS Layer Map @@ -43,8 +64,8 @@ layer["active"] = (43, 0) layer["pimplant"] = (44, 0) layer["nimplant"] = (45, 0) layer["poly"] = (46, 0) -layer["active_contact"] = (48, 0) layer["poly_contact"] = (47, 0) +layer["active_contact"] = (48, 0) layer["metal1"] = (49, 0) layer["via1"] = (50, 0) layer["metal2"] = (51, 0) @@ -56,11 +77,7 @@ layer["text"] = (63, 0) layer["boundary"] = (63, 0) ################################################### -##END GDS Layer Map -################################################### - -################################################### -##DRC/LVS Rules Setup +# DRC/LVS Rules Setup ################################################### _lambda_ = 0.2 @@ -90,163 +107,168 @@ drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/scn3me_subm/layers.map" drc["minwidth_tx"] = 4*_lambda_ drc["minlength_channel"] = 2*_lambda_ -# 1.3 Minimum spacing between wells of same type (if both are drawn) -drc["well_to_well"] = 6*_lambda_ # 1.4 Minimum spacing between wells of different type (if both are drawn) drc["pwell_to_nwell"] = 0 +# 1.3 Minimum spacing between wells of same type (if both are drawn) # 1.1 Minimum width -drc["minwidth_well"] = 12*_lambda_ +drc.add_layer("well", + width = 12*_lambda_, + spacing = 6*_lambda_) # 3.1 Minimum width -drc["minwidth_poly"] = 2*_lambda_ # 3.2 Minimum spacing over active -drc["poly_to_poly"] = 3*_lambda_ +drc.add_layer("poly", + width = 2*_lambda_, + spacing = 3*_lambda_) # 3.3 Minimum gate extension of active drc["poly_extend_active"] = 2*_lambda_ # 5.5.b Minimum spacing between poly contact and other poly (alternative rules) -drc["poly_to_poly_contact"] = 4*_lambda_ +drc["poly_to_contact"] = 4*_lambda_ # ?? drc["active_enclosure_gate"] = 0.0 # 3.5 Minimum field poly to active drc["poly_to_active"] = _lambda_ # 3.2.a Minimum spacing over field poly drc["poly_to_field_poly"] = 3*_lambda_ -# Not a rule -drc["minarea_poly"] = 0.0 # ?? drc["active_to_body_active"] = 4*_lambda_ # Fix me # 2.1 Minimum width -drc["minwidth_active"] = 3*_lambda_ # 2.2 Minimum spacing -drc["active_to_active"] = 3*_lambda_ +drc.add_layer("active", + width = 3*_lambda_, + spacing = 3*_lambda_) # 2.3 Source/drain active to well edge -drc["well_enclosure_active"] = 6*_lambda_ -# Reserved for asymmetric enclosures -drc["well_extend_active"] = 6*_lambda_ -# Not a rule -drc["minarea_active"] = 0.0 +drc.add_enclosure("well", + layer = "active", + enclosure = 6*_lambda_) # 4.1 Minimum select spacing to channel of transistor to ensure adequate source/drain width drc["implant_to_channel"] = 3*_lambda_ # 4.2 Minimum select overlap of active -drc["implant_enclosure_active"] = 2*_lambda_ +drc.add_enclosure("implant", + layer = "active", + enclosure = 2*_lambda_) # 4.3 Minimum select overlap of contact -drc["implant_enclosure_contact"] = _lambda_ +drc.add_enclosure("implant", + layer = "contact", + enclosure = _lambda_) # Not a rule drc["implant_to_contact"] = 0 # Not a rule -drc["implant_to_implant"] = 0 -# Not a rule -drc["minwidth_implant"] = 0 +drc.add_layer("implant", + width = 0, + spacing = 0) # 6.1 Exact contact size -drc["minwidth_active_contact"] = 2*_lambda_ # 5.3 Minimum contact spacing -drc["active_contact_to_active_contact"] = 3*_lambda_ -# 6.2.b Minimum active overlap -drc["active_enclosure_active_contact"] = _lambda_ -# Reserved for asymmetric enclosure -drc["active_extend_active_contact"] = _lambda_ +drc.add_layer("active_contact", + width = 2*_lambda_, + spacing = 3*_lambda_) +# 6.2.b Minimum active overlap +drc.add_enclosure("active", + layer = "active_contact", + enclosure = _lambda_) +drc.add_enclosure("active", + layer = "contact", + enclosure = _lambda_) # Reserved for other technologies -drc["active_contact_to_gate"] = 2*_lambda_ +drc["contact_to_gate"] = 2*_lambda_ # 5.4 Minimum spacing to gate of transistor -drc["active_contact_to_poly"] = 2*_lambda_ +drc["contact_to_poly"] = 2*_lambda_ # 6.1 Exact contact size -drc["minwidth_poly_contact"] = 2*_lambda_ # 5.3 Minimum contact spacing -drc["poly_contact_to_poly_contact"] = 3*_lambda_ +drc.add_layer("poly_contact", + width = 2*_lambda_, + spacing = 3*_lambda_) # 5.2.b Minimum poly overlap -drc["poly_enclosure_poly_contact"] = _lambda_ -# Reserved for asymmetric enclosures -drc["poly_extend_poly_contact"] = _lambda_ +drc.add_enclosure("poly", + layer = "poly_contact", + enclosure = _lambda_) # Reserved for other technologies drc["poly_contact_to_gate"] = 2*_lambda_ # 5.4 Minimum spacing to gate of transistor drc["poly_contact_to_poly"] = 2*_lambda_ # 7.1 Minimum width -drc["minwidth_metal1"] = 3*_lambda_ # 7.2 Minimum spacing -drc["metal1_to_metal1"] = 3*_lambda_ +drc.add_layer("metal1", + width = 3*_lambda_, + spacing = 3*_lambda_) # 7.3 Minimum overlap of any contact -drc["metal1_enclosure_active_contact"] = _lambda_ -# Reserved for asymmetric enclosure -drc["metal1_extend_active_contact"] = _lambda_ -# 7.3 Minimum overlap of any contact -drc["metal1_enclosure_poly_contact"] = _lambda_ -# Reserved for asymmetric enclosure -drc["metal1_extend_poly_contact"] = _lambda_ -# 8.3 Minimum overlap by metal1 -drc["metal1_enclosure_via1"] = _lambda_ -# Reserve for asymmetric enclosures -drc["metal1_extend_via1"] = _lambda_ -# Not a rule -drc["minarea_metal1"] = 0 +drc.add_enclosure("metal1", + layer = "poly_contact", + enclosure = _lambda_) +drc.add_enclosure("metal1", + layer = "active_contact", + enclosure = _lambda_) +# 8.3 Minimum overlap by metal1 +drc.add_enclosure("metal1", + layer = "via1", + enclosure = _lambda_) # 8.1 Exact size -drc["minwidth_via1"] = 2*_lambda_ # 8.2 Minimum via1 spacing -drc["via1_to_via1"] = 3*_lambda_ +drc.add_layer("via1", + width = 2*_lambda_, + spacing = 3*_lambda_) # 9.1 Minimum width -drc["minwidth_metal2"] = 3*_lambda_ # 9.2 Minimum spacing -drc["metal2_to_metal2"] = 3*_lambda_ +drc.add_layer("metal2", + width = 3*_lambda_, + spacing = 3*_lambda_) # 9.3 Minimum overlap of via1 -drc["metal2_extend_via1"] = _lambda_ -# Reserved for asymmetric enclosures -drc["metal2_enclosure_via1"] = _lambda_ +drc.add_enclosure("metal2", + layer = "via1", + enclosure = _lambda_) # 14.3 Minimum overlap by metal2 -drc["metal2_extend_via2"] = _lambda_ -# Reserved for asymmetric enclosures -drc["metal2_enclosure_via2"] = _lambda_ -# Not a rule -drc["minarea_metal2"] = 0 +drc.add_enclosure("metal2", + layer = "via2", + enclosure = _lambda_) # 14.1 Exact size -drc["minwidth_via2"] = 2*_lambda_ # 14.2 Minimum spacing -drc["via2_to_via2"] = 3*_lambda_ +drc.add_layer("via2", + width = 2*_lambda_, + spacing = 3*_lambda_) # 15.1 Minimum width -drc["minwidth_metal3"] = 3*_lambda_ # 15.2 Minimum spacing to metal3 -drc["metal3_to_metal3"] = 3*_lambda_ +drc.add_layer("metal3", + width = 3*_lambda_, + spacing = 3*_lambda_) + # 15.3 Minimum overlap of via 2 -drc["metal3_extend_via2"] = _lambda_ -# Reserved for asymmetric enclosures -drc["metal3_enclosure_via2"] = _lambda_ +drc.add_enclosure("metal3", + layer = "via2", + enclosure = _lambda_) + # 21.3 Minimum overlap by metal3 -drc["metal3_extend_via3"] = _lambda_ -# Reserved for asymmetric enclosures -drc["metal3_enclosure_via3"] = _lambda_ -# Not a rule -drc["minarea_metal3"] = 0 +drc.add_enclosure("metal3", + layer = "via3", + enclosure = _lambda_) # 21.1 Exact size -drc["minwidth_via3"] = 2*_lambda_ # 21.2 Minimum spacing -drc["via3_to_via3"] = 3*_lambda_ +drc.add_layer("via3", + width = 2*_lambda_, + spacing = 3*_lambda_) # 22.1 Minimum width -drc["minwidth_metal4"] = 6*_lambda_ # 22.2 Minimum spacing to metal4 -drc["metal4_to_metal4"] = 6*_lambda_ +drc.add_layer("metal4", + width = 6*_lambda_, + spacing = 6*_lambda_) + # 22.3 Minimum overlap of via 3 -drc["metal4_extend_via3"] = 2*_lambda_ -# Reserved for asymmetric enclosures -drc["metal4_enclosure_via3"] = 2*_lambda_ -# Not a rule -drc["minarea_metal4"] = 0 +drc.add_enclosure("metal4", + layer = "via3", + enclosure = 2*_lambda_) ################################################### -##END DRC/LVS Rules -################################################### - -################################################### -##Spice Simulation Parameters +# Spice Simulation Parameters ################################################### # spice model info @@ -311,11 +333,7 @@ parameter["sa_inv_nmos_size"] = 9*_lambda_ parameter["bitcell_drain_cap"] = 0.2 #In Femto-Farad, approximation of drain capacitance ################################################### -##END Spice Simulation Parameters -################################################### - -################################################### -##BEGIN Technology Tool Preferences +# Technology Tool Preferences ################################################### drc_name = "magic" @@ -323,7 +341,3 @@ lvs_name = "netgen" pex_name = "magic" blackbox_bitcell = False - -################################################### -##END Technology Tool Preferences -################################################### From 8d3f1d19cb2dcdb2fbba62f1a98779a914994916 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 11 Dec 2019 18:02:32 -0800 Subject: [PATCH 004/103] Fix missing rule --- compiler/bitcells/pbitcell.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 0e0e1bf7..1e3eb506 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -243,7 +243,7 @@ class pbitcell(bitcell_base.bitcell_base): (self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height) self.inverter_gap = max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ - + self.poly_to_poly_contact + 2 * contact.poly.width \ + + self.poly_to_contact + 2 * contact.poly.width \ + self.m1_space + inverter_pmos_contact_extension self.cross_couple_lower_ypos = self.inverter_nmos_ypos \ + self.inverter_nmos.active_height \ @@ -254,7 +254,7 @@ class pbitcell(bitcell_base.bitcell_base): + self.inverter_nmos.active_height \ + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ - + self.poly_to_poly_contact \ + + self.poly_to_contact \ + 1.5 * contact.poly.width # spacing between wordlines (and gnd) @@ -926,14 +926,14 @@ class pbitcell(bitcell_base.bitcell_base): """ # add poly to metal1 contacts for gates of the inverters left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x \ - - self.poly_to_poly_contact - 0.5*contact.poly.width, + - self.poly_to_contact - 0.5*contact.poly.width, self.cross_couple_upper_ypos) self.add_via_center(layers=("poly", "contact", "metal1"), offset=left_storage_contact, directions=("H", "H")) right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \ - + self.poly_to_poly_contact + 0.5*contact.poly.width, + + self.poly_to_contact + 0.5*contact.poly.width, self.cross_couple_upper_ypos) self.add_via_center(layers=("poly", "contact", "metal1"), offset=right_storage_contact, From e143a6033fdd26c3e8cd0b28b379c49a608c744e Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 13 Dec 2019 14:13:41 -0800 Subject: [PATCH 005/103] Use layer stacks from tech file in design class and throughout --- compiler/base/contact.py | 6 ++-- compiler/base/design.py | 18 ++++++++++-- compiler/base/hierarchy_layout.py | 14 ++++----- compiler/bitcells/pbitcell.py | 28 +++++++++--------- compiler/modules/bank.py | 14 ++++----- compiler/modules/bank_select.py | 10 +++---- compiler/modules/hierarchical_decoder.py | 10 +++---- compiler/modules/hierarchical_predecode.py | 10 +++---- compiler/modules/multibank.py | 14 ++++----- compiler/modules/port_data.py | 4 +-- compiler/modules/sense_amp_array.py | 4 +-- .../modules/single_level_column_mux_array.py | 8 ++--- compiler/modules/tri_gate_array.py | 2 +- compiler/modules/wordline_driver.py | 6 ++-- compiler/modules/write_driver_array.py | 2 +- compiler/modules/write_mask_and_array.py | 8 ++--- compiler/pgates/pgate.py | 2 +- compiler/pgates/pinv.py | 4 +-- compiler/pgates/pinvbuf.py | 10 +++---- compiler/pgates/pnand2.py | 6 ++-- compiler/pgates/pnand3.py | 8 ++--- compiler/pgates/pnor2.py | 6 ++-- compiler/pgates/precharge.py | 13 ++++----- compiler/pgates/ptx.py | 1 - compiler/pgates/single_level_column_mux.py | 12 ++++---- compiler/sram/sram_1bank.py | 29 ++++++++++--------- compiler/tests/03_wire_test.py | 12 ++++---- technology/scn4m_subm/tech/tech.py | 12 ++++---- 28 files changed, 141 insertions(+), 132 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 26a59d31..5f169e07 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -245,14 +245,14 @@ else: lim1 = None m1m2 = factory.create(module_type="contact", - layer_stack=metal1_stack, + layer_stack=m1_stack, directions=("H", "V")) m2m3 = factory.create(module_type="contact", - layer_stack=metal2_stack, + layer_stack=m2_stack, directions=("V", "H")) if "metal4" in layer.keys(): m3m4 = factory.create(module_type="contact", - layer_stack=metal3_stack, + layer_stack=m3_stack, directions=("H", "V")) else: m3m4 = None diff --git a/compiler/base/design.py b/compiler/base/design.py index 0ecfbbd4..16237cfd 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -13,7 +13,7 @@ from globals import OPTS class design(hierarchy_design): """ This is the same as the hierarchy_design class except it contains - some DRC constants and analytical models for other modules to reuse. + some DRC/layer constants and analytical models for other modules to reuse. """ @@ -21,16 +21,28 @@ class design(hierarchy_design): hierarchy_design.__init__(self, name) self.setup_drc_constants() + self.setup_layer_constants() self.setup_multiport_constants() - from tech import layer + + def setup_layer_constants(self): + """ These are some layer constants used in many places in the compiler.""" + + import tech + self.m1_pitch = max(contact.m1m2.width, contact.m1m2.height) + max(self.m1_space, self.m2_space) self.m2_pitch = max(contact.m2m3.width, contact.m2m3.height) + max(self.m2_space, self.m3_space) - if "metal4" in layer: + if "metal4" in tech.layer: self.m3_pitch = max(contact.m3m4.width, contact.m3m4.height) + max(self.m3_space, self.m4_space) else: self.m3_pitch = self.m2_pitch + self.poly_stack = tech.poly_stack + self.active_stack = tech.active_stack + self.m1_stack = tech.m1_stack + self.m2_stack = tech.m2_stack + self.m3_stack = tech.m3_stack + def setup_drc_constants(self): """ These are some DRC constants used in many places in the compiler.""" from tech import drc, layer diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 494ab4ca..ad1e3edb 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -824,7 +824,7 @@ class layout(): def create_channel_route(self, netlist, offset, - layer_stack=("metal1", "via1", "metal2"), + layer_stack, vertical=False): """ The net list is a list of the nets. Each net is a list of pins @@ -950,15 +950,13 @@ class layout(): offset += vector(0,self.horizontal_pitch) - def create_vertical_channel_route(self, netlist, offset, - layer_stack=("metal1", "via1", "metal2")): + def create_vertical_channel_route(self, netlist, offset, layer_stack): """ Wrapper to create a vertical channel route """ self.create_channel_route(netlist, offset, layer_stack, vertical=True) - def create_horizontal_channel_route(self, netlist, offset, - layer_stack=("metal1", "via1", "metal2")): + def create_horizontal_channel_route(self, netlist, offset, layer_stack): """ Wrapper to create a horizontal channel route """ @@ -1028,13 +1026,13 @@ class layout(): direction=("H","H") if start_layer=="metal1": - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=loc, directions=direction) if start_layer=="metal1" or start_layer=="metal2": - via=self.add_via_center(layers=("metal2", "via2", "metal3"), + via=self.add_via_center(layers=self.m2_stack, offset=loc, directions=direction) @@ -1159,7 +1157,7 @@ class layout(): vector(self.right_vdd_x_center, self.top_vdd_y_center)] for pt in via_points: - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=pt, size = (self.supply_vias, self.supply_vias)) diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 1e3eb506..0f4b8183 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -378,7 +378,7 @@ class pbitcell(bitcell_base.bitcell_base): contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x \ + 0.5 * contact.poly.height, self.cross_couple_upper_ypos) - self.add_via_center(layers=("poly", "contact", "metal1"), + self.add_via_center(layers=self.poly_stack, offset=contact_offset_left, directions=("H", "H")) @@ -386,7 +386,7 @@ class pbitcell(bitcell_base.bitcell_base): contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \ - 0.5*contact.poly.height, self.cross_couple_lower_ypos) - self.add_via_center(layers=("poly", "contact", "metal1"), + self.add_via_center(layers=self.poly_stack, offset=contact_offset_right, directions=("H", "H")) @@ -771,7 +771,7 @@ class pbitcell(bitcell_base.bitcell_base): # first transistor on either side of the cross coupled inverters # does not need to route to wordline on metal2 if (k == 0) or (k == 1): - self.add_via_center(layers=("poly", "contact", "metal1"), + self.add_via_center(layers=self.poly_stack, offset=port_contact_offset) self.add_path("poly", [gate_offset, port_contact_offset]) @@ -779,12 +779,12 @@ class pbitcell(bitcell_base.bitcell_base): [port_contact_offset, wl_contact_offset]) else: - self.add_via_center(layers=("poly", "contact", "metal1"), + self.add_via_center(layers=self.poly_stack, offset=port_contact_offset) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=port_contact_offset) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=wl_contact_offset, directions=("H", "H")) @@ -824,7 +824,7 @@ class pbitcell(bitcell_base.bitcell_base): # Leave bitline disconnected if a dummy cell if not self.dummy_bitcell: - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=port_contact_offest) self.add_path("metal2", @@ -836,7 +836,7 @@ class pbitcell(bitcell_base.bitcell_base): # Leave bitline disconnected if a dummy cell if not self.dummy_bitcell: - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=port_contact_offest) self.add_path("metal2", @@ -853,7 +853,7 @@ class pbitcell(bitcell_base.bitcell_base): nmos_contact_positions.append(self.read_access_nmos_right[k].get_pin("S").center()) for position in nmos_contact_positions: - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=position) if position.x > 0: @@ -862,7 +862,7 @@ class pbitcell(bitcell_base.bitcell_base): contact_correct = -0.5 * contact.m1m2.height supply_offset = vector(position.x + contact_correct, self.gnd_position.y) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=supply_offset, directions=("H", "H")) @@ -928,14 +928,14 @@ class pbitcell(bitcell_base.bitcell_base): left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x \ - self.poly_to_contact - 0.5*contact.poly.width, self.cross_couple_upper_ypos) - self.add_via_center(layers=("poly", "contact", "metal1"), + self.add_via_center(layers=self.poly_stack, offset=left_storage_contact, directions=("H", "H")) right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \ + self.poly_to_contact + 0.5*contact.poly.width, self.cross_couple_upper_ypos) - self.add_via_center(layers=("poly", "contact", "metal1"), + self.add_via_center(layers=self.poly_stack, offset=right_storage_contact, directions=("H", "H")) @@ -952,7 +952,7 @@ class pbitcell(bitcell_base.bitcell_base): + vector(0, self.gate_contact_yoffset - self.poly_extend_active) - self.add_via_center(layers=("poly", "contact", "metal1"), + self.add_via_center(layers=self.poly_stack, offset=port_contact_offset) self.add_path("poly", @@ -967,7 +967,7 @@ class pbitcell(bitcell_base.bitcell_base): + vector(0, self.gate_contact_yoffset - self.poly_extend_active) - self.add_via_center(layers=("poly", "contact", "metal1"), + self.add_via_center(layers=self.poly_stack, offset=port_contact_offset) self.add_path("poly", diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 3e105d09..b1ce2d55 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -606,11 +606,11 @@ class bank(design.design): name = self.control_signals[port][signal] bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y) self.add_path("metal3",[out_pos, bus_pos]) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=bus_pos) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=out_pos) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=out_pos) @@ -756,7 +756,7 @@ class bank(design.design): bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))] top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))] route_map = list(zip(bottom_names, top_names)) - self.create_horizontal_channel_route(route_map, offset) + self.create_horizontal_channel_route(route_map, offset, self.m1_stack) def connect_bitline(self, inst1, inst2, inst1_name, inst2_name): """ @@ -865,7 +865,7 @@ class bank(design.design): column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names] route_map = list(zip(decode_pins, column_mux_pins)) - self.create_vertical_channel_route(route_map, offset) + self.create_vertical_channel_route(route_map, offset, self.m1_stack) def add_lvs_correspondence_points(self): @@ -942,7 +942,7 @@ class bank(design.design): control_mid_pos = self.bus_xoffset[port][control_signal] control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y) self.add_wire(("metal1","via1","metal2"), [control_mid_pos, control_pos, pin_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=control_pos) @@ -957,7 +957,7 @@ class bank(design.design): control_x_offset = self.bus_xoffset[port][control_signal].x control_pos = vector(control_x_offset, mid_pos.y) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=control_pos) def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 296cef8b..14b6b203 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -258,7 +258,7 @@ class bank_select(design.design): logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1m2.height,0) input_pos = vector(xoffset_bank_signal, logic_pos.y) self.add_path("metal2",[logic_pos, input_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=logic_pos, directions=("H","H")) @@ -266,10 +266,10 @@ class bank_select(design.design): # Connect the logic A input to the input pin logic_pos = logic_inst.get_pin("A").lc() input_pos = vector(0,logic_pos.y) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=logic_pos, directions=("H","H")) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=logic_pos, directions=("H","H")) self.add_layout_pin_segment_center(text=input_name, @@ -301,10 +301,10 @@ class bank_select(design.design): # Add pins in two locations for xoffset in [a_xoffset, b_xoffset]: pin_pos = vector(xoffset, supply_pin.cy()) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=pin_pos, directions=("H","H")) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=pin_pos, directions=("H","H")) self.add_layout_pin_rect_center(text=n, diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 4648519a..9c7cafd8 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -221,9 +221,9 @@ class hierarchical_decoder(design.design): def route_input_rail(self, input_offset, output_offset): """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=input_offset) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=output_offset) self.add_path(("metal3"), [input_offset, output_offset]) @@ -585,7 +585,7 @@ class hierarchical_decoder(design.design): """ Connect the routing rail to the given metal1 pin """ rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y) self.add_path("metal1", [rail_pos, pin.lc()]) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=rail_pos) @@ -595,10 +595,10 @@ class hierarchical_decoder(design.design): # It would be fixed with a channel router. mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2) rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=pin.center()) self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()]) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=rail_pos) def input_load(self): diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index bec0ce06..0de67cae 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -184,9 +184,9 @@ class hierarchical_predecode(design.design): in_pos = vector(self.input_rails[in_pin].x,y_offset) a_pos = vector(self.decode_rails[a_pin].x,y_offset) self.add_path("metal1",[in_pos, a_pos]) - self.add_via_center(layers = ("metal1", "via1", "metal2"), + self.add_via_center(layers = self.m1_stack, offset=[self.input_rails[in_pin].x, y_offset]) - self.add_via_center(layers = ("metal1", "via1", "metal2"), + self.add_via_center(layers = self.m1_stack, offset=[self.decode_rails[a_pin].x, y_offset]) def route_output_inverters(self): @@ -227,7 +227,7 @@ class hierarchical_predecode(design.design): right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0) rail_pos = vector(self.decode_rails[out_pin].x,y_offset) self.add_path("metal1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) - self.add_via_center(layers = ("metal1", "via1", "metal2"), + self.add_via_center(layers = self.m1_stack, offset=rail_pos) @@ -235,7 +235,7 @@ class hierarchical_predecode(design.design): inv_in_pos = self.in_inst[inv_num].get_pin("A").lc() in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y) self.add_path("metal1", [in_pos, inv_in_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=in_pos) @@ -256,7 +256,7 @@ class hierarchical_predecode(design.design): pin_pos = self.nand_inst[k].get_pin(gate_pin).lc() rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) self.add_path("metal1", [rail_pos, pin_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=rail_pos) diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index 2f933a2d..3459f5e0 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -452,13 +452,13 @@ class multibank(design.design): out_pos = self.bank_select_inst.get_pin(gated_name).rc() bus_pos = vector(self.bus_xoffset[gated_name], out_pos.y) self.add_path("metal3",[out_pos, bus_pos]) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=bus_pos, rotate=90) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=out_pos, rotate=90) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=out_pos, rotate=90) @@ -586,9 +586,9 @@ class multibank(design.design): tri_gate_in = self.tri_gate_array_inst.get_pin("in_{}".format(i)).lc() sa_data_out = self.sense_amp_array_inst.get_pin("data_{}".format(i)).bc() - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=tri_gate_in) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=sa_data_out) self.add_path("metal3",[sa_data_out,tri_gate_in]) @@ -766,7 +766,7 @@ class multibank(design.design): for (control_signal, pin_pos) in connection: control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) self.add_path("metal1", [control_pos, pin_pos]) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=control_pos, rotate=90) @@ -778,7 +778,7 @@ class multibank(design.design): control_pos = vector(control_x_offset + self.m1_width, mid_pos.y) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) control_via_pos = vector(control_x_offset, mid_pos.y) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=control_via_pos, rotate=90) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index c3473803..a5bc4399 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -475,7 +475,7 @@ class port_data(design.design): end_pos = vector(length, wdriver_en_pin.cy()) # Add via for the write driver array's enable input - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=end_pos) # Route between write mask AND array and write driver array @@ -626,7 +626,7 @@ class port_data(design.design): bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit+bottom_start_bit)), bottom_inst.get_pin(bottom_br_name.format(bit+bottom_start_bit))] top_names = [top_inst.get_pin(top_bl_name.format(bit+top_start_bit)), top_inst.get_pin(top_br_name.format(bit+top_start_bit))] route_map = list(zip(bottom_names, top_names)) - self.create_horizontal_channel_route(route_map, offset) + self.create_horizontal_channel_route(route_map, offset, self.m1_stack) def connect_bitlines(self, inst1, inst2, num_bits, diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 598886c0..21ee4a8d 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -99,13 +99,13 @@ class sense_amp_array(design.design): inst = self.local_insts[i] gnd_pos = inst.get_pin("gnd").center() - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=gnd_pos) self.add_layout_pin_rect_center(text="gnd", layer="metal3", offset=gnd_pos) vdd_pos = inst.get_pin("vdd").center() - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=vdd_pos) self.add_layout_pin_rect_center(text="vdd", layer="metal3", diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 7e3beaad..3e6420b9 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -191,11 +191,11 @@ class single_level_column_mux_array(design.design): # This via is on the right of the wire - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=bl_out_offset) # This via is on the left of the wire - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=br_out_offset) else: @@ -204,10 +204,10 @@ class single_level_column_mux_array(design.design): self.add_path("metal2", [ br_out_offset, br_out_offset_end]) # This via is on the right of the wire - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=bl_out_offset) # This via is on the left of the wire - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=br_out_offset) def get_drain_cin(self): diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index 7d1c21d0..501e2389 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -100,7 +100,7 @@ class tri_gate_array(design.design): for n in ["vdd", "gnd"]: for supply_pin in self.tri_inst[i].get_pins(n): pin_pos = supply_pin.center() - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=pin_pos) self.add_layout_pin_rect_center(text=n, layer="metal3", diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index fbd800b2..99365659 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -158,7 +158,7 @@ class wordline_driver(design.design): self.add_segment_center(layer="metal1", start=clk_offset, end=a_pos) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=clk_offset) # Nand2 out to 2nd inv @@ -185,14 +185,14 @@ class wordline_driver(design.design): layer="metal1", start=input_offset, end=mid_via_offset) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=mid_via_offset, directions=("V", "V")) # now connect to the nand2 B self.add_path("metal2", [mid_via_offset, b_pos]) contact_offset = b_pos - vector(0.5 * contact.m1m2.height, 0) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=contact_offset, directions=("H", "H")) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 100ee3a2..4dfa938c 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -143,7 +143,7 @@ class write_driver_array(design.design): for pin in pin_list: pin_pos = pin.center() # Add the M2->M3 stack - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=pin_pos) self.add_layout_pin_rect_center(text=n, layer="metal3", diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 258bbd8d..3a4ec651 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -117,9 +117,9 @@ class write_mask_and_array(design.design): layer="metal3", offset=beg_en_pin.bc(), width=end_en_pin.cx() - beg_en_pin.cx() + en_to_edge) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy())) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy())) else: self.add_layout_pin(text="en", @@ -134,9 +134,9 @@ class write_mask_and_array(design.design): # Add via connections to metal3 for AND array's B pin en_pin = self.and2_insts[i].get_pin("B") - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=en_pin.center()) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=en_pin.center()) self.add_power_pin("gnd", vector(supply_pin.width() + i * self.wmask_en_len, 0)) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 0c1c0b52..5ad98fad 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -115,7 +115,7 @@ class pgate(design.design): # Non-preferred direction via - self.add_via_center(layers=("poly", "contact", "metal1"), + self.add_via_center(layers=self.poly_stack, offset=contact_offset, directions=directions) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 2b8ec7b7..5c53480b 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -115,8 +115,8 @@ class pinv(pgate.pgate): # Divide the height in half. Could divide proportional to beta, # but this makes connecting wells of multiple cells easier. # Subtract the poly space under the rail of the tx - nmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly") - pmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly") + nmos_height_available = 0.5 * tx_height_available - 0.5 * self.poly_space + pmos_height_available = 0.5 * tx_height_available - 0.5 * self.poly_space debug.info(2, "Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available, diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 75b7ce6f..579e1198 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -126,9 +126,9 @@ class pinvbuf(pgate.pgate): z1_pin = self.inv1_inst.get_pin("Z") a4_pin = self.inv4_inst.get_pin("A") mid_point = vector(z1_pin.cx(), a4_pin.cy()) - self.add_wire(("metal1", "via1", "metal2"), + self.add_wire(self.m1_stack, [z1_pin.center(), mid_point, a4_pin.center()]) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=z1_pin.center()) def add_layout_pins(self): @@ -161,21 +161,21 @@ class pinvbuf(pgate.pgate): self.add_layout_pin_rect_center(text="Z", layer="metal2", offset=z_pin.center()) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=z_pin.center()) zb_pin = self.inv3_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Zb", layer="metal2", offset=zb_pin.center()) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=zb_pin.center()) a_pin = self.inv1_inst.get_pin("A") self.add_layout_pin_rect_center(text="A", layer="metal2", offset=a_pin.center()) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False): diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index c5c69fd4..4623048c 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -225,13 +225,13 @@ class pnand2(pgate.pgate): mid1_offset = vector(out_offset.x, top_pin_offset.y) mid2_offset = vector(out_offset.x, bottom_pin_offset.y) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=pmos_pin.center(), directions=("V", "H")) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=nmos_pin.center(), directions=("V", "H")) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=out_offset) # PMOS1 to mid-drain to NMOS2 drain diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 621829f1..6d885e25 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -237,11 +237,11 @@ class pnand3(pgate.pgate): nmos3_pin = self.nmos3_inst.get_pin("D") # Go up to metal2 for ease on all output pins - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=pmos1_pin.center()) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=pmos3_pin.center()) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=nmos3_pin.center()) # PMOS3 and NMOS3 are drain aligned @@ -251,7 +251,7 @@ class pnand3(pgate.pgate): self.add_path("metal2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) # This extends the output to the edge of the cell - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=mid_offset) self.add_layout_pin_rect_center(text="Z", layer="metal1", diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index a99253cd..a79d19a0 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -216,9 +216,9 @@ class pnor2(pgate.pgate): nmos2_pin = self.nmos2_inst.get_pin("D") # Go up to metal2 for ease on all output pins - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=pmos_pin.center()) - m1m2_contact = self.add_via_center(layers=("metal1", "via1", "metal2"), + m1m2_contact = self.add_via_center(layers=self.m1_stack, offset=nmos_pin.center()) mid1_offset = vector(pmos_pin.center().x, nmos2_pin.center().y) @@ -231,7 +231,7 @@ class pnor2(pgate.pgate): self.add_path("metal2", [nmos_pin.rc(), mid1_offset, mid2_offset]) # This extends the output to the edge of the cell - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=mid3_offset) self.add_layout_pin_rect_center(text="Z", layer="metal1", diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index bd391ecc..d27b9f9c 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -159,7 +159,7 @@ class precharge(design.design): # adds the en contact to connect the gates to the en rail on metal1 offset = self.lower_pmos_inst.get_pin("G").ul() \ + vector(0, 0.5 * self.poly_space) - self.add_via_center(layers=("poly", "contact", "metal1"), + self.add_via_center(layers=self.poly_stack, offset=offset) # adds the en rail on metal1 @@ -202,7 +202,6 @@ class precharge(design.design): self.bl_pin = self.add_layout_pin(text="bl", layer="metal2", offset=offset, - width=drc("minwidth_metal2"), height=self.height) # adds the BR on metal 2 @@ -211,7 +210,6 @@ class precharge(design.design): self.br_pin = self.add_layout_pin(text="br", layer="metal2", offset=offset, - width=drc("minwidth_metal2"), height=self.height) def connect_to_bitlines(self): @@ -233,23 +231,22 @@ class precharge(design.design): Adds contacts/via from metal1 to metal2 for bit-lines """ - stack = ("metal1", "via1", "metal2") upper_pin = self.upper_pmos1_inst.get_pin("S") lower_pin = self.lower_pmos_inst.get_pin("S") # BL goes up to M2 at the transistor - self.bl_contact =self.add_via_center(layers=stack, + self.bl_contact =self.add_via_center(layers=self.m1_stack, offset=upper_pin.center(), directions=("V", "V")) - self.add_via_center(layers=stack, + self.add_via_center(layers=self.m1_stack, offset=lower_pin.center(), directions=("V", "V")) # BR routes over on M1 first - self.add_via_center(layers=stack, + self.add_via_center(layers=self.m1_stack, offset=vector(self.br_pin.cx(), upper_pin.cy()), directions=("V", "V")) - self.add_via_center(layers=stack, + self.add_via_center(layers=self.m1_stack, offset=vector(self.br_pin.cx(), lower_pin.cy()), directions=("V", "V")) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 588152c0..d0854711 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -52,7 +52,6 @@ class ptx(design.design): self.connect_poly = connect_poly self.num_contacts = num_contacts - # Do NOT create the netlist and layout (not a pgate) # Since it has variable height, it is not a pgate. self.create_netlist() # We must always create ptx layout for pbitcell diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index ffe313c8..a642eeca 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -126,16 +126,16 @@ class single_level_column_mux(pgate.pgate): nmos_upper_d_pin = self.nmos_upper.get_pin("D") # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=bl_pin.bc(), directions=("V", "V")) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=br_out_pin.uc(), directions=("V", "V")) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=nmos_upper_s_pin.center(), directions=("V", "V")) - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=nmos_lower_d_pin.center(), directions=("V", "V")) @@ -181,9 +181,9 @@ class single_level_column_mux(pgate.pgate): well_type="p") # Add the M1->M2->M3 stack - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=active_pos) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=active_pos) self.add_layout_pin_rect_center(text="gnd", layer="metal3", diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 9e578c7c..6c887790 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -371,12 +371,12 @@ class sram_1bank(sram_base): if self.write_size: for x in dff_names: pin_offset = self.data_dff_insts[port].get_pin(x).center() - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=pin_offset, directions = ("V", "V")) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=pin_offset) - self.add_via_center(layers=("metal3", "via3", "metal4"), + self.add_via_center(layers=self.m3_stack, offset=pin_offset) bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)] @@ -387,20 +387,21 @@ class sram_1bank(sram_base): pin_offset = self.bank_inst.get_pin(x).uc() else: pin_offset = self.bank_inst.get_pin(x).bc() - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=pin_offset) - self.add_via_center(layers=("metal2", "via2", "metal3"), + self.add_via_center(layers=self.m2_stack, offset=pin_offset) - self.add_via_center(layers=("metal3", "via3", "metal4"), + self.add_via_center(layers=self.m3_stack, offset=pin_offset) route_map = list(zip(bank_pins, dff_pins)) if self.write_size: - self.create_horizontal_channel_route(netlist=route_map, - offset=offset, - layer_stack=("metal3", "via3", "metal4")) + layer_stack = self.m3_stack else: - self.create_horizontal_channel_route(route_map, offset) + layer_stack = self.m1_stack + self.create_horizontal_channel_route(netlist=route_map, + offset=offset, + layer_stack=layer_stack) def route_wmask_dff(self): """ Connect the output of the wmask flops to the write mask AND array """ @@ -415,7 +416,7 @@ class sram_1bank(sram_base): dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names] for x in dff_names: offset_pin = self.wmask_dff_insts[port].get_pin(x).center() - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=offset_pin, directions=("V", "V")) @@ -423,12 +424,14 @@ class sram_1bank(sram_base): bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] for x in bank_names: offset_pin = self.bank_inst.get_pin(x).center() - self.add_via_center(layers=("metal1", "via1", "metal2"), + self.add_via_center(layers=self.m1_stack, offset=offset_pin) route_map = list(zip(bank_pins, dff_pins)) - self.create_horizontal_channel_route(route_map,offset) + self.create_horizontal_channel_route(netlist=route_map, + offset=offset, + layer_stack=self.m1_stack) def add_lvs_correspondence_points(self): diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py index 74d5ae1e..0ea079e4 100755 --- a/compiler/tests/03_wire_test.py +++ b/compiler/tests/03_wire_test.py @@ -25,7 +25,7 @@ class wire_test(openram_test): min_space = 2 * (tech.drc["minwidth_poly"] + tech.drc["minwidth_metal1"]) - layer_stack = ("poly", "contact", "metal1") + layer_stack = tech.poly_stack old_position_list = [[0, 0], [0, 3 * min_space], [1 * min_space, 3 * min_space], @@ -42,7 +42,7 @@ class wire_test(openram_test): min_space = 2 * (tech.drc["minwidth_poly"] + tech.drc["minwidth_metal1"]) - layer_stack = ("poly", "contact", "metal1") + layer_stack = tech.poly_stack old_position_list = [[0, 0], [0, 3 * min_space], [1 * min_space, 3 * min_space], @@ -59,7 +59,7 @@ class wire_test(openram_test): min_space = 2 * (tech.drc["minwidth_metal2"] + tech.drc["minwidth_metal1"]) - layer_stack = ("metal1", "via1", "metal2") + layer_stack = tech.m1_stack position_list = [[0, 0], [0, 3 * min_space], [1 * min_space, 3 * min_space], @@ -76,7 +76,7 @@ class wire_test(openram_test): min_space = 2 * (tech.drc["minwidth_metal2"] + tech.drc["minwidth_metal1"]) - layer_stack = ("metal2", "via1", "metal1") + layer_stack = tech.m2_stack[::-1] position_list = [[0, 0], [0, 3 * min_space], [1 * min_space, 3 * min_space], @@ -92,7 +92,7 @@ class wire_test(openram_test): min_space = 2 * (tech.drc["minwidth_metal2"] + tech.drc["minwidth_metal3"]) - layer_stack = ("metal2", "via2", "metal3") + layer_stack = tech.m2_stack position_list = [[0, 0], [0, 3 * min_space], [1 * min_space, 3 * min_space], @@ -109,7 +109,7 @@ class wire_test(openram_test): min_space = 2 * (tech.drc["minwidth_metal2"] + tech.drc["minwidth_metal3"]) - layer_stack = ("metal3", "via2", "metal2") + layer_stack = tech.m2_stack[::-1] position_list = [[0, 0], [0, 3 * min_space], [1 * min_space, 3 * min_space], diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 23a0d9fe..3f42e8b3 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -34,18 +34,18 @@ GDS["zoom"] = 0.5 poly_stack = ("poly", "poly_contact", "metal1") active_stack = ("active", "active_contact", "metal1") -metal1_stack = ("metal1", "via1", "metal2") -metal2_stack = ("metal2", "via2", "metal3") -metal3_stack = ("metal3", "via3", "metal4") +m1_stack = ("metal1", "via1", "metal2") +m2_stack = ("metal2", "via2", "metal3") +m3_stack = ("metal3", "via3", "metal4") # The FEOL stacks get us up to metal1 feol_stacks = [poly_stack, active_stack] # The BEOL stacks are metal1 and up -beol_stacks = [metal1_stack, - metal2_stack, - metal3_stack] +beol_stacks = [m1_stack, + m2_stack, + m3_stack] layer_stacks = feol_stacks + beol_stacks From 3ad7c8a8b530e87b2ae75def4b9a4faa214f8f91 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 13 Dec 2019 14:25:00 -0800 Subject: [PATCH 006/103] Fix freepdk45 tech layer stacks --- technology/freepdk45/tech/tech.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index f8dc7d06..fcdb2c10 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -34,18 +34,18 @@ GDS["zoom"] = 0.05 poly_stack = ("poly", "contact", "metal1") active_stack = ("active", "contact", "metal1") -metal1_stack = ("metal1", "via1", "metal2") -metal2_stack = ("metal2", "via2", "metal3") -metal3_stack = ("metal3", "via3", "metal4") +m1_stack = ("metal1", "via1", "metal2") +m2_stack = ("metal2", "via2", "metal3") +m3_stack = ("metal3", "via3", "metal4") # The FEOL stacks get us up to metal1 feol_stacks = [poly_stack, active_stack] # The BEOL stacks are metal1 and up -beol_stacks = [metal1_stack, - metal2_stack, - metal3_stack] +beol_stacks = [m1_stack, + m2_stack, + m3_stack] layer_stacks = feol_stacks + beol_stacks From f71cfe0d9d41c36c3739c49a27c239a26cac01b5 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 13 Dec 2019 14:56:14 -0800 Subject: [PATCH 007/103] Generalize active and poly stacks --- compiler/bitcells/pbitcell.py | 4 ++-- compiler/pgates/pgate.py | 4 ++-- compiler/pgates/precharge.py | 2 +- compiler/pgates/ptristate_inv.py | 2 +- compiler/pgates/ptx.py | 6 +++--- compiler/pgates/single_level_column_mux.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 0f4b8183..8f9cd602 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -1019,7 +1019,7 @@ class pbitcell(bitcell_base.bitcell_base): # add well contacts # connect pimplants to gnd offset = vector(0, self.gnd_position.y) - self.add_via_center(layers=("active", "contact", "metal1"), + self.add_via_center(layers=self.active_stack, offset=offset, directions=("H", "H"), implant_type="p", @@ -1027,7 +1027,7 @@ class pbitcell(bitcell_base.bitcell_base): # connect nimplants to vdd offset = vector(0, self.vdd_position.y) - self.add_via_center(layers=("active", "contact", "metal1"), + self.add_via_center(layers=self.active_stack, offset=offset, directions=("H", "H"), implant_type="n", diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 5ad98fad..b55ad9c3 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -168,7 +168,7 @@ class pgate(design.design): def add_nwell_contact(self, pmos, pmos_pos): """ Add an nwell contact next to the given pmos device. """ - layer_stack = ("active", "contact", "metal1") + layer_stack = self.active_stack # To the right a spacing away from the pmos right active edge contact_xoffset = pmos_pos.x + pmos.active_width \ @@ -221,7 +221,7 @@ class pgate(design.design): def add_pwell_contact(self, nmos, nmos_pos): """ Add an pwell contact next to the given nmos device. """ - layer_stack = ("active", "contact", "metal1") + layer_stack = self.active_stack pwell_position = vector(0, -0.5 * self.m1_width) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index d27b9f9c..9641a1f3 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -177,7 +177,7 @@ class precharge(design.design): well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ + vector(0, self.upper_pmos1_inst.uy() + contact.well.height / 2 \ + drc("well_extend_active")) - self.add_via_center(layers=("active", "contact", "metal1"), + self.add_via_center(layers=self.active_stack, offset=well_contact_pos, implant_type="n", well_type="n") diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py index 2a972407..4a17b46a 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/pgates/ptristate_inv.py @@ -191,7 +191,7 @@ class ptristate_inv(pgate.pgate): supplies AFTER the wells are created """ - layer_stack = ("active", "contact", "metal1") + layer_stack = self.active_stack drain_pos = self.nmos1_inst.get_pin("S").center() vdd_pos = self.get_pin("vdd").center() diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index d0854711..61610a88 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -124,7 +124,7 @@ class ptx(design.design): # This is not actually instantiated but used for calculations self.active_contact = factory.create(module_type="contact", - layer_stack=("active", "contact", "metal1"), + layer_stack=self.active_stack, dimensions=(1, self.num_contacts)) @@ -373,7 +373,7 @@ class ptx(design.design): [source_positions,drain_positions] = self.get_contact_positions() for pos in source_positions: - contact=self.add_via_center(layers=("active", "contact", "metal1"), + contact=self.add_via_center(layers=self.active_stack, offset=pos, size=(1, self.num_contacts), directions=("H","V"), @@ -387,7 +387,7 @@ class ptx(design.design): for pos in drain_positions: - contact=self.add_via_center(layers=("active", "contact", "metal1"), + contact=self.add_via_center(layers=self.active_stack, offset=pos, size=(1, self.num_contacts), directions=("H","V"), diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index a642eeca..525c1680 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -175,7 +175,7 @@ class single_level_column_mux(pgate.pgate): # Add it to the right, aligned in between the two tx active_pos = vector(self.bitcell.width, self.nmos_upper.by() - 0.5 * self.poly_space) - self.add_via_center(layers=("active", "contact", "metal1"), + self.add_via_center(layers=self.active_stack, offset=active_pos, implant_type="p", well_type="p") From 233ec010fffecd2f1fe45d4f9bee3f0ec4dbe00a Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 16 Dec 2019 14:27:06 +0100 Subject: [PATCH 008/103] modules: Create a class that wraps all the module class names this removes hard coded values from the module instatiations. It also allows users to override certain modules with their custom cells. Signed-off-by: Bastian Koppelmann --- compiler/modules/module_type.py | 70 +++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 compiler/modules/module_type.py diff --git a/compiler/modules/module_type.py b/compiler/modules/module_type.py new file mode 100644 index 00000000..624a62c8 --- /dev/null +++ b/compiler/modules/module_type.py @@ -0,0 +1,70 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +class ModuleType(): + """ + This is a class that maps cell names to python classes implementing them. + """ + def __init__(self): + self.names = {} + self.names['contact'] = 'contact' + self.names['precharge'] = 'precharge' + self.names['pinv'] = 'pinv' + self.names['dff_buf'] = 'dff_buf' + self.names['sense_amp'] = 'sense_amp' + self.names['bitcell'] = 'bitcell' + self.names['port_data'] = 'port_data' + self.names['port_address'] = 'port_address' + self.names['replica_bitcell_array'] = 'replica_bitcell_array' + self.names['bank_select'] = 'bank_select' + self.names['dff'] = 'dff' + self.names['pinvbuf'] = 'pinvbuf' + self.names['hierarchical_predecode2x4'] = 'hierarchical_predecode2x4' + self.names['hierarchical_predecode3x8'] = 'hierarchical_predecode3x8' + self.names['replica_bitcell'] = 'replica_bitcell' + self.names['dummy_bitcell'] = 'dummy_bitcell' + self.names['bitcell'] = 'bitcell' + self.names['pnor2'] = 'pnor2' + self.names['pnand2'] = 'pnand2' + self.names['precharge_array'] = 'precharge_array' + self.names['sense_amp_array'] = 'sense_amp_array' + self.names['column_mux_array'] = 'column_mux_array' + self.names['write_driver_array'] = 'write_driver_array' + self.names['write_mask_and_array'] = 'write_mask_and_array' + self.names['pand2'] = 'pand2' + self.names['write_driver'] = 'write_driver' + self.names['dff_buf_array'] = 'dff_buf_array' + self.names['pdriver'] = 'pdriver' + self.names['pand3'] = 'pand3' + self.names['delay_chain'] = 'delay_chain' + self.names['decoder'] = 'decoder' + self.names['wordline_driver'] = 'wordline_driver' + self.names['tri_gate'] = 'tri_gate' + self.names['tri_gate_array'] = 'tri_gate_array' + self.names['bitcell_array'] = 'bitcell_array' + self.names['replica_column'] = 'replica_column' + self.names['dummy_array'] = 'dummy_array' + self.names['single_level_column_mux_array'] = 'single_level_column_mux_array' + self.names['single_level_column_mux'] = 'single_level_column_mux' + self.names['sram'] = 'sram' + self.names['ptx'] = 'ptx' + self.names['hierarchical_decoder'] = 'hierarchical_decoder' + self.names['pbuf'] = 'pbuf' + self.names['control_logic'] = 'control_logic' + self.names['bank'] = 'bank' + self.names['pbitcell'] = 'pbitcell' + self.names['pnand3'] = 'pnand3' + self.names['pwrite_driver'] = 'pwrite_driver' + self.names['ptristate_inv'] = 'ptristate_inv' + self.names['ptristate_buf'] = 'ptristate_buf' + + def __setitem__(self, b, c): + self.names[b] = c + + def __getitem__(self, b): + return self.names[b] From 1380cbc50c733ccffeb816111061974fbeb94c0a Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 16 Dec 2019 14:40:52 +0100 Subject: [PATCH 009/103] technology: Add tech_module to all technologies this allows each technology to override each cell class. Signed-off-by: Bastian Koppelmann --- technology/freepdk45/tech/tech.py | 7 +++++++ technology/scn3me_subm/tech/tech.py | 7 +++++++ technology/scn4m_subm/tech/tech.py | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index fcdb2c10..839a38af 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -7,10 +7,17 @@ # import os from design_rules import * +from module_type import * """ File containing the process technology parameters for FreePDK 45nm. """ +# This uses the default classes to instantiate module from +# '$OPENRAM_HOME/compiler/modules'. +# Using tech_modules['cellname'] you can override each class by providing a custom +# implementation in '$OPENRAM_TECHDIR/modules/' +# For example: tech_modules['contact'] = 'contact_freepdk45' +tech_modules = ModuleType() #GDS file info GDS = {} diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index 6fc68686..cb476df5 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -1,9 +1,16 @@ import os from design_rules import * +from module_type import * """ File containing the process technology parameters for SCMOS 3me, subm, 180nm. """ +# This uses the default classes to instantiate module from +# '$OPENRAM_HOME/compiler/modules'. +# Using tech_modules['cellname'] you can override each class by providing a custom +# implementation in '$OPENRAM_TECHDIR/modules/' +# For example: tech_modules['contact'] = 'contact_scn3me' +tech_modules = ModuleType() #GDS file info GDS={} diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 3f42e8b3..d17a0927 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -7,10 +7,17 @@ # import os from design_rules import * +from module_type import * """ File containing the process technology parameters for SCMOS 4m, 0.35um """ +# This uses the default classes to instantiate module from +# '$OPENRAM_HOME/compiler/modules'. +# Using tech_modules['cellname'] you can override each class by providing a custom +# implementation in '$OPENRAM_TECHDIR/modules/' +# For example: tech_modules['contact'] = 'contact_scn4m' +tech_modules = ModuleType() #GDS file info GDS={} From 74cd4f989d82ba9a92834cae821e3fdbb96116e3 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 16 Dec 2019 16:56:06 +0100 Subject: [PATCH 010/103] Remove hardcoded module class names Signed-off-by: Bastian Koppelmann --- compiler/sram_factory.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index bd602980..e971efc0 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -6,6 +6,7 @@ # All rights reserved. # from globals import OPTS +from tech import tech_modules class sram_factory: @@ -37,6 +38,8 @@ class sram_factory: A generic function to create a module with a given module_type. The args are passed directly to the module constructor. """ + + module_type = tech_modules[module_type] # if name!="": # # This is a special case where the name and type don't match # # Can't be overridden in the config file From 306c0b92c3a098f391d3b2b064bb428c558a5ac8 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 16 Dec 2019 16:10:09 +0100 Subject: [PATCH 011/103] globals: Add tech module path to Pythonpath if it exists Signed-off-by: Bastian Koppelmann --- compiler/globals.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/globals.py b/compiler/globals.py index e9b77d66..8bca4b3f 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -504,6 +504,11 @@ def import_tech(): except ImportError: debug.error("Could not load tech module.", -1) + # Add custom modules of the technology to the path, if they exist + custom_mod_path = os.path.join(tech_path, "modules/") + if os.path.exists(custom_mod_path): + sys.path.append(custom_mod_path) + def print_time(name, now_time, last_time=None, indentation=2): """ Print a statement about the time delta. """ From be4893839bd8bd3ca987595253c85b652c7a2ed0 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 16 Dec 2019 10:05:52 -0800 Subject: [PATCH 012/103] Remove old drc/lvs override name --- compiler/example_configs/example_config_1w_1r_scn4m_subm.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/example_configs/example_config_1w_1r_scn4m_subm.py b/compiler/example_configs/example_config_1w_1r_scn4m_subm.py index 330d8d46..55ac4016 100644 --- a/compiler/example_configs/example_config_1w_1r_scn4m_subm.py +++ b/compiler/example_configs/example_config_1w_1r_scn4m_subm.py @@ -19,6 +19,3 @@ output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size, num_words, tech_name) -drc_name = "magic" -lvs_name = "netgen" -pex_name = "magic" From 2a9129ef45bd916385289ef8f023cb93d70f6bd8 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 16 Dec 2019 10:11:26 -0800 Subject: [PATCH 013/103] Small fix to tech and config over-rides --- compiler/sram_factory.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index e971efc0..d3dadf7d 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -6,8 +6,6 @@ # All rights reserved. # from globals import OPTS -from tech import tech_modules - class sram_factory: """ @@ -38,8 +36,8 @@ class sram_factory: A generic function to create a module with a given module_type. The args are passed directly to the module constructor. """ - - module_type = tech_modules[module_type] + from tech import tech_modules + real_module_type = tech_modules[module_type] # if name!="": # # This is a special case where the name and type don't match # # Can't be overridden in the config file @@ -47,21 +45,21 @@ class sram_factory: if hasattr(OPTS, module_type): # Retrieve the name from OPTS if it exists, # otherwise just use the name - module_type = getattr(OPTS, module_type) + real_module_type = getattr(OPTS, module_type) # Either retrieve the already loaded module or load it try: - mod = self.modules[module_type] + mod = self.modules[real_module_type] except KeyError: import importlib - c = importlib.reload(__import__(module_type)) - mod = getattr(c, module_type) - self.modules[module_type] = mod - self.module_indices[module_type] = 0 - self.objects[module_type] = [] + c = importlib.reload(__import__(real_module_type)) + mod = getattr(c, real_module_type) + self.modules[real_module_type] = mod + self.module_indices[real_module_type] = 0 + self.objects[real_module_type] = [] # Either retreive a previous object or create a new one - for obj in self.objects[module_type]: + for obj in self.objects[real_module_type]: (obj_kwargs, obj_item) = obj # Must have the same dictionary exactly (conservative) if obj_kwargs == kwargs: @@ -72,19 +70,19 @@ class sram_factory: # spice and gds files can be found. if len(kwargs) > 0: # Create a unique name and increment the index - module_name = "{0}_{1}".format(module_type, - self.module_indices[module_type]) - self.module_indices[module_type] += 1 + module_name = "{0}_{1}".format(real_module_type, + self.module_indices[real_module_type]) + self.module_indices[real_module_type] += 1 else: - module_name = module_type + module_name = real_module_type - # type_str = "type={}".format(module_type) + # type_str = "type={}".format(real_module_type) # name_str = "name={}".format(module_name) # kwargs_str = "kwargs={}".format(str(kwargs)) # import debug # debug.info(0, "New module:" + type_str + name_str + kwargs_str) obj = mod(name=module_name, **kwargs) - self.objects[module_type].append((kwargs, obj)) + self.objects[real_module_type].append((kwargs, obj)) return obj def get_mods(self, module_type): From 5176a70f846527bc97008716567608f4a12c6651 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 16 Dec 2019 10:21:24 -0800 Subject: [PATCH 014/103] Add comments to module importing routines --- compiler/sram_factory.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index d3dadf7d..4b9b13d9 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -36,12 +36,14 @@ class sram_factory: A generic function to create a module with a given module_type. The args are passed directly to the module constructor. """ - from tech import tech_modules - real_module_type = tech_modules[module_type] - # if name!="": - # # This is a special case where the name and type don't match - # # Can't be overridden in the config file - # module_name = name + try: + from tech import tech_modules + real_module_type = tech_modules[module_type] + except ImportError: + # If they didn't define these, then don't use the option types. + # Primarily for backward compatibility and simplicity of tech files. + pass + if hasattr(OPTS, module_type): # Retrieve the name from OPTS if it exists, # otherwise just use the name @@ -49,8 +51,10 @@ class sram_factory: # Either retrieve the already loaded module or load it try: + # Load a cached version from previous usage mod = self.modules[real_module_type] except KeyError: + # Dynamically load the module import importlib c = importlib.reload(__import__(real_module_type)) mod = getattr(c, real_module_type) From f30d0b919784c39d13788f572fc01a405c03c405 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 16 Dec 2019 12:04:33 -0800 Subject: [PATCH 015/103] Fix KeyError for bitell types. --- compiler/modules/module_type.py | 4 ++++ compiler/sram_factory.py | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/compiler/modules/module_type.py b/compiler/modules/module_type.py index 624a62c8..90e1fdbe 100644 --- a/compiler/modules/module_type.py +++ b/compiler/modules/module_type.py @@ -67,4 +67,8 @@ class ModuleType(): self.names[b] = c def __getitem__(self, b): + if b not in self.names.keys(): + raise KeyError + return self.names[b] + diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 4b9b13d9..f5e19d87 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -36,6 +36,8 @@ class sram_factory: A generic function to create a module with a given module_type. The args are passed directly to the module constructor. """ + real_module_type = module_type + try: from tech import tech_modules real_module_type = tech_modules[module_type] @@ -43,6 +45,9 @@ class sram_factory: # If they didn't define these, then don't use the option types. # Primarily for backward compatibility and simplicity of tech files. pass + except KeyError: + # If it wasn't a tech module type, we can ignore that too. + pass if hasattr(OPTS, module_type): # Retrieve the name from OPTS if it exists, From acbbbe94031ce62f6fd222d155630a6115961935 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 16 Dec 2019 12:07:40 -0800 Subject: [PATCH 016/103] Make exception more readable. --- compiler/sram_factory.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index f5e19d87..6bb3578c 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -36,18 +36,16 @@ class sram_factory: A generic function to create a module with a given module_type. The args are passed directly to the module constructor. """ - real_module_type = module_type - try: from tech import tech_modules real_module_type = tech_modules[module_type] except ImportError: # If they didn't define these, then don't use the option types. # Primarily for backward compatibility and simplicity of tech files. - pass + real_module_type = module_type except KeyError: # If it wasn't a tech module type, we can ignore that too. - pass + real_module_type = module_type if hasattr(OPTS, module_type): # Retrieve the name from OPTS if it exists, From 3eb0dad06a0f4cb6842c5156560cc09a278b0e27 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 16 Dec 2019 15:33:30 -0800 Subject: [PATCH 017/103] Remove cells from DRC/LVS in the blackbox tech list. --- compiler/tests/01_library_drc_test.py | 12 +++++++----- compiler/tests/02_library_lvs_test.py | 9 +++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/compiler/tests/01_library_drc_test.py b/compiler/tests/01_library_drc_test.py index ad8671d2..42fa1335 100755 --- a/compiler/tests/01_library_drc_test.py +++ b/compiler/tests/01_library_drc_test.py @@ -42,11 +42,13 @@ def setup_files(): files = os.listdir(gds_dir) nametest = re.compile("\.gds$", re.IGNORECASE) gds_files = list(filter(nametest.search, files)) - import tech - if tech.blackbox_bitcell: - # Ignore DRC of all bitcells - nametest = re.compile("cell", re.IGNORECASE) - gds_files = list(filter(lambda v: not nametest.search(v), gds_files)) + + try: + from tech import blackbox_cells + gds_files = list(set(gds_files) - set(blackbox_cells)) + except ImportError: + pass + return (gds_dir, gds_files) diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index 5f53753b..35ac5d76 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -62,8 +62,13 @@ def setup_files(): tempnames[i] = re.sub('\.gds$', '', tempnames[i]) tempnames[i] = re.sub('\.sp$', '', tempnames[i]) - # remove duplicate base names - nameset = set(tempnames) + try: + from tech import blackbox_cells + nameset = list(set(tempnames) - set(blackbox_cells)) + except ImportError: + # remove duplicate base names + nameset = set(tempnames) + allnames = list(nameset) return (gds_dir, sp_dir, allnames) From 6058af994cd446836fb5148b3a465498738bae19 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 16 Dec 2019 15:39:32 -0800 Subject: [PATCH 018/103] Fix ignore gds files --- compiler/tests/01_library_drc_test.py | 28 +++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/compiler/tests/01_library_drc_test.py b/compiler/tests/01_library_drc_test.py index 42fa1335..8b254bbc 100755 --- a/compiler/tests/01_library_drc_test.py +++ b/compiler/tests/01_library_drc_test.py @@ -21,16 +21,15 @@ class library_drc_test(openram_test): globals.init_openram(config_file) import verify - (gds_dir, gds_files) = setup_files() + (gds_dir, allnames) = setup_files() drc_errors = 0 - debug.info(1, "\nPerforming DRC on: " + ", ".join(gds_files)) - for f in gds_files: - name = re.sub('\.gds$', '', f) - gds_name = "{0}/{1}".format(gds_dir, f) + debug.info(1, "\nPerforming DRC on: " + ", ".join(allnames)) + for f in allnames: + gds_name = "{0}/{1}.gds".format(gds_dir, f) if not os.path.isfile(gds_name): drc_errors += 1 debug.error("Missing GDS file: {}".format(gds_name)) - drc_errors += verify.run_drc(name, gds_name) + drc_errors += verify.run_drc(f, gds_name) # fails if there are any DRC errors on any cells self.assertEqual(drc_errors, 0) @@ -42,14 +41,23 @@ def setup_files(): files = os.listdir(gds_dir) nametest = re.compile("\.gds$", re.IGNORECASE) gds_files = list(filter(nametest.search, files)) + + tempnames = gds_files + # remove the .gds and .sp suffixes + for i in range(len(tempnames)): + gds_files[i] = re.sub('\.gds$', '', tempnames[i]) + try: from tech import blackbox_cells - gds_files = list(set(gds_files) - set(blackbox_cells)) + nameset = list(set(tempnames) - set(blackbox_cells)) except ImportError: - pass - - return (gds_dir, gds_files) + # remove duplicate base names + nameset = set(tempnames) + + allnames = list(nameset) + + return (gds_dir, allnames) # run the test from the command line From d3a2c46cb93200065c3b59ebf3132e78a2c19627 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 16 Dec 2019 17:15:29 -0800 Subject: [PATCH 019/103] Remove some contact tests --- compiler/tests/03_contact_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index 60d8cd6b..18604213 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -23,7 +23,9 @@ class contact_test(openram_test): from tech import poly_stack, beol_stacks - for layer_stack in [poly_stack] + beol_stacks: + # Don't do active because of nwell contact rules + # Don't do metal3 because of min area rules + for layer_stack in [poly_stack] + [beol_stacks[0]]: stack_name = ":".join(map(str, layer_stack)) # Check single 1 x 1 contact" From a79d03fef40c2cd04c8fb776929734d8e0cc8fe1 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 16 Dec 2019 17:18:49 -0800 Subject: [PATCH 020/103] Remove poly contact --- compiler/base/design.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/base/design.py b/compiler/base/design.py index 16237cfd..7ee1624d 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -67,7 +67,10 @@ class design(hierarchy_design): self.poly_to_active = drc("poly_to_active") self.poly_extend_active = drc("poly_extend_active") - self.poly_to_contact = drc("poly_to_contact") + if "contact" in layer: + self.poly_to_contact = drc("poly_to_contact") + else: + self.poly_to_contact = drc("poly_to_active_contact") self.contact_to_gate = drc("contact_to_gate") self.well_enclose_active = drc("well_enclosure_active") self.implant_enclose_active = drc("implant_enclosure_active") From ed28b4983bdd6232e91472fd82022fd7cc2f1966 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 17 Dec 2019 11:03:36 -0800 Subject: [PATCH 021/103] Clean up and generalize layer rules. Convert metalN to mN. Generalize helper constants in modules for space, width, enclose, etc. Use layer stacks whever possible. Try to remove drc() calls in liu of helper constants. --- compiler/base/contact.py | 18 ++-- compiler/base/design.py | 88 ++++++++++++------- compiler/base/hierarchy_layout.py | 54 ++++++------ compiler/bitcells/pbitcell.py | 54 ++++++------ compiler/drc/design_rules.py | 8 +- compiler/modules/bank.py | 26 +++--- compiler/modules/bank_select.py | 26 +++--- compiler/modules/bitcell_array.py | 4 +- compiler/modules/control_logic.py | 54 ++++++------ compiler/modules/delay_chain.py | 28 +++--- compiler/modules/dff_array.py | 12 +-- compiler/modules/dff_buf.py | 26 +++--- compiler/modules/dff_buf_array.py | 16 ++-- compiler/modules/dff_inv.py | 16 ++-- compiler/modules/dff_inv_array.py | 16 ++-- compiler/modules/dummy_array.py | 4 +- compiler/modules/hierarchical_decoder.py | 16 ++-- compiler/modules/hierarchical_predecode.py | 18 ++-- compiler/modules/multibank.py | 58 ++++++------ compiler/modules/port_address.py | 4 +- compiler/modules/port_data.py | 8 +- compiler/modules/precharge_array.py | 12 +-- compiler/modules/replica_bitcell_array.py | 2 +- compiler/modules/replica_column.py | 4 +- compiler/modules/sense_amp_array.py | 14 +-- .../modules/single_level_column_mux_array.py | 20 ++--- compiler/modules/tri_gate_array.py | 14 +-- compiler/modules/wordline_driver.py | 12 +-- compiler/modules/write_driver_array.py | 10 +-- compiler/modules/write_mask_and_array.py | 6 +- compiler/pgates/pand2.py | 6 +- compiler/pgates/pand3.py | 6 +- compiler/pgates/pbuf.py | 6 +- compiler/pgates/pdriver.py | 6 +- compiler/pgates/pgate.py | 12 +-- compiler/pgates/pinv.py | 16 ++-- compiler/pgates/pinvbuf.py | 16 ++-- compiler/pgates/pnand2.py | 14 +-- compiler/pgates/pnand3.py | 16 ++-- compiler/pgates/pnor2.py | 17 ++-- compiler/pgates/precharge.py | 16 ++-- compiler/pgates/ptristate_inv.py | 8 +- compiler/pgates/ptx.py | 26 +++--- compiler/pgates/pwrite_driver.py | 58 ++++++------ compiler/pgates/single_level_column_mux.py | 18 ++-- compiler/sram/sram_1bank.py | 22 ++--- compiler/sram/sram_2bank.py | 60 ++++++------- compiler/sram/sram_base.py | 34 +++---- compiler/tests/03_path_test.py | 16 ++-- compiler/tests/03_wire_test.py | 20 ++--- technology/freepdk45/tech/tech.py | 78 ++++++++-------- technology/scn4m_subm/tech/tech.py | 63 +++++++------ 52 files changed, 604 insertions(+), 578 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 5f169e07..c5b1ef9e 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -104,11 +104,11 @@ class contact(hierarchy_design.hierarchy_design): # The enclosure rule applies to symmetric enclosure component. first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) - first_layer_enclosure = drc("{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)) + first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name)) first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)) second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name)) - second_layer_enclosure = drc("{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)) + second_layer_enclosure = drc("{0}_enclose_{1}".format(self.second_layer_name, self.via_layer_name)) second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)) # In some technologies, the minimum width may be larger @@ -204,16 +204,16 @@ class contact(hierarchy_design.hierarchy_design): height=self.second_layer_height) def create_implant_well_enclosures(self): - implant_position = self.first_layer_position - [drc("implant_enclosure_active")] * 2 - implant_width = self.first_layer_width + 2 * drc("implant_enclosure_active") - implant_height = self.first_layer_height + 2 * drc("implant_enclosure_active") + implant_position = self.first_layer_position - [drc("implant_enclose_active")] * 2 + implant_width = self.first_layer_width + 2 * drc("implant_enclose_active") + implant_height = self.first_layer_height + 2 * drc("implant_enclose_active") self.add_rect(layer="{}implant".format(self.implant_type), offset=implant_position, width=implant_width, height=implant_height) - well_position = self.first_layer_position - [drc("well_enclosure_active")] * 2 - well_width = self.first_layer_width + 2 * drc("well_enclosure_active") - well_height = self.first_layer_height + 2 * drc("well_enclosure_active") + well_position = self.first_layer_position - [drc("well_enclose_active")] * 2 + well_width = self.first_layer_width + 2 * drc("well_enclose_active") + well_height = self.first_layer_height + 2 * drc("well_enclose_active") self.add_rect(layer="{}well".format(self.well_type), offset=well_position, width=well_width, @@ -250,7 +250,7 @@ m1m2 = factory.create(module_type="contact", m2m3 = factory.create(module_type="contact", layer_stack=m2_stack, directions=("V", "H")) -if "metal4" in layer.keys(): +if "m4" in layer.keys(): m3m4 = factory.create(module_type="contact", layer_stack=m3_stack, directions=("H", "V")) diff --git a/compiler/base/design.py b/compiler/base/design.py index 7ee1624d..3b6ae914 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -8,7 +8,7 @@ from hierarchy_design import hierarchy_design import contact from globals import OPTS - +import re class design(hierarchy_design): """ @@ -32,7 +32,7 @@ class design(hierarchy_design): self.m1_pitch = max(contact.m1m2.width, contact.m1m2.height) + max(self.m1_space, self.m2_space) self.m2_pitch = max(contact.m2m3.width, contact.m2m3.height) + max(self.m2_space, self.m3_space) - if "metal4" in tech.layer: + if "m4" in tech.layer: self.m3_pitch = max(contact.m3m4.width, contact.m3m4.height) + max(self.m3_space, self.m4_space) else: self.m3_pitch = self.m2_pitch @@ -46,36 +46,62 @@ class design(hierarchy_design): def setup_drc_constants(self): """ These are some DRC constants used in many places in the compiler.""" from tech import drc, layer - self.well_width = drc("minwidth_well") - self.poly_width = drc("minwidth_poly") - self.poly_space = drc("poly_to_poly") - self.m1_width = drc("minwidth_metal1") - self.m1_space = drc("metal1_to_metal1") - self.m2_width = drc("minwidth_metal2") - self.m2_space = drc("metal2_to_metal2") - self.m3_width = drc("minwidth_metal3") - self.m3_space = drc("metal3_to_metal3") - if "metal4" in layer: - self.m4_width = drc("minwidth_metal4") - self.m4_space = drc("metal4_to_metal4") - self.active_width = drc("minwidth_active") - self.active_space = drc("active_to_body_active") - if "contact" in layer: - self.contact_width = drc("minwidth_contact") - else: - self.contact_width = drc("minwidth_active_contact") - self.poly_to_active = drc("poly_to_active") - self.poly_extend_active = drc("poly_extend_active") - if "contact" in layer: - self.poly_to_contact = drc("poly_to_contact") - else: - self.poly_to_contact = drc("poly_to_active_contact") - self.contact_to_gate = drc("contact_to_gate") - self.well_enclose_active = drc("well_enclosure_active") - self.implant_enclose_active = drc("implant_enclosure_active") - self.implant_space = drc("implant_to_implant") - + # Make some local rules for convenience + for rule in drc.keys(): + # Single layer width rules + match = re.search(r"minwidth_(.*)", rule) + if match: + if match.group(1)=="active_contact": + setattr(self, "contact_width", drc(match.group(0))) + else: + setattr(self, match.group(1)+"_width", drc(match.group(0))) + + # Single layer area rules + match = re.search(r"minarea_(.*)", rule) + if match: + setattr(self, match.group(0), drc(match.group(0))) + + # Single layer spacing rules + match = re.search(r"(.*)_to_(.*)", rule) + if match and match.group(1)==match.group(2): + setattr(self, match.group(1)+"_space", drc(match.group(0))) + elif match and match.group(1)!=match.group(2): + if match.group(2)=="poly_active": + setattr(self, match.group(1)+"_to_contact", drc(match.group(0))) + else: + setattr(self, match.group(0), drc(match.group(0))) + + match = re.search(r"(.*)_enclose_(.*)", rule) + if match: + setattr(self, match.group(0), drc(match.group(0))) + + match = re.search(r"(.*)_extend_(.*)", rule) + if match: + setattr(self, match.group(0), drc(match.group(0))) + + # These are for debugging previous manual rules + # print("poly_width", self.poly_width) + # print("poly_space", self.poly_space) + # print("m1_width", self.m1_width) + # print("m1_space", self.m1_space) + # print("m2_width", self.m2_width) + # print("m2_space", self.m2_space) + # print("m3_width", self.m3_width) + # print("m3_space", self.m3_space) + # print("m4_width", self.m4_width) + # print("m4_space", self.m4_space) + # print("active_width", self.active_width) + # print("active_space", self.active_space) + # print("contact_width", self.contact_width) + # print("poly_to_active", self.poly_to_active) + # print("poly_extend_active", self.poly_extend_active) + # print("poly_to_contact", self.poly_to_contact) + # print("contact_to_gate", self.contact_to_gate) + # print("well_enclose_active", self.well_enclose_active) + # print("implant_enclose_active", self.implant_enclose_active) + # print("implant_space", self.implant_space) + def setup_multiport_constants(self): """ These are contants and lists that aid multiport design. diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index ad1e3edb..7f23e942 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -61,7 +61,7 @@ class layout(): y_dir = 1 else: # we lose a rail after every 2 gates - base_offset=vector(x_offset, (inv_num+1) * height - (inv_num%2)*drc["minwidth_metal1"]) + base_offset=vector(x_offset, (inv_num+1) * height - (inv_num%2)*drc["minwidth_m1"]) y_dir = -1 return (base_offset,y_dir) @@ -388,9 +388,9 @@ class layout(): def get_preferred_direction(self, layer): """ Return the preferred routing directions """ - if layer in ["metal1", "metal3", "metal5"]: + if layer in ["m1", "m3", "m5"]: return "H" - elif layer in ["active", "poly", "metal2", "metal4"]: + elif layer in ["active", "poly", "m2", "m4"]: return "V" else: return "N" @@ -682,12 +682,12 @@ class layout(): return line_positions def connect_horizontal_bus(self, mapping, inst, bus_offsets, - layer_stack=("metal1","via1","metal2")): + layer_stack=("m1", "via1", "m2")): """ Horizontal version of connect_bus. """ self.connect_bus(mapping, inst, bus_offsets, layer_stack, True) def connect_vertical_bus(self, mapping, inst, bus_offsets, - layer_stack=("metal1","via1","metal2")): + layer_stack=("m1", "via1", "m2")): """ Vertical version of connect_bus. """ self.connect_bus(mapping, inst, bus_offsets, layer_stack, False) @@ -734,15 +734,15 @@ class layout(): rotate=90) def get_layer_pitch(self, layer): """ Return the track pitch on a given layer """ - if layer=="metal1": + if layer=="m1": return (self.m1_pitch,self.m1_pitch-self.m1_space,self.m1_space) - elif layer=="metal2": + elif layer=="m2": return (self.m2_pitch,self.m2_pitch-self.m2_space,self.m2_space) - elif layer=="metal3": + elif layer=="m3": return (self.m3_pitch,self.m3_pitch-self.m3_space,self.m3_space) - elif layer=="metal4": + elif layer=="m4": from tech import layer as tech_layer - if "metal4" in tech_layer: + if "m4" in tech_layer: return (self.m3_pitch,self.m3_pitch-self.m4_space,self.m4_space) else: return (self.m3_pitch,self.m3_pitch-self.m3_space,self.m3_space) @@ -1006,16 +1006,16 @@ class layout(): """ pins=inst.get_pins(name) for pin in pins: - if pin.layer=="metal3": + if pin.layer=="m3": self.add_layout_pin(name, pin.layer, pin.ll(), pin.width(), pin.height()) - elif pin.layer=="metal1": + elif pin.layer=="m1": self.add_power_pin(name, pin.center()) else: debug.warning("{0} pins of {1} should be on metal3 or metal1 for supply router.".format(name,inst.name)) - def add_power_pin(self, name, loc, vertical=False, start_layer="metal1"): + def add_power_pin(self, name, loc, vertical=False, start_layer="m1"): """ Add a single power pin from M3 down to M1 at the given center location. The starting layer is specified to determine which vias are needed. @@ -1025,24 +1025,24 @@ class layout(): else: direction=("H","H") - if start_layer=="metal1": + if start_layer=="m1": self.add_via_center(layers=self.m1_stack, offset=loc, directions=direction) - if start_layer=="metal1" or start_layer=="metal2": + if start_layer=="m1" or start_layer=="m2": via=self.add_via_center(layers=self.m2_stack, offset=loc, directions=direction) - if start_layer=="metal3": + if start_layer=="m3": self.add_layout_pin_rect_center(text=name, - layer="metal3", + layer="m3", offset=loc) else: self.add_layout_pin_rect_center(text=name, - layer="metal3", + layer="m3", offset=loc, width=via.width, height=via.height) @@ -1064,7 +1064,7 @@ class layout(): # LEFT vertical rails offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch) left_gnd_pin=self.add_layout_pin(text="gnd", - layer="metal2", + layer="m2", offset=offset, width=self.supply_rail_width, height=height) @@ -1072,7 +1072,7 @@ class layout(): offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch) left_vdd_pin=self.add_layout_pin(text="vdd", - layer="metal2", + layer="m2", offset=offset, width=self.supply_rail_width, height=height) @@ -1080,14 +1080,14 @@ class layout(): # RIGHT vertical rails offset = vector(ur.x,ll.y) + vector(0,-2*self.supply_rail_pitch) right_gnd_pin = self.add_layout_pin(text="gnd", - layer="metal2", + layer="m2", offset=offset, width=self.supply_rail_width, height=height) offset = vector(ur.x,ll.y) + vector(self.supply_rail_pitch,-1*self.supply_rail_pitch) right_vdd_pin=self.add_layout_pin(text="vdd", - layer="metal2", + layer="m2", offset=offset, width=self.supply_rail_width, height=height) @@ -1095,14 +1095,14 @@ class layout(): # BOTTOM horizontal rails offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch) bottom_gnd_pin=self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=offset, width=width, height=self.supply_rail_width) offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch) bottom_vdd_pin=self.add_layout_pin(text="vdd", - layer="metal1", + layer="m1", offset=offset, width=width, height=self.supply_rail_width) @@ -1110,14 +1110,14 @@ class layout(): # TOP horizontal rails offset = vector(ll.x, ur.y) + vector(-2*self.supply_rail_pitch,0) top_gnd_pin=self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=offset, width=width, height=self.supply_rail_width) offset = vector(ll.x, ur.y) + vector(-1*self.supply_rail_pitch, self.supply_rail_pitch) top_vdd_pin=self.add_layout_pin(text="vdd", - layer="metal1", + layer="m1", offset=offset, width=width, height=self.supply_rail_width) @@ -1139,7 +1139,7 @@ class layout(): from sram_factory import factory while True: c=factory.create(module_type="contact", - layer_stack=("metal1","via1","metal2"), + layer_stack=self.m1_stack, dimensions=(self.supply_vias, self.supply_vias)) if c.second_layer_width < self.supply_rail_width and c.second_layer_height < self.supply_rail_width: self.supply_vias += 1 diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 8f9cd602..bdb9161b 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -364,11 +364,11 @@ class pbitcell(bitcell_base.bitcell_base): self.inverter_pmos_right.get_pin("G").bc()]) # connect output (drain/source) of inverters - self.add_path("metal1", + self.add_path("m1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], width=contact.active.second_layer_width) - self.add_path("metal1", + self.add_path("m1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.active.second_layer_width) @@ -404,7 +404,7 @@ class pbitcell(bitcell_base.bitcell_base): # Add rails for vdd and gnd gnd_ypos = self.m1_offset - self.total_ports * self.m1_pitch self.gnd_position = vector(0, gnd_ypos) - self.add_rect_center(layer="metal1", + self.add_rect_center(layer="m1", offset=self.gnd_position, width=self.width) self.add_power_pin("gnd", vector(0, gnd_ypos)) @@ -416,7 +416,7 @@ class pbitcell(bitcell_base.bitcell_base): + self.inverter_pmos.active_height \ + self.vdd_offset self.vdd_position = vector(0, vdd_ypos) - self.add_rect_center(layer="metal1", + self.add_rect_center(layer="m1", offset=self.vdd_position, width=self.width) self.add_power_pin("vdd", vector(0, vdd_ypos)) @@ -489,7 +489,7 @@ class pbitcell(bitcell_base.bitcell_base): rwwl_ypos = self.m1_offset - k * self.m1_pitch self.rwwl_positions[k] = vector(0, rwwl_ypos) self.add_layout_pin_rect_center(text=self.rw_wl_names[k], - layer="metal1", + layer="m1", offset=self.rwwl_positions[k], width=self.width) @@ -499,7 +499,7 @@ class pbitcell(bitcell_base.bitcell_base): + 0.5 * self.m2_width self.rwbl_positions[k] = vector(rwbl_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.rw_bl_names[k], - layer="metal2", + layer="m2", offset=self.rwbl_positions[k], height=self.height) @@ -509,7 +509,7 @@ class pbitcell(bitcell_base.bitcell_base): - 0.5 * self.m2_width self.rwbr_positions[k] = vector(rwbr_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.rw_br_names[k], - layer="metal2", + layer="m2", offset=self.rwbr_positions[k], height=self.height) @@ -586,7 +586,7 @@ class pbitcell(bitcell_base.bitcell_base): - k * self.m1_pitch self.wwl_positions[k] = vector(0, wwl_ypos) self.add_layout_pin_rect_center(text=self.w_wl_names[k], - layer="metal1", + layer="m1", offset=self.wwl_positions[k], width=self.width) @@ -596,7 +596,7 @@ class pbitcell(bitcell_base.bitcell_base): + 0.5 * self.m2_width self.wbl_positions[k] = vector(wbl_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.w_bl_names[k], - layer="metal2", + layer="m2", offset=self.wbl_positions[k], height=self.height) @@ -606,7 +606,7 @@ class pbitcell(bitcell_base.bitcell_base): - 0.5 * self.m2_width self.wbr_positions[k] = vector(wbr_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.w_br_names[k], - layer="metal2", + layer="m2", offset=self.wbr_positions[k], height=self.height) @@ -713,7 +713,7 @@ class pbitcell(bitcell_base.bitcell_base): - k * self.m1_pitch self.rwl_positions[k] = vector(0, rwl_ypos) self.add_layout_pin_rect_center(text=self.r_wl_names[k], - layer="metal1", + layer="m1", offset=self.rwl_positions[k], width=self.width) @@ -723,7 +723,7 @@ class pbitcell(bitcell_base.bitcell_base): + 0.5 * self.m2_width self.rbl_positions[k] = vector(rbl_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.r_bl_names[k], - layer="metal2", + layer="m2", offset=self.rbl_positions[k], height=self.height) @@ -733,7 +733,7 @@ class pbitcell(bitcell_base.bitcell_base): - 0.5 * self.m2_width self.rbr_positions[k] = vector(rbr_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.r_br_names[k], - layer="metal2", + layer="m2", offset=self.rbr_positions[k], height=self.height) @@ -775,7 +775,7 @@ class pbitcell(bitcell_base.bitcell_base): offset=port_contact_offset) self.add_path("poly", [gate_offset, port_contact_offset]) - self.add_path("metal1", + self.add_path("m1", [port_contact_offset, wl_contact_offset]) else: @@ -789,7 +789,7 @@ class pbitcell(bitcell_base.bitcell_base): directions=("H", "H")) self.add_path("poly", [gate_offset, port_contact_offset]) - self.add_path("metal2", + self.add_path("m2", [port_contact_offset, wl_contact_offset]) def route_bitlines(self): @@ -827,7 +827,7 @@ class pbitcell(bitcell_base.bitcell_base): self.add_via_center(layers=self.m1_stack, offset=port_contact_offest) - self.add_path("metal2", + self.add_path("m2", [port_contact_offest, bl_offset], width=contact.m1m2.height) for k in range(self.total_ports): @@ -839,7 +839,7 @@ class pbitcell(bitcell_base.bitcell_base): self.add_via_center(layers=self.m1_stack, offset=port_contact_offest) - self.add_path("metal2", + self.add_path("m2", [port_contact_offest, br_offset], width=contact.m1m2.height) def route_supply(self): @@ -866,17 +866,17 @@ class pbitcell(bitcell_base.bitcell_base): offset=supply_offset, directions=("H", "H")) - self.add_path("metal2", [position, supply_offset]) + self.add_path("m2", [position, supply_offset]) # route inverter pmos to vdd vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x, self.vdd_position.y) - self.add_path("metal1", + self.add_path("m1", [self.inverter_pmos_left.get_pin("S").uc(), vdd_pos_left]) vdd_pos_right = vector(self.inverter_nmos_right.get_pin("D").uc().x, self.vdd_position.y) - self.add_path("metal1", + self.add_path("m1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) def route_readwrite_access(self): @@ -889,14 +889,14 @@ class pbitcell(bitcell_base.bitcell_base): self.cross_couple_lower_ypos) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) - self.add_path("metal1", + self.add_path("m1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid, Q_pos]) mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) - self.add_path("metal1", + self.add_path("m1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos]) def route_write_access(self): @@ -909,14 +909,14 @@ class pbitcell(bitcell_base.bitcell_base): self.cross_couple_lower_ypos) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) - self.add_path("metal1", + self.add_path("m1", [self.write_nmos_left[k].get_pin("D").uc(), mid, Q_pos]) mid = vector(self.write_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) - self.add_path("metal1", + self.add_path("m1", [self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos]) def route_read_access(self): @@ -960,7 +960,7 @@ class pbitcell(bitcell_base.bitcell_base): mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) - self.add_path("metal1", + self.add_path("m1", [port_contact_offset, mid, left_storage_contact]) port_contact_offset = self.read_access_nmos_right[k].get_pin("G").uc() \ @@ -975,7 +975,7 @@ class pbitcell(bitcell_base.bitcell_base): mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) - self.add_path("metal1", + self.add_path("m1", [port_contact_offset, mid, right_storage_contact]) def extend_well(self): @@ -1076,7 +1076,7 @@ class pbitcell(bitcell_base.bitcell_base): """ Q_bar_pos = self.inverter_pmos_right.get_pin("S").center() vdd_pos = self.inverter_pmos_right.get_pin("D").center() - self.add_path("metal1", [Q_bar_pos, vdd_pos]) + self.add_path("m1", [Q_bar_pos, vdd_pos]) def get_storage_net_names(self): """ diff --git a/compiler/drc/design_rules.py b/compiler/drc/design_rules.py index cf54bdb3..05168b77 100644 --- a/compiler/drc/design_rules.py +++ b/compiler/drc/design_rules.py @@ -9,7 +9,7 @@ import debug from drc_value import * from drc_lut import * -class design_rules(): +class design_rules(dict): """ This is a class that implements the design rules structures. """ @@ -43,7 +43,9 @@ class design_rules(): else: debug.error("Must call complex DRC rule {} with arguments.".format(b),-1) - + def keys(self): + return self.rules.keys() + def add_layer(self, name, width, spacing, area=0): # Minimum width self.add("minwidth_{}".format(name), width) @@ -53,7 +55,7 @@ class design_rules(): self.add("minarea_{}".format(name), area) def add_enclosure(self, name, layer, enclosure, extension=None): - self.add("{0}_enclosure_{1}".format(name, layer), enclosure) + self.add("{0}_enclose_{1}".format(name, layer), enclosure) # Reserved for asymmetric enclosures if extension: self.add("{0}_extend_{1}".format(name, layer), extension) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index b1ce2d55..4527044c 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -333,7 +333,7 @@ class bank(design.design): self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines # A space for wells or jogging m2 - self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"), 3*self.m2_pitch) @@ -605,7 +605,7 @@ class bank(design.design): out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc() name = self.control_signals[port][signal] bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y) - self.add_path("metal3",[out_pos, bus_pos]) + self.add_path("m3",[out_pos, bus_pos]) self.add_via_center(layers=self.m2_stack, offset=bus_pos) self.add_via_center(layers=self.m1_stack, @@ -648,7 +648,7 @@ class bank(design.design): control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_pitch, self.min_y_offset) # The control bus is routed up to two pitches below the bitcell array control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2*self.m1_pitch - self.bus_xoffset[0] = self.create_bus(layer="metal2", + self.bus_xoffset[0] = self.create_bus(layer="m2", pitch=self.m2_pitch, offset=control_bus_offset, names=self.control_signals[0], @@ -663,7 +663,7 @@ class bank(design.design): control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch, self.max_y_offset - control_bus_length) # The bus for the right port is reversed so that the rbl_wl is closest to the array - self.bus_xoffset[1] = self.create_bus(layer="metal2", + self.bus_xoffset[1] = self.create_bus(layer="m2", pitch=self.m2_pitch, offset=control_bus_offset, names=list(reversed(self.control_signals[1])), @@ -817,7 +817,7 @@ class bank(design.design): bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).lc() mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].rx() + 0.5*self.bitcell_array_inst.lx(),0) mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0.5,1) - self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) def route_port_address_right(self, port): @@ -829,7 +829,7 @@ class bank(design.design): bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).rc() mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].lx() + 0.5*self.bitcell_array_inst.rx(),0) mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0,1) - self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) def route_column_address_lines(self, port): """ Connecting the select lines of column mux to the address bus """ @@ -878,7 +878,7 @@ class bank(design.design): wl_name = "wl_{}".format(i) wl_pin = self.bitcell_array_inst.get_pin(wl_name) self.add_label(text=wl_name, - layer="metal1", + layer="m1", offset=wl_pin.center()) # Add the bitline names @@ -888,10 +888,10 @@ class bank(design.design): bl_pin = self.bitcell_array_inst.get_pin(bl_name) br_pin = self.bitcell_array_inst.get_pin(br_name) self.add_label(text=bl_name, - layer="metal2", + layer="m2", offset=bl_pin.center()) self.add_label(text=br_name, - layer="metal2", + layer="m2", offset=br_pin.center()) # # Add the data output names to the sense amp output @@ -899,7 +899,7 @@ class bank(design.design): # data_name = "data_{}".format(i) # data_pin = self.sense_amp_array_inst.get_pin(data_name) # self.add_label(text="sa_out_{}".format(i), - # layer="metal2", + # layer="m2", # offset=data_pin.center()) # Add labels on the decoder @@ -909,7 +909,7 @@ class bank(design.design): pin_name = "in_{}".format(i) data_pin = self.wordline_driver_inst[port].get_pin(pin_name) self.add_label(text=data_name, - layer="metal1", + layer="m1", offset=data_pin.center()) @@ -941,7 +941,7 @@ class bank(design.design): for (control_signal, pin_pos) in connection: control_mid_pos = self.bus_xoffset[port][control_signal] control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y) - self.add_wire(("metal1","via1","metal2"), [control_mid_pos, control_pos, pin_pos]) + self.add_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos]) self.add_via_center(layers=self.m1_stack, offset=control_pos) @@ -956,7 +956,7 @@ class bank(design.design): mid_pos = pin_pos - vector(0,2*self.m2_gap) # to route down to the top of the bus control_x_offset = self.bus_xoffset[port][control_signal].x control_pos = vector(control_x_offset, mid_pos.y) - self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) + self.add_wire(self.m1_stack,[pin_pos, mid_pos, control_pos]) self.add_via_center(layers=self.m1_stack, offset=control_pos) diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 14b6b203..4b49750d 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -208,18 +208,18 @@ class bank_select(design.design): xoffset_bank_sel = bank_sel_inv_pin.lx() bank_sel_line_pos = vector(xoffset_bank_sel, 0) bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint) - self.add_path("metal2", [bank_sel_line_pos, bank_sel_line_end]) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_path("m2", [bank_sel_line_pos, bank_sel_line_end]) + self.add_via_center(layers=self.m1_stack, offset=bank_sel_inv_pin.lc()) # Route the pin to the left edge as well bank_sel_pin_pos=vector(0, 0) bank_sel_pin_end=vector(bank_sel_line_pos.x, bank_sel_pin_pos.y) self.add_layout_pin_segment_center(text="bank_sel", - layer="metal3", + layer="m3", start=bank_sel_pin_pos, end=bank_sel_pin_end) - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_via_center(layers=self.m2_stack, offset=bank_sel_pin_end, directions=("H","H")) @@ -227,10 +227,10 @@ class bank_select(design.design): bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z") xoffset_bank_sel_bar = bank_sel_bar_pin.rx() self.add_label_pin(text="bank_sel_bar", - layer="metal2", + layer="m2", offset=vector(xoffset_bank_sel_bar, 0), height=self.inv4x.height) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=bank_sel_bar_pin.rc()) @@ -251,13 +251,13 @@ class bank_select(design.design): out_position = logic_inst.get_pin("Z").rc() + vector(0.5*self.m1_width,0) in_position = inv_inst.get_pin("A").lc() + vector(0.5*self.m1_width,0) post = inv_inst.get_pin("A").rc() - self.add_path("metal1", [pre, out_position, in_position, post]) + self.add_path("m1", [pre, out_position, in_position, post]) # Connect the logic B input to bank_sel/bank_sel_bar logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1m2.height,0) input_pos = vector(xoffset_bank_signal, logic_pos.y) - self.add_path("metal2",[logic_pos, input_pos]) + self.add_path("m2",[logic_pos, input_pos]) self.add_via_center(layers=self.m1_stack, offset=logic_pos, directions=("H","H")) @@ -273,7 +273,7 @@ class bank_select(design.design): offset=logic_pos, directions=("H","H")) self.add_layout_pin_segment_center(text=input_name, - layer="metal3", + layer="m3", start=input_pos, end=logic_pos) @@ -294,7 +294,7 @@ class bank_select(design.design): for n in ["vdd", "gnd"]: supply_pin = self.inv_inst[num].get_pin(n) supply_offset = supply_pin.ll().scale(0,1) - self.add_rect(layer="metal1", + self.add_rect(layer="m1", offset=supply_offset, width=self.width) @@ -308,20 +308,20 @@ class bank_select(design.design): offset=pin_pos, directions=("H","H")) self.add_layout_pin_rect_center(text=n, - layer="metal3", + layer="m3", offset=pin_pos) # Add vdd/gnd supply rails gnd_pin = inv_inst.get_pin("gnd") left_gnd_pos = vector(0, gnd_pin.cy()) self.add_layout_pin_segment_center(text="gnd", - layer="metal1", + layer="m1", start=left_gnd_pos, end=gnd_pin.rc()) vdd_pin = inv_inst.get_pin("vdd") left_vdd_pos = vector(0, vdd_pin.cy()) self.add_layout_pin_segment_center(text="vdd", - layer="metal1", + layer="m1", start=left_vdd_pos, end=vdd_pin.rc()) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index b1b61487..bf43736b 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -173,7 +173,7 @@ class bitcell_array(design.design): width = 0 else: width = self.width - wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_metal1")) + wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1")) wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell return wl_wire @@ -183,7 +183,7 @@ class bitcell_array(design.design): else: height = self.height bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_metal1")) + bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_m1")) bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 3256c9ac..dbb7c3b7 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -371,7 +371,7 @@ class control_logic(design.design): height = self.control_logic_center.y - self.m2_pitch offset = vector(self.ctrl_dff_array.width,0) - self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height) + self.rail_offsets = self.create_vertical_bus("m2", self.m2_pitch, offset, self.internal_bus_list, height) def create_instances(self): @@ -483,8 +483,8 @@ class control_logic(design.design): vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by() in_pos = vector(self.rail_offsets["rbl_bl_delay"].x,vdd_ypos) mid1 = vector(out_pos.x,in_pos.y) - self.add_wire(("metal1","via1","metal2"),[out_pos, mid1, in_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_wire(self.m1_stack,[out_pos, mid1, in_pos]) + self.add_via_center(layers=self.m1_stack, offset=in_pos) @@ -509,10 +509,10 @@ class control_logic(design.design): clk_pin = self.clk_buf_inst.get_pin("A") clk_pos = clk_pin.center() self.add_layout_pin_segment_center(text="clk", - layer="metal2", + layer="m2", start=clk_pos, end=clk_pos.scale(1,0)) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=clk_pos) @@ -521,9 +521,9 @@ class control_logic(design.design): mid1 = vector(out_pos.x,2*self.m2_pitch) mid2 = vector(self.rail_offsets["clk_buf"].x, mid1.y) bus_pos = self.rail_offsets["clk_buf"] - self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, mid2, bus_pos]) + self.add_wire(("m3","via2","m2"),[out_pos, mid1, mid2, bus_pos]) # The pin is on M1, so we need another via as well - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=self.clk_buf_inst.get_pin("Z").center()) self.connect_output(self.clk_buf_inst, "Z", "clk_buf") @@ -552,21 +552,21 @@ class control_logic(design.design): out_pos = self.clk_bar_inst.get_pin("Z").center() in_pos = self.gated_clk_bar_inst.get_pin("B").center() mid1 = vector(in_pos.x,out_pos.y) - self.add_path("metal1",[out_pos, mid1, in_pos]) + self.add_path("m1",[out_pos, mid1, in_pos]) # This is the second gate over, so it needs to be on M3 clkbuf_map = zip(["A"], ["cs"]) - self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("m3", "via2", "m2")) # The pin is on M1, so we need another via as well - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=self.gated_clk_bar_inst.get_pin("A").center()) # This is the second gate over, so it needs to be on M3 clkbuf_map = zip(["Z"], ["gated_clk_bar"]) - self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("m3", "via2", "m2")) # The pin is on M1, so we need another via as well - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=self.gated_clk_bar_inst.get_pin("Z").center()) def create_gated_clk_buf_row(self): @@ -587,9 +587,9 @@ class control_logic(design.design): clkbuf_map = zip(["Z"], ["gated_clk_buf"]) - self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("m3", "via2", "m2")) # The pin is on M1, so we need another via as well - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=self.gated_clk_buf_inst.get_pin("Z").center()) def create_wlen_row(self): @@ -637,7 +637,7 @@ class control_logic(design.design): out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc() in_pos = self.p_en_bar_driver_inst.get_pin("A").lc() mid1 = vector(out_pos.x,in_pos.y) - self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos]) + self.add_wire(self.m1_stack,[out_pos, mid1,in_pos]) self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar") @@ -694,9 +694,9 @@ class control_logic(design.design): # Connect to rail rbl_map = zip(["Z"], ["rbl_bl_delay_bar"]) - self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets, ("m3", "via2", "m2")) # The pin is on M1, so we need another via as well - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center()) @@ -754,14 +754,14 @@ class control_logic(design.design): dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"]) else: dff_out_map = zip(["dout_bar_0"], ["cs"]) - self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("metal3", "via2", "metal2")) + self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("m3", "via2", "m2")) # Connect the clock rail to the other clock rail in_pos = self.ctrl_dff_inst.get_pin("clk").uc() mid_pos = in_pos + vector(0,2*self.m2_pitch) rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y) - self.add_wire(("metal1","via1","metal2"),[in_pos, mid_pos, rail_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_wire(self.m1_stack,[in_pos, mid_pos, rail_pos]) + self.add_via_center(layers=self.m1_stack, offset=rail_pos) self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb") @@ -786,7 +786,7 @@ class control_logic(design.design): out_pin = inst.get_pin(pin_name) right_pos=out_pin.center() + vector(self.width-out_pin.cx(),0) self.add_layout_pin_segment_center(text=out_name, - layer="metal1", + layer="m1", start=out_pin.center(), end=right_pos) @@ -799,19 +799,19 @@ class control_logic(design.design): for inst in self.row_end_inst: pins = inst.get_pins("vdd") for pin in pins: - if pin.layer == "metal1": + if pin.layer == "m1": row_loc = pin.rc() pin_loc = vector(max_row_x_loc, pin.rc().y) self.add_power_pin("vdd", pin_loc) - self.add_path("metal1", [row_loc, pin_loc]) + self.add_path("m1", [row_loc, pin_loc]) pins = inst.get_pins("gnd") for pin in pins: - if pin.layer == "metal1": + if pin.layer == "m1": row_loc = pin.rc() pin_loc = vector(max_row_x_loc, pin.rc().y) self.add_power_pin("gnd", pin_loc) - self.add_path("metal1", [row_loc, pin_loc]) + self.add_path("m1", [row_loc, pin_loc]) self.copy_layout_pin(self.delay_inst,"gnd") self.copy_layout_pin(self.delay_inst,"vdd") @@ -828,14 +828,14 @@ class control_logic(design.design): """ # pin=self.clk_inv1.get_pin("Z") # self.add_label_pin(text="clk1_bar", - # layer="metal1", + # layer="m1", # offset=pin.ll(), # height=pin.height(), # width=pin.width()) # pin=self.clk_inv2.get_pin("Z") # self.add_label_pin(text="clk2", - # layer="metal1", + # layer="m1", # offset=pin.ll(), # height=pin.height(), # width=pin.width()) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index bc932a26..a07576d4 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -132,11 +132,11 @@ class delay_chain(design.design): pin1_pos = pin1.center() pin2_pos = pin2.center() if pin1_pos.y == pin2_pos.y: - self.add_path("metal2", [pin1_pos, pin2_pos]) + self.add_path("m2", [pin1_pos, pin2_pos]) else: mid_point = vector(pin2_pos.x, 0.5*(pin1_pos.y+pin2_pos.y)) # Written this way to guarantee it goes right first if we are switching rows - self.add_path("metal2", [pin1_pos, vector(pin1_pos.x,mid_point.y), mid_point, vector(mid_point.x,pin2_pos.y), pin2_pos]) + self.add_path("m2", [pin1_pos, vector(pin1_pos.x,mid_point.y), mid_point, vector(mid_point.x,pin2_pos.y), pin2_pos]) def route_inverters(self): """ Add metal routing for each of the fanout stages """ @@ -146,22 +146,22 @@ class delay_chain(design.design): for load in self.load_inst_map[inv]: # Drop a via on each A pin a_pin = load.get_pin("A") - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_via_center(layers=self.m2_stack, offset=a_pin.center()) # Route an M3 horizontal wire to the furthest z_pin = inv.get_pin("Z") a_pin = inv.get_pin("A") a_max = self.rightest_load_inst[inv].get_pin("A") - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=z_pin.center()) - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_via_center(layers=self.m2_stack, offset=z_pin.center()) - self.add_path("metal3",[z_pin.center(), a_max.center()]) + self.add_path("m3",[z_pin.center(), a_max.center()]) # Route Z to the A of the next stage @@ -172,7 +172,7 @@ class delay_chain(design.design): y_mid = (z_pin.cy() + next_a_pin.cy())/2 mid1_point = vector(z_pin.cx(), y_mid) mid2_point = vector(next_a_pin.cx(), y_mid) - self.add_path("metal2",[z_pin.center(), mid1_point, mid2_point, next_a_pin.center()]) + self.add_path("m2",[z_pin.center(), mid1_point, mid2_point, next_a_pin.center()]) def add_layout_pins(self): @@ -205,10 +205,10 @@ class delay_chain(design.design): # input is A pin of first inverter a_pin = self.driver_inst_list[0].get_pin("A") - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) self.add_layout_pin(text="in", - layer="metal2", + layer="m2", offset=a_pin.ll().scale(1,0), height=a_pin.cy()) @@ -216,12 +216,12 @@ class delay_chain(design.design): # output is A pin of last load inverter last_driver_inst = self.driver_inst_list[-1] a_pin = self.rightest_load_inst[last_driver_inst].get_pin("A") - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) mid_point = vector(a_pin.cx()+3*self.m2_width,a_pin.cy()) - self.add_path("metal2",[a_pin.center(), mid_point, mid_point.scale(1,0)]) + self.add_path("m2",[a_pin.center(), mid_point, mid_point.scale(1,0)]) self.add_layout_pin_segment_center(text="out", - layer="metal2", + layer="m2", start=mid_point, end=mid_point.scale(1,0)) diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 89b29476..6ba020c7 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -124,7 +124,7 @@ class dff_array(design.design): for row in range(self.rows): for col in range(self.columns): din_pin = self.dff_insts[row,col].get_pin("D") - debug.check(din_pin.layer=="metal2","DFF D pin not on metal2") + debug.check(din_pin.layer=="m2","DFF D pin not on metal2") self.add_layout_pin(text=self.get_din_name(row,col), layer=din_pin.layer, offset=din_pin.ll(), @@ -132,7 +132,7 @@ class dff_array(design.design): height=din_pin.height()) dout_pin = self.dff_insts[row,col].get_pin("Q") - debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2") + debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2") self.add_layout_pin(text=self.get_dout_name(row,col), layer=dout_pin.layer, offset=dout_pin.ll(), @@ -144,20 +144,20 @@ class dff_array(design.design): # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") clk_ypos = 2*self.m3_pitch+self.m3_width - debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") + debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2") self.add_layout_pin_segment_center(text="clk", - layer="metal3", + layer="m3", start=vector(0,clk_ypos), end=vector(self.width,clk_ypos)) for col in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk") # Make a vertical strip for each column - self.add_rect(layer="metal2", + self.add_rect(layer="m2", offset=clk_pin.ll().scale(1,0), width=self.m2_width, height=self.height) # Drop a via to the M3 pin - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_via_center(layers=self.m2_stack, offset=vector(clk_pin.cx(),clk_ypos)) def get_clk_cin(self): diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index f6fc1cf2..9e2ff0aa 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -112,12 +112,12 @@ class dff_buf(design.design): mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx()) mid1 = vector(mid_x_offset, q_pin.cy()) mid2 = vector(mid_x_offset, a1_pin.cy()) - self.add_path("metal3", [q_pin.center(), mid1, mid2, a1_pin.center()]) - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_path("m3", [q_pin.center(), mid1, mid2, a1_pin.center()]) + self.add_via_center(layers=self.m2_stack, offset=q_pin.center()) - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_via_center(layers=self.m2_stack, offset=a1_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=a1_pin.center()) # Route inv1 z to inv2 a @@ -126,14 +126,14 @@ class dff_buf(design.design): mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx()) self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy()) mid2 = vector(mid_x_offset, a2_pin.cy()) - self.add_path("metal1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()]) + self.add_path("m1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()]) def add_layout_pins(self): # Continous vdd rail along with label. vdd_pin=self.dff_inst.get_pin("vdd") self.add_layout_pin(text="vdd", - layer="metal1", + layer="m1", offset=vdd_pin.ll(), width=self.width, height=vdd_pin.height()) @@ -141,7 +141,7 @@ class dff_buf(design.design): # Continous gnd rail along with label. gnd_pin=self.dff_inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=gnd_pin.ll(), width=self.width, height=vdd_pin.height()) @@ -164,18 +164,18 @@ class dff_buf(design.design): mid_pos = dout_pin.center() + vector(self.m1_pitch,0) q_pos = mid_pos - vector(0,self.m2_pitch) self.add_layout_pin_rect_center(text="Q", - layer="metal2", + layer="m2", offset=q_pos) - self.add_path("metal1", [dout_pin.center(), mid_pos, q_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_path("m1", [dout_pin.center(), mid_pos, q_pos]) + self.add_via_center(layers=self.m1_stack, offset=q_pos) qb_pos = self.mid_qb_pos + vector(0,self.m2_pitch) self.add_layout_pin_rect_center(text="Qb", - layer="metal2", + layer="m2", offset=qb_pos) - self.add_path("metal1", [self.mid_qb_pos, qb_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_path("m1", [self.mid_qb_pos, qb_pos]) + self.add_via_center(layers=self.m1_stack, offset=qb_pos) def get_clk_cin(self): diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 2fafee76..8b8e21dc 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -142,7 +142,7 @@ class dff_buf_array(design.design): for row in range(self.rows): for col in range(self.columns): din_pin = self.dff_insts[row,col].get_pin("D") - debug.check(din_pin.layer=="metal2","DFF D pin not on metal2") + debug.check(din_pin.layer=="m2","DFF D pin not on metal2") self.add_layout_pin(text=self.get_din_name(row,col), layer=din_pin.layer, offset=din_pin.ll(), @@ -150,7 +150,7 @@ class dff_buf_array(design.design): height=din_pin.height()) dout_pin = self.dff_insts[row,col].get_pin("Q") - debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2") + debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2") self.add_layout_pin(text=self.get_dout_name(row,col), layer=dout_pin.layer, offset=dout_pin.ll(), @@ -158,7 +158,7 @@ class dff_buf_array(design.design): height=dout_pin.height()) dout_bar_pin = self.dff_insts[row,col].get_pin("Qb") - debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2") + debug.check(dout_bar_pin.layer=="m2","DFF Qb pin not on metal2") self.add_layout_pin(text=self.get_dout_bar_name(row,col), layer=dout_bar_pin.layer, offset=dout_bar_pin.ll(), @@ -169,28 +169,28 @@ class dff_buf_array(design.design): # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") clk_ypos = 2*self.m3_pitch+self.m3_width - debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") + debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2") if self.columns==1: self.add_layout_pin(text="clk", - layer="metal2", + layer="m2", offset=clk_pin.ll().scale(1,0), width=self.m2_width, height=self.height) else: self.add_layout_pin_segment_center(text="clk", - layer="metal3", + layer="m3", start=vector(0,clk_ypos), end=vector(self.width,clk_ypos)) for col in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk") # Make a vertical strip for each column - self.add_rect(layer="metal2", + self.add_rect(layer="m2", offset=clk_pin.ll().scale(1,0), width=self.m2_width, height=self.height) # Drop a via to the M3 pin - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_via_center(layers=self.m2_stack, offset=vector(clk_pin.cx(),clk_ypos)) def get_clk_cin(self): diff --git a/compiler/modules/dff_inv.py b/compiler/modules/dff_inv.py index 207a5ad0..9dcb84c5 100644 --- a/compiler/modules/dff_inv.py +++ b/compiler/modules/dff_inv.py @@ -97,13 +97,13 @@ class dff_inv(design.design): mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx()) mid1 = vector(mid_x_offset, q_pin.cy()) mid2 = vector(mid_x_offset, a1_pin.cy()) - self.add_path("metal3", + self.add_path("m3", [q_pin.center(), mid1, mid2, a1_pin.center()]) - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_via_center(layers=self.m2_stack, offset=q_pin.center()) - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_via_center(layers=self.m2_stack, offset=a1_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=a1_pin.center()) @@ -112,7 +112,7 @@ class dff_inv(design.design): # Continous vdd rail along with label. vdd_pin=self.dff_inst.get_pin("vdd") self.add_layout_pin(text="vdd", - layer="metal1", + layer="m1", offset=vdd_pin.ll(), width=self.width, height=vdd_pin.height()) @@ -120,7 +120,7 @@ class dff_inv(design.design): # Continous gnd rail along with label. gnd_pin=self.dff_inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=gnd_pin.ll(), width=self.width, height=vdd_pin.height()) @@ -146,9 +146,9 @@ class dff_inv(design.design): dout_pin = self.inv1_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Qb", - layer="metal2", + layer="m2", offset=dout_pin.center()) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=dout_pin.center()) def get_clk_cin(self): diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index 760a2337..aadb4257 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -140,7 +140,7 @@ class dff_inv_array(design.design): for row in range(self.rows): for col in range(self.columns): din_pin = self.dff_insts[row,col].get_pin("D") - debug.check(din_pin.layer=="metal2","DFF D pin not on metal2") + debug.check(din_pin.layer=="m2","DFF D pin not on metal2") self.add_layout_pin(text=self.get_din_name(row,col), layer=din_pin.layer, offset=din_pin.ll(), @@ -148,7 +148,7 @@ class dff_inv_array(design.design): height=din_pin.height()) dout_pin = self.dff_insts[row,col].get_pin("Q") - debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2") + debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2") self.add_layout_pin(text=self.get_dout_name(row,col), layer=dout_pin.layer, offset=dout_pin.ll(), @@ -156,7 +156,7 @@ class dff_inv_array(design.design): height=dout_pin.height()) dout_bar_pin = self.dff_insts[row,col].get_pin("Qb") - debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2") + debug.check(dout_bar_pin.layer=="m2","DFF Qb pin not on metal2") self.add_layout_pin(text=self.get_dout_bar_name(row,col), layer=dout_bar_pin.layer, offset=dout_bar_pin.ll(), @@ -167,27 +167,27 @@ class dff_inv_array(design.design): # Create vertical spines to a single horizontal rail clk_pin = self.dff_insts[0,0].get_pin("clk") clk_ypos = 2*self.m3_pitch+self.m3_width - debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2") + debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2") if self.columns==1: self.add_layout_pin(text="clk", - layer="metal2", + layer="m2", offset=clk_pin.ll().scale(1,0), width=self.m2_width, height=self.height) else: self.add_layout_pin_segment_center(text="clk", - layer="metal3", + layer="m3", start=vector(0,clk_ypos), end=vector(self.width,clk_ypos)) for col in range(self.columns): clk_pin = self.dff_insts[0,col].get_pin("clk") # Make a vertical strip for each column - self.add_rect(layer="metal2", + self.add_rect(layer="m2", offset=clk_pin.ll().scale(1,0), width=self.m2_width, height=self.height) # Drop a via to the M3 pin - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_via_center(layers=self.m2_stack, offset=vector(clk_pin.cx(),clk_ypos)) def get_clk_cin(self): diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index f1f433ce..1e5a2121 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -122,7 +122,7 @@ class dummy_array(design.design): for cell_column in column_list: bl_pin = self.cell_inst[0,col].get_pin(cell_column) self.add_layout_pin(text=cell_column+"_{0}".format(col), - layer="metal2", + layer="m2", offset=bl_pin.ll(), width=bl_pin.width(), height=self.height) @@ -131,7 +131,7 @@ class dummy_array(design.design): for cell_row in row_list: wl_pin = self.cell_inst[row,0].get_pin(cell_row) self.add_layout_pin(text=cell_row+"_{0}".format(row), - layer="metal1", + layer="m1", offset=wl_pin.ll(), width=self.width, height=wl_pin.height()) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 9c7cafd8..bb637ed1 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -173,7 +173,7 @@ class hierarchical_decoder(design.design): input_offset=vector(min_x - self.input_routing_width,0) input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] - self.input_rails = self.create_vertical_pin_bus(layer="metal2", + self.input_rails = self.create_vertical_pin_bus(layer="m2", pitch=self.m2_pitch, offset=input_offset, names=input_bus_names, @@ -225,7 +225,7 @@ class hierarchical_decoder(design.design): offset=input_offset) self.add_via_center(layers=self.m2_stack, offset=output_offset) - self.add_path(("metal3"), [input_offset, output_offset]) + self.add_path(("m3"), [input_offset, output_offset]) def add_pins(self): @@ -467,11 +467,11 @@ class hierarchical_decoder(design.design): # ensure the bend is in the middle mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y) mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y) - self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos]) + self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos]) z_pin = self.inv_inst[row].get_pin("Z") self.add_layout_pin(text="decode_{0}".format(row), - layer="metal1", + layer="m1", offset=z_pin.ll(), width=z_pin.width(), height=z_pin.height()) @@ -485,7 +485,7 @@ class hierarchical_decoder(design.design): if (self.num_inputs >= 4): input_offset = vector(0.5*self.m2_width,0) input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)] - self.predecode_rails = self.create_vertical_pin_bus(layer="metal2", + self.predecode_rails = self.create_vertical_pin_bus(layer="m2", pitch=self.m2_pitch, offset=input_offset, names=input_bus_names, @@ -570,7 +570,7 @@ class hierarchical_decoder(design.design): start = self.nand_inst[num].get_pin(pin_name).lc() end = self.inv_inst[num].get_pin(pin_name).rc() mid = (start+end).scale(0.5,0.5) - self.add_rect_center(layer="metal1", + self.add_rect_center(layer="m1", offset=mid, width=end.x-start.x) @@ -584,7 +584,7 @@ class hierarchical_decoder(design.design): def route_predecode_rail(self, rail_name, pin): """ Connect the routing rail to the given metal1 pin """ rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y) - self.add_path("metal1", [rail_pos, pin.lc()]) + self.add_path("m1", [rail_pos, pin.lc()]) self.add_via_center(layers=self.m1_stack, offset=rail_pos) @@ -597,7 +597,7 @@ class hierarchical_decoder(design.design): rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y) self.add_via_center(layers=self.m1_stack, offset=pin.center()) - self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()]) + self.add_wire(("m3","via2","m2"), [rail_pos, mid_point, pin.uc()]) self.add_via_center(layers=self.m2_stack, offset=rail_pos) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 0de67cae..dec04073 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -73,7 +73,7 @@ class hierarchical_predecode(design.design): """ Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """ input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)] offset = vector(0.5*self.m2_width,2*self.m1_width) - self.input_rails = self.create_vertical_pin_bus(layer="metal2", + self.input_rails = self.create_vertical_pin_bus(layer="m2", pitch=self.m2_pitch, offset=offset, names=input_names, @@ -83,7 +83,7 @@ class hierarchical_predecode(design.design): non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)] decode_names = invert_names + non_invert_names offset = vector(self.x_off_inv_1 + self.inv.width + 2*self.m2_pitch, 2*self.m1_width) - self.decode_rails = self.create_vertical_bus(layer="metal2", + self.decode_rails = self.create_vertical_bus(layer="m2", pitch=self.m2_pitch, offset=offset, names=decode_names, @@ -183,7 +183,7 @@ class hierarchical_predecode(design.design): a_pin = "A_{}".format(num) in_pos = vector(self.input_rails[in_pin].x,y_offset) a_pos = vector(self.decode_rails[a_pin].x,y_offset) - self.add_path("metal1",[in_pos, a_pos]) + self.add_path("m1",[in_pos, a_pos]) self.add_via_center(layers = self.m1_stack, offset=[self.input_rails[in_pin].x, y_offset]) self.add_via_center(layers = self.m1_stack, @@ -201,11 +201,11 @@ class hierarchical_predecode(design.design): # ensure the bend is in the middle mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y) mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y) - self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos]) + self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos]) z_pin = self.inv_inst[num].get_pin("Z") self.add_layout_pin(text="out_{}".format(num), - layer="metal1", + layer="m1", offset=z_pin.ll(), height=z_pin.height(), width=z_pin.width()) @@ -226,7 +226,7 @@ class hierarchical_predecode(design.design): inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc() right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0) rail_pos = vector(self.decode_rails[out_pin].x,y_offset) - self.add_path("metal1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) + self.add_path("m1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) self.add_via_center(layers = self.m1_stack, offset=rail_pos) @@ -234,7 +234,7 @@ class hierarchical_predecode(design.design): #route input inv_in_pos = self.in_inst[inv_num].get_pin("A").lc() in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y) - self.add_path("metal1", [in_pos, inv_in_pos]) + self.add_path("m1", [in_pos, inv_in_pos]) self.add_via_center(layers=self.m1_stack, offset=in_pos) @@ -255,7 +255,7 @@ class hierarchical_predecode(design.design): for rail_pin,gate_pin in zip(index_lst,gate_lst): pin_pos = self.nand_inst[k].get_pin(gate_pin).lc() rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) - self.add_path("metal1", [rail_pos, pin_pos]) + self.add_path("m1", [rail_pos, pin_pos]) self.add_via_center(layers=self.m1_stack, offset=rail_pos) @@ -275,7 +275,7 @@ class hierarchical_predecode(design.design): for n in ["vdd", "gnd"]: nand_pin = self.nand_inst[num].get_pin(n) supply_offset = nand_pin.ll().scale(0,1) - self.add_rect(layer="metal1", + self.add_rect(layer="m1", offset=supply_offset, width=self.inv_inst[num].rx()) diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index 3459f5e0..33076bfb 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -160,7 +160,7 @@ class multibank(design.design): self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width # A space for wells or jogging m2 - self.m2_gap = max(2*drc("pwell_to_nwell"] + drc["well_enclosure_active"), + self.m2_gap = max(2*drc("pwell_to_nwell"] + drc["well_enclose_active"), 2*self.m2_pitch) @@ -451,7 +451,7 @@ class multibank(design.design): # Connect the inverter output to the central bus out_pos = self.bank_select_inst.get_pin(gated_name).rc() bus_pos = vector(self.bus_xoffset[gated_name], out_pos.y) - self.add_path("metal3",[out_pos, bus_pos]) + self.add_path("m3",[out_pos, bus_pos]) self.add_via_center(layers=self.m2_stack, offset=bus_pos, rotate=90) @@ -512,7 +512,7 @@ class multibank(design.design): # 2 pitches on the right for vias/jogs to access the inputs control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0) control_bus_length = self.bitcell_array_inst.uy() - self.bus_xoffset = self.create_vertical_bus(layer="metal2", + self.bus_xoffset = self.create_vertical_bus(layer="m2", pitch=self.m2_pitch, offset=control_bus_offset, names=self.control_signals, @@ -530,9 +530,9 @@ class multibank(design.design): bitcell_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).uc() yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) - self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), + self.add_path("m2",[precharge_bl, vector(precharge_bl.x,yoffset), vector(bitcell_bl.x,yoffset), bitcell_bl]) - self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset), + self.add_path("m2",[precharge_br, vector(precharge_br.x,yoffset), vector(bitcell_br.x,yoffset), bitcell_br]) @@ -550,9 +550,9 @@ class multibank(design.design): bitcell_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).bc() yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y) - self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), + self.add_path("m2",[col_mux_bl, vector(col_mux_bl.x,yoffset), vector(bitcell_bl.x,yoffset), bitcell_bl]) - self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset), + self.add_path("m2",[col_mux_br, vector(col_mux_br.x,yoffset), vector(bitcell_br.x,yoffset), bitcell_br]) def route_sense_amp_to_col_mux_or_bitcell_array(self): @@ -573,9 +573,9 @@ class multibank(design.design): yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) - self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset), + self.add_path("m2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset), vector(connect_bl.x,yoffset), connect_bl]) - self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset), + self.add_path("m2",[sense_amp_br, vector(sense_amp_br.x,yoffset), vector(connect_br.x,yoffset), connect_br]) def route_sense_amp_to_trigate(self): @@ -590,7 +590,7 @@ class multibank(design.design): offset=tri_gate_in) self.add_via_center(layers=self.m2_stack, offset=sa_data_out) - self.add_path("metal3",[sa_data_out,tri_gate_in]) + self.add_path("m3",[sa_data_out,tri_gate_in]) def route_sense_amp_out(self): """ Add pins for the sense amp output """ @@ -644,14 +644,14 @@ class multibank(design.design): driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(i)).lc() mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) - self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) + self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos]) # The mid guarantees we exit the input cell to the right. driver_wl_pos = self.wordline_driver_inst.get_pin("wl_{}".format(i)).rc() bitcell_wl_pos = self.bitcell_array_inst.get_pin("wl_{}".format(i)).lc() mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0) mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) - self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) @@ -698,8 +698,8 @@ class multibank(design.design): else: mid1_pos = vector(decode_out_pos.x + delta_offset + (self.num_col_addr_lines-i)*self.m2_pitch,decode_out_pos.y) mid2_pos = vector(mid1_pos.x,mux_addr_pos.y) - #self.add_wire(("metal1","via1","metal2"),[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) - self.add_path("metal1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) + #self.add_wire(self.m1_stack,[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) + self.add_path("m1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) @@ -715,7 +715,7 @@ class multibank(design.design): wl_name = "wl_{}".format(i) wl_pin = self.bitcell_array_inst.get_pin(wl_name) self.add_label(text=wl_name, - layer="metal1", + layer="m1", offset=wl_pin.center()) # Add the bitline names @@ -725,10 +725,10 @@ class multibank(design.design): bl_pin = self.bitcell_array_inst.get_pin(bl_name) br_pin = self.bitcell_array_inst.get_pin(br_name) self.add_label(text=bl_name, - layer="metal2", + layer="m2", offset=bl_pin.center()) self.add_label(text=br_name, - layer="metal2", + layer="m2", offset=br_pin.center()) # # Add the data output names to the sense amp output @@ -736,7 +736,7 @@ class multibank(design.design): # data_name = "data_{}".format(i) # data_pin = self.sense_amp_array_inst.get_pin(data_name) # self.add_label(text="sa_out_{}".format(i), - # layer="metal2", + # layer="m2", # offset=data_pin.center()) # Add labels on the decoder @@ -745,7 +745,7 @@ class multibank(design.design): pin_name = "in_{}".format(i) data_pin = self.wordline_driver_inst.get_pin(pin_name) self.add_label(text=data_name, - layer="metal1", + layer="m1", offset=data_pin.center()) @@ -765,7 +765,7 @@ class multibank(design.design): for (control_signal, pin_pos) in connection: control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) - self.add_path("metal1", [control_pos, pin_pos]) + self.add_path("m1", [control_pos, pin_pos]) self.add_via_center(layers=self.m1_stack, offset=control_pos, rotate=90) @@ -776,7 +776,7 @@ class multibank(design.design): mid_pos = pin_pos + vector(0,self.m1_pitch) control_x_offset = self.bus_xoffset[control_signal].x control_pos = vector(control_x_offset + self.m1_width, mid_pos.y) - self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) + self.add_wire(self.m1_stack,[pin_pos, mid_pos, control_pos]) control_via_pos = vector(control_x_offset, mid_pos.y) self.add_via_center(layers=self.m1_stack, offset=control_via_pos, @@ -791,13 +791,13 @@ class multibank(design.design): if self.num_banks > 1: # it's not an input pin if we have multiple banks self.add_label_pin(text=ctrl, - layer="metal2", + layer="m2", offset=vector(x_offset, self.min_y_offset), width=self.m2_width, height=self.max_y_offset-self.min_y_offset) else: self.add_layout_pin(text=ctrl, - layer="metal2", + layer="m2", offset=vector(x_offset, self.min_y_offset), width=self.m2_width, height=self.max_y_offset-self.min_y_offset) @@ -807,12 +807,12 @@ class multibank(design.design): """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ in_pin = inst.get_pin(pin).lc() rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y) - self.add_wire(("metal3","via2","metal2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)]) + self.add_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)]) # Bring it up to M2 for M2/M3 routing - self.add_via(layers=("metal1","via1","metal2"), + self.add_via(layers=self.m1_stack, offset=in_pin + contact.m1m2.offset, rotate=90) - self.add_via(layers=("metal2","via2","metal3"), + self.add_via(layers=self.m2_stack, offset=in_pin + self.m2m3_via_offset, rotate=90) @@ -821,10 +821,10 @@ class multibank(design.design): """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ in_pin = inst.get_pin(pin).rc() rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y) - self.add_wire(("metal3","via2","metal2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)]) - self.add_via(layers=("metal1","via1","metal2"), + self.add_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)]) + self.add_via(layers=self.m1_stack, offset=in_pin + contact.m1m2.offset, rotate=90) - self.add_via(layers=("metal2","via2","metal3"), + self.add_via(layers=self.m2_stack, offset=in_pin + self.m2m3_via_offset, rotate=90) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 0a624c60..7f1fdd49 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -92,7 +92,7 @@ class port_address(design.design): driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc() mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) - self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) + self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos]) @@ -149,7 +149,7 @@ class port_address(design.design): """ # A space for wells or jogging m2 - self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"), 3*self.m2_pitch) row_decoder_offset = vector(0,0) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index a5bc4399..33ecdd01 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -212,7 +212,7 @@ class port_data(design.design): # A space for wells or jogging m2 between modules - self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"), 3*self.m2_pitch) @@ -479,7 +479,7 @@ class port_data(design.design): offset=end_pos) # Route between write mask AND array and write driver array - self.add_wire(("metal1","via1","metal2"), [beg_pos, middle_pos, end_pos]) + self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos]) def route_column_mux_to_precharge_array(self, port): @@ -655,9 +655,9 @@ class port_data(design.design): top_br = top_inst.get_pin(top_br_name.format(col+top_start_bit)).bc() yoffset = 0.5*(top_bl.y+bottom_bl.y) - self.add_path("metal2",[bottom_bl, vector(bottom_bl.x,yoffset), + self.add_path("m2",[bottom_bl, vector(bottom_bl.x,yoffset), vector(top_bl.x,yoffset), top_bl]) - self.add_path("metal2",[bottom_br, vector(bottom_br.x,yoffset), + self.add_path("m2",[bottom_br, vector(bottom_br.x,yoffset), vector(top_br.x,yoffset), top_br]) def graph_exclude_precharge(self): diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 2d98ba14..c2617f50 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -68,10 +68,10 @@ class precharge_array(design.design): def add_layout_pins(self): self.add_layout_pin(text="en_bar", - layer="metal1", + layer="m1", offset=self.pc_cell.get_pin("en_bar").ll(), width=self.width, - height=drc("minwidth_metal1")) + height=drc("minwidth_m1")) for inst in self.local_insts: self.copy_layout_pin(inst, "vdd") @@ -80,15 +80,15 @@ class precharge_array(design.design): inst = self.local_insts[i] bl_pin = inst.get_pin("bl") self.add_layout_pin(text="bl_{0}".format(i), - layer="metal2", + layer="m2", offset=bl_pin.ll(), - width=drc("minwidth_metal2"), + width=drc("minwidth_m2"), height=bl_pin.height()) br_pin = inst.get_pin("br") self.add_layout_pin(text="br_{0}".format(i), - layer="metal2", + layer="m2", offset=br_pin.ll(), - width=drc("minwidth_metal2"), + width=drc("minwidth_m2"), height=bl_pin.height()) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 639d0714..74962ba2 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -399,7 +399,7 @@ class replica_bitcell_array(design.design): else: height = self.height bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_metal1")) + bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_m1")) bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index c3f63b19..5552ed55 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -116,7 +116,7 @@ class replica_column(design.design): for bl_name in self.cell.get_all_bitline_names(): bl_pin = self.cell_inst[0].get_pin(bl_name) self.add_layout_pin(text=bl_name, - layer="metal2", + layer="m2", offset=bl_pin.ll(), width=bl_pin.width(), height=self.height) @@ -125,7 +125,7 @@ class replica_column(design.design): for wl_name in self.cell.get_all_wl_names(): wl_pin = self.cell_inst[row].get_pin(wl_name) self.add_layout_pin(text="{0}_{1}".format(wl_name,row), - layer="metal1", + layer="m1", offset=wl_pin.ll().scale(0,1), width=self.width, height=wl_pin.height()) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 21ee4a8d..2447e4b8 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -102,13 +102,13 @@ class sense_amp_array(design.design): self.add_via_center(layers=self.m2_stack, offset=gnd_pos) self.add_layout_pin_rect_center(text="gnd", - layer="metal3", + layer="m3", offset=gnd_pos) vdd_pos = inst.get_pin("vdd").center() self.add_via_center(layers=self.m2_stack, offset=vdd_pos) self.add_layout_pin_rect_center(text="vdd", - layer="metal3", + layer="m3", offset=vdd_pos) bl_pin = inst.get_pin("bl") @@ -116,18 +116,18 @@ class sense_amp_array(design.design): dout_pin = inst.get_pin("dout") self.add_layout_pin(text="bl_{0}".format(i), - layer="metal2", + layer="m2", offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) self.add_layout_pin(text="br_{0}".format(i), - layer="metal2", + layer="m2", offset=br_pin.ll(), width=br_pin.width(), height=br_pin.height()) self.add_layout_pin(text="data_{0}".format(i), - layer="metal2", + layer="m2", offset=dout_pin.ll(), width=dout_pin.width(), height=dout_pin.height()) @@ -137,10 +137,10 @@ class sense_amp_array(design.design): # add sclk rail across entire array sclk_offset = self.amp.get_pin("en").ll().scale(0,1) self.add_layout_pin(text="en", - layer="metal1", + layer="m1", offset=sclk_offset, width=self.width, - height=drc("minwidth_metal1")) + height=drc("minwidth_m1")) def input_load(self): return self.amp.input_load() diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 3e6420b9..09f21cfa 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -113,13 +113,13 @@ class single_level_column_mux_array(design.design): mux_inst = self.mux_inst[col_num] offset = mux_inst.get_pin("bl").ll() self.add_layout_pin(text="bl_{}".format(col_num), - layer="metal2", + layer="m2", offset=offset, height=self.height-offset.y) offset = mux_inst.get_pin("br").ll() self.add_layout_pin(text="br_{}".format(col_num), - layer="metal2", + layer="m2", offset=offset, height=self.height-offset.y) @@ -137,7 +137,7 @@ class single_level_column_mux_array(design.design): for j in range(self.words_per_row): offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch) self.add_layout_pin(text="sel_{}".format(j), - layer="metal1", + layer="m1", offset=offset, width=self.mux.width * self.columns) @@ -155,7 +155,7 @@ class single_level_column_mux_array(design.design): # use the y offset from the sel pin and the x offset from the gate offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy()) # Add the poly contact with a shift to account for the rotation - self.add_via_center(layers=("metal1", "contact", "poly"), + self.add_via_center(layers=("m1", "contact", "poly"), offset=offset) self.add_path("poly", [offset, gate_offset]) @@ -176,16 +176,16 @@ class single_level_column_mux_array(design.design): # These will be located below the select lines. Yes, these are M2 width # to ensure vias are enclosed and M1 min width rules. width = self.m2_width + self.mux.width * (self.words_per_row - 1) - self.add_path("metal1", [bl_out_offset, bl_out_offset+vector(width,0)]) - self.add_path("metal1", [br_out_offset, br_out_offset+vector(width,0)]) + self.add_path("m1", [bl_out_offset, bl_out_offset+vector(width,0)]) + self.add_path("m1", [br_out_offset, br_out_offset+vector(width,0)]) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)), - layer="metal2", + layer="m2", start=bl_out_offset, end=bl_out_offset_end) self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)), - layer="metal2", + layer="m2", start=br_out_offset, end=br_out_offset_end) @@ -200,8 +200,8 @@ class single_level_column_mux_array(design.design): else: - self.add_path("metal2", [ bl_out_offset, bl_out_offset_end]) - self.add_path("metal2", [ br_out_offset, br_out_offset_end]) + self.add_path("m2", [ bl_out_offset, bl_out_offset_end]) + self.add_path("m2", [ br_out_offset, br_out_offset_end]) # This via is on the right of the wire self.add_via_center(layers=self.m1_stack, diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index 501e2389..e7ebd802 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -83,14 +83,14 @@ class tri_gate_array(design.design): in_pin = self.tri_inst[i].get_pin("in") self.add_layout_pin(text="in_{0}".format(index), - layer="metal2", + layer="m2", offset=in_pin.ll(), width=in_pin.width(), height=in_pin.height()) out_pin = self.tri_inst[i].get_pin("out") self.add_layout_pin(text="out_{0}".format(index), - layer="metal2", + layer="m2", offset=out_pin.ll(), width=out_pin.width(), height=out_pin.height()) @@ -103,21 +103,21 @@ class tri_gate_array(design.design): self.add_via_center(layers=self.m2_stack, offset=pin_pos) self.add_layout_pin_rect_center(text=n, - layer="metal3", + layer="m3", offset=pin_pos) width = self.tri.width * self.columns - (self.words_per_row - 1) * self.tri.width en_pin = self.tri_inst[0].get_pin("en") self.add_layout_pin(text="en", - layer="metal1", + layer="m1", offset=en_pin.ll().scale(0, 1), width=width, - height=drc("minwidth_metal1")) + height=drc("minwidth_m1")) enbar_pin = self.tri_inst[0].get_pin("en_bar") self.add_layout_pin(text="en_bar", - layer="metal1", + layer="m1", offset=enbar_pin.ll().scale(0, 1), width=width, - height=drc("minwidth_metal1")) \ No newline at end of file + height=drc("minwidth_m1")) \ No newline at end of file diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 99365659..2af2c8c9 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -142,7 +142,7 @@ class wordline_driver(design.design): # Wordline enable connection en_offset = [self.m1_width + 2 * self.m1_space, 0] en_pin = self.add_layout_pin(text="en", - layer="metal2", + layer="m2", offset=en_offset, width=self.m2_width, height=self.height) @@ -155,7 +155,7 @@ class wordline_driver(design.design): a_pin = nand_inst.get_pin("A") a_pos = a_pin.lc() clk_offset = vector(en_pin.bc().x, a_pos.y) - self.add_segment_center(layer="metal1", + self.add_segment_center(layer="m1", start=clk_offset, end=a_pos) self.add_via_center(layers=self.m1_stack, @@ -167,7 +167,7 @@ class wordline_driver(design.design): # ensure the bend is in the middle mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y) mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y) - self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos]) + self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos]) # connect the decoder input pin to nand2 B b_pin = nand_inst.get_pin("B") @@ -182,7 +182,7 @@ class wordline_driver(design.design): # must under the clk line in M1 self.add_layout_pin_segment_center(text="in_{0}".format(row), - layer="metal1", + layer="m1", start=input_offset, end=mid_via_offset) self.add_via_center(layers=self.m1_stack, @@ -190,7 +190,7 @@ class wordline_driver(design.design): directions=("V", "V")) # now connect to the nand2 B - self.add_path("metal2", [mid_via_offset, b_pos]) + self.add_path("m2", [mid_via_offset, b_pos]) contact_offset = b_pos - vector(0.5 * contact.m1m2.height, 0) self.add_via_center(layers=self.m1_stack, offset=contact_offset, @@ -199,7 +199,7 @@ class wordline_driver(design.design): # output each WL on the right wl_offset = inv2_inst.get_pin("Z").rc() self.add_layout_pin_segment_center(text="wl_{0}".format(row), - layer="metal1", + layer="m1", start=wl_offset, end=wl_offset - vector(self.m1_width, 0)) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 4dfa938c..ac4dab00 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -120,20 +120,20 @@ class write_driver_array(design.design): for i in range(self.word_size): din_pin = self.driver_insts[i].get_pin("din") self.add_layout_pin(text="data_{0}".format(i), - layer="metal2", + layer="m2", offset=din_pin.ll(), width=din_pin.width(), height=din_pin.height()) bl_pin = self.driver_insts[i].get_pin("bl") self.add_layout_pin(text="bl_{0}".format(i), - layer="metal2", + layer="m2", offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) br_pin = self.driver_insts[i].get_pin("br") self.add_layout_pin(text="br_{0}".format(i), - layer="metal2", + layer="m2", offset=br_pin.ll(), width=br_pin.width(), height=br_pin.height()) @@ -146,7 +146,7 @@ class write_driver_array(design.design): self.add_via_center(layers=self.m2_stack, offset=pin_pos) self.add_layout_pin_rect_center(text=n, - layer="metal3", + layer="m3", offset=pin_pos) if self.write_size: for bit in range(self.num_wmasks): @@ -165,7 +165,7 @@ class write_driver_array(design.design): height=en_pin.height()) else: self.add_layout_pin(text="en", - layer="metal1", + layer="m1", offset=self.driver_insts[0].get_pin("en").ll().scale(0,1), width=self.width) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 3a4ec651..40f386d2 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -114,7 +114,7 @@ class write_mask_and_array(design.design): # Extend metal3 to edge of AND array in multiport en_to_edge = self.and2.width - beg_en_pin.cx() self.add_layout_pin(text="en", - layer="metal3", + layer="m3", offset=beg_en_pin.bc(), width=end_en_pin.cx() - beg_en_pin.cx() + en_to_edge) self.add_via_center(layers=self.m1_stack, @@ -123,7 +123,7 @@ class write_mask_and_array(design.design): offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy())) else: self.add_layout_pin(text="en", - layer="metal3", + layer="m3", offset=beg_en_pin.bc(), width=end_en_pin.cx() - beg_en_pin.cx()) @@ -146,7 +146,7 @@ class write_mask_and_array(design.design): for n in ["gnd","vdd"]: pin = self.and2_insts[i].get_pin(n) next_pin = self.and2_insts[i+1].get_pin(n) - self.add_path("metal1",[pin.center(),next_pin.center()]) + self.add_path("m1",[pin.center(),next_pin.center()]) def get_cin(self): """Get the relative capacitance of all the input connections in the bank""" diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index d410a8c7..23fd5e5b 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -76,14 +76,14 @@ class pand2(pgate.pgate): a2_pin = self.inv_inst.get_pin("A") mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy()) mid2_point = vector(mid1_point, a2_pin.cy()) - self.add_path("metal1", + self.add_path("m1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) def add_layout_pins(self): # Continous vdd rail along with label. vdd_pin = self.inv_inst.get_pin("vdd") self.add_layout_pin(text="vdd", - layer="metal1", + layer="m1", offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) @@ -91,7 +91,7 @@ class pand2(pgate.pgate): # Continous gnd rail along with label. gnd_pin = self.inv_inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index b02e18e4..dd1d87f7 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -76,14 +76,14 @@ class pand3(pgate.pgate): a2_pin = self.inv_inst.get_pin("A") mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) mid2_point = vector(mid1_point, a2_pin.cy()) - self.add_path("metal1", + self.add_path("m1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) def add_layout_pins(self): # Continous vdd rail along with label. vdd_pin = self.inv_inst.get_pin("vdd") self.add_layout_pin(text="vdd", - layer="metal1", + layer="m1", offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) @@ -91,7 +91,7 @@ class pand3(pgate.pgate): # Continous gnd rail along with label. gnd_pin = self.inv_inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index 0149f75f..3171324b 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -78,13 +78,13 @@ class pbuf(pgate.pgate): z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") mid_point = vector(z1_pin.cx(), a2_pin.cy()) - self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) + self.add_path("m1", [z1_pin.center(), mid_point, a2_pin.center()]) def add_layout_pins(self): # Continous vdd rail along with label. vdd_pin = self.inv1_inst.get_pin("vdd") self.add_layout_pin(text="vdd", - layer="metal1", + layer="m1", offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) @@ -92,7 +92,7 @@ class pbuf(pgate.pgate): # Continous gnd rail along with label. gnd_pin = self.inv1_inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index bb7739b7..5aa5393e 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -140,7 +140,7 @@ class pdriver(pgate.pgate): z_inst_list.append(self.inv_inst_list[x].get_pin("Z")) a_inst_list.append(self.inv_inst_list[x + 1].get_pin("A")) mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy()) - self.add_path("metal1", + self.add_path("m1", [z_inst_list[x].center(), mid_point, a_inst_list[x].center()]) @@ -148,7 +148,7 @@ class pdriver(pgate.pgate): # Continous vdd rail along with label. vdd_pin = self.inv_inst_list[0].get_pin("vdd") self.add_layout_pin(text="vdd", - layer="metal1", + layer="m1", offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) @@ -156,7 +156,7 @@ class pdriver(pgate.pgate): # Continous gnd rail along with label. gnd_pin = self.inv_inst_list[0].get_pin("gnd") self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index b55ad9c3..d5f2395b 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -59,7 +59,7 @@ class pgate(design.design): debug.error("Invalid supply name.", -1) if abs(height) > 0: - self.add_rect(layer="metal1", + self.add_rect(layer="m1", offset=source_pin.ll(), height=height, width=source_pin.width()) @@ -120,7 +120,7 @@ class pgate(design.design): directions=directions) self.add_layout_pin_rect_center(text=name, - layer="metal1", + layer="m1", offset=contact_offset, width=contact_m1_width, height=contact_m1_height) @@ -172,7 +172,7 @@ class pgate(design.design): # To the right a spacing away from the pmos right active edge contact_xoffset = pmos_pos.x + pmos.active_width \ - + drc("active_to_body_active") + + self.active_space # Must be at least an well enclosure of active down # from the top of the well @@ -189,7 +189,7 @@ class pgate(design.design): directions=("H", "V"), implant_type="n", well_type="n") - self.add_rect_center(layer="metal1", + self.add_rect_center(layer="m1", offset=contact_offset + vector(0, 0.5 * (self.height-contact_offset.y)), width=self.nwell_contact.mod.second_layer_width, height=self.height - contact_offset.y) @@ -227,7 +227,7 @@ class pgate(design.design): # To the right a spacing away from the nmos right active edge contact_xoffset = nmos_pos.x + nmos.active_width \ - + drc("active_to_body_active") + + self.active_space # Must be at least an well enclosure of active up # from the bottom of the well contact_yoffset = max(nmos_pos.y, @@ -243,7 +243,7 @@ class pgate(design.design): directions=("H", "V"), implant_type="p", well_type="p") - self.add_rect_center(layer="metal1", + self.add_rect_center(layer="m1", offset=contact_offset.scale(1,0.5), width=self.pwell_contact.mod.second_layer_width, height=contact_offset.y) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 5c53480b..1ff5b67b 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -96,14 +96,14 @@ class pinv(pgate.pgate): tx_height = nmos.poly_height + pmos.poly_height # rotated m1 pitch or poly to active spacing min_channel = max(contact.poly.width + self.m1_space, - contact.poly.width + 2 * drc("poly_to_active")) + contact.poly.width + 2 * self.poly_to_active) # This is the extra space needed to ensure DRC rules # to the active contacts extra_contact_space = max(-nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - drc("poly_extend_active"), self.poly_space) + self.poly_extend_active, self.poly_space) total_height = tx_height + min_channel + 2 * self.top_bottom_space debug.check(self.height > total_height, "Cell height {0} too small for simple min height {1}.".format(self.height, @@ -153,7 +153,7 @@ class pinv(pgate.pgate): # the well width is determined the multi-finger PMOS device width plus # the well contact width and half well enclosure on both sides self.well_width = self.pmos.active_width + self.pmos.active_contact.width \ - + drc("active_to_body_active") + 2*drc("well_enclosure_active") + + self.active_space + 2*self.well_enclose_active self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -178,12 +178,12 @@ class pinv(pgate.pgate): def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", - layer="metal1", + layer="m1", offset=vector(0.5 * self.width, 0), width=self.width) self.add_layout_pin_rect_center(text="vdd", - layer="metal1", + layer="m1", offset=vector(0.5 * self.width, self.height), width=self.width) @@ -238,7 +238,7 @@ class pinv(pgate.pgate): # Pick point at right most of NMOS and connect down to PMOS nmos_drain_pos = nmos_drain_pin.bc() pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y) - self.add_path("metal1", [nmos_drain_pos, pmos_drain_pos]) + self.add_path("m1", [nmos_drain_pos, pmos_drain_pos]) # Remember the mid for the output mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y) @@ -247,13 +247,13 @@ class pinv(pgate.pgate): # This extends the output to the edge of the cell output_offset = mid_drain_offset.scale(0, 1) + vector(self.width, 0) self.add_layout_pin_segment_center(text="Z", - layer="metal1", + layer="m1", start=mid_drain_offset, end=output_offset) else: # This leaves the output as an internal pin (min sized) self.add_layout_pin_rect_center(text="Z", - layer="metal1", + layer="m1", offset=mid_drain_offset \ + vector(0.5 * self.m1_width, 0)) diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index 579e1198..b8ecb3bf 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -114,13 +114,13 @@ class pinvbuf(pgate.pgate): z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") mid_point = vector(z1_pin.cx(), a2_pin.cy()) - self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) + self.add_path("m1", [z1_pin.center(), mid_point, a2_pin.center()]) # inv2 Z to inv3 A z2_pin = self.inv2_inst.get_pin("Z") a3_pin = self.inv3_inst.get_pin("A") mid_point = vector(z2_pin.cx(), a3_pin.cy()) - self.add_path("metal1", [z2_pin.center(), mid_point, a3_pin.center()]) + self.add_path("m1", [z2_pin.center(), mid_point, a3_pin.center()]) # inv1 Z to inv4 A (up and over) z1_pin = self.inv1_inst.get_pin("Z") @@ -136,7 +136,7 @@ class pinvbuf(pgate.pgate): # Continous vdd rail along with label. vdd_pin = self.inv1_inst.get_pin("vdd") self.add_layout_pin(text="vdd", - layer="metal1", + layer="m1", offset=vdd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) @@ -144,7 +144,7 @@ class pinvbuf(pgate.pgate): # Continous vdd rail along with label. gnd_pin = self.inv4_inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=gnd_pin.ll().scale(0, 1), width=self.width, height=gnd_pin.height()) @@ -152,28 +152,28 @@ class pinvbuf(pgate.pgate): # Continous gnd rail along with label. gnd_pin = self.inv1_inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=gnd_pin.ll().scale(0, 1), width=self.width, height=vdd_pin.height()) z_pin = self.inv4_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Z", - layer="metal2", + layer="m2", offset=z_pin.center()) self.add_via_center(layers=self.m1_stack, offset=z_pin.center()) zb_pin = self.inv3_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Zb", - layer="metal2", + layer="m2", offset=zb_pin.center()) self.add_via_center(layers=self.m1_stack, offset=zb_pin.center()) a_pin = self.inv1_inst.get_pin("A") self.add_layout_pin_rect_center(text="A", - layer="metal2", + layer="m2", offset=a_pin.center()) self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 4623048c..8acde2ce 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -99,8 +99,8 @@ class pnand2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 2 * self.pmos.active_width + contact.active.width \ - + 2 * drc("active_to_body_active") \ - + 2 * drc("well_enclosure_active") + + 2 * self.active_space \ + + 2 * self.well_enclose_active self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -110,17 +110,17 @@ class pnand2(pgate.pgate): extra_contact_space = max(-self.nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space, - drc("poly_extend_active"), self.poly_space) + self.poly_extend_active, self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", - layer="metal1", + layer="m1", offset=vector(0.5*self.width, 0), width=self.width) self.add_layout_pin_rect_center(text="vdd", - layer="metal1", + layer="m1", offset=vector(0.5 * self.width, self.height), width=self.width) @@ -235,13 +235,13 @@ class pnand2(pgate.pgate): offset=out_offset) # PMOS1 to mid-drain to NMOS2 drain - self.add_path("metal2", + self.add_path("m2", [top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset]) # This extends the output to the edge of the cell self.add_layout_pin_rect_center(text="Z", - layer="metal1", + layer="m1", offset=out_offset, width=contact.m1m2.first_layer_height, height=contact.m1m2.first_layer_width) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 6d885e25..7a773497 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -92,7 +92,7 @@ class pnand3(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \ - + 2 * drc("active_to_body_active") + 2 * drc("well_enclosure_active") \ + + 2 * self.active_space + 2 * self.well_enclose_active \ - self.overlap_offset.x self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -107,18 +107,18 @@ class pnand3(pgate.pgate): # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space \ + extra_contact_space, - drc("poly_extend_active"), + self.poly_extend_active, self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", - layer="metal1", + layer="m1", offset=vector(0.5 * self.width, 0), width=self.width) self.add_layout_pin_rect_center(text="vdd", - layer="metal1", + layer="m1", offset=vector(0.5 * self.width, self.height), width=self.width) @@ -205,7 +205,7 @@ class pnand3(pgate.pgate): self.m1_space + 0.5 *contact.poly.width + 0.5 * self.m1_width) active_spacing = max(self.m1_space, - 0.5 * contact.poly.first_layer_width + drc("poly_to_active")) + 0.5 * contact.poly.first_layer_width + self.poly_to_active) inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing self.route_input_gate(self.pmos3_inst, self.nmos3_inst, @@ -245,16 +245,16 @@ class pnand3(pgate.pgate): offset=nmos3_pin.center()) # PMOS3 and NMOS3 are drain aligned - self.add_path("metal2", [pmos3_pin.bc(), nmos3_pin.uc()]) + self.add_path("m2", [pmos3_pin.bc(), nmos3_pin.uc()]) # Route in the A input track (top track) mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) - self.add_path("metal2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) + self.add_path("m2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) # This extends the output to the edge of the cell self.add_via_center(layers=self.m1_stack, offset=mid_offset) self.add_layout_pin_rect_center(text="Z", - layer="metal1", + layer="m1", offset=mid_offset, width=contact.m1m2.first_layer_width, height=contact.m1m2.first_layer_height) diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index a79d19a0..d75df0a9 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -97,9 +97,8 @@ class pnor2(pgate.pgate): # Enclosure space on the sides. self.well_width = 2 * self.pmos.active_width \ + self.pmos.active_contact.width \ - + 2 * drc("active_to_body_active") \ - + 2 * drc("well_enclosure_active") - + + 2 * self.active_space \ + + 2 * self.well_enclose_active self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -108,18 +107,18 @@ class pnor2(pgate.pgate): extra_contact_space = max(-self.nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space, - drc("poly_extend_active"), + self.poly_extend_active, self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", - layer="metal1", + layer="m1", offset=vector(0.5 * self.width, 0), width=self.width) self.add_layout_pin_rect_center(text="vdd", - layer="metal1", + layer="m1", offset=vector(0.5 * self.width, self.height), width=self.width) @@ -226,15 +225,15 @@ class pnor2(pgate.pgate): mid3_offset = mid2_offset + vector(self.m2_width, 0) # PMOS1 to mid-drain to NMOS2 drain - self.add_path("metal2", + self.add_path("m2", [pmos_pin.bc(), mid2_offset, mid3_offset]) - self.add_path("metal2", + self.add_path("m2", [nmos_pin.rc(), mid1_offset, mid2_offset]) # This extends the output to the edge of the cell self.add_via_center(layers=self.m1_stack, offset=mid3_offset) self.add_layout_pin_rect_center(text="Z", - layer="metal1", + layer="m1", offset=mid3_offset, width=contact.m1m2.first_layer_height, height=contact.m1m2.first_layer_width) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 9641a1f3..784bb418 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -72,7 +72,7 @@ class precharge(design.design): # Adds the rail across the width of the cell vdd_position = vector(0.5 * self.width, self.height) - self.add_rect_center(layer="metal1", + self.add_rect_center(layer="m1", offset=vdd_position, width=self.width, height=self.m1_width) @@ -80,7 +80,7 @@ class precharge(design.design): pmos_pin = self.upper_pmos2_inst.get_pin("S") # center of vdd rail pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) - self.add_path("metal1", [pmos_pin.uc(), pmos_vdd_pos]) + self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos]) # Add vdd pin above the transistor self.add_power_pin("vdd", pmos_pin.center(), vertical=True) @@ -164,7 +164,7 @@ class precharge(design.design): # adds the en rail on metal1 self.add_layout_pin_segment_center(text="en_bar", - layer="metal1", + layer="m1", start=offset.scale(0, 1), end=offset.scale(0, 1) + vector(self.width, 0)) @@ -176,7 +176,7 @@ class precharge(design.design): # adds the contact from active to metal1 well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ + vector(0, self.upper_pmos1_inst.uy() + contact.well.height / 2 \ - + drc("well_extend_active")) + + self.well_extend_active) self.add_via_center(layers=self.active_stack, offset=well_contact_pos, implant_type="n", @@ -200,7 +200,7 @@ class precharge(design.design): offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(), 0) \ - vector(0.5 * self.m2_width, 0) self.bl_pin = self.add_layout_pin(text="bl", - layer="metal2", + layer="m2", offset=offset, height=self.height) @@ -208,7 +208,7 @@ class precharge(design.design): offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(), 0) \ - vector(0.5 * self.m2_width, 0) self.br_pin = self.add_layout_pin(text="br", - layer="metal2", + layer="m2", offset=offset, height=self.height) @@ -258,7 +258,7 @@ class precharge(design.design): left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) - self.add_path("metal1", [left_pos, right_pos] ) + self.add_path("m1", [left_pos, right_pos] ) def connect_pmos_m2(self, pmos_pin, bit_pin): """ @@ -268,7 +268,7 @@ class precharge(design.design): left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) - self.add_path("metal2", [left_pos, right_pos], self.bl_contact.height) + self.add_path("m2", [left_pos, right_pos], self.bl_contact.height) def get_en_cin(self): """Get the relative capacitance of the enable in the precharge cell""" diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py index 4a17b46a..5c0028b2 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/pgates/ptristate_inv.py @@ -73,7 +73,7 @@ class ptristate_inv(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. - self.well_width = 2 * self.pmos.active_width + drc("well_enclosure_active") + self.well_width = 2 * self.pmos.active_width + self.well_enclose_active # Add an extra space because we route the output on the right of the S/D self.width = self.well_width + 0.5 * self.m1_space @@ -101,12 +101,12 @@ class ptristate_inv(pgate.pgate): def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ self.add_layout_pin_rect_center(text="gnd", - layer="metal1", + layer="m1", offset=vector(0.5 * self.width, 0), width=self.width) self.add_layout_pin_rect_center(text="vdd", - layer="metal1", + layer="m1", offset=vector(0.5 * self.width, self.height), width=self.width) @@ -181,7 +181,7 @@ class ptristate_inv(pgate.pgate): pmos_drain_pos = pmos_drain_pin.ur() self.add_layout_pin(text="out", - layer="metal1", + layer="m1", offset=nmos_drain_pos, height=pmos_drain_pos.y - nmos_drain_pos.y) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 61610a88..9907485f 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -91,8 +91,8 @@ class ptx(design.design): # " ".join(self.pins))) # Just make a guess since these will actually # be decided in the layout later. - area_sd = 2.5 * drc("minwidth_poly") * self.tx_width - perimeter_sd = 2 * drc("minwidth_poly") + 2 * self.tx_width + area_sd = 2.5 * self.poly_width * self.tx_width + perimeter_sd = 2 * self.poly_width + 2 * self.tx_width main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], self.mults, self.tx_width, @@ -136,7 +136,7 @@ class ptx(design.design): self.contact_pitch = 2 * self.contact_to_gate + self.contact_width + self.poly_width # The enclosure of an active contact. Not sure about second term. - active_enclose_contact = max(drc("active_enclosure_contact"), + active_enclose_contact = max(self.active_enclose_contact, (self.active_width - self.contact_width) / 2) # This is the distance from the edge of poly to the contacted end of active @@ -180,11 +180,11 @@ class ptx(design.design): # Min area results are just flagged for now. - debug.check(self.active_width * self.active_height >= drc("minarea_active"), + debug.check(self.active_width * self.active_height >= self.minarea_active, "Minimum active area violated.") # We do not want to increase the poly dimensions to fix # an area problem as it would cause an LVS issue. - debug.check(self.poly_width * self.poly_height >= drc("minarea_poly"), + debug.check(self.poly_width * self.poly_height >= self.minarea_poly, "Minimum poly area violated.") def connect_fingered_poly(self, poly_positions): @@ -219,7 +219,7 @@ class ptx(design.design): layer="poly", offset=poly_offset, width=poly_width, - height=drc("minwidth_poly")) + height=self.poly_width) def connect_fingered_active(self, drain_positions, source_positions): @@ -249,12 +249,12 @@ class ptx(design.design): self.remove_layout_pin("S") # remove the individual connections # Add each vertical segment for a in source_positions: - self.add_path(("metal1"), + self.add_path(("m1"), [a, a + pin_offset.scale(source_dir, source_dir)]) # Add a single horizontal pin self.add_layout_pin_segment_center(text="S", - layer="metal1", + layer="m1", start=source_positions[0] + source_offset - end_offset, end=source_positions[-1] + source_offset + end_offset) @@ -263,10 +263,10 @@ class ptx(design.design): self.remove_layout_pin("D") # remove the individual connections # Add each vertical segment for a in drain_positions: - self.add_path(("metal1"), [a,a+drain_offset]) + self.add_path(("m1"), [a,a+drain_offset]) # Add a single horizontal pin self.add_layout_pin_segment_center(text="D", - layer="metal1", + layer="m1", start=drain_positions[0] + drain_offset - end_offset, end=drain_positions[-1] + drain_offset + end_offset) @@ -313,7 +313,7 @@ class ptx(design.design): height=self.active_height) # If the implant must enclose the active, shift offset # and increase width/height - enclose_width = drc("implant_enclosure_active") + enclose_width = self.implant_enclose_active enclose_offset = [enclose_width] * 2 self.add_rect(layer="{}implant".format(self.implant_type), offset=self.active_offset - enclose_offset, @@ -380,7 +380,7 @@ class ptx(design.design): implant_type=self.implant_type, well_type=self.well_type) self.add_layout_pin_rect_center(text="S", - layer="metal1", + layer="m1", offset=pos, width=contact.mod.second_layer_width, height=contact.mod.second_layer_height) @@ -394,7 +394,7 @@ class ptx(design.design): implant_type=self.implant_type, well_type=self.well_type) self.add_layout_pin_rect_center(text="D", - layer="metal1", + layer="m1", offset=pos, width=contact.mod.second_layer_width, height=contact.mod.second_layer_height) diff --git a/compiler/pgates/pwrite_driver.py b/compiler/pgates/pwrite_driver.py index a808737b..2dc356c3 100644 --- a/compiler/pgates/pwrite_driver.py +++ b/compiler/pgates/pwrite_driver.py @@ -131,26 +131,26 @@ class pwrite_driver(design.design): bl_xoffset = left_x bl_out=vector(bl_xoffset, self.height) bl_in=self.bl_inst.get_pin("out").center() - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=bl_in) bl_mid = vector(bl_out.x,bl_in.y) - self.add_path("metal2", [bl_in, bl_mid, bl_out]) + self.add_path("m2", [bl_in, bl_mid, bl_out]) self.add_layout_pin_rect_center(text="bl", - layer="metal2", + layer="m2", offset=bl_out) br_xoffset = right_x br_out=vector(br_xoffset, self.height) br_in=self.br_inst.get_pin("out").center() - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_via_center(layers=self.m1_stack, offset=br_in) br_mid = vector(br_out.x,br_in.y) - self.add_path("metal2", [br_in, br_mid, br_out]) + self.add_path("m2", [br_in, br_mid, br_out]) self.add_layout_pin_rect_center(text="br", - layer="metal2", + layer="m2", offset=br_out) #br_xoffset = b.get_pin("br".cx() @@ -162,19 +162,19 @@ class pwrite_driver(design.design): track_xoff = self.get_m2_track(1) din_loc = self.din_inst.get_pin("A").center() - self.add_via_stack("metal1", "metal2", din_loc) + self.add_via_stack("m1", "m2", din_loc) din_track = vector(track_xoff,din_loc.y) br_in = self.br_inst.get_pin("in").center() - self.add_via_stack("metal1", "metal2", br_in) + self.add_via_stack("m1", "m2", br_in) br_track = vector(track_xoff,br_in.y) din_in = vector(track_xoff,0) - self.add_path("metal2", [din_in, din_track, din_loc, din_track, br_track, br_in]) + self.add_path("m2", [din_in, din_track, din_loc, din_track, br_track, br_in]) self.add_layout_pin_rect_center(text="din", - layer="metal2", + layer="m2", offset=din_in) def route_din_bar(self): @@ -183,19 +183,19 @@ class pwrite_driver(design.design): track_xoff = self.get_m4_track(self.din_bar_track) din_bar_in = self.din_inst.get_pin("Z").center() - self.add_via_stack("metal1", "metal3", din_bar_in) + self.add_via_stack("m1", "m3", din_bar_in) din_bar_track = vector(track_xoff,din_bar_in.y) bl_in = self.bl_inst.get_pin("in").center() - self.add_via_stack("metal1", "metal3", bl_in) + self.add_via_stack("m1", "m3", bl_in) bl_track = vector(track_xoff,bl_in.y) din_in = vector(track_xoff,0) - self.add_wire(("metal3","via3","metal4"), [din_bar_in, din_bar_track, bl_track, bl_in]) + self.add_wire(self.m3_stack, [din_bar_in, din_bar_track, bl_track, bl_in]) self.add_layout_pin_rect_center(text="din", - layer="metal4", + layer="m4", offset=din_in) @@ -206,22 +206,22 @@ class pwrite_driver(design.design): # This M2 pitch is a hack since the A and Z pins align horizontally en_bar_loc = self.en_inst.get_pin("Z").uc() en_bar_track = vector(track_xoff, en_bar_loc.y) - self.add_via_stack("metal1", "metal3", en_bar_loc) + self.add_via_stack("m1", "m3", en_bar_loc) # This is a U route to the right down then left bl_en_loc = self.bl_inst.get_pin("en_bar").center() bl_en_track = vector(track_xoff, bl_en_loc.y) - self.add_via_stack("metal1", "metal3", bl_en_loc) + self.add_via_stack("m1", "m3", bl_en_loc) br_en_loc = self.br_inst.get_pin("en_bar").center() br_en_track = vector(track_xoff, bl_en_loc.y) - self.add_via_stack("metal1", "metal3", br_en_loc) + self.add_via_stack("m1", "m3", br_en_loc) # L shape - self.add_wire(("metal3","via3","metal4"), + self.add_wire(self.m3_stack, [en_bar_loc, en_bar_track, bl_en_track]) # U shape - self.add_wire(("metal3","via3","metal4"), + self.add_wire(self.m3_stack, [bl_en_loc, bl_en_track, br_en_track, br_en_loc]) @@ -233,30 +233,30 @@ class pwrite_driver(design.design): # The en pin will be over the vdd rail vdd_yloc = self.en_inst.get_pin("vdd").cy() self.add_layout_pin_segment_center(text="en", - layer="metal3", + layer="m3", start=vector(0,vdd_yloc), end=vector(self.width,vdd_yloc)) en_loc = self.en_inst.get_pin("A").center() en_rail = vector(en_loc.x, vdd_yloc) - self.add_via_stack("metal1", "metal2", en_loc) - self.add_path("metal2", [en_loc, en_rail]) - self.add_via_stack("metal2", "metal3", en_rail) + self.add_via_stack("m1", "m2", en_loc) + self.add_path("m2", [en_loc, en_rail]) + self.add_via_stack("m2", "m3", en_rail) # Start point in the track on the pin rail en_track = vector(track_xoff, vdd_yloc) - self.add_via_stack("metal3", "metal4", en_track) + self.add_via_stack("m3", "m4", en_track) # This is a U route to the right down then left bl_en_loc = self.bl_inst.get_pin("en").center() bl_en_track = vector(track_xoff, bl_en_loc.y) - self.add_via_stack("metal1", "metal3", bl_en_loc) + self.add_via_stack("m1", "m3", bl_en_loc) br_en_loc = self.br_inst.get_pin("en").center() br_en_track = vector(track_xoff, bl_en_loc.y) - self.add_via_stack("metal1", "metal3", br_en_loc) + self.add_via_stack("m1", "m3", br_en_loc) # U shape - self.add_wire(("metal3","via3","metal4"), + self.add_wire(self.m3_stack, [en_track, bl_en_track, bl_en_loc, bl_en_track, br_en_track, br_en_loc]) @@ -284,7 +284,7 @@ class pwrite_driver(design.design): # Continous vdd rail along with label. vdd_pin=inst.get_pin("vdd") self.add_layout_pin(text="vdd", - layer="metal1", + layer="m1", offset=vdd_pin.ll().scale(0,1), width=self.width, height=vdd_pin.height()) @@ -292,7 +292,7 @@ class pwrite_driver(design.design): # Continous gnd rail along with label. gnd_pin=inst.get_pin("gnd") self.add_layout_pin(text="gnd", - layer="metal1", + layer="m1", offset=gnd_pin.ll().scale(0,1), width=self.width, height=vdd_pin.height()) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 525c1680..18b4b10f 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -65,21 +65,21 @@ class single_level_column_mux(pgate.pgate): # bl and br self.add_layout_pin(text="bl", - layer="metal2", + layer="m2", offset=bl_pos + vector(0, self.height - self.pin_height), height=self.pin_height) self.add_layout_pin(text="br", - layer="metal2", + layer="m2", offset=br_pos + vector(0, self.height - self.pin_height), height=self.pin_height) # bl_out and br_out self.add_layout_pin(text="bl_out", - layer="metal2", + layer="m2", offset=bl_pos, height=self.pin_height) self.add_layout_pin(text="br_out", - layer="metal2", + layer="m2", offset=br_pos, height=self.pin_height) @@ -141,7 +141,7 @@ class single_level_column_mux(pgate.pgate): # bl -> nmos_upper/D on metal1 # bl_out -> nmos_upper/S on metal2 - self.add_path("metal1", + self.add_path("m1", [bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()), nmos_upper_d_pin.center()]) # halfway up, move over @@ -149,12 +149,12 @@ class single_level_column_mux(pgate.pgate): + nmos_upper_s_pin.bc().scale(0, 0.4) mid2 = bl_out_pin.uc().scale(0, 0.4) \ + nmos_upper_s_pin.bc().scale(1, 0.4) - self.add_path("metal2", + self.add_path("m2", [bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()]) # br -> nmos_lower/D on metal2 # br_out -> nmos_lower/S on metal1 - self.add_path("metal1", + self.add_path("m1", [br_out_pin.uc(), vector(nmos_lower_s_pin.cx(), br_out_pin.uy()), nmos_lower_s_pin.center()]) @@ -163,7 +163,7 @@ class single_level_column_mux(pgate.pgate): + nmos_lower_d_pin.uc().scale(0,0.5) mid2 = br_pin.bc().scale(0,0.5) \ + nmos_lower_d_pin.uc().scale(1,0.5) - self.add_path("metal2", + self.add_path("m2", [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()]) def add_wells(self): @@ -186,7 +186,7 @@ class single_level_column_mux(pgate.pgate): self.add_via_center(layers=self.m2_stack, offset=active_pos) self.add_layout_pin_rect_center(text="gnd", - layer="metal3", + layer="m3", offset=active_pos) # Add well enclosure over all the tx and contact diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 6c887790..151c8a70 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -261,18 +261,18 @@ class sram_1bank(sram_base): # This is the steiner point where the net branches out clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y) - self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos]) - self.add_via_center(layers=("metal1","via1","metal2"), + self.add_path("m1", [control_clk_buf_pos, clk_steiner_pos]) + self.add_via_center(layers=self.m1_stack, offset=clk_steiner_pos) # Note, the via to the control logic is taken care of above - self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos]) + self.add_wire(("m3","via2","m2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos]) if self.col_addr_dff: dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") dff_clk_pos = dff_clk_pin.center() mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) - self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, clk_steiner_pos]) + self.add_wire(("m3","via2","m2"),[dff_clk_pos, mid_pos, clk_steiner_pos]) if port in self.write_ports: data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") @@ -280,8 +280,8 @@ class sram_1bank(sram_base): mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y) # In some designs, the steiner via will be too close to the mid_pos via # so make the wire as wide as the contacts - self.add_path("metal2",[mid_pos, clk_steiner_pos], width=max(m2m3.width,m2m3.height)) - self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos]) + self.add_path("m2",[mid_pos, clk_steiner_pos], width=max(m2m3.width,m2m3.height)) + self.add_wire(("m3","via2","m2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos]) if self.write_size: wmask_dff_clk_pin = self.wmask_dff_insts[port].get_pin("clk") @@ -289,8 +289,8 @@ class sram_1bank(sram_base): mid_pos = vector(clk_steiner_pos.x, wmask_dff_clk_pos.y) # In some designs, the steiner via will be too close to the mid_pos via # so make the wire as wide as the contacts - self.add_path("metal2", [mid_pos, clk_steiner_pos], width=max(m2m3.width, m2m3.height)) - self.add_wire(("metal3", "via2", "metal2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos]) + self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2m3.width, m2m3.height)) + self.add_wire(("m3", "via2", "m2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos]) def route_control_logic(self): @@ -323,8 +323,8 @@ class sram_1bank(sram_base): flop_pos = flop_pin.center() bank_pos = bank_pin.center() mid_pos = vector(bank_pos.x,flop_pos.y) - self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos]) - self.add_via_center(layers=("metal2","via2","metal3"), + self.add_wire(("m3","via2","m2"),[flop_pos, mid_pos,bank_pos]) + self.add_via_center(layers=self.m2_stack, offset=flop_pos) def route_col_addr_dff(self): @@ -336,7 +336,7 @@ class sram_1bank(sram_base): offset = self.col_addr_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)] - col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1", + col_addr_bus_offsets = self.create_horizontal_bus(layer="m1", pitch=self.m1_pitch, offset=offset, names=bus_names, diff --git a/compiler/sram/sram_2bank.py b/compiler/sram/sram_2bank.py index e1224795..dace1ed9 100644 --- a/compiler/sram/sram_2bank.py +++ b/compiler/sram/sram_2bank.py @@ -103,25 +103,25 @@ class sram_2bank(sram_base): for n in self.control_logic_outputs + ["vdd", "gnd"]: pins = self.control_logic_inst.get_pins(n) for pin in pins: - if pin.layer=="metal2": + if pin.layer=="m2": pin_pos = pin.bc() break rail_pos = vector(pin_pos.x,self.horz_control_bus_positions[n].y) - self.add_path("metal2",[pin_pos,rail_pos]) - self.add_via_center(("metal1","via1","metal2"),rail_pos) + self.add_path("m2",[pin_pos,rail_pos]) + self.add_via_center(self.m1_stack,rail_pos) # connect the control logic cross bar for n in self.control_logic_outputs: cross_pos = vector(self.vert_control_bus_positions[n].x,self.horz_control_bus_positions[n].y) - self.add_via_center(("metal1","via1","metal2"),cross_pos) + self.add_via_center(self.m1_stack,cross_pos) # connect the bank select signals to the vertical bus for i in range(self.num_banks): pin = self.bank_inst[i].get_pin("bank_sel") pin_pos = pin.rc() if i==0 else pin.lc() rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,pin_pos.y) - self.add_path("metal3",[pin_pos,rail_pos]) - self.add_via_center(("metal2","via2","metal3"),rail_pos) + self.add_path("m3",[pin_pos,rail_pos]) + self.add_via_center(self.m2_stack,rail_pos) def route_single_msb_address(self): """ Route one MSB address bit for 2-bank SRAM """ @@ -129,37 +129,37 @@ class sram_2bank(sram_base): # connect the bank MSB flop supplies vdd_pins = self.msb_address_inst.get_pins("vdd") for vdd_pin in vdd_pins: - if vdd_pin.layer != "metal1": continue + if vdd_pin.layer != "m1": continue vdd_pos = vdd_pin.bc() down_pos = vdd_pos - vector(0,self.m1_pitch) rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y) - self.add_path("metal1",[vdd_pos,down_pos]) - self.add_via_center(("metal1","via1","metal2"),down_pos,rotate=90) - self.add_path("metal2",[down_pos,rail_pos]) - self.add_via_center(("metal1","via1","metal2"),rail_pos) + self.add_path("m1",[vdd_pos,down_pos]) + self.add_via_center(self.m1_stack,down_pos,rotate=90) + self.add_path("m2",[down_pos,rail_pos]) + self.add_via_center(self.m1_stack,rail_pos) gnd_pins = self.msb_address_inst.get_pins("gnd") # Only add the ground connection to the lowest metal2 rail in the flop array # FIXME: SCMOS doesn't have a vertical rail in the cell, or we could use those lowest_y = None for gnd_pin in gnd_pins: - if gnd_pin.layer != "metal2": continue + if gnd_pin.layer != "m2": continue if lowest_y==None or gnd_pin.by() Date: Tue, 17 Dec 2019 11:23:59 -0800 Subject: [PATCH 022/103] Fix over-writing of active spacing rule. --- compiler/base/design.py | 44 ++++++++++++++++--------------- technology/freepdk45/tech/tech.py | 5 ++-- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/compiler/base/design.py b/compiler/base/design.py index 3b6ae914..2b11111d 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -81,27 +81,29 @@ class design(hierarchy_design): setattr(self, match.group(0), drc(match.group(0))) # These are for debugging previous manual rules - # print("poly_width", self.poly_width) - # print("poly_space", self.poly_space) - # print("m1_width", self.m1_width) - # print("m1_space", self.m1_space) - # print("m2_width", self.m2_width) - # print("m2_space", self.m2_space) - # print("m3_width", self.m3_width) - # print("m3_space", self.m3_space) - # print("m4_width", self.m4_width) - # print("m4_space", self.m4_space) - # print("active_width", self.active_width) - # print("active_space", self.active_space) - # print("contact_width", self.contact_width) - # print("poly_to_active", self.poly_to_active) - # print("poly_extend_active", self.poly_extend_active) - # print("poly_to_contact", self.poly_to_contact) - # print("contact_to_gate", self.contact_to_gate) - # print("well_enclose_active", self.well_enclose_active) - # print("implant_enclose_active", self.implant_enclose_active) - # print("implant_space", self.implant_space) - + if False: + print("poly_width", self.poly_width) + print("poly_space", self.poly_space) + print("m1_width", self.m1_width) + print("m1_space", self.m1_space) + print("m2_width", self.m2_width) + print("m2_space", self.m2_space) + print("m3_width", self.m3_width) + print("m3_space", self.m3_space) + print("m4_width", self.m4_width) + print("m4_space", self.m4_space) + print("active_width", self.active_width) + print("active_space", self.active_space) + print("contact_width", self.contact_width) + print("poly_to_active", self.poly_to_active) + print("poly_extend_active", self.poly_extend_active) + print("poly_to_contact", self.poly_to_contact) + print("contact_to_gate", self.contact_to_gate) + print("well_enclose_active", self.well_enclose_active) + print("implant_enclose_active", self.implant_enclose_active) + print("implant_space", self.implant_space) + import sys; sys.exit(1) + def setup_multiport_constants(self): """ These are contants and lists that aid multiport design. diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 78b031c4..d87e98f1 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -152,12 +152,11 @@ drc["poly_to_field_poly"] = 0.075 # Not a rule drc["minarea_poly"] = 0.0 -# ACTIVE.2 Minimum spacing of active -drc["active_to_active"] = 0.08 # ACTIVE.1 Minimum width of active +# ACTIVE.2 Minimum spacing of active drc.add_layer("active", width = 0.09, - spacing = 0) + spacing = 0.08) # ACTIVE.3 Minimum enclosure/spacing of nwell/pwell to active drc.add_enclosure("well", layer = "active", From c025ce63563ee64b617fa054f28392ff7388fbc7 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 17 Dec 2019 14:06:23 -0800 Subject: [PATCH 023/103] Add li to preferred direction --- compiler/base/hierarchy_layout.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 7f23e942..95b27618 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -390,10 +390,10 @@ class layout(): """ Return the preferred routing directions """ if layer in ["m1", "m3", "m5"]: return "H" - elif layer in ["active", "poly", "m2", "m4"]: + elif layer in ["active", "poly", "li", "m2", "m4"]: return "V" else: - return "N" + debug.error("Unable to find preferred direction for {}".format(layer)) def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None): From 449b0a7c28d8b4be4c8b17e675214ebecfe66703 Mon Sep 17 00:00:00 2001 From: Matthew Guthaus Date: Tue, 17 Dec 2019 22:36:38 +0000 Subject: [PATCH 024/103] Make wire test programmatic --- compiler/tests/03_wire_test.py | 121 +++++++-------------------------- 1 file changed, 23 insertions(+), 98 deletions(-) diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py index ae4f8499..bd4b9345 100755 --- a/compiler/tests/03_wire_test.py +++ b/compiler/tests/03_wire_test.py @@ -22,107 +22,32 @@ class wire_test(openram_test): import wire import tech import design - - min_space = 2 * (tech.drc["minwidth_poly"] + - tech.drc["minwidth_m1"]) - layer_stack = tech.poly_stack - old_position_list = [[0, 0], - [0, 3 * min_space], - [1 * min_space, 3 * min_space], - [4 * min_space, 3 * min_space], - [4 * min_space, 0], - [7 * min_space, 0], - [7 * min_space, 4 * min_space], - [-1 * min_space, 4 * min_space], - [-1 * min_space, 0]] - position_list = [[x-min_space, y-min_space] for x,y in old_position_list] - w = design.design("wire_test1") - wire.wire(w, layer_stack, position_list) - self.local_drc_check(w) - min_space = 2 * (tech.drc["minwidth_poly"] + - tech.drc["minwidth_m1"]) - layer_stack = tech.poly_stack - old_position_list = [[0, 0], - [0, 3 * min_space], - [1 * min_space, 3 * min_space], - [4 * min_space, 3 * min_space], - [4 * min_space, 0], - [7 * min_space, 0], - [7 * min_space, 4 * min_space], - [-1 * min_space, 4 * min_space], - [-1 * min_space, 0]] - position_list = [[x+min_space, y+min_space] for x,y in old_position_list] - w = design.design("wire_test2") - wire.wire(w, layer_stack, position_list) - self.local_drc_check(w) + layer_stacks = [tech.poly_stack] + tech.beol_stacks - min_space = 2 * (tech.drc["minwidth_m2"] + - tech.drc["minwidth_m1"]) - layer_stack = tech.m1_stack - position_list = [[0, 0], - [0, 3 * min_space], - [1 * min_space, 3 * min_space], - [4 * min_space, 3 * min_space], - [4 * min_space, 0], - [7 * min_space, 0], - [7 * min_space, 4 * min_space], - [-1 * min_space, 4 * min_space], - [-1 * min_space, 0]] - w = design.design("wire_test3") - wire.wire(w, layer_stack, position_list) - self.local_drc_check(w) + for reverse in [False, True]: + for stack in layer_stacks: + if reverse: + layer_stack = stack[::-1] + else: + layer_stack = stack + + min_space = 2 * (tech.drc["minwidth_{}".format(layer_stack[0])] + + tech.drc["minwidth_{}".format(layer_stack[2])]) - - min_space = 2 * (tech.drc["minwidth_m2"] + - tech.drc["minwidth_m1"]) - layer_stack = tech.m2_stack[::-1] - position_list = [[0, 0], - [0, 3 * min_space], - [1 * min_space, 3 * min_space], - [4 * min_space, 3 * min_space], - [4 * min_space, 0], - [7 * min_space, 0], - [7 * min_space, 4 * min_space], - [-1 * min_space, 4 * min_space], - [-1 * min_space, 0]] - w = design.design("wire_test4") - wire.wire(w, layer_stack, position_list) - self.local_drc_check(w) - - min_space = 2 * (tech.drc["minwidth_m2"] + - tech.drc["minwidth_m3"]) - layer_stack = tech.m2_stack - position_list = [[0, 0], - [0, 3 * min_space], - [1 * min_space, 3 * min_space], - [4 * min_space, 3 * min_space], - [4 * min_space, 0], - [7 * min_space, 0], - [7 * min_space, 4 * min_space], - [-1 * min_space, 4 * min_space], - [-1 * min_space, 0]] - position_list.reverse() - w = design.design("wire_test5") - wire.wire(w, layer_stack, position_list) - self.local_drc_check(w) - - min_space = 2 * (tech.drc["minwidth_m2"] + - tech.drc["minwidth_m3"]) - layer_stack = tech.m2_stack[::-1] - position_list = [[0, 0], - [0, 3 * min_space], - [1 * min_space, 3 * min_space], - [4 * min_space, 3 * min_space], - [4 * min_space, 0], - [7 * min_space, 0], - [7 * min_space, 4 * min_space], - [-1 * min_space, 4 * min_space], - [-1 * min_space, 0]] - position_list.reverse() - w = design.design("wire_test6") - wire.wire(w, layer_stack, position_list) - self.local_drc_check(w) + position_list = [[0, 0], + [0, 3 * min_space], + [1 * min_space, 3 * min_space], + [4 * min_space, 3 * min_space], + [4 * min_space, 0], + [7 * min_space, 0], + [7 * min_space, 4 * min_space], + [-1 * min_space, 4 * min_space], + [-1 * min_space, 0]] + position_list = [[x - min_space, y - min_space] for x, y in position_list] + w = design.design("wire_test_{}".format("_".join(layer_stack))) + wire.wire(w, layer_stack, position_list) + self.local_drc_check(w) globals.end_openram() From fc4685c7f70214c6601ff0d44b328f47de4ced1b Mon Sep 17 00:00:00 2001 From: Matthew Guthaus Date: Tue, 17 Dec 2019 23:07:01 +0000 Subject: [PATCH 025/103] Cleanup. --- compiler/base/contact.py | 47 ++++++++++++++++++---------------- compiler/base/design.py | 42 +++++++++++++++++++----------- compiler/tests/03_wire_test.py | 9 ++++--- 3 files changed, 57 insertions(+), 41 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index c5b1ef9e..24b0e866 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -7,8 +7,10 @@ # import hierarchy_design import debug -from tech import * +from tech import drc +import tech from vector import vector +from sram_factory import factory class contact(hierarchy_design.hierarchy_design): @@ -65,7 +67,8 @@ class contact(hierarchy_design.hierarchy_design): if self.implant_type and self.well_type: self.create_implant_well_enclosures() elif self.implant_type or self.well_type: - debug.error(-1, "Must define both implant and well type or none at all.") + debug.error(-1, + "Must define both implant and well type or none.") def setup_layers(self): """ Locally assign the layer names. """ @@ -75,7 +78,7 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_name = second_layer # Contacts will have unique per first layer - if via_layer in layer.keys(): + if via_layer in tech.layer.keys(): self.via_layer_name = via_layer elif via_layer == "contact": if first_layer in ("active", "poly"): @@ -86,8 +89,6 @@ class contact(hierarchy_design.hierarchy_design): debug.error("Invalid via layer {}".format(via_layer), -1) else: debug.error("Invalid via layer {}".format(via_layer), -1) - - def setup_layout_constants(self): """ Determine the design rules for the enclosure layers """ @@ -127,7 +128,9 @@ class contact(hierarchy_design.hierarchy_design): else: debug.error("Invalid first layer direction.", -1) - # In some technologies, the minimum width may be larger than the overlap requirement around the via, so + # In some technologies, + # the minimum width may be larger than + # the overlap requirement around the via, so # check this for each dimension. if self.directions[1] == "V": self.second_layer_horizontal_enclosure = max(second_layer_enclosure, @@ -146,11 +149,14 @@ class contact(hierarchy_design.hierarchy_design): """ Create the contact array at the origin""" # offset for the via array self.via_layer_position = vector( - max(self.first_layer_horizontal_enclosure, self.second_layer_horizontal_enclosure), - max(self.first_layer_vertical_enclosure, self.second_layer_vertical_enclosure)) + max(self.first_layer_horizontal_enclosure, + self.second_layer_horizontal_enclosure), + max(self.first_layer_vertical_enclosure, + self.second_layer_vertical_enclosure)) for i in range(self.dimensions[1]): - offset = self.via_layer_position + vector(0, self.contact_pitch * i) + offset = self.via_layer_position + vector(0, + self.contact_pitch * i) for j in range(self.dimensions[0]): self.add_rect(layer=self.via_layer_name, offset=offset, @@ -161,7 +167,7 @@ class contact(hierarchy_design.hierarchy_design): def create_nitride_cut_enclosure(self): """ Special layer that encloses poly contacts in some processes """ # Check if there is a special poly nitride cut layer - if "npc" not in layer.keys(): + if "npc" not in tech.layer.keys(): return # Only add for poly layers @@ -175,7 +181,6 @@ class contact(hierarchy_design.hierarchy_design): offset=self.second_layer_position, width=self.second_layer_width, height=self.second_layer_height) - def create_first_layer_enclosure(self): # this is if the first and second layers are different @@ -224,35 +229,33 @@ class contact(hierarchy_design.hierarchy_design): return self.return_power() -from sram_factory import factory - # This is not instantiated and used for calculations only. # These are static 1x1 contacts to reuse in all the design modules. well = factory.create(module_type="contact", - layer_stack=active_stack, + layer_stack=tech.active_stack, directions=("H", "V")) active = factory.create(module_type="contact", - layer_stack=active_stack, + layer_stack=tech.active_stack, directions=("H", "V")) poly = factory.create(module_type="contact", - layer_stack=poly_stack, + layer_stack=tech.poly_stack, directions=("V", "H")) -if "li" in layer.keys(): +if "li" in tech.layer.keys(): lim1 = factory.create(module_type="contact", - layer_stack=li_stack, + layer_stack=tech.li_stack, directions=("V", "H")) else: lim1 = None m1m2 = factory.create(module_type="contact", - layer_stack=m1_stack, + layer_stack=tech.m1_stack, directions=("H", "V")) m2m3 = factory.create(module_type="contact", - layer_stack=m2_stack, + layer_stack=tech.m2_stack, directions=("V", "H")) -if "m4" in layer.keys(): +if "m4" in tech.layer.keys(): m3m4 = factory.create(module_type="contact", - layer_stack=m3_stack, + layer_stack=tech.m3_stack, directions=("H", "V")) else: m3m4 = None diff --git a/compiler/base/design.py b/compiler/base/design.py index 2b11111d..1066cbc0 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -10,6 +10,7 @@ import contact from globals import OPTS import re + class design(hierarchy_design): """ This is the same as the hierarchy_design class except it contains @@ -24,16 +25,23 @@ class design(hierarchy_design): self.setup_layer_constants() self.setup_multiport_constants() - def setup_layer_constants(self): - """ These are some layer constants used in many places in the compiler.""" + """ + These are some layer constants used + in many places in the compiler. + """ import tech - - self.m1_pitch = max(contact.m1m2.width, contact.m1m2.height) + max(self.m1_space, self.m2_space) - self.m2_pitch = max(contact.m2m3.width, contact.m2m3.height) + max(self.m2_space, self.m3_space) + + # This is contact direction independent pitch, + # i.e. we take the maximum contact dimension + max_m1m2_contact = max(contact.m1m2.width, contact.m1m2.height) + self.m1_pitch = max_m1m2_contact + max(self.m1_space, self.m2_space) + max_m2m3_contact = max(contact.m2m3.width, contact.m2m3.height) + self.m2_pitch = max_m2m3_contact + max(self.m2_space, self.m3_space) if "m4" in tech.layer: - self.m3_pitch = max(contact.m3m4.width, contact.m3m4.height) + max(self.m3_space, self.m4_space) + max_m3m4_contact = max(contact.m3m4.width, contact.m3m4.height) + self.m3_pitch = max_m3m4_contact + max(self.m3_space, self.m4_space) else: self.m3_pitch = self.m2_pitch @@ -44,15 +52,17 @@ class design(hierarchy_design): self.m3_stack = tech.m3_stack def setup_drc_constants(self): - """ These are some DRC constants used in many places in the compiler.""" - from tech import drc, layer - + """ + These are some DRC constants used in many places + in the compiler. + """ # Make some local rules for convenience + from tech import drc for rule in drc.keys(): # Single layer width rules match = re.search(r"minwidth_(.*)", rule) if match: - if match.group(1)=="active_contact": + if match.group(1) == "active_contact": setattr(self, "contact_width", drc(match.group(0))) else: setattr(self, match.group(1)+"_width", drc(match.group(0))) @@ -64,11 +74,12 @@ class design(hierarchy_design): # Single layer spacing rules match = re.search(r"(.*)_to_(.*)", rule) - if match and match.group(1)==match.group(2): + if match and match.group(1) == match.group(2): setattr(self, match.group(1)+"_space", drc(match.group(0))) - elif match and match.group(1)!=match.group(2): - if match.group(2)=="poly_active": - setattr(self, match.group(1)+"_to_contact", drc(match.group(0))) + elif match and match.group(1) != match.group(2): + if match.group(2) == "poly_active": + setattr(self, match.group(1)+"_to_contact", + drc(match.group(0))) else: setattr(self, match.group(0), drc(match.group(0))) @@ -102,7 +113,8 @@ class design(hierarchy_design): print("well_enclose_active", self.well_enclose_active) print("implant_enclose_active", self.implant_enclose_active) print("implant_space", self.implant_space) - import sys; sys.exit(1) + import sys + sys.exit(1) def setup_multiport_constants(self): """ diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py index bd4b9345..61e21985 100755 --- a/compiler/tests/03_wire_test.py +++ b/compiler/tests/03_wire_test.py @@ -8,11 +8,11 @@ # import unittest from testutils import * -import sys,os +import sys +import os sys.path.append(os.getenv("OPENRAM_HOME")) import globals -from globals import OPTS -import debug + class wire_test(openram_test): @@ -31,7 +31,8 @@ class wire_test(openram_test): layer_stack = stack[::-1] else: layer_stack = stack - + + # Just make a conservative spacing. Make it wire pitch instead? min_space = 2 * (tech.drc["minwidth_{}".format(layer_stack[0])] + tech.drc["minwidth_{}".format(layer_stack[2])]) From 8e151553e43b1fd71ef20d7ff8c50eafeb0bc527 Mon Sep 17 00:00:00 2001 From: Matthew Guthaus Date: Tue, 17 Dec 2019 23:45:07 +0000 Subject: [PATCH 026/103] Update contact types. Use preferred directions in tech files. Programmatically generate based on interconnect stacks. --- compiler/base/contact.py | 50 ++++++++++-------------------- compiler/base/design.py | 45 +++++++++++++++++---------- technology/freepdk45/tech/tech.py | 7 +++++ technology/scn4m_subm/tech/tech.py | 6 ++++ 4 files changed, 58 insertions(+), 50 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 24b0e866..0a7ace88 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -11,6 +11,7 @@ from tech import drc import tech from vector import vector from sram_factory import factory +import sys class contact(hierarchy_design.hierarchy_design): @@ -28,7 +29,7 @@ class contact(hierarchy_design.hierarchy_design): """ - def __init__(self, layer_stack, dimensions=(1, 1), directions=("V", "V"), + def __init__(self, layer_stack, dimensions=(1, 1), directions=None, implant_type=None, well_type=None, name=""): # This will ignore the name parameter since # we can guarantee a unique name here @@ -43,7 +44,11 @@ class contact(hierarchy_design.hierarchy_design): self.layer_stack = layer_stack self.dimensions = dimensions - self.directions = directions + if directions: + self.directions = directions + else: + self.directions = (tech.preferred_directions[layer_stack[0]], + tech.preferred_directions[layer_stack[2]]) self.offset = vector(0, 0) self.implant_type = implant_type self.well_type = well_type @@ -228,35 +233,14 @@ class contact(hierarchy_design.hierarchy_design): """ Get total power of a module """ return self.return_power() - -# This is not instantiated and used for calculations only. -# These are static 1x1 contacts to reuse in all the design modules. -well = factory.create(module_type="contact", - layer_stack=tech.active_stack, - directions=("H", "V")) -active = factory.create(module_type="contact", - layer_stack=tech.active_stack, - directions=("H", "V")) -poly = factory.create(module_type="contact", - layer_stack=tech.poly_stack, - directions=("V", "H")) -if "li" in tech.layer.keys(): - lim1 = factory.create(module_type="contact", - layer_stack=tech.li_stack, - directions=("V", "H")) -else: - lim1 = None - -m1m2 = factory.create(module_type="contact", - layer_stack=tech.m1_stack, - directions=("H", "V")) -m2m3 = factory.create(module_type="contact", - layer_stack=tech.m2_stack, - directions=("V", "H")) -if "m4" in tech.layer.keys(): - m3m4 = factory.create(module_type="contact", - layer_stack=tech.m3_stack, - directions=("H", "V")) -else: - m3m4 = None + +# Set up a static for each layer to be used for measurements +for layer_stack in tech.layer_stacks: + (layer1, via, layer2) = layer_stack + cont = factory.create(module_type="contact", + layer_stack=layer_stack) + module = sys.modules[__name__] + # Create the contact as just the concat of the layer names + setattr(module, layer1 + layer2, cont) + diff --git a/compiler/base/design.py b/compiler/base/design.py index 1066cbc0..2e1488f9 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -26,30 +26,41 @@ class design(hierarchy_design): self.setup_multiport_constants() def setup_layer_constants(self): - """ + """ These are some layer constants used in many places in the compiler. """ import tech + for key in dir(tech): + # Single layer width rules + match = re.match(r".*_stack$", key) + if match: + layer_stack = getattr(tech, key) - # This is contact direction independent pitch, - # i.e. we take the maximum contact dimension - max_m1m2_contact = max(contact.m1m2.width, contact.m1m2.height) - self.m1_pitch = max_m1m2_contact + max(self.m1_space, self.m2_space) - max_m2m3_contact = max(contact.m2m3.width, contact.m2m3.height) - self.m2_pitch = max_m2m3_contact + max(self.m2_space, self.m3_space) - if "m4" in tech.layer: - max_m3m4_contact = max(contact.m3m4.width, contact.m3m4.height) - self.m3_pitch = max_m3m4_contact + max(self.m3_space, self.m4_space) - else: - self.m3_pitch = self.m2_pitch + # Set the stack as a local helper + setattr(self, key, layer_stack) - self.poly_stack = tech.poly_stack - self.active_stack = tech.active_stack - self.m1_stack = tech.m1_stack - self.m2_stack = tech.m2_stack - self.m3_stack = tech.m3_stack + # Add the pitch + setattr(self, + "{}_pitch".format(layer_stack[0]), + self.compute_pitch(layer_stack)) + + def compute_pitch(self, layer_stack): + + """ + This is contact direction independent pitch, + i.e. we take the maximum contact dimension + """ + (layer1, via, layer2) = layer_stack + + contact1 = getattr(contact, layer1 + layer2) + max_contact = max(contact1.width, contact1.height) + layer1_space = getattr(self, layer1+"_space") + layer2_space = getattr(self, layer2+"_space") + pitch = max_contact + max(layer1_space, layer2_space) + + return pitch def setup_drc_constants(self): """ diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index d87e98f1..3eba1a58 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -56,6 +56,13 @@ beol_stacks = [m1_stack, layer_stacks = feol_stacks + beol_stacks +preferred_directions = {"poly": "V", + "active": "H", + "m1": "H", + "m2": "V", + "m3": "H", + "m4": "V"} + ################################################### # GDS Layer Map ################################################### diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 349310f6..44ffe9be 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -56,6 +56,12 @@ beol_stacks = [m1_stack, layer_stacks = feol_stacks + beol_stacks +preferred_directions = {"poly": "V", + "active": "H", + "m1": "H", + "m2": "V", + "m3": "H", + "m4": "V"} ################################################### ##GDS Layer Map From aceaa9fb2178cd9b076926a22f752e583ecfca6b Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 17 Dec 2019 15:55:20 -0800 Subject: [PATCH 027/103] Standardize contact names. --- compiler/bitcells/pbitcell.py | 24 ++++++++++++------------ compiler/pgates/pgate.py | 16 ++++++++-------- compiler/pgates/pinv.py | 4 ++-- compiler/pgates/pnand2.py | 4 ++-- compiler/pgates/pnand3.py | 4 ++-- compiler/pgates/pnor2.py | 2 +- compiler/pgates/precharge.py | 6 +++--- compiler/pgates/ptristate_inv.py | 6 +++--- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index bdb9161b..6df230e9 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -213,7 +213,7 @@ class pbitcell(bitcell_base.bitcell_base): # y-offset for the access transistor's gate contact self.gate_contact_yoffset = max_contact_extension + self.m2_space \ - + 0.5 * max(contact.poly.height, contact.m1m2.height) + + 0.5 * max(contact.polym1.height, contact.m1m2.height) # y-position of access transistors self.port_ypos = self.m1_space + 0.5 * contact.m1m2.height + self.gate_contact_yoffset @@ -234,7 +234,7 @@ class pbitcell(bitcell_base.bitcell_base): self.read_port_spacing = self.bitline_offset + self.m2_space # spacing between cross coupled inverters - self.inverter_to_inverter_spacing = contact.poly.width + self.m1_space + self.inverter_to_inverter_spacing = contact.polym1.width + self.m1_space # calculations related to inverter connections inverter_pmos_contact_extension = 0.5 * \ @@ -243,19 +243,19 @@ class pbitcell(bitcell_base.bitcell_base): (self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height) self.inverter_gap = max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ - + self.poly_to_contact + 2 * contact.poly.width \ + + self.poly_to_contact + 2 * contact.polym1.width \ + self.m1_space + inverter_pmos_contact_extension self.cross_couple_lower_ypos = self.inverter_nmos_ypos \ + self.inverter_nmos.active_height \ + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ - + 0.5 * contact.poly.width + + 0.5 * contact.polym1.width self.cross_couple_upper_ypos = self.inverter_nmos_ypos \ + self.inverter_nmos.active_height \ + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + self.poly_to_contact \ - + 1.5 * contact.poly.width + + 1.5 * contact.polym1.width # spacing between wordlines (and gnd) self.m1_offset = -0.5 * self.m1_width @@ -263,7 +263,7 @@ class pbitcell(bitcell_base.bitcell_base): # spacing for vdd implant_constraint = max(inverter_pmos_contact_extension, 0) \ + 2 * self.implant_enclose_active \ - + 0.5 * (contact.well.width - self.m1_width) + + 0.5 * (contact.activem1.width - self.m1_width) metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width @@ -367,16 +367,16 @@ class pbitcell(bitcell_base.bitcell_base): self.add_path("m1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], - width=contact.active.second_layer_width) + width=contact.activem1.second_layer_width) self.add_path("m1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], - width=contact.active.second_layer_width) + width=contact.activem1.second_layer_width) # add contacts to connect gate poly to drain/source # metal1 (to connect Q to Q_bar) contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x \ - + 0.5 * contact.poly.height, + + 0.5 * contact.polym1.height, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, offset=contact_offset_left, @@ -384,7 +384,7 @@ class pbitcell(bitcell_base.bitcell_base): contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \ - - 0.5*contact.poly.height, + - 0.5*contact.polym1.height, self.cross_couple_lower_ypos) self.add_via_center(layers=self.poly_stack, offset=contact_offset_right, @@ -926,14 +926,14 @@ class pbitcell(bitcell_base.bitcell_base): """ # add poly to metal1 contacts for gates of the inverters left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x \ - - self.poly_to_contact - 0.5*contact.poly.width, + - self.poly_to_contact - 0.5*contact.polym1.width, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, offset=left_storage_contact, directions=("H", "H")) right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \ - + self.poly_to_contact + 0.5*contact.poly.width, + + self.poly_to_contact + 0.5*contact.polym1.width, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, offset=right_storage_contact, diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index d5f2395b..8f94ed07 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -88,14 +88,14 @@ class pgate(design.design): # Center is completely symmetric. if rotate: - contact_width = contact.poly.height - contact_m1_width = contact.poly.second_layer_height - contact_m1_height = contact.poly.second_layer_width + contact_width = contact.polym1.height + contact_m1_width = contact.polym1.second_layer_height + contact_m1_height = contact.polym1.second_layer_width directions = ("H", "V") else: - contact_width = contact.poly.width - contact_m1_width = contact.poly.second_layer_width - contact_m1_height = contact.poly.second_layer_height + contact_width = contact.polym1.width + contact_m1_width = contact.polym1.second_layer_width + contact_m1_height = contact.polym1.second_layer_height directions = ("V", "H") if position == "center": @@ -103,7 +103,7 @@ class pgate(design.design): + vector(0.5 * self.poly_width, 0) elif position == "farleft": contact_offset = left_gate_offset \ - - vector(0.5 * contact.poly.width, 0) + - vector(0.5 * contact.polym1.width, 0) elif position == "left": contact_offset = left_gate_offset \ - vector(0.5 * contact_width - 0.5 * self.poly_width, 0) @@ -131,7 +131,7 @@ class pgate(design.design): + left_gate_offset.scale(0.5, 0) self.add_rect_center(layer="poly", offset=mid_point, - height=contact.poly.first_layer_width, + height=contact.polym1.first_layer_width, width=left_gate_offset.x - contact_offset.x) def extend_wells(self, middle_position): diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 1ff5b67b..ce477a83 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -95,8 +95,8 @@ class pinv(pgate.pgate): tx_type="pmos") tx_height = nmos.poly_height + pmos.poly_height # rotated m1 pitch or poly to active spacing - min_channel = max(contact.poly.width + self.m1_space, - contact.poly.width + 2 * self.poly_to_active) + min_channel = max(contact.polym1.width + self.m1_space, + contact.polym1.width + 2 * self.poly_to_active) # This is the extra space needed to ensure DRC rules # to the active contacts diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 8acde2ce..c7f184b2 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -85,7 +85,7 @@ class pnand2(pgate.pgate): """ Pre-compute some handy layout parameters. """ # metal spacing to allow contacts on any layer - self.input_spacing = max(self.poly_space + contact.poly.first_layer_width, + self.input_spacing = max(self.poly_space + contact.polym1.first_layer_width, self.m1_space + contact.m1m2.first_layer_width, self.m2_space + contact.m2m3.first_layer_width, self.m3_space + contact.m2m3.second_layer_width) @@ -98,7 +98,7 @@ class pnand2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. - self.well_width = 2 * self.pmos.active_width + contact.active.width \ + self.well_width = 2 * self.pmos.active_width + contact.activem1.width \ + 2 * self.active_space \ + 2 * self.well_enclose_active diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 7a773497..8ccbff2c 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -202,10 +202,10 @@ class pnand3(pgate.pgate): # wire space or wire and one contact space metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width, - self.m1_space + 0.5 *contact.poly.width + 0.5 * self.m1_width) + self.m1_space + 0.5 *contact.polym1.width + 0.5 * self.m1_width) active_spacing = max(self.m1_space, - 0.5 * contact.poly.first_layer_width + self.poly_to_active) + 0.5 * contact.polym1.first_layer_width + self.poly_to_active) inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing self.route_input_gate(self.pmos3_inst, self.nmos3_inst, diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index d75df0a9..d24189ac 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -84,7 +84,7 @@ class pnor2(pgate.pgate): """ Pre-compute some handy layout parameters. """ # metal spacing to allow contacts on any layer - self.input_spacing = max(self.poly_space + contact.poly.first_layer_width, + self.input_spacing = max(self.poly_space + contact.polym1.first_layer_width, self.m1_space + contact.m1m2.first_layer_width, self.m2_space + contact.m2m3.first_layer_width, self.m3_space + contact.m2m3.second_layer_width) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 784bb418..f9e8ce87 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -121,7 +121,7 @@ class precharge(design.design): self.lower_pmos_inst.place(self.lower_pmos_position) # adds the upper pmos(s) to layout - ydiff = self.pmos.height + 2 * self.m1_space + contact.poly.width + ydiff = self.pmos.height + 2 * self.m1_space + contact.polym1.width self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff) self.upper_pmos1_inst.place(self.upper_pmos1_pos) @@ -175,7 +175,7 @@ class precharge(design.design): # adds the contact from active to metal1 well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ - + vector(0, self.upper_pmos1_inst.uy() + contact.well.height / 2 \ + + vector(0, self.upper_pmos1_inst.uy() + contact.activem1.height / 2 \ + self.well_extend_active) self.add_via_center(layers=self.active_stack, offset=well_contact_pos, @@ -183,7 +183,7 @@ class precharge(design.design): well_type="n") # leave an extra pitch for the height - self.height = well_contact_pos.y + contact.well.height + self.m1_pitch + self.height = well_contact_pos.y + contact.activem1.height + self.m1_pitch # nwell should span the whole design since it is pmos only self.add_rect(layer="nwell", diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py index 5c0028b2..d2965cde 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/pgates/ptristate_inv.py @@ -80,7 +80,7 @@ class ptristate_inv(pgate.pgate): # Height is an input parameter, so it is not recomputed. # Make sure we can put a well above and below - self.top_bottom_space = max(contact.well.width, contact.well.height) + self.top_bottom_space = max(contact.activem1.width, contact.activem1.height) def add_ptx(self): @@ -138,8 +138,8 @@ class ptristate_inv(pgate.pgate): """ pmos_yoff = self.height - self.pmos.active_height \ - - self.top_bottom_space - 0.5 * contact.well.height - nmos_yoff = self.top_bottom_space + 0.5 * contact.well.height + - self.top_bottom_space - 0.5 * contact.activem1.height + nmos_yoff = self.top_bottom_space + 0.5 * contact.activem1.height # Tristate transistors pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff) From de6b207798cd8ac4ff0b9674999f6ae392778910 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Wed, 18 Dec 2019 11:33:21 +0100 Subject: [PATCH 028/103] hierachy_layout: Move number of via arg to add_power_pins() this allows custom modules to state how many vias they need for power rails. Signed-off-by: Bastian Koppelmann --- compiler/base/hierarchy_layout.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 7f23e942..4751000a 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1015,7 +1015,7 @@ class layout(): - def add_power_pin(self, name, loc, 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 M3 down to M1 at the given center location. The starting layer is specified to determine which vias are needed. @@ -1027,12 +1027,14 @@ class layout(): if start_layer=="m1": self.add_via_center(layers=self.m1_stack, + size=size, offset=loc, directions=direction) if start_layer=="m1" or start_layer=="m2": via=self.add_via_center(layers=self.m2_stack, + size=size, offset=loc, directions=direction) From 451ef4d8965e54131add58b62ede96a75f5061f4 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Wed, 18 Dec 2019 11:58:04 +0100 Subject: [PATCH 029/103] sram_factory: Allow a prefered module name Signed-off-by: Bastian Koppelmann --- compiler/sram_factory.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 6bb3578c..f323fbdc 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -31,7 +31,7 @@ class sram_factory: """ self.__init__() - def create(self, module_type, **kwargs): + def create(self, module_type, module_name=None, **kwargs): """ A generic function to create a module with a given module_type. The args are passed directly to the module constructor. @@ -72,16 +72,18 @@ class sram_factory: if obj_kwargs == kwargs: return obj_item - # Use the default name if there are default arguments - # This is especially for library cells so that the - # spice and gds files can be found. - if len(kwargs) > 0: - # Create a unique name and increment the index - module_name = "{0}_{1}".format(real_module_type, - self.module_indices[real_module_type]) - self.module_indices[real_module_type] += 1 - else: - module_name = real_module_type + # If no prefered module name is provided, we generate one. + if module_name is None: + # Use the default name if there are default arguments + # This is especially for library cells so that the + # spice and gds files can be found. + if len(kwargs) > 0: + # Create a unique name and increment the index + module_name = "{0}_{1}".format(real_module_type, + self.module_indices[real_module_type]) + self.module_indices[real_module_type] += 1 + else: + module_name = real_module_type # type_str = "type={}".format(real_module_type) # name_str = "name={}".format(module_name) From fab963701b8eb5fd1443fc8a16c18f2150314bf1 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Wed, 18 Dec 2019 12:00:02 +0100 Subject: [PATCH 030/103] sram_base: Instantiate "dff_array" and "bank" through sram_factory Signed-off-by: Bastian Koppelmann --- compiler/modules/module_type.py | 1 + compiler/sram/sram_base.py | 13 +++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/compiler/modules/module_type.py b/compiler/modules/module_type.py index 90e1fdbe..ee3e8eb2 100644 --- a/compiler/modules/module_type.py +++ b/compiler/modules/module_type.py @@ -62,6 +62,7 @@ class ModuleType(): self.names['pwrite_driver'] = 'pwrite_driver' self.names['ptristate_inv'] = 'ptristate_inv' self.names['ptristate_buf'] = 'ptristate_buf' + self.names['dff_array'] = 'dff_array' def __setitem__(self, b, c): self.names[b] = c diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index cfb5af71..65297889 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -271,28 +271,25 @@ class sram_base(design, verilog, lef): self.dff = factory.create(module_type="dff") # Create the address and control flops (but not the clk) - from dff_array import dff_array - self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size, columns=1) + self.row_addr_dff = factory.create("dff_array", module_name="row_addr_dff", rows=self.row_addr_size, columns=1) self.add_mod(self.row_addr_dff) if self.col_addr_size > 0: - self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size) + self.col_addr_dff = factory.create("dff_array", module_name="col_addr_dff", rows=1, columns=self.col_addr_size) self.add_mod(self.col_addr_dff) else: self.col_addr_dff = None - self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size) + self.data_dff = factory.create("dff_array", module_name="data_dff", rows=1, columns=self.word_size) self.add_mod(self.data_dff) if self.write_size: - self.wmask_dff = dff_array(name="wmask_dff", rows=1, columns=self.num_wmasks) + self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks) self.add_mod(self.wmask_dff) # Create the bank module (up to four are instantiated) - from bank import bank - self.bank = bank(self.sram_config, - name="bank") + self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank") self.add_mod(self.bank) # Create bank decoder From 36cb675150f795c98ca9d42827d97891e635ff69 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 18 Dec 2019 09:30:00 -0800 Subject: [PATCH 031/103] Fix minwidth for multiple via bug. --- compiler/base/contact.py | 63 +++++++++++++++++++++------------------- compiler/base/design.py | 7 +++++ 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 0a7ace88..8def5bfd 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -109,44 +109,43 @@ class contact(hierarchy_design.hierarchy_design): # The extend rule applies to asymmetric enclosures in one direction. # The enclosure rule applies to symmetric enclosure component. - first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) - first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name)) - first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)) + self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) + self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name)) + self.first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)) - second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name)) - second_layer_enclosure = drc("{0}_enclose_{1}".format(self.second_layer_name, self.via_layer_name)) - second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)) + self.second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name)) + self.second_layer_enclosure = drc("{0}_enclose_{1}".format(self.second_layer_name, self.via_layer_name)) + self.second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)) # In some technologies, the minimum width may be larger # than the overlap requirement around the via, so # check this for each dimension. if self.directions[0] == "V": - self.first_layer_horizontal_enclosure = max(first_layer_enclosure, - (first_layer_minwidth - self.contact_array_width) / 2) - self.first_layer_vertical_enclosure = max(first_layer_extend, - (first_layer_minwidth - self.contact_array_height) / 2) + self.first_layer_horizontal_enclosure = max(self.first_layer_enclosure, + (self.first_layer_minwidth - self.contact_array_width) / 2) + self.first_layer_vertical_enclosure = max(self.first_layer_extend, + (self.first_layer_minwidth - self.contact_array_height) / 2) elif self.directions[0] == "H": - self.first_layer_horizontal_enclosure = max(first_layer_extend, - (first_layer_minwidth - self.contact_array_width) / 2) - self.first_layer_vertical_enclosure = max(first_layer_enclosure, - (first_layer_minwidth - self.contact_array_height) / 2) + self.first_layer_horizontal_enclosure = max(self.first_layer_extend, + (self.first_layer_minwidth - self.contact_array_width) / 2) + self.first_layer_vertical_enclosure = max(self.first_layer_enclosure, + (self.first_layer_minwidth - self.contact_array_height) / 2) else: debug.error("Invalid first layer direction.", -1) - # In some technologies, - # the minimum width may be larger than - # the overlap requirement around the via, so + # In some technologies, the minimum width may be larger + # than the overlap requirement around the via, so # check this for each dimension. if self.directions[1] == "V": - self.second_layer_horizontal_enclosure = max(second_layer_enclosure, - (second_layer_minwidth - self.contact_array_width) / 2) - self.second_layer_vertical_enclosure = max(second_layer_extend, - (second_layer_minwidth - self.contact_array_height) / 2) + self.second_layer_horizontal_enclosure = max(self.second_layer_enclosure, + (self.second_layer_minwidth - self.contact_array_width) / 2) + self.second_layer_vertical_enclosure = max(self.second_layer_extend, + (self.second_layer_minwidth - self.contact_array_height) / 2) elif self.directions[1] == "H": - self.second_layer_horizontal_enclosure = max(second_layer_extend, - (second_layer_minwidth - self.contact_array_height) / 2) - self.second_layer_vertical_enclosure = max(second_layer_enclosure, - (second_layer_minwidth - self.contact_array_width) / 2) + self.second_layer_horizontal_enclosure = max(self.second_layer_extend, + (self.second_layer_minwidth - self.contact_array_height) / 2) + self.second_layer_vertical_enclosure = max(self.second_layer_enclosure, + (self.second_layer_minwidth - self.contact_array_width) / 2) else: debug.error("Invalid second layer direction.", -1) @@ -193,8 +192,10 @@ class contact(hierarchy_design.hierarchy_design): max(self.second_layer_horizontal_enclosure - self.first_layer_horizontal_enclosure, 0), max(self.second_layer_vertical_enclosure - self.first_layer_vertical_enclosure, 0)) - self.first_layer_width = self.contact_array_width + 2 * self.first_layer_horizontal_enclosure - self.first_layer_height = self.contact_array_height + 2 * self.first_layer_vertical_enclosure + self.first_layer_width = max(self.contact_array_width + 2 * self.first_layer_horizontal_enclosure, + self.first_layer_minwidth) + self.first_layer_height = max(self.contact_array_height + 2 * self.first_layer_vertical_enclosure, + self.first_layer_minwidth) self.add_rect(layer=self.first_layer_name, offset=self.first_layer_position, width=self.first_layer_width, @@ -206,8 +207,10 @@ class contact(hierarchy_design.hierarchy_design): max(self.first_layer_horizontal_enclosure - self.second_layer_horizontal_enclosure, 0), max(self.first_layer_vertical_enclosure - self.second_layer_vertical_enclosure, 0)) - self.second_layer_width = self.contact_array_width + 2 * self.second_layer_horizontal_enclosure - self.second_layer_height = self.contact_array_height + 2 * self.second_layer_vertical_enclosure + self.second_layer_width = max(self.contact_array_width + 2 * self.second_layer_horizontal_enclosure, + self.second_layer_minwidth) + self.second_layer_height = max(self.contact_array_height + 2 * self.second_layer_vertical_enclosure, + self.second_layer_minwidth) self.add_rect(layer=self.second_layer_name, offset=self.second_layer_position, width=self.second_layer_width, @@ -242,5 +245,5 @@ for layer_stack in tech.layer_stacks: module = sys.modules[__name__] # Create the contact as just the concat of the layer names setattr(module, layer1 + layer2, cont) - + diff --git a/compiler/base/design.py b/compiler/base/design.py index 2e1488f9..59772a02 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -46,6 +46,13 @@ class design(hierarchy_design): "{}_pitch".format(layer_stack[0]), self.compute_pitch(layer_stack)) + if False: + print("m1_pitch", self.m1_pitch) + print("m2_pitch", self.m2_pitch) + print("m3_pitch", self.m3_pitch) + import sys + sys.exit(1) + def compute_pitch(self, layer_stack): """ From 1df16eceb60f2a50f78e18b5256cc3c318c9654a Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Thu, 19 Dec 2019 15:58:00 +0100 Subject: [PATCH 032/103] sram_factory: Give proper priority to overrides modules overridden by the user are the highest priority, then modules overridden by the technology. If nothing is overriden, use the defaults from OPTS (if they exist) or use the requested module_type. This fixes that custom tech_modules could not be used, if they had a default in OPTS even if the latter was not overridden by the user. We don't need extra defaults in the tech_modules, as we now only use them, if they have been overridden by the tech_module. Signed-off-by: Bastian Koppelmann --- compiler/globals.py | 2 ++ compiler/modules/module_type.py | 57 +++------------------------------ compiler/sram_factory.py | 43 +++++++++++++++++++++---- 3 files changed, 42 insertions(+), 60 deletions(-) diff --git a/compiler/globals.py b/compiler/globals.py index 8bca4b3f..70e4b54b 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -304,6 +304,7 @@ def read_config(config_file, is_unit_test=True): except: debug.error("Unable to read configuration file: {0}".format(config_file),2) + OPTS.overridden = {} for k, v in config.__dict__.items(): # The command line will over-ride the config file # except in the case of the tech name! This is because the tech name @@ -311,6 +312,7 @@ def read_config(config_file, is_unit_test=True): # Note that if we re-read a config file, nothing will get read again! if k not in OPTS.__dict__ or k == "tech_name": OPTS.__dict__[k] = v + OPTS.overridden[k] = True # Massage the output path to be an absolute one if not OPTS.output_path.endswith('/'): diff --git a/compiler/modules/module_type.py b/compiler/modules/module_type.py index ee3e8eb2..1d8d0cba 100644 --- a/compiler/modules/module_type.py +++ b/compiler/modules/module_type.py @@ -12,64 +12,15 @@ class ModuleType(): """ def __init__(self): self.names = {} - self.names['contact'] = 'contact' - self.names['precharge'] = 'precharge' - self.names['pinv'] = 'pinv' - self.names['dff_buf'] = 'dff_buf' - self.names['sense_amp'] = 'sense_amp' - self.names['bitcell'] = 'bitcell' - self.names['port_data'] = 'port_data' - self.names['port_address'] = 'port_address' - self.names['replica_bitcell_array'] = 'replica_bitcell_array' - self.names['bank_select'] = 'bank_select' - self.names['dff'] = 'dff' - self.names['pinvbuf'] = 'pinvbuf' - self.names['hierarchical_predecode2x4'] = 'hierarchical_predecode2x4' - self.names['hierarchical_predecode3x8'] = 'hierarchical_predecode3x8' - self.names['replica_bitcell'] = 'replica_bitcell' - self.names['dummy_bitcell'] = 'dummy_bitcell' - self.names['bitcell'] = 'bitcell' - self.names['pnor2'] = 'pnor2' - self.names['pnand2'] = 'pnand2' - self.names['precharge_array'] = 'precharge_array' - self.names['sense_amp_array'] = 'sense_amp_array' - self.names['column_mux_array'] = 'column_mux_array' - self.names['write_driver_array'] = 'write_driver_array' - self.names['write_mask_and_array'] = 'write_mask_and_array' - self.names['pand2'] = 'pand2' - self.names['write_driver'] = 'write_driver' - self.names['dff_buf_array'] = 'dff_buf_array' - self.names['pdriver'] = 'pdriver' - self.names['pand3'] = 'pand3' - self.names['delay_chain'] = 'delay_chain' - self.names['decoder'] = 'decoder' - self.names['wordline_driver'] = 'wordline_driver' - self.names['tri_gate'] = 'tri_gate' - self.names['tri_gate_array'] = 'tri_gate_array' - self.names['bitcell_array'] = 'bitcell_array' - self.names['replica_column'] = 'replica_column' - self.names['dummy_array'] = 'dummy_array' - self.names['single_level_column_mux_array'] = 'single_level_column_mux_array' - self.names['single_level_column_mux'] = 'single_level_column_mux' - self.names['sram'] = 'sram' - self.names['ptx'] = 'ptx' - self.names['hierarchical_decoder'] = 'hierarchical_decoder' - self.names['pbuf'] = 'pbuf' - self.names['control_logic'] = 'control_logic' - self.names['bank'] = 'bank' - self.names['pbitcell'] = 'pbitcell' - self.names['pnand3'] = 'pnand3' - self.names['pwrite_driver'] = 'pwrite_driver' - self.names['ptristate_inv'] = 'ptristate_inv' - self.names['ptristate_buf'] = 'ptristate_buf' - self.names['dff_array'] = 'dff_array' def __setitem__(self, b, c): self.names[b] = c + def is_overridden(self, b): + return (b in self.names.keys()) + def __getitem__(self, b): if b not in self.names.keys(): raise KeyError - + return self.names[b] - diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index f323fbdc..2ba34b40 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -30,15 +30,16 @@ class sram_factory: Clear the factory instances for testing. """ self.__init__() - - def create(self, module_type, module_name=None, **kwargs): + + def get_techmodule_type(self, module_type): """ - A generic function to create a module with a given module_type. - The args are passed directly to the module constructor. + Try to load the custom tech module type. """ + overridden = False try: from tech import tech_modules real_module_type = tech_modules[module_type] + overridden = tech_modules.is_overridden(module_type) except ImportError: # If they didn't define these, then don't use the option types. # Primarily for backward compatibility and simplicity of tech files. @@ -46,12 +47,40 @@ class sram_factory: except KeyError: # If it wasn't a tech module type, we can ignore that too. real_module_type = module_type - + return (real_module_type, overridden) + + def get_usermodule_type(self, module_type): + """ + Try to load the custom user module type. If the user hasn't specified + anything, we use the default from 'options.py'. If we cannot find anything, we + fall back to the original 'module_type'. + """ + overridden = False if hasattr(OPTS, module_type): # Retrieve the name from OPTS if it exists, # otherwise just use the name - real_module_type = getattr(OPTS, module_type) - + module_type = getattr(OPTS, module_type) + overridden = module_type in OPTS.overridden.keys() + return (module_type, overridden) + + def create(self, module_type, module_name=None, **kwargs): + """ + A generic function to create a module with a given module_type. + The args are passed directly to the module constructor. + """ + tech_module_type, tm_overridden = self.get_techmodule_type(module_type) + user_module_type, um_overridden = self.get_usermodule_type(module_type) + + # overridden user modules have priority + if um_overridden: + real_module_type = user_module_type + # then overridden tech modules + elif tm_overridden: + real_module_type = tech_module_type + # if nothing else works use the name generated by get_usermodule_type() + else: + real_module_type = user_module_type + # Either retrieve the already loaded module or load it try: # Load a cached version from previous usage From 11760a999317fb7a56ab3b826424890a71a64463 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Thu, 19 Dec 2019 16:31:52 +0100 Subject: [PATCH 033/103] sram_factory: Add check for duplicate module name sram_factory cannot handle duplicate module name, thus we bail out and raise an error if a user attempts that. Signed-off-by: Bastian Koppelmann --- compiler/sram_factory.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 2ba34b40..110dbe15 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -63,6 +63,13 @@ class sram_factory: overridden = module_type in OPTS.overridden.keys() return (module_type, overridden) + def is_duplicate_name(self, name): + for mods in self.objects.values(): + for insts in mods: + if insts[1].name == name: + return True + return False + def create(self, module_type, module_name=None, **kwargs): """ A generic function to create a module with a given module_type. @@ -113,6 +120,10 @@ class sram_factory: self.module_indices[real_module_type] += 1 else: module_name = real_module_type + else: + if self.is_duplicate_name(module_name): + raise ValueError("Modules with duplicate name are not allowed." \ + " '{}'".format(module_name)) # type_str = "type={}".format(real_module_type) # name_str = "name={}".format(module_name) From b7d78ec2ec5193284765b67e69acb3d39863370d Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 19 Dec 2019 12:54:10 -0800 Subject: [PATCH 034/103] Fix ptx active contact orientation to non-default M1 direction. --- compiler/base/hierarchy_layout.py | 15 ++++++--------- compiler/bitcells/pbitcell.py | 7 +++---- compiler/pgates/pgate.py | 22 +++++++--------------- compiler/pgates/ptx.py | 1 + compiler/tests/03_contact_test.py | 2 +- technology/freepdk45/tech/tech.py | 2 +- technology/scn4m_subm/tech/tech.py | 2 +- 7 files changed, 20 insertions(+), 31 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 95b27618..4baa8fc4 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -388,19 +388,15 @@ class layout(): def get_preferred_direction(self, layer): """ Return the preferred routing directions """ - if layer in ["m1", "m3", "m5"]: - return "H" - elif layer in ["active", "poly", "li", "m2", "m4"]: - return "V" - else: - debug.error("Unable to find preferred direction for {}".format(layer)) - + from tech import preferred_directions + return preferred_directions[layer] def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None): """ Add a three layer via structure. """ if directions==None: - directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2])) + directions = (self.get_preferred_direction(layers[0]), + self.get_preferred_direction(layers[2])) from sram_factory import factory via = factory.create(module_type="contact", @@ -421,7 +417,8 @@ class layout(): """ Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """ if directions==None: - directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2])) + directions = (self.get_preferred_direction(layers[0]), + self.get_preferred_direction(layers[2])) from sram_factory import factory via = factory.create(module_type="contact", diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 6df230e9..2d9b4384 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -199,7 +199,6 @@ class pbitcell(bitcell_base.bitcell_base): def calculate_spacing(self): """ Calculate transistor spacings """ - # calculate metal contact extensions over transistor active readwrite_nmos_contact_extension = 0.5 * \ (self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height) @@ -263,7 +262,7 @@ class pbitcell(bitcell_base.bitcell_base): # spacing for vdd implant_constraint = max(inverter_pmos_contact_extension, 0) \ + 2 * self.implant_enclose_active \ - + 0.5 * (contact.activem1.width - self.m1_width) + + 0.5*(self.inverter_pmos.active_contact.height - self.m1_width) metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width @@ -367,11 +366,11 @@ class pbitcell(bitcell_base.bitcell_base): self.add_path("m1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], - width=contact.activem1.second_layer_width) + width=self.inverter_nmos_left.get_pin("D").width()) self.add_path("m1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], - width=contact.activem1.second_layer_width) + width=self.inverter_nmos_right.get_pin("S").width()) # add contacts to connect gate poly to drain/source # metal1 (to connect Q to Q_bar) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 8f94ed07..5bf3e5e3 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -64,7 +64,7 @@ class pgate(design.design): height=height, width=source_pin.width()) - def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=False): + def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left"): """ Route the input gate to the left side of the cell for access. Position specifies to place the contact the left, center, or @@ -87,16 +87,9 @@ class pgate(design.design): left_gate_offset = vector(nmos_gate_pin.lx(), ypos) # Center is completely symmetric. - if rotate: - contact_width = contact.polym1.height - contact_m1_width = contact.polym1.second_layer_height - contact_m1_height = contact.polym1.second_layer_width - directions = ("H", "V") - else: - contact_width = contact.polym1.width - contact_m1_width = contact.polym1.second_layer_width - contact_m1_height = contact.polym1.second_layer_height - directions = ("V", "H") + contact_width = contact.polym1.width + contact_m1_width = contact.polym1.second_layer_width + contact_m1_height = contact.polym1.second_layer_height if position == "center": contact_offset = left_gate_offset \ @@ -115,16 +108,15 @@ class pgate(design.design): # Non-preferred direction via - self.add_via_center(layers=self.poly_stack, - offset=contact_offset, - directions=directions) + v=self.add_via_center(layers=self.poly_stack, + offset=contact_offset, + directions = ("V", "H")) self.add_layout_pin_rect_center(text=name, layer="m1", offset=contact_offset, width=contact_m1_width, height=contact_m1_height) - # This is to ensure that the contact is # connected to the gate mid_point = contact_offset.scale(0.5, 1) \ diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 9907485f..5d4c015a 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -125,6 +125,7 @@ class ptx(design.design): # This is not actually instantiated but used for calculations self.active_contact = factory.create(module_type="contact", layer_stack=self.active_stack, + directions = ("V", "V"), dimensions=(1, self.num_contacts)) diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index 18604213..f7a13118 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -30,7 +30,7 @@ class contact_test(openram_test): # Check single 1 x 1 contact" debug.info(2, "1 x 1 {} test".format(stack_name)) - c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1)) + c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1), directions=("H", "H")) self.local_drc_check(c) # Check single 1 x 1 contact" diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 3eba1a58..a76624d4 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -57,7 +57,7 @@ beol_stacks = [m1_stack, layer_stacks = feol_stacks + beol_stacks preferred_directions = {"poly": "V", - "active": "H", + "active": "V", "m1": "H", "m2": "V", "m3": "H", diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 44ffe9be..beb604c5 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -57,7 +57,7 @@ beol_stacks = [m1_stack, layer_stacks = feol_stacks + beol_stacks preferred_directions = {"poly": "V", - "active": "H", + "active": "V", "m1": "H", "m2": "V", "m3": "H", From 0da8164ea6391693c8d40c70c9eb7aab865eb5fc Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 19 Dec 2019 13:54:50 -0800 Subject: [PATCH 035/103] Remove some unnecessary via directions. --- compiler/bitcells/pbitcell.py | 18 ++++++------------ compiler/pgates/pgate.py | 5 +---- compiler/pgates/pnand2.py | 6 ++---- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 2d9b4384..d809c6f1 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -378,16 +378,14 @@ class pbitcell(bitcell_base.bitcell_base): + 0.5 * contact.polym1.height, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, - offset=contact_offset_left, - directions=("H", "H")) + offset=contact_offset_left) contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \ - 0.5*contact.polym1.height, self.cross_couple_lower_ypos) self.add_via_center(layers=self.poly_stack, - offset=contact_offset_right, - directions=("H", "H")) + offset=contact_offset_right) # connect contacts to gate poly (cross couple connections) gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, @@ -784,8 +782,7 @@ class pbitcell(bitcell_base.bitcell_base): offset=port_contact_offset) self.add_via_center(layers=self.m1_stack, - offset=wl_contact_offset, - directions=("H", "H")) + offset=wl_contact_offset) self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("m2", @@ -862,8 +859,7 @@ class pbitcell(bitcell_base.bitcell_base): supply_offset = vector(position.x + contact_correct, self.gnd_position.y) self.add_via_center(layers=self.m1_stack, - offset=supply_offset, - directions=("H", "H")) + offset=supply_offset) self.add_path("m2", [position, supply_offset]) @@ -928,15 +924,13 @@ class pbitcell(bitcell_base.bitcell_base): - self.poly_to_contact - 0.5*contact.polym1.width, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, - offset=left_storage_contact, - directions=("H", "H")) + offset=left_storage_contact) right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \ + self.poly_to_contact + 0.5*contact.polym1.width, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, - offset=right_storage_contact, - directions=("H", "H")) + offset=right_storage_contact) inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos) self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 5bf3e5e3..9cb73542 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -106,11 +106,8 @@ class pgate(design.design): else: debug.error("Invalid contact placement option.", -1) - # Non-preferred direction via - v=self.add_via_center(layers=self.poly_stack, - offset=contact_offset, - directions = ("V", "H")) + offset=contact_offset) self.add_layout_pin_rect_center(text=name, layer="m1", diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index c7f184b2..bc520c7b 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -226,11 +226,9 @@ class pnand2(pgate.pgate): mid2_offset = vector(out_offset.x, bottom_pin_offset.y) self.add_via_center(layers=self.m1_stack, - offset=pmos_pin.center(), - directions=("V", "H")) + offset=pmos_pin.center()) self.add_via_center(layers=self.m1_stack, - offset=nmos_pin.center(), - directions=("V", "H")) + offset=nmos_pin.center()) self.add_via_center(layers=self.m1_stack, offset=out_offset) From d2461e5011a3dce9ce376746dc03f3125dabe10b Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 19 Dec 2019 16:19:21 -0800 Subject: [PATCH 036/103] Supply indexing bug resolved. Recompute width/height basted on insts. --- compiler/modules/bank_select.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 4b49750d..0c15401e 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -42,6 +42,10 @@ class bank_select(design.design): self.place_instances() self.route_instances() + self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width + self.width = max([x.rx() for x in self.inv_inst]) + + self.add_boundary() self.DRC_LVS() @@ -96,14 +100,11 @@ class bank_select(design.design): self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") - self.xoffset_inv = max(self.xoffset_nand + self.nand2.width, self.xoffset_nor + self.nor2.width) self.xoffset_bank_sel_inv = 0 self.xoffset_inputs = 0 self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height - # Include the M1 pitches for the supply rails and spacing - self.height = self.yoffset_maxpoint + 2*self.m1_pitch - self.width = self.xoffset_inv + self.inv4x.width + def create_instances(self): @@ -197,7 +198,7 @@ class bank_select(design.design): mirror=mirror) # They all get inverters on the output - inv_inst.place(offset=[self.xoffset_inv, y_offset], + inv_inst.place(offset=[logic_inst.rx(), y_offset], mirror=mirror) @@ -312,14 +313,14 @@ class bank_select(design.design): offset=pin_pos) # Add vdd/gnd supply rails - gnd_pin = inv_inst.get_pin("gnd") + gnd_pin = self.inv_inst[num].get_pin("gnd") left_gnd_pos = vector(0, gnd_pin.cy()) self.add_layout_pin_segment_center(text="gnd", layer="m1", start=left_gnd_pos, end=gnd_pin.rc()) - vdd_pin = inv_inst.get_pin("vdd") + vdd_pin = self.inv_inst[num].get_pin("vdd") left_vdd_pos = vector(0, vdd_pin.cy()) self.add_layout_pin_segment_center(text="vdd", layer="m1", From 82496a66fe77b4ca3e1d9fd87cada9e6ee4866b9 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 20 Dec 2019 10:35:57 -0800 Subject: [PATCH 037/103] Simplify supply code. --- compiler/modules/write_mask_and_array.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 40f386d2..e6dcfff9 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -104,8 +104,6 @@ class write_mask_and_array(design.design): def add_layout_pins(self): - self.nand2 = factory.create(module_type="pnand2") - supply_pin=self.nand2.get_pin("vdd") # Create the enable pin that connects all write mask AND array's B pins beg_en_pin = self.and2_insts[0].get_pin("B") @@ -139,15 +137,16 @@ class write_mask_and_array(design.design): self.add_via_center(layers=self.m2_stack, offset=en_pin.center()) - self.add_power_pin("gnd", vector(supply_pin.width() + i * self.wmask_en_len, 0)) - self.add_power_pin("vdd", vector(supply_pin.width() + i * self.wmask_en_len, self.height)) - # Route power and ground rails together - if i < self.num_wmasks-1: - for n in ["gnd","vdd"]: - pin = self.and2_insts[i].get_pin(n) - next_pin = self.and2_insts[i+1].get_pin(n) - self.add_path("m1",[pin.center(),next_pin.center()]) + for supply in ["gnd", "vdd"]: + supply_pin=self.and2_insts[i].get_pin(supply) + self.add_power_pin(supply, supply_pin.rc()) + + 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("m1",[supply_pin_left.lc(), supply_pin_right.rc()]) + def get_cin(self): """Get the relative capacitance of all the input connections in the bank""" # The enable is connected to an and2 for every row. From 89396698ef69f8e8adcd33689ca38735812b24e6 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 20 Dec 2019 10:36:14 -0800 Subject: [PATCH 038/103] Non-preferred via in pnand active --- compiler/pgates/pnand2.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index bc520c7b..dda736d2 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -224,10 +224,14 @@ class pnand2(pgate.pgate): # Midpoints of the L routes go horizontal first then vertical mid1_offset = vector(out_offset.x, top_pin_offset.y) mid2_offset = vector(out_offset.x, bottom_pin_offset.y) - + + # Non-preferred active contacts self.add_via_center(layers=self.m1_stack, + directions=("V","H"), offset=pmos_pin.center()) + # Non-preferred active contacts self.add_via_center(layers=self.m1_stack, + directions=("V","H"), offset=nmos_pin.center()) self.add_via_center(layers=self.m1_stack, offset=out_offset) From 9ad06a77707ebf8108ac1d767d8a21a974faaf30 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 20 Dec 2019 11:48:27 -0800 Subject: [PATCH 039/103] Move write mask vias to center to avoid data pins. --- 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 e6dcfff9..25acb324 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -139,7 +139,7 @@ class write_mask_and_array(design.design): for supply in ["gnd", "vdd"]: supply_pin=self.and2_insts[i].get_pin(supply) - self.add_power_pin(supply, supply_pin.rc()) + self.add_power_pin(supply, supply_pin.center()) for supply in ["gnd", "vdd"]: From a8d370ee8c733d29c2ce3eeee9c7012a1a888880 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 20 Dec 2019 16:35:31 -0800 Subject: [PATCH 040/103] Improved comments in tech files --- technology/freepdk45/tech/tech.py | 11 ++++++++++- technology/scn4m_subm/tech/tech.py | 12 +++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index a76624d4..79773878 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -12,6 +12,11 @@ from module_type import * """ File containing the process technology parameters for FreePDK 45nm. """ + +################################################### +# Custom modules +################################################### + # This uses the default classes to instantiate module from # '$OPENRAM_HOME/compiler/modules'. # Using tech_modules['cellname'] you can override each class by providing a custom @@ -19,7 +24,11 @@ File containing the process technology parameters for FreePDK 45nm. # For example: tech_modules['contact'] = 'contact_freepdk45' tech_modules = ModuleType() -#GDS file info + +################################################### +# GDS file info +################################################### + GDS = {} # gds units # From http://www.cnf.cornell.edu/cnf_spie9.html: "The first diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index beb604c5..3fb7acc0 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -12,6 +12,11 @@ from module_type import * """ File containing the process technology parameters for SCMOS 4m, 0.35um """ + +################################################### +# Custom modules +################################################### + # This uses the default classes to instantiate module from # '$OPENRAM_HOME/compiler/modules'. # Using tech_modules['cellname'] you can override each class by providing a custom @@ -19,7 +24,10 @@ File containing the process technology parameters for SCMOS 4m, 0.35um # For example: tech_modules['contact'] = 'contact_scn4m' tech_modules = ModuleType() -#GDS file info + +################################################### +# GDS file info +################################################### GDS={} # gds units # From http://www.cnf.cornell.edu/cnf_spie9.html: "The first @@ -69,8 +77,6 @@ preferred_directions = {"poly": "V", # create the GDS layer map layer={} -layer["vtg"] = None -layer["vth"] = None layer["pwell"] = (41, 0) layer["nwell"] = (42, 0) layer["active"] = (43, 0) From 4ad920eaf7b098a9f21649ef7be41187a6407afa Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Mon, 23 Dec 2019 08:42:52 -0800 Subject: [PATCH 041/103] Small fixes to tech usage. --- compiler/base/contact.py | 22 +++++++++++++--------- compiler/pgates/ptx.py | 6 +++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 8def5bfd..64433633 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -83,7 +83,7 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_name = second_layer # Contacts will have unique per first layer - if via_layer in tech.layer.keys(): + if via_layer in tech.layer: self.via_layer_name = via_layer elif via_layer == "contact": if first_layer in ("active", "poly"): @@ -171,7 +171,7 @@ class contact(hierarchy_design.hierarchy_design): def create_nitride_cut_enclosure(self): """ Special layer that encloses poly contacts in some processes """ # Check if there is a special poly nitride cut layer - if "npc" not in tech.layer.keys(): + if "npc" not in tech.layer: return # Only add for poly layers @@ -224,13 +224,17 @@ class contact(hierarchy_design.hierarchy_design): offset=implant_position, width=implant_width, height=implant_height) - well_position = self.first_layer_position - [drc("well_enclose_active")] * 2 - well_width = self.first_layer_width + 2 * drc("well_enclose_active") - well_height = self.first_layer_height + 2 * drc("well_enclose_active") - self.add_rect(layer="{}well".format(self.well_type), - offset=well_position, - width=well_width, - height=well_height) + + # Optionally implant well if layer exists + well_layer = "{}well".format(self.well_type) + if well_layer in tech.layer: + well_position = self.first_layer_position - [drc("well_enclose_active")] * 2 + well_width = self.first_layer_width + 2 * drc("well_enclose_active") + well_height = self.first_layer_height + 2 * drc("well_enclose_active") + self.add_rect(layer=well_layer, + offset=well_position, + width=well_width, + height=well_height) def analytical_power(self, corner, load): """ Get total power of a module """ diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 5d4c015a..20c513bb 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -159,7 +159,7 @@ class ptx(design.design): # Well enclosure of active, ensure minwidth as well well_name = "{}well".format(self.well_type) - if layer[well_name]: + if well_name in layer: self.cell_well_width = max(self.active_width + 2 * self.well_enclose_active, self.well_width) self.cell_well_height = max(self.tx_width + 2 * self.well_enclose_active, @@ -326,12 +326,12 @@ class ptx(design.design): Add an (optional) well and implant for the type of transistor. """ well_name = "{}well".format(self.well_type) - if layer[well_name]: + if well_name in layer: self.add_rect(layer=well_name, offset=(0,0), width=self.cell_well_width, height=self.cell_well_height) - if layer["vtg"]: + if "vtg" in layer: self.add_rect(layer="vtg", offset=(0,0), width=self.cell_well_width, From bec12f5b94d8dbbe4300c3994c54aa6c5436258d Mon Sep 17 00:00:00 2001 From: Matthew Guthaus Date: Mon, 23 Dec 2019 21:16:08 +0000 Subject: [PATCH 042/103] Cleanup. --- compiler/base/hierarchy_layout.py | 674 ++++++++++++++++-------------- compiler/pgates/ptx.py | 50 +-- 2 files changed, 395 insertions(+), 329 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 27bf60eb..7b870ae7 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -5,7 +5,6 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import itertools import collections import geometry import gdsMill @@ -16,7 +15,7 @@ import os from globals import OPTS from vector import vector from pin_layout import pin_layout -import lef + class layout(): """ @@ -57,63 +56,65 @@ class layout(): """ if (inv_num % 2 == 0): - base_offset=vector(x_offset, inv_num * height) + base_offset = vector(x_offset, inv_num * height) y_dir = 1 else: - # we lose a rail after every 2 gates - base_offset=vector(x_offset, (inv_num+1) * height - (inv_num%2)*drc["minwidth_m1"]) + # we lose a rail after every 2 gates + base_offset = vector(x_offset, + (inv_num + 1) * height - \ + (inv_num % 2) * drc["minwidth_m1"]) y_dir = -1 - return (base_offset,y_dir) - + return (base_offset, y_dir) def find_lowest_coords(self): """Finds the lowest set of 2d cartesian coordinates within this layout""" - if len(self.objs)>0: - lowestx1 = min(obj.lx() for obj in self.objs if obj.name!="label") - lowesty1 = min(obj.by() for obj in self.objs if obj.name!="label") + if len(self.objs) > 0: + lowestx1 = min(obj.lx() for obj in self.objs if obj.name != "label") + lowesty1 = min(obj.by() for obj in self.objs if obj.name != "label") else: - lowestx1=lowesty1=None - if len(self.insts)>0: + lowestx1 = lowesty1 = None + if len(self.insts) > 0: lowestx2 = min(inst.lx() for inst in self.insts) lowesty2 = min(inst.by() for inst in self.insts) else: - lowestx2=lowesty2=None + lowestx2 = lowesty2 = None - if lowestx1==None and lowestx2==None: + if lowestx1 == None and lowestx2 == None: return None - elif lowestx1==None: - return vector(lowestx2,lowesty2) - elif lowestx2==None: - return vector(lowestx1,lowesty1) + elif lowestx1 == None: + return vector(lowestx2, lowesty2) + elif lowestx2 == None: + return vector(lowestx1, lowesty1) else: return vector(min(lowestx1, lowestx2), min(lowesty1, lowesty2)) def find_highest_coords(self): - """Finds the highest set of 2d cartesian coordinates within - this layout""" - - if len(self.objs)>0: - highestx1 = max(obj.rx() for obj in self.objs if obj.name!="label") - highesty1 = max(obj.uy() for obj in self.objs if obj.name!="label") + """ + Finds the highest set of 2d cartesian coordinates within + this layout + """ + if len(self.objs) > 0: + highestx1 = max(obj.rx() for obj in self.objs if obj.name != "label") + highesty1 = max(obj.uy() for obj in self.objs if obj.name != "label") else: - highestx1=highesty1=None - if len(self.insts)>0: + highestx1 = highesty1 = None + if len(self.insts) > 0: highestx2 = max(inst.rx() for inst in self.insts) highesty2 = max(inst.uy() for inst in self.insts) else: - highestx2=highesty2=None - if highestx1==None and highestx2==None: + highestx2 = highesty2 = None + if highestx1 == None and highestx2 == None: return None - elif highestx1==None: - return vector(highestx2,highesty2) - elif highestx2==None: - return vector(highestx1,highesty1) + elif highestx1 == None: + return vector(highestx2, highesty2) + elif highestx2 == None: + return vector(highestx1, highesty1) else: - return vector(max(highestx1, highestx2), max(highesty1, highesty2)) - + return vector(max(highestx1, highestx2), + max(highesty1, highesty2)) def translate_all(self, offset): """ @@ -132,8 +133,8 @@ class layout(): for pin in pin_list: pin.rect = [pin.ll() - offset, pin.ur() - offset] - def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0): - """Adds an instance of a mod to this module""" + def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0): + """ Adds an instance of a mod to this module """ self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) debug.info(3, "adding instance {}".format(self.insts[-1])) # This is commented out for runtime reasons @@ -141,7 +142,7 @@ class layout(): return self.insts[-1] def get_inst(self, name): - """Retrieve an instance by name""" + """ Retrieve an instance by name """ for inst in self.insts: if inst.name == name: return inst @@ -152,9 +153,9 @@ class layout(): Adds a rectangle on a given layer,offset with width and height """ if not width: - width=drc["minwidth_{}".format(layer)] + width = drc["minwidth_{}".format(layer)] if not height: - height=drc["minwidth_{}".format(layer)] + height = drc["minwidth_{}".format(layer)] # negative layers indicate "unused" layers in a given technology lpp = techlayer[layer] if lpp[0] >= 0: @@ -164,58 +165,63 @@ class layout(): def add_rect_center(self, layer, offset, width=None, height=None): """ - Adds a rectangle on a given layer at the center point with width and height + Adds a rectangle on a given layer at the center + point with width and height """ if not width: - width=drc["minwidth_{}".format(layer)] + width = drc["minwidth_{}".format(layer)] if not height: - height=drc["minwidth_{}".format(layer)] + height = drc["minwidth_{}".format(layer)] # negative layers indicate "unused" layers in a given technology lpp = techlayer[layer] - corrected_offset = offset - vector(0.5*width,0.5*height) + corrected_offset = offset - vector(0.5 * width, 0.5 * height) if lpp[0] >= 0: - self.objs.append(geometry.rectangle(lpp, corrected_offset, width, height)) + self.objs.append(geometry.rectangle(lpp, + corrected_offset, + width, + height)) return self.objs[-1] return None - def add_segment_center(self, layer, start, end): - """ - Add a min-width rectanglular segment using center line on the start to end point """ - minwidth_layer = drc["minwidth_{}".format(layer)] - if start.x!=end.x and start.y!=end.y: - debug.error("Nonrectilinear center rect!",-1) - elif start.x!=end.x: - offset = vector(0,0.5*minwidth_layer) - return self.add_rect(layer,start-offset,end.x-start.x,minwidth_layer) + Add a min-width rectanglular segment using center + line on the start to end point + """ + minwidth_layer = drc["minwidth_{}".format(layer)] + if start.x != end.x and start.y != end.y: + debug.error("Nonrectilinear center rect!", -1) + elif start.x != end.x: + offset = vector(0, 0.5 * minwidth_layer) + return self.add_rect(layer, + start-offset, + end.x-start.x, + minwidth_layer) else: - offset = vector(0.5*minwidth_layer,0) - return self.add_rect(layer,start-offset,minwidth_layer,end.y-start.y) - - + offset = vector(0.5 * minwidth_layer, 0) + return self.add_rect(layer, + start-offset, + minwidth_layer, + end.y-start.y) def get_pin(self, text): - """ - Return the pin or list of pins + """ + Return the pin or list of pins """ try: - if len(self.pin_map[text])>1: + if len(self.pin_map[text]) > 1: debug.error("Should use a pin iterator since more than one pin {}".format(text),-1) # If we have one pin, return it and not the list. # Otherwise, should use get_pins() any_pin = next(iter(self.pin_map[text])) return any_pin - except Exception as e: - #print e + except Exception: 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) - - def get_pins(self, text): - """ - Return a pin list (instead of a single pin) + """ + Return a pin list (instead of a single pin) """ if text in self.pin_map.keys(): return self.pin_map[text] @@ -223,87 +229,97 @@ class layout(): return set() def get_pin_names(self): - """ + """ Return a pin list of all pins """ return self.pin_map.keys() def copy_layout_pin(self, instance, pin_name, new_name=""): - """ - Create a copied version of the layout pin at the current level. - You can optionally rename the pin to a new name. """ - pins=instance.get_pins(pin_name) + Create a copied version of the layout pin at the current level. + You can optionally rename the pin to a new name. + """ + pins = instance.get_pins(pin_name) - debug.check(len(pins)>0,"Could not find pin {}".format(pin_name)) + debug.check(len(pins) > 0, + "Could not find pin {}".format(pin_name)) for pin in pins: - if new_name=="": + if new_name == "": new_name = pin.name - self.add_layout_pin(new_name, pin.layer, pin.ll(), pin.width(), pin.height()) + self.add_layout_pin(new_name, + pin.layer, + pin.ll(), + pin.width(), + pin.height()) def copy_layout_pins(self, instance, prefix=""): - """ + """ 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. """ for pin_name in self.pin_map.keys(): self.copy_layout_pin(instance, pin_name, prefix+pin_name) 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 """ - debug.check(start.x==end.x or start.y==end.y,"Cannot have a non-manhatten layout pin.") + debug.check(start.x == end.x or start.y == end.y, + "Cannot have a non-manhatten layout pin.") minwidth_layer = drc["minwidth_{}".format(layer)] # one of these will be zero - width = max(start.x,end.x) - min(start.x,end.x) - height = max(start.y,end.y) - min(start.y,end.y) - ll_offset = vector(min(start.x,end.x),min(start.y,end.y)) + width = max(start.x, end.x) - min(start.x, end.x) + height = max(start.y, end.y) - min(start.y, end.y) + ll_offset = vector(min(start.x, end.x), min(start.y, end.y)) # Shift it down 1/2 a width in the 0 dimension - if height==0: - ll_offset -= vector(0,0.5*minwidth_layer) - if width==0: - ll_offset -= vector(0.5*minwidth_layer,0) + if height == 0: + ll_offset -= vector(0, 0.5 * minwidth_layer) + if width == 0: + ll_offset -= vector(0.5 * minwidth_layer, 0) # This makes sure it is long enough, but also it is not 0 width! - height = max(minwidth_layer,height) - width = max(minwidth_layer,width) + height = max(minwidth_layer, height) + width = max(minwidth_layer, width) - - return self.add_layout_pin(text, layer, ll_offset, width, height) + return self.add_layout_pin(text, + layer, + ll_offset, + width, + height) def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None): """ Creates a path like pin with center-line convention """ if not width: - width=drc["minwidth_{0}".format(layer)] + width = drc["minwidth_{0}".format(layer)] if not height: - height=drc["minwidth_{0}".format(layer)] + height = drc["minwidth_{0}".format(layer)] - 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) - def remove_layout_pin(self, text): """ 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): """ - Create a labeled pin + Create a labeled pin """ if not width: - width=drc["minwidth_{0}".format(layer)] + width = drc["minwidth_{0}".format(layer)] if not height: - height=drc["minwidth_{0}".format(layer)] + height = drc["minwidth_{0}".format(layer)] - new_pin = pin_layout(text, [offset,offset+vector(width,height)], layer) + new_pin = pin_layout(text, + [offset, offset+vector(width, height)], + layer) try: # Check if there's a duplicate! @@ -324,42 +340,41 @@ class layout(): in LVS. """ if not width: - width=drc["minwidth_{0}".format(layer)] + width = drc["minwidth_{0}".format(layer)] if not height: - height=drc["minwidth_{0}".format(layer)] + height = drc["minwidth_{0}".format(layer)] self.add_rect(layer=layer, offset=offset, width=width, height=height) self.add_label(text=text, layer=layer, - offset=offset+vector(0.5*width,0.5*height)) + offset=offset + vector(0.5 * width, + 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""" # negative layers indicate "unused" layers in a given technology - debug.info(5,"add label " + str(text) + " " + layer + " " + str(offset)) + debug.info(5, "add label " + str(text) + " " + layer + " " + str(offset)) lpp = techlayer[layer] if lpp[0] >= 0: self.objs.append(geometry.label(text, lpp, offset, zoom)) return self.objs[-1] return None - def add_path(self, layer, coordinates, width=None): """Connects a routing path on given layer,coordinates,width.""" - debug.info(4,"add path " + str(layer) + " " + str(coordinates)) + debug.info(4, "add path " + str(layer) + " " + str(coordinates)) import wire_path # NOTE: (UNTESTED) add_path(...) is currently not used # negative layers indicate "unused" layers in a given technology - #lpp = techlayer[layer] - #if lpp[0] >= 0: - # self.objs.append(geometry.path(lpp, coordinates, width)) + # lpp = techlayer[layer] + # if lpp[0] >= 0: + # self.objs.append(geometry.path(lpp, coordinates, width)) wire_path.wire_path(obj=self, - layer=layer, - position_list=coordinates, + layer=layer, + position_list=coordinates, width=width) def add_route(self, layers, coordinates, layer_widths): @@ -369,21 +384,21 @@ class layout(): the coordinates. """ import route - debug.info(4,"add route " + str(layers) + " " + str(coordinates)) + debug.info(4, "add route " + str(layers) + " " + str(coordinates)) # add an instance of our path that breaks down into rectangles and contacts route.route(obj=self, - layer_stack=layers, + layer_stack=layers, path=coordinates, layer_widths=layer_widths) - def add_wire(self, layers, coordinates): """Connects a routing path on given layer,coordinates,width. The layers are the (horizontal, via, vertical). """ import wire - # add an instance of our path that breaks down into rectangles and contacts + # add an instance of our path that breaks down + # into rectangles and contacts wire.wire(obj=self, - layer_stack=layers, + layer_stack=layers, position_list=coordinates) def get_preferred_direction(self, layer): @@ -394,7 +409,7 @@ class layout(): def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None): """ Add a three layer via structure. """ - if directions==None: + if not directions: directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2])) @@ -406,17 +421,20 @@ class layout(): implant_type=implant_type, well_type=well_type) self.add_mod(via) - inst=self.add_inst(name=via.name, - mod=via, - offset=offset) + inst = self.add_inst(name=via.name, + mod=via, + offset=offset) # We don't model the logical connectivity of wires/paths self.connect_inst([]) return inst def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None): - """ Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """ + """ + Add a three layer via structure by the center coordinate + accounting for mirroring and rotation. + """ - if directions==None: + if not directions: directions = (self.get_preferred_direction(layers[0]), self.get_preferred_direction(layers[2])) @@ -430,12 +448,13 @@ class layout(): height = via.height width = via.width - corrected_offset = offset + vector(-0.5*width,-0.5*height) + corrected_offset = offset + vector(-0.5 * width, + -0.5 * height) self.add_mod(via) - inst=self.add_inst(name=via.name, - mod=via, - offset=corrected_offset) + inst = self.add_inst(name=via.name, + mod=via, + offset=corrected_offset) # We don't model the logical connectivity of wires/paths self.connect_inst([]) return inst @@ -447,22 +466,20 @@ class layout(): mults=mults, tx_type=tx_type) self.add_mod(mos) - inst=self.add_inst(name=mos.name, - mod=mos, - offset=offset, - mirror=mirror, - rotate=rotate) + inst = self.add_inst(name=mos.name, + mod=mos, + offset=offset, + mirror=mirror, + rotate=rotate) return inst - - def gds_read(self): """Reads a GDSII file in the library and checks if it exists Otherwise, start a new layout for dynamic generation.""" # This must be done for netlist only mode too if os.path.isfile(self.gds_file): - self.is_library_cell=True + self.is_library_cell = True if OPTS.netlist_only: self.gds = None @@ -480,7 +497,7 @@ class layout(): def print_gds(self, gds_file=None): """Print the gds file (not the vlsi class) to the terminal """ - if gds_file == None: + if not gds_file: gds_file = self.gds_file debug.info(4, "Printing {}".format(gds_file)) arrayCellLayout = gdsMill.VlsiLayout(units=GDS["unit"]) @@ -511,10 +528,12 @@ class layout(): # If there is a boundary layer, and we didn't create one, add one. if "stdc" in techlayer.keys(): boundary_layer = "stdc" - boundary = [self.find_lowest_coords(), self.find_highest_coords()] + boundary = [self.find_lowest_coords(), + self.find_highest_coords()] height = boundary[1][1] - boundary[0][1] width = boundary[1][0] - boundary[0][0] (layer_number, layer_purpose) = techlayer[boundary_layer] + print(self.name, boundary, height, width) gds_layout.addBox(layerNumber=layer_number, purposeNumber=layer_purpose, offsetInMicrons=boundary[0], @@ -522,7 +541,6 @@ class layout(): height=height, center=False) debug.info(2, "Adding {0} boundary {1}".format(self.name, boundary)) - self.visited.append(self.name) @@ -549,20 +567,21 @@ class layout(): # populates the xyTree data structure for gds # self.gds.prepareForWrite() writer.writeToFile(gds_name) - debug.info(3, "Done writing to {}".format(gds_name)) + debug.info(3, "Done writing to {}".format(gds_name)) def get_boundary(self): """ Return the lower-left and upper-right coordinates of boundary """ # This assumes nothing spans outside of the width and height! - return [vector(0,0), vector(self.width, self.height)] + return [vector(0, 0), vector(self.width, self.height)] #return [self.find_lowest_coords(), self.find_highest_coords()] def get_blockages(self, layer, top_level=False): - """ - Write all of the obstacles in the current (and children) modules to the lef file + """ + Write all of the obstacles in the current (and children) + modules to the lef file. Do not write the pins since they aren't obstructions. """ - if type(layer)==str: + if type(layer) == str: lpp = techlayer[layer] else: lpp = layer @@ -659,11 +678,13 @@ class layout(): self.add_rect(layer=layer, offset=line_offset, height=length) - # Make this the center of the rail - line_positions[names[i]]=line_offset+vector(half_minwidth,0.5*length) + # Make this the center of the rail + line_positions[names[i]] = line_offset + vector(half_minwidth, + 0.5 * length) else: for i in range(len(names)): - line_offset = offset + vector(0,i*pitch + half_minwidth) + line_offset = offset + vector(0, + i * pitch + half_minwidth) if make_pins: self.add_layout_pin(text=names[i], layer=layer, @@ -674,7 +695,8 @@ class layout(): offset=line_offset, width=length) # Make this the center of the rail - line_positions[names[i]]=line_offset+vector(0.5*length,half_minwidth) + line_positions[names[i]] = line_offset + vector(0.5 * length, + half_minwidth) return line_positions @@ -684,17 +706,18 @@ class layout(): self.connect_bus(mapping, inst, bus_offsets, layer_stack, True) def connect_vertical_bus(self, mapping, inst, bus_offsets, - layer_stack=("m1", "via1", "m2")): + layer_stack=("m1", "via1", "m2")): """ Vertical version of connect_bus. """ self.connect_bus(mapping, inst, bus_offsets, layer_stack, False) def connect_bus(self, mapping, inst, bus_offsets, layer_stack, horizontal): - """ - Connect a mapping of pin -> name for a bus. This could be - replaced with a channel router in the future. - NOTE: This has only really been tested with point-to-point connections (not multiple pins on a net). """ - (horizontal_layer, via_layer, vertical_layer)=layer_stack + Connect a mapping of pin -> name for a bus. This could be + replaced with a channel router in the future. + NOTE: This has only really been tested with point-to-point + connections (not multiple pins on a net). + """ + (horizontal_layer, via_layer, vertical_layer) = layer_stack if horizontal: route_layer = vertical_layer else: @@ -712,37 +735,48 @@ class layout(): # left/right then up/down mid_pos = vector(bus_pos.x, pin_pos.y) - self.add_wire(layer_stack,[bus_pos, mid_pos, pin_pos]) + self.add_wire(layer_stack, + [bus_pos, mid_pos, pin_pos]) # Connect to the pin on the instances with a via if it is # not on the right layer if pin.layer != route_layer: self.add_via_center(layers=layer_stack, offset=pin_pos) - # FIXME: output pins tend to not be rotate, but supply pins are. Make consistent? + # FIXME: output pins tend to not be rotate, + # but supply pins are. Make consistent? - - # We only need a via if they happened to align perfectly # 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): self.add_via_center(layers=layer_stack, offset=bus_pos, rotate=90) + def get_layer_pitch(self, layer): """ Return the track pitch on a given layer """ - if layer=="m1": - return (self.m1_pitch,self.m1_pitch-self.m1_space,self.m1_space) - elif layer=="m2": - return (self.m2_pitch,self.m2_pitch-self.m2_space,self.m2_space) - elif layer=="m3": - return (self.m3_pitch,self.m3_pitch-self.m3_space,self.m3_space) - elif layer=="m4": + if layer == "m1": + return (self.m1_pitch, + self.m1_pitch - self.m1_space, + self.m1_space) + elif layer == "m2": + return (self.m2_pitch, + self.m2_pitch - self.m2_space, + self.m2_space) + elif layer == "m3": + return (self.m3_pitch, + self.m3_pitch - self.m3_space, + self.m3_space) + elif layer == "m4": from tech import layer as tech_layer if "m4" in tech_layer: - return (self.m3_pitch,self.m3_pitch-self.m4_space,self.m4_space) + return (self.m3_pitch, + self.m3_pitch - self.m4_space, + self.m4_space) else: - return (self.m3_pitch,self.m3_pitch-self.m3_space,self.m3_space) + return (self.m3_pitch, + self.m3_pitch - self.m3_space, + self.m3_space) else: debug.error("Cannot find layer pitch.") @@ -752,18 +786,20 @@ class layout(): layer_stack, pitch): """ - Create a trunk route for all pins with the trunk located at the given y offset. + Create a trunk route for all pins with + the trunk located at the given y offset. """ max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) # if we are less than a pitch, just create a non-preferred layer jog 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! - self.add_path(self.vertical_layer,[vector(min_x-half_layer_width,trunk_offset.y), vector(max_x+half_layer_width,trunk_offset.y)]) + self.add_path(self.vertical_layer, + [vector(min_x - half_layer_width, trunk_offset.y), + vector(max_x + half_layer_width, trunk_offset.y)]) # Route each pin to the trunk for pin in pins: @@ -772,8 +808,9 @@ class layout(): self.add_path(self.vertical_layer, [pin.center(), mid]) else: # Add the horizontal trunk - self.add_path(self.horizontal_layer,[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)]) - trunk_mid = vector(0.5*(max_x+min_x),trunk_offset.y) + self.add_path(self.horizontal_layer, + [vector(min_x, trunk_offset.y), + vector(max_x, trunk_offset.y)]) # Route each pin to the trunk for pin in pins: @@ -788,7 +825,8 @@ class layout(): layer_stack, pitch): """ - Create a trunk route for all pins with the trunk located at the given x offset. + Create a trunk route for all pins with the + trunk located at the given x offset. """ max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) @@ -796,10 +834,12 @@ class layout(): # if we are less than a pitch, just create a non-preferred layer jog 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! - self.add_path(self.horizontal_layer,[vector(trunk_offset.x,min_y-half_layer_width), vector(trunk_offset.x,max_y+half_layer_width)]) + self.add_path(self.horizontal_layer, + [vector(trunk_offset.x, min_y - half_layer_width), + vector(trunk_offset.x,max_y + half_layer_width)]) # Route each pin to the trunk for pin in pins: @@ -808,8 +848,9 @@ class layout(): self.add_path(self.horizontal_layer, [pin.center(), mid]) else: # Add the vertical trunk - self.add_path(self.vertical_layer,[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)]) - trunk_mid = vector(trunk_offset.x,0.5*(max_y+min_y),) + self.add_path(self.vertical_layer, + [vector(trunk_offset.x, min_y), + vector(trunk_offset.x, max_y)]) # Route each pin to the trunk for pin in pins: @@ -817,10 +858,9 @@ class layout(): self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid) - def create_channel_route(self, netlist, - offset, + offset, layer_stack, vertical=False): """ @@ -835,7 +875,7 @@ class layout(): """ Remove the pin from the graph and all conflicts """ - g.pop(pin,None) + g.pop(pin, None) # Remove the pin from all conflicts # FIXME: This is O(n^2), so maybe optimize it. @@ -848,7 +888,7 @@ class layout(): def vcg_nets_overlap(net1, net2, vertical, pitch): """ 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: @@ -861,7 +901,8 @@ class layout(): def vcg_pin_overlap(pin1, pin2, vertical, pitch): """ Check for vertical or horizontal overlap of the two pins """ # FIXME: If the pins are not in a row, this may break. - # However, a top pin shouldn't overlap another top pin, for example, so the + # However, a top pin shouldn't overlap another top pin, + # for example, so the # extra comparison *shouldn't* matter. # Pin 1 must be in the "BOTTOM" set @@ -872,80 +913,93 @@ class layout(): overlaps = (not vertical and x_overlap) or (vertical and y_overlap) return overlaps - if self.get_preferred_direction(layer_stack[0])=="V": + if self.get_preferred_direction(layer_stack[0]) == "V": self.vertical_layer = layer_stack[0] self.horizontal_layer = layer_stack[2] else: self.vertical_layer = layer_stack[2] self.horizontal_layer = layer_stack[0] - (self.vertical_pitch,self.vertical_width,self.vertical_space) = self.get_layer_pitch(self.vertical_layer) - (self.horizontal_pitch,self.horizontal_width,self.horizontal_space) = self.get_layer_pitch(self.horizontal_layer) + layer_stuff = self.get_layer_pitch(self.vertical_layer) + (self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff + layer_stuff = self.get_layer_pitch(self.horizontal_layer) + (self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff - - # FIXME: Must extend this to a horizontal conflict graph too if we want to minimize the + # FIXME: Must extend this to a horizontal conflict graph + # too if we want to minimize the # number of tracks! - #hcg = {} + # hcg = {} - # Initialize the vertical conflict graph (vcg) and make a list of all pins + # Initialize the vertical conflict graph (vcg) + # and make a list of all pins vcg = collections.OrderedDict() # Create names for the nets for the graphs nets = collections.OrderedDict() index = 0 - #print(netlist) + # print(netlist) for pin_list in netlist: - net_name = "n{}".format(index) - index += 1 - nets[net_name] = pin_list + net_name = "n{}".format(index) + index += 1 + nets[net_name] = pin_list # Find the vertical pin conflicts # FIXME: O(n^2) but who cares for now for net_name1 in nets: if net_name1 not in vcg.keys(): - vcg[net_name1]=[] + vcg[net_name1] = [] for net_name2 in nets: if net_name2 not in vcg.keys(): - vcg[net_name2]=[] + vcg[net_name2] = [] # Skip yourself if net_name1 == net_name2: continue - if vertical and vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical, self.vertical_pitch): + if vertical and vcg_nets_overlap(nets[net_name1], + nets[net_name2], + vertical, + self.vertical_pitch): vcg[net_name2].append(net_name1) - elif not vertical and vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical, self.horizontal_pitch): + elif not vertical and vcg_nets_overlap(nets[net_name1], + nets[net_name2], + vertical, + self.horizontal_pitch): vcg[net_name2].append(net_name1) # list of routes to do while vcg: - #from pprint import pformat - #print("VCG:\n",pformat(vcg)) + # from pprint import pformat + # print("VCG:\n",pformat(vcg)) # get a route from conflict graph with empty fanout set - net_name=None - for net_name,conflicts in vcg.items(): - if len(conflicts)==0: - vcg=remove_net_from_graph(net_name,vcg) + net_name = None + for net_name, conflicts in vcg.items(): + if len(conflicts) == 0: + vcg = remove_net_from_graph(net_name, vcg) break else: # FIXME: We don't support cyclic VCGs right now. - debug.error("Cyclic VCG in channel router.",-1) - - + debug.error("Cyclic VCG in channel router.", -1) # These are the pins we'll have to connect pin_list = nets[net_name] - #print("Routing:",net_name,[x.name for x in pin_list]) + # print("Routing:", net_name, [x.name for x in pin_list]) # 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 horizontal or the left to right for vertical + # Add the trunk routes from the bottom up for + # horizontal or the left to right for vertical if vertical: - self.add_vertical_trunk_route(pin_list, offset, layer_stack, self.vertical_pitch) - offset += vector(self.vertical_pitch,0) + self.add_vertical_trunk_route(pin_list, + offset, + layer_stack, + self.vertical_pitch) + offset += vector(self.vertical_pitch, 0) else: - self.add_horizontal_trunk_route(pin_list, offset, layer_stack, self.horizontal_pitch) - offset += vector(0,self.horizontal_pitch) - + self.add_horizontal_trunk_route(pin_list, + offset, + layer_stack, + self.horizontal_pitch) + offset += vector(0, self.horizontal_pitch) def create_vertical_channel_route(self, netlist, offset, layer_stack): """ @@ -959,7 +1013,7 @@ class layout(): """ self.create_channel_route(netlist, offset, layer_stack, vertical=False) - def add_boundary(self, ll=vector(0,0), ur=None): + def add_boundary(self, ll=vector(0, 0), ur=None): """ Add boundary for debugging dimensions """ if "stdc" in techlayer.keys(): boundary_layer = "stdc" @@ -981,10 +1035,10 @@ class layout(): for creating wells, for example. Doesn't check for minimum widths or spacings.""" - xmin=insts[0].lx() - ymin=insts[0].by() - xmax=insts[0].rx() - ymax=insts[0].uy() + xmin = insts[0].lx() + ymin = insts[0].by() + xmax = insts[0].rx() + ymax = insts[0].uy() for inst in insts: xmin = min(xmin, inst.lx()) ymin = min(ymin, inst.by()) @@ -992,50 +1046,52 @@ class layout(): ymax = max(ymax, inst.uy()) self.add_rect(layer=layer, - offset=vector(xmin,ymin), + offset=vector(xmin, ymin), width=xmax-xmin, height=ymax-ymin) - def copy_power_pins(self, inst, name): """ - This will copy a power pin if it is on M3. If it is on M1, it will add a power via too. + This will copy a power pin if it is on M3. + If it is on M1, it will add a power via too. """ - pins=inst.get_pins(name) + pins = inst.get_pins(name) for pin in pins: - if pin.layer=="m3": - self.add_layout_pin(name, pin.layer, pin.ll(), pin.width(), pin.height()) - elif pin.layer=="m1": + if pin.layer == "m3": + self.add_layout_pin(name, + pin.layer, + pin.ll(), + pin.width(), + pin.height()) + elif pin.layer == "m1": self.add_power_pin(name, pin.center()) else: debug.warning("{0} pins of {1} should be on metal3 or metal1 for supply router.".format(name,inst.name)) - 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 M3 down to M1 at the given center location. The starting layer is specified to determine which vias are needed. """ if vertical: - direction=("V","V") + direction = ("V", "V") else: - direction=("H","H") + direction = ("H", "H") - if start_layer=="m1": + if start_layer == "m1": self.add_via_center(layers=self.m1_stack, size=size, offset=loc, directions=direction) - - if start_layer=="m1" or start_layer=="m2": - via=self.add_via_center(layers=self.m2_stack, - size=size, - offset=loc, - directions=direction) - - if start_layer=="m3": + if start_layer == "m1" or start_layer == "m2": + via = self.add_via_center(layers=self.m2_stack, + size=size, + offset=loc, + directions=direction) + if start_layer == "m3": self.add_layout_pin_rect_center(text=name, layer="m3", offset=loc) @@ -1048,10 +1104,11 @@ class layout(): def add_power_ring(self, bbox): """ - Create vdd and gnd power rings around an area of the bounding box argument. Must - have a supply_rail_width and supply_rail_pitch defined as a member variable. - Defines local variables of the left/right/top/bottom vdd/gnd center offsets - for use in other modules.. + Create vdd and gnd power rings around an area of the bounding box + argument. Must have a supply_rail_width and supply_rail_pitch + defined as a member variable. Defines local variables of the + left/right/top/bottom vdd/gnd center offsets for use in other + modules.. """ [ll, ur] = bbox @@ -1061,65 +1118,71 @@ class layout(): width = (ur.x-ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing # LEFT vertical rails - offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch) - left_gnd_pin=self.add_layout_pin(text="gnd", - layer="m2", - offset=offset, - width=self.supply_rail_width, - height=height) + offset = ll + vector(-2 * self.supply_rail_pitch, + -2 * self.supply_rail_pitch) + left_gnd_pin = self.add_layout_pin(text="gnd", + layer="m2", + offset=offset, + width=self.supply_rail_width, + height=height) + offset = ll + vector(-1 * self.supply_rail_pitch, + -1 * self.supply_rail_pitch) + left_vdd_pin = self.add_layout_pin(text="vdd", + layer="m2", + offset=offset, + width=self.supply_rail_width, + height=height) - offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch) - left_vdd_pin=self.add_layout_pin(text="vdd", - layer="m2", - offset=offset, - width=self.supply_rail_width, - height=height) - - # RIGHT vertical rails - offset = vector(ur.x,ll.y) + vector(0,-2*self.supply_rail_pitch) + # RIGHT vertical rails + offset = vector(ur.x, ll.y) + vector(0, -2 * self.supply_rail_pitch) right_gnd_pin = self.add_layout_pin(text="gnd", - layer="m2", + layer="m2", offset=offset, width=self.supply_rail_width, height=height) - offset = vector(ur.x,ll.y) + vector(self.supply_rail_pitch,-1*self.supply_rail_pitch) - right_vdd_pin=self.add_layout_pin(text="vdd", - layer="m2", - offset=offset, - width=self.supply_rail_width, - height=height) + offset = vector(ur.x, ll.y) + vector(self.supply_rail_pitch, + -1 * self.supply_rail_pitch) + right_vdd_pin = self.add_layout_pin(text="vdd", + layer="m2", + offset=offset, + width=self.supply_rail_width, + height=height) # BOTTOM horizontal rails - offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch) - bottom_gnd_pin=self.add_layout_pin(text="gnd", - layer="m1", - offset=offset, - width=width, - height=self.supply_rail_width) + offset = ll + vector(-2 * self.supply_rail_pitch, + -2 * self.supply_rail_pitch) + bottom_gnd_pin = self.add_layout_pin(text="gnd", + layer="m1", + offset=offset, + width=width, + height=self.supply_rail_width) - offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch) - bottom_vdd_pin=self.add_layout_pin(text="vdd", - layer="m1", - offset=offset, - width=width, - height=self.supply_rail_width) + offset = ll + vector(-1 * self.supply_rail_pitch, + -1 * self.supply_rail_pitch) + bottom_vdd_pin = self.add_layout_pin(text="vdd", + layer="m1", + offset=offset, + width=width, + height=self.supply_rail_width) - # TOP horizontal rails - offset = vector(ll.x, ur.y) + vector(-2*self.supply_rail_pitch,0) - top_gnd_pin=self.add_layout_pin(text="gnd", - layer="m1", - offset=offset, - width=width, - height=self.supply_rail_width) + # TOP horizontal rails + offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch, + 0) + top_gnd_pin = self.add_layout_pin(text="gnd", + layer="m1", + offset=offset, + width=width, + height=self.supply_rail_width) - offset = vector(ll.x, ur.y) + vector(-1*self.supply_rail_pitch, self.supply_rail_pitch) - top_vdd_pin=self.add_layout_pin(text="vdd", - layer="m1", - offset=offset, - width=width, - height=self.supply_rail_width) + offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch, + self.supply_rail_pitch) + top_vdd_pin = self.add_layout_pin(text="vdd", + layer="m1", + offset=offset, + width=width, + height=self.supply_rail_width) # Remember these for connecting things in the design self.left_gnd_x_center = left_gnd_pin.cx() @@ -1132,14 +1195,13 @@ class layout(): self.top_gnd_y_center = top_gnd_pin.cy() self.top_vdd_y_center = top_vdd_pin.cy() - # Find the number of vias for this pitch self.supply_vias = 1 from sram_factory import factory while True: - c=factory.create(module_type="contact", - layer_stack=self.m1_stack, - dimensions=(self.supply_vias, self.supply_vias)) + c = factory.create(module_type="contact", + layer_stack=self.m1_stack, + dimensions=(self.supply_vias, self.supply_vias)) if c.second_layer_width < self.supply_rail_width and c.second_layer_height < self.supply_rail_width: self.supply_vias += 1 else: @@ -1158,13 +1220,15 @@ class layout(): for pt in via_points: self.add_via_center(layers=self.m1_stack, offset=pt, - size = (self.supply_vias, self.supply_vias)) + size=(self.supply_vias, + self.supply_vias)) - - def pdf_write(self, pdf_name): - # NOTE: Currently does not work (Needs further research) - #self.pdf_name = self.name + ".pdf" + """ + Display the layout to a PDF file. + """ + debug.error("NOTE: Currently does not work (Needs further research)") + # self.pdf_name = self.name + ".pdf" debug.info(0, "Writing to {}".format(pdf_name)) pdf = gdsMill.pdfLayout(self.gds) @@ -1184,22 +1248,22 @@ class layout(): def print_attr(self): """Prints a list of attributes for the current layout object""" - debug.info(0, + debug.info(0, "|==============================================================================|") - debug.info(0, + debug.info(0, "|========= LIST OF OBJECTS (Rects) FOR: " + self.name) - debug.info(0, + debug.info(0, "|==============================================================================|") for obj in self.objs: debug.info(0, "layer={0} : offset={1} : size={2}".format(obj.layerNumber, obj.offset, obj.size)) - debug.info(0, + debug.info(0, "|==============================================================================|") - debug.info(0, + debug.info(0, "|========= LIST OF INSTANCES FOR: " + self.name) - debug.info(0, + debug.info(0, "|==============================================================================|") for inst in self.insts: debug.info(0, "name={0} : mod={1} : offset={2}".format(inst.name, diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 20c513bb..bfe03c7b 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -62,7 +62,7 @@ class ptx(design.design): #ur = self.find_highest_coords() #self.add_boundary(ll, ur) - # (0,0) will be the corner ofthe active area (not the larger well) + # (0,0) will be the corner of the active area (not the larger well) self.translate_all(self.active_offset) def create_layout(self): @@ -108,8 +108,8 @@ class ptx(design.design): Pre-compute some handy layout parameters. """ - if self.num_contacts==None: - self.num_contacts=self.calculate_num_contacts() + if not self.num_contacts: + self.num_contacts = self.calculate_num_contacts() # Determine layer types needed if self.tx_type == "nmos": @@ -119,8 +119,7 @@ class ptx(design.design): self.implant_type = "p" self.well_type = "n" else: - self.error("Invalid transitor type.",-1) - + self.error("Invalid transitor type.", -1) # This is not actually instantiated but used for calculations self.active_contact = factory.create(module_type="contact", @@ -128,28 +127,30 @@ class ptx(design.design): directions = ("V", "V"), dimensions=(1, self.num_contacts)) - # The contacted poly pitch (or uncontacted in an odd technology) self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width, self.poly_space) # The contacted poly pitch (or uncontacted in an odd technology) - self.contact_pitch = 2 * self.contact_to_gate + self.contact_width + self.poly_width + self.contact_pitch = 2 * self.contact_to_gate + \ + self.contact_width + self.poly_width # The enclosure of an active contact. Not sure about second term. active_enclose_contact = max(self.active_enclose_contact, (self.active_width - self.contact_width) / 2) - # This is the distance from the edge of poly to the contacted end of active - self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate + # This is the distance from the edge of + # poly to the contacted end of active + self.end_to_poly = active_enclose_contact + \ + self.contact_width + self.contact_to_gate - # Active width is determined by enclosure on both ends and contacted pitch, # at least one poly and n-1 poly pitches - self.active_width = 2 * self.end_to_poly + self.poly_width + (self.mults - 1) * self.poly_pitch + self.ptx_active_width = 2 * self.end_to_poly + self.poly_width + \ + (self.mults - 1) * self.poly_pitch # Active height is just the transistor width - self.active_height = self.tx_width + self.ptx_active_height = self.tx_width # Poly height must include poly extension over active self.poly_height = self.tx_width + 2 * self.poly_extend_active @@ -177,11 +178,11 @@ class ptx(design.design): # This is the center of the first active contact offset (centered vertically) self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5 * self.contact_width, - 0.5 * self.active_height) + 0.5 * self.ptx_active_height) # Min area results are just flagged for now. - debug.check(self.active_width * self.active_height >= self.minarea_active, + debug.check(self.ptx_active_width * self.ptx_active_height >= self.minarea_active, "Minimum active area violated.") # We do not want to increase the poly dimensions to fix # an area problem as it would cause an LVS issue. @@ -215,14 +216,14 @@ class ptx(design.design): poly_offset = poly_positions[0] + vector(-0.5 * self.poly_width, distance_above_active) # Remove the old pin and add the new one - self.remove_layout_pin("G") # only keep the main pin + # only keep the main pin + self.remove_layout_pin("G") self.add_layout_pin(text="G", layer="poly", offset=poly_offset, width=poly_width, height=self.poly_width) - def connect_fingered_active(self, drain_positions, source_positions): """ Connect each contact up/down to a source or drain pin @@ -230,10 +231,10 @@ class ptx(design.design): # This is the distance that we must route up or down from the center # of the contacts to avoid DRC violations to the other contacts - pin_offset = vector(0, 0.5 * self.active_contact.second_layer_height \ + pin_offset = vector(0, 0.5 * self.active_contact.second_layer_height + self.m1_space + 0.5 * self.m1_width) # This is the width of a m1 extend the ends of the pin - end_offset = vector(self.m1_width/2,0) + end_offset = vector(self.m1_width / 2.0, 0) # drains always go to the MIDDLE of the cell, # so top of NMOS, bottom of PMOS @@ -246,8 +247,9 @@ class ptx(design.design): source_dir = -1 if len(source_positions) > 1: - source_offset = pin_offset.scale(source_dir,source_dir) - self.remove_layout_pin("S") # remove the individual connections + source_offset = pin_offset.scale(source_dir, source_dir) + # remove the individual connections + self.remove_layout_pin("S") # Add each vertical segment for a in source_positions: self.add_path(("m1"), @@ -310,16 +312,16 @@ class ptx(design.design): """ self.add_rect(layer="active", offset=self.active_offset, - width=self.active_width, - height=self.active_height) + width=self.ptx_active_width, + height=self.ptx_active_height) # If the implant must enclose the active, shift offset # and increase width/height enclose_width = self.implant_enclose_active enclose_offset = [enclose_width] * 2 self.add_rect(layer="{}implant".format(self.implant_type), offset=self.active_offset - enclose_offset, - width=self.active_width + 2 * enclose_width, - height=self.active_height + 2 * enclose_width) + width=self.ptx_active_width + 2 * enclose_width, + height=self.ptx_active_height + 2 * enclose_width) def add_well_implant(self): """ From 082f575e2a8fca9c347990bc4161a5be437f2401 Mon Sep 17 00:00:00 2001 From: Matthew Guthaus Date: Mon, 23 Dec 2019 21:45:09 +0000 Subject: [PATCH 043/103] Use active_width in ptx again despite colliding with DRC rule --- compiler/pgates/ptx.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index bfe03c7b..282e17d8 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -146,11 +146,11 @@ class ptx(design.design): # Active width is determined by enclosure on both ends and contacted pitch, # at least one poly and n-1 poly pitches - self.ptx_active_width = 2 * self.end_to_poly + self.poly_width + \ + self.active_width = 2 * self.end_to_poly + self.poly_width + \ (self.mults - 1) * self.poly_pitch # Active height is just the transistor width - self.ptx_active_height = self.tx_width + self.active_height = self.tx_width # Poly height must include poly extension over active self.poly_height = self.tx_width + 2 * self.poly_extend_active @@ -178,11 +178,11 @@ class ptx(design.design): # This is the center of the first active contact offset (centered vertically) self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5 * self.contact_width, - 0.5 * self.ptx_active_height) + 0.5 * self.active_height) # Min area results are just flagged for now. - debug.check(self.ptx_active_width * self.ptx_active_height >= self.minarea_active, + debug.check(self.active_width * self.active_height >= self.minarea_active, "Minimum active area violated.") # We do not want to increase the poly dimensions to fix # an area problem as it would cause an LVS issue. @@ -312,16 +312,16 @@ class ptx(design.design): """ self.add_rect(layer="active", offset=self.active_offset, - width=self.ptx_active_width, - height=self.ptx_active_height) + width=self.active_width, + height=self.active_height) # If the implant must enclose the active, shift offset # and increase width/height enclose_width = self.implant_enclose_active enclose_offset = [enclose_width] * 2 self.add_rect(layer="{}implant".format(self.implant_type), offset=self.active_offset - enclose_offset, - width=self.ptx_active_width + 2 * enclose_width, - height=self.ptx_active_height + 2 * enclose_width) + width=self.active_width + 2 * enclose_width, + height=self.active_height + 2 * enclose_width) def add_well_implant(self): """ From a6f5e59e186984a936e3e82c4d2dab66b22817d5 Mon Sep 17 00:00:00 2001 From: Matthew Guthaus Date: Mon, 23 Dec 2019 21:49:47 +0000 Subject: [PATCH 044/103] Remove unused layers and simplify layer check to work without it. --- compiler/pgates/pgate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 9cb73542..7e1fc87f 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -130,12 +130,12 @@ class pgate(design.design): max_y_offset = self.height + 0.5 * self.m1_width self.nwell_position = middle_position nwell_height = max_y_offset - middle_position.y - if layer["nwell"]: + if "nwell" in layer: self.add_rect(layer="nwell", offset=middle_position, width=self.well_width, height=nwell_height) - if layer["vtg"]: + if "vtg" in layer: self.add_rect(layer="vtg", offset=self.nwell_position, width=self.well_width, @@ -143,12 +143,12 @@ class pgate(design.design): pwell_position = vector(0, -0.5 * self.m1_width) pwell_height = middle_position.y - pwell_position.y - if layer["pwell"]: + if "pwell" in layer: self.add_rect(layer="pwell", offset=pwell_position, width=self.well_width, height=pwell_height) - if layer["vtg"]: + if "vtg" in layer: self.add_rect(layer="vtg", offset=pwell_position, width=self.well_width, From 14e8a26246d9ff0f70a94970ab527a26389255b5 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 3 Jan 2020 11:33:34 +0100 Subject: [PATCH 045/103] base/pin_layout: Make rect and layer properties only rect and layer are used to compute the hash for a pin. Having those as properties allows us to cache the hash value and only update it if either rect or layer are written. Signed-off-by: Bastian Koppelmann --- compiler/base/pin_layout.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index ea114ac6..451bb1ed 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -22,31 +22,47 @@ class pin_layout: self.name = name # repack the rect as a vector, just in case if type(rect[0]) == vector: - self.rect = rect + self._rect = rect else: - self.rect = [vector(rect[0]), vector(rect[1])] + self._rect = [vector(rect[0]), vector(rect[1])] # snap the rect to the grid - self.rect = [x.snap_to_grid() for x in self.rect] + self._rect = [x.snap_to_grid() for x in self.rect] debug.check(self.width() > 0, "Zero width pin.") debug.check(self.height() > 0, "Zero height pin.") # if it's a string, use the name if type(layer_name_pp) == str: - self.layer = layer_name_pp + self._layer = layer_name_pp # else it is required to be a lpp else: for (layer_name, lpp) in layer.items(): if not lpp: continue if self.same_lpp(layer_name_pp, lpp): - self.layer = layer_name + self._layer = layer_name break else: debug.error("Couldn't find layer {}".format(layer_name_pp), -1) self.lpp = layer[self.layer] + @property + def layer(self): + return self._layer + + @layer.setter + def layer(self, l): + self._layer = l + + @property + def rect(self): + return self._rect + + @rect.setter + def rect(self, r): + self._rect = r + def __str__(self): """ override print function output """ return "({} layer={} ll={} ur={})".format(self.name, From 7ff5121d8ce33ba7ae45d0914e493da13da5d515 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 3 Jan 2020 11:39:59 +0100 Subject: [PATCH 046/103] base/pin_layout: Implement hash cache the hash value only depends of the properties 'rect' and 'layer' so we only compute the hash if those values are changed. Otherwise we just return the precomputed value. This gives us a major speedup (~10x) if the hash is used as a key in a dict. During the grouping of pins in analyze_pins() this gives the best improvements. For example for FreePDK45 with num_bits=8, num_words=256 Improved **** Analyzing pins: 20.9 seconds ** Routing: 293.8 seconds ** SRAM creation: 349.8 seconds Non-improved **** Analyzing pins: 267.9 seconds ** Routing: 537.5 seconds ** SRAM creation: 592.9 seconds Signed-off-by: Bastian Koppelmann --- compiler/base/pin_layout.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 451bb1ed..d7f6c49e 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -46,6 +46,7 @@ class pin_layout: debug.error("Couldn't find layer {}".format(layer_name_pp), -1) self.lpp = layer[self.layer] + self._recompute_hash() @property def layer(self): @@ -54,6 +55,7 @@ class pin_layout: @layer.setter def layer(self, l): self._layer = l + self._recompute_hash() @property def rect(self): @@ -62,6 +64,11 @@ class pin_layout: @rect.setter def rect(self, r): self._rect = r + self._recompute_hash() + + def _recompute_hash(self): + """ Recompute the hash for our hash cache """ + self._hash = hash(repr(self)) def __str__(self): """ override print function output """ @@ -80,8 +87,12 @@ class pin_layout: self.rect[1]) def __hash__(self): - """ Implement the hash function for sets etc. """ - return hash(repr(self)) + """ + Implement the hash function for sets etc. We only return a cached + value, that is updated when either 'rect' or 'layer' are changed. This + is a major speedup, if pin_layout is used as a key for dicts. + """ + return self._hash def __lt__(self, other): """ Provide a function for ordering items by the ll point """ From c0c89e465a191d676c8a8f2f16a4ea2939046616 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 3 Jan 2020 11:49:51 +0100 Subject: [PATCH 047/103] vector: Implement hash cache for vector3d and vector this gives us a small runtime improvement in the router. For FreePDK45 word_size=8, num_words=256 Improved *** Maze routing supplies: 89.8 seconds ** Routing: 279.3 seconds Non-improved *** Maze routing supplies: 105.1 seconds ** Routing: 293.5 seconds Signed-off-by: Bastian Koppelmann --- compiler/base/vector.py | 3 ++- compiler/router/vector3d.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/base/vector.py b/compiler/base/vector.py index 8bf09f7d..6688462d 100644 --- a/compiler/base/vector.py +++ b/compiler/base/vector.py @@ -28,6 +28,7 @@ class vector(): else: self.x = float(x) self.y = float(y) + self._hash = hash((self.x,self.y)) def __str__(self): """ override print function output """ @@ -97,7 +98,7 @@ class vector(): Note: This assumes that you DON'T CHANGE THE VECTOR or it will break things. """ - return hash((self.x,self.y)) + return self._hash def snap_to_grid(self): self.x = self.snap_offset_to_grid(self.x) diff --git a/compiler/router/vector3d.py b/compiler/router/vector3d.py index 0d183021..066f843f 100644 --- a/compiler/router/vector3d.py +++ b/compiler/router/vector3d.py @@ -27,7 +27,7 @@ class vector3d(): self.x = x self.y = y self.z = z - + self._hash = hash((self.x,self.y,self.z)) def __str__(self): """ override print function output """ @@ -96,7 +96,7 @@ class vector3d(): Note: This assumes that you DON'T CHANGE THE VECTOR or it will break things. """ - return hash((self.x,self.y,self.z)) + return self._hash def __rsub__(self, other): From 2c610036b2ee00d10286353c2e758e3778ed56b5 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 3 Jan 2020 12:24:14 +0100 Subject: [PATCH 048/103] router/supply_grid_router: Print init time to the user this can take considerable amount of time, so the user knows that useful work is done. Signed-off-by: Bastian Koppelmann --- compiler/router/supply_grid_router.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 881e87b2..b28f875e 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -31,6 +31,8 @@ class supply_grid_router(router): This will route on layers in design. It will get the blockages from either the gds file name or the design itself (by saving to a gds file). """ + start_time = datetime.now() + # Power rail width in minimum wire widths self.rail_track_width = 3 @@ -40,8 +42,8 @@ class supply_grid_router(router): self.supply_rails = {} # This is the same as above but as a sigle set for the all the rails self.supply_rail_tracks = {} - + print_time("Init supply router", datetime.now(), start_time, 3) def create_routing_grid(self): From ea00258be9b7474c5aa4b6f6781c28d8a32426c9 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 16 Jan 2020 19:26:43 +0000 Subject: [PATCH 049/103] Cleanup contact --- compiler/base/contact.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 64433633..1a5785aa 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -55,7 +55,7 @@ class contact(hierarchy_design.hierarchy_design): # Module does not have pins, but has empty pin list. self.pins = [] self.create_layout() - + def create_layout(self): self.setup_layers() @@ -65,8 +65,10 @@ class contact(hierarchy_design.hierarchy_design): self.create_second_layer_enclosure() self.create_nitride_cut_enclosure() - self.height = max(obj.offset.y + obj.height for obj in self.objs) - self.width = max(obj.offset.x + obj.width for obj in self.objs) + self.height = max(self.first_layer_position.y + self.first_layer_height, + self.second_layer_position.y + self.second_layer_height) + self.width = max(self.first_layer_position.x + self.first_layer_width, + self.second_layer_position.x + self.second_layer_width) # Do not include the select layer in the height/width if self.implant_type and self.well_type: @@ -228,9 +230,10 @@ class contact(hierarchy_design.hierarchy_design): # Optionally implant well if layer exists well_layer = "{}well".format(self.well_type) if well_layer in tech.layer: - well_position = self.first_layer_position - [drc("well_enclose_active")] * 2 - well_width = self.first_layer_width + 2 * drc("well_enclose_active") - well_height = self.first_layer_height + 2 * drc("well_enclose_active") + well_enclose_active = drc(well_layer + "_enclose_active") + well_position = self.first_layer_position - [well_enclose_active] * 2 + well_width = self.first_layer_width + 2 * well_enclose_active + well_height = self.first_layer_height + 2 * well_enclose_active self.add_rect(layer=well_layer, offset=well_position, width=well_width, From a2387da29d48d9064991f7726bf8c448c9053c51 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 16 Jan 2020 19:26:57 +0000 Subject: [PATCH 050/103] PEP format design --- compiler/base/design.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/base/design.py b/compiler/base/design.py index 59772a02..9b595b5f 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -63,8 +63,8 @@ class design(hierarchy_design): contact1 = getattr(contact, layer1 + layer2) max_contact = max(contact1.width, contact1.height) - layer1_space = getattr(self, layer1+"_space") - layer2_space = getattr(self, layer2+"_space") + layer1_space = getattr(self, layer1 + "_space") + layer2_space = getattr(self, layer2 + "_space") pitch = max_contact + max(layer1_space, layer2_space) return pitch @@ -83,7 +83,7 @@ class design(hierarchy_design): if match.group(1) == "active_contact": setattr(self, "contact_width", drc(match.group(0))) else: - setattr(self, match.group(1)+"_width", drc(match.group(0))) + setattr(self, match.group(1) + "_width", drc(match.group(0))) # Single layer area rules match = re.search(r"minarea_(.*)", rule) @@ -93,10 +93,10 @@ class design(hierarchy_design): # Single layer spacing rules match = re.search(r"(.*)_to_(.*)", rule) if match and match.group(1) == match.group(2): - setattr(self, match.group(1)+"_space", drc(match.group(0))) + setattr(self, match.group(1) + "_space", drc(match.group(0))) elif match and match.group(1) != match.group(2): if match.group(2) == "poly_active": - setattr(self, match.group(1)+"_to_contact", + setattr(self, match.group(1) + "_to_contact", drc(match.group(0))) else: setattr(self, match.group(0), drc(match.group(0))) From 262782cba09b1c3167239d5876c83e1c3253262a Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 16 Jan 2020 19:27:39 +0000 Subject: [PATCH 051/103] Remove print, fix compare --- compiler/base/hierarchy_layout.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 7b870ae7..caf8d02b 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -533,7 +533,6 @@ class layout(): height = boundary[1][1] - boundary[0][1] width = boundary[1][0] - boundary[0][0] (layer_number, layer_purpose) = techlayer[boundary_layer] - print(self.name, boundary, height, width) gds_layout.addBox(layerNumber=layer_number, purposeNumber=layer_purpose, offsetInMicrons=boundary[0], @@ -1019,7 +1018,7 @@ class layout(): boundary_layer = "stdc" else: boundary_layer = "boundary" - if ur == None: + if not ur: self.boundary = self.add_rect(layer=boundary_layer, offset=ll, height=self.height, From 306740f0f3cd21e4615e7732f58246e1fc38d307 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 16 Jan 2020 19:27:59 +0000 Subject: [PATCH 052/103] Add empty minarea function --- compiler/base/pin_layout.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index ea114ac6..a600d474 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -101,7 +101,14 @@ class pin_layout: max_y = max(max_y, pin.ur().y) self.rect = [vector(min_x, min_y), vector(max_x, max_y)] - + + def fix_minarea(self): + """ + Try to fix minimum area rule. + """ + min_area = drc("{}_minarea".format(self.layer)) + pass + def inflate(self, spacing=None): """ Inflate the rectangle by the spacing (or other rule) From 9beb0f4ece894618e65f0c9528639c50a5e0c61a Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 23 Jan 2020 19:43:41 +0000 Subject: [PATCH 053/103] Add separate well design rules. Needed to fix various pgates with wells. Did some cleanup of these gates as well. --- compiler/base/hierarchy_layout.py | 67 +++++++++++++++--- compiler/bitcells/pbitcell.py | 18 ++--- compiler/modules/bank.py | 2 +- compiler/modules/port_address.py | 2 +- compiler/modules/port_data.py | 2 +- compiler/pgates/pgate.py | 20 +++--- compiler/pgates/pinv.py | 4 +- compiler/pgates/pnand2.py | 4 +- compiler/pgates/pnand3.py | 12 ++-- compiler/pgates/pnor2.py | 5 +- compiler/pgates/precharge.py | 4 +- compiler/pgates/ptristate_inv.py | 7 +- compiler/pgates/ptx.py | 105 ++++++++++++++++------------- compiler/pgates/pwrite_driver.py | 16 ++--- technology/freepdk45/tech/tech.py | 10 ++- technology/scn4m_subm/tech/tech.py | 14 ++-- 16 files changed, 181 insertions(+), 111 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index caf8d02b..bfb1c2d2 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -68,8 +68,10 @@ class layout(): return (base_offset, y_dir) def find_lowest_coords(self): - """Finds the lowest set of 2d cartesian coordinates within - this layout""" + """ + Finds the lowest set of 2d cartesian coordinates within + this layout + """ if len(self.objs) > 0: lowestx1 = min(obj.lx() for obj in self.objs if obj.name != "label") @@ -116,6 +118,54 @@ class layout(): return vector(max(highestx1, highestx2), max(highesty1, highesty2)) + def find_highest_layer_coords(self, layer): + """ + Finds the highest set of 2d cartesian coordinates within + this layout on a layer + """ + # Only consider the layer not the purpose for now + layerNumber = techlayer[layer][0] + try: + highestx = max(obj.rx() for obj in self.objs if obj.layerNumber == layerNumber) + except ValueError: + highestx =0 + try: + highesty = max(obj.uy() for obj in self.objs if obj.layerNumber == layerNumber) + except ValueError: + highesty = 0 + + for inst in self.insts: + # This really should be rotated/mirrored etc... + subcoord = inst.mod.find_highest_layer_coords(layer) + inst.offset + highestx = max(highestx, subcoord.x) + highesty = max(highesty, subcoord.y) + + return vector(highestx, highesty) + + def find_lowest_layer_coords(self, layer): + """ + Finds the highest set of 2d cartesian coordinates within + this layout on a layer + """ + # Only consider the layer not the purpose for now + layerNumber = techlayer[layer][0] + try: + lowestx = min(obj.lx() for obj in self.objs if obj.layerNumber == layerNumber) + except ValueError: + lowestx = 0 + try: + lowesty = min(obj.by() for obj in self.objs if obj.layerNumber == layerNumber) + except ValueError: + lowesty = 0 + + for inst in self.insts: + # This really should be rotated/mirrored etc... + subcoord = inst.mod.find_lowest_layer_coords(layer) + inst.offset + lowestx = min(lowestx, subcoord.x) + lowesty = min(lowesty, subcoord.y) + + return vector(lowestx, lowesty) + def translate_all(self, offset): """ Translates all objects, instances, and pins by the given (x,y) offset @@ -429,7 +479,7 @@ class layout(): return inst def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None): - """ + """ Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """ @@ -466,7 +516,7 @@ class layout(): mults=mults, tx_type=tx_type) self.add_mod(mos) - inst = self.add_inst(name=mos.name, + inst = self.add_inst(name=mos.name, mod=mos, offset=offset, mirror=mirror, @@ -652,22 +702,21 @@ class layout(): vertical=False, make_pins=False) - def create_bus(self, layer, pitch, offset, names, length, vertical, make_pins): """ Create a horizontal or vertical bus. It can be either just rectangles, or actual - layout pins. It returns an map of line center line positions indexed by name. + layout pins. It returns an map of line center line positions indexed by name. The other coordinate is a 0 since the bus provides a range. TODO: combine with channel router. """ # 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 = {} if vertical: for i in range(len(names)): - line_offset = offset + vector(i*pitch,0) + line_offset = offset + vector(i * pitch, 0) if make_pins: self.add_layout_pin(text=names[i], layer=layer, @@ -885,7 +934,7 @@ class layout(): return g def vcg_nets_overlap(net1, net2, vertical, pitch): - """ + """ Check all the pin pairs on two nets and return a pin overlap if any pin overlaps. """ diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index d809c6f1..6de87507 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -977,12 +977,12 @@ class pbitcell(bitcell_base.bitcell_base): """ # extend pwell to encompass entire nmos region of the cell up to the # height of the tallest nmos transistor - max_nmos_well_height = max(self.inverter_nmos.cell_well_height, - self.readwrite_nmos.cell_well_height, - self.write_nmos.cell_well_height, - self.read_nmos.cell_well_height) + max_nmos_well_height = max(self.inverter_nmos.well_height, + self.readwrite_nmos.well_height, + self.write_nmos.well_height, + self.read_nmos.well_height) well_height = max_nmos_well_height + self.port_ypos \ - - self.well_enclose_active - self.gnd_position.y + - self.nwell_enclose_active - self.gnd_position.y offset = vector(self.leftmost_xpos, self.botmost_ypos) self.add_rect(layer="pwell", offset=offset, @@ -992,16 +992,16 @@ class pbitcell(bitcell_base.bitcell_base): # extend nwell to encompass inverter_pmos # calculate offset of the left pmos well inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ - - self.well_enclose_active + - self.nwell_enclose_active inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ - + self.inverter_gap - self.well_enclose_active + + self.inverter_gap - self.nwell_enclose_active # calculate width of the two combined nwells # calculate height to encompass nimplant connected to vdd well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ - + 2 * self.well_enclose_active + + 2 * self.nwell_enclose_active well_height = self.vdd_position.y - inverter_well_ypos \ - + self.well_enclose_active + drc["minwidth_tx"] + + self.nwell_enclose_active + drc["minwidth_tx"] offset = [inverter_well_xpos, inverter_well_ypos] self.add_rect(layer="nwell", diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 4527044c..7ed0c9a3 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -333,7 +333,7 @@ class bank(design.design): self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines # A space for wells or jogging m2 - self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"), + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"), 3*self.m2_pitch) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 7f1fdd49..44b0d5a5 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -149,7 +149,7 @@ class port_address(design.design): """ # A space for wells or jogging m2 - self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"), + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"), 3*self.m2_pitch) row_decoder_offset = vector(0,0) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 33ecdd01..9e7f4048 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -212,7 +212,7 @@ class port_data(design.design): # A space for wells or jogging m2 between modules - self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"), + self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"), 3*self.m2_pitch) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 7e1fc87f..a964cbe4 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -8,7 +8,7 @@ import contact import design import debug -from tech import layer, drc +from tech import layer from vector import vector from globals import OPTS from sram_factory import factory @@ -127,9 +127,10 @@ class pgate(design.design): """ Extend the n/p wells to cover whole cell """ # Add a rail width to extend the well to the top of the rail - max_y_offset = self.height + 0.5 * self.m1_width - self.nwell_position = middle_position - nwell_height = max_y_offset - middle_position.y + nwell_max_offset = max(self.find_highest_layer_coords("nwell").y, + self.height + 0.5 * self.m1_width) + nwell_position = middle_position + nwell_height = nwell_max_offset - middle_position.y if "nwell" in layer: self.add_rect(layer="nwell", offset=middle_position, @@ -137,11 +138,14 @@ class pgate(design.design): height=nwell_height) if "vtg" in layer: self.add_rect(layer="vtg", - offset=self.nwell_position, + offset=nwell_position, width=self.well_width, height=nwell_height) - pwell_position = vector(0, -0.5 * self.m1_width) + # Start this half a rail width below the cell + pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y, + -0.5 * self.m1_width) + pwell_position = vector(0, pwell_min_offset) pwell_height = middle_position.y - pwell_position.y if "pwell" in layer: self.add_rect(layer="pwell", @@ -168,7 +172,7 @@ class pgate(design.design): # OR align the active with the top of PMOS active. max_y_offset = self.height + 0.5 * self.m1_width contact_yoffset = min(pmos_pos.y + pmos.active_height - pmos.active_contact.first_layer_height, - max_y_offset - pmos.active_contact.first_layer_height / 2 - self.well_enclose_active) + max_y_offset - pmos.active_contact.first_layer_height / 2 - self.nwell_enclose_active) contact_offset = vector(contact_xoffset, contact_yoffset) # Offset by half a contact in x and y contact_offset += vector(0.5 * pmos.active_contact.first_layer_width, @@ -220,7 +224,7 @@ class pgate(design.design): # Must be at least an well enclosure of active up # from the bottom of the well contact_yoffset = max(nmos_pos.y, - self.well_enclose_active \ + self.nwell_enclose_active \ - nmos.active_contact.first_layer_height / 2) contact_offset = vector(contact_xoffset, contact_yoffset) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index ce477a83..8a3a8326 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -153,7 +153,7 @@ class pinv(pgate.pgate): # the well width is determined the multi-finger PMOS device width plus # the well contact width and half well enclosure on both sides self.well_width = self.pmos.active_width + self.pmos.active_contact.width \ - + self.active_space + 2*self.well_enclose_active + + self.active_space + 2*self.nwell_enclose_active self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -223,7 +223,7 @@ class pinv(pgate.pgate): self.output_pos = vector(0, 0.5 * (pmos_drain_pos.y + nmos_drain_pos.y)) # This will help with the wells - self.well_pos = vector(0, self.nmos_inst.uy()) + self.well_pos = self.output_pos def route_outputs(self): """ diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index dda736d2..8d3605a2 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -100,7 +100,7 @@ class pnand2(pgate.pgate): # Enclosure space on the sides. self.well_width = 2 * self.pmos.active_width + contact.activem1.width \ + 2 * self.active_space \ - + 2 * self.well_enclose_active + + 2 * self.nwell_enclose_active self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -171,7 +171,7 @@ class pnand2(pgate.pgate): 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height)) # This will help with the wells - self.well_pos = vector(0, self.nmos1_inst.uy()) + self.well_pos = self.output_pos def add_well_contacts(self): """ diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 8ccbff2c..bb379db7 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -92,14 +92,11 @@ class pnand3(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \ - + 2 * self.active_space + 2 * self.well_enclose_active \ + + 2 * self.active_space + 2 * self.nwell_enclose_active \ - self.overlap_offset.x self.width = self.well_width # Height is an input parameter, so it is not recomputed. - # This will help with the wells and the input/output placement - self.output_pos = vector(0, 0.5*self.height) - # This is the extra space needed to ensure DRC rules # to the active contacts nmos = factory.create(module_type="ptx", tx_type="nmos") @@ -178,9 +175,12 @@ class pnand3(pgate.pgate): self.nmos3_pos = nmos2_pos + self.overlap_offset self.nmos3_inst.place(self.nmos3_pos) - + + # This will help with the wells and the input/output placement + self.output_pos = vector(0, 0.5*self.height) + # This should be placed at the top of the NMOS well - self.well_pos = vector(0, self.nmos1_inst.uy()) + self.well_pos = self.output_pos def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index d24189ac..9895e9d9 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -98,7 +98,7 @@ class pnor2(pgate.pgate): self.well_width = 2 * self.pmos.active_width \ + self.pmos.active_contact.width \ + 2 * self.active_space \ - + 2 * self.well_enclose_active + + 2 * self.nwell_enclose_active self.width = self.well_width # Height is an input parameter, so it is not recomputed. @@ -136,7 +136,6 @@ class pnor2(pgate.pgate): mod=self.pmos) self.connect_inst(["net1", "B", "Z", "vdd"]) - self.nmos1_inst = self.add_inst(name="pnor2_nmos1", mod=self.nmos) self.connect_inst(["Z", "A", "gnd", "gnd"]) @@ -170,7 +169,7 @@ class pnor2(pgate.pgate): 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height)) # This will help with the wells - self.well_pos = vector(0, self.nmos1_inst.uy()) + self.well_pos = self.output_pos def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index f9e8ce87..d1e2f3af 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -116,7 +116,7 @@ class precharge(design.design): # adds the lower pmos to layout bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx() self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, - self.well_enclose_active), + self.nwell_enclose_active), self.pmos.active_offset.y) self.lower_pmos_inst.place(self.lower_pmos_position) @@ -176,7 +176,7 @@ class precharge(design.design): # adds the contact from active to metal1 well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ + vector(0, self.upper_pmos1_inst.uy() + contact.activem1.height / 2 \ - + self.well_extend_active) + + self.nwell_extend_active) self.add_via_center(layers=self.active_stack, offset=well_contact_pos, implant_type="n", diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py index d2965cde..764b87c0 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/pgates/ptristate_inv.py @@ -44,7 +44,7 @@ class ptristate_inv(pgate.pgate): """ Calls all functions related to the generation of the netlist """ self.add_pins() self.add_ptx() - self.create_ptx() + self.create_ptx() def create_layout(self): """ Calls all functions related to the generation of the layout """ @@ -61,7 +61,6 @@ class ptristate_inv(pgate.pgate): """ Adds pins for spice netlist """ self.add_pin_list(["in", "out", "en", "en_bar", "vdd", "gnd"]) - def setup_layout_constants(self): """ Pre-compute some handy layout parameters. @@ -73,7 +72,7 @@ class ptristate_inv(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. - self.well_width = 2 * self.pmos.active_width + self.well_enclose_active + self.well_width = 2 * self.pmos.active_width + self.nwell_enclose_active # Add an extra space because we route the output on the right of the S/D self.width = self.well_width + 0.5 * self.m1_space @@ -81,7 +80,6 @@ class ptristate_inv(pgate.pgate): # Make sure we can put a well above and below self.top_bottom_space = max(contact.activem1.width, contact.activem1.height) - def add_ptx(self): """ Create the PMOS and NMOS transistors. """ @@ -95,7 +93,6 @@ class ptristate_inv(pgate.pgate): width=self.pmos_width, mults=1, tx_type="pmos") - self.add_mod(self.pmos) def route_supply_rails(self): diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 282e17d8..32bbeefe 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -58,9 +58,9 @@ class ptx(design.design): # some transistor sizes in other netlist depend on pbitcell self.create_layout() - #ll = self.find_lowest_coords() - #ur = self.find_highest_coords() - #self.add_boundary(ll, ur) + ll = self.find_lowest_coords() + ur = self.find_highest_coords() + self.add_boundary(ll, ur) # (0,0) will be the corner of the active area (not the larger well) self.translate_all(self.active_offset) @@ -99,7 +99,7 @@ class ptx(design.design): drc("minwidth_poly")) area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd, area_sd) - self.spice_device= main_str + area_str + self.spice_device = main_str + area_str self.spice.append("\n* ptx " + self.spice_device) # self.spice.append(".ENDS {0}".format(self.name)) @@ -124,30 +124,30 @@ class ptx(design.design): # This is not actually instantiated but used for calculations self.active_contact = factory.create(module_type="contact", layer_stack=self.active_stack, - directions = ("V", "V"), + directions=("V", "V"), dimensions=(1, self.num_contacts)) - # The contacted poly pitch (or uncontacted in an odd technology) + # The contacted poly pitch self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width, self.poly_space) - # The contacted poly pitch (or uncontacted in an odd technology) - self.contact_pitch = 2 * self.contact_to_gate + \ - self.contact_width + self.poly_width + # The contacted poly pitch + self.contact_spacing = 2 * self.contact_to_gate + \ + self.contact_width + self.poly_width + + # This is measured because of asymmetric enclosure rules + active_enclose_contact = 0.5*(self.active_contact.width - self.contact_width) - # The enclosure of an active contact. Not sure about second term. - active_enclose_contact = max(self.active_enclose_contact, - (self.active_width - self.contact_width) / 2) - - # This is the distance from the edge of - # poly to the contacted end of active - self.end_to_poly = active_enclose_contact + \ - self.contact_width + self.contact_to_gate + # This is the distance from the side of + # poly gate to the contacted end of active + # (i.e. the "outside" contacted diffusion sizes) + self.end_to_poly = self.active_contact.width - active_enclose_contact + \ + self.contact_to_gate # Active width is determined by enclosure on both ends and contacted pitch, # at least one poly and n-1 poly pitches self.active_width = 2 * self.end_to_poly + self.poly_width + \ - (self.mults - 1) * self.poly_pitch + (self.mults - 1) * self.poly_pitch # Active height is just the transistor width self.active_height = self.tx_width @@ -155,32 +155,35 @@ class ptx(design.design): # Poly height must include poly extension over active self.poly_height = self.tx_width + 2 * self.poly_extend_active + well_name = "{}well".format(self.well_type) + # The active offset is due to the well extension - self.active_offset = vector([self.well_enclose_active] * 2) + if well_name in layer: + well_enclose_active = drc(well_name + "_enclose_active") + self.active_offset = vector([well_enclose_active] * 2) + else: + self.active_offset = vector(0, 0) # Well enclosure of active, ensure minwidth as well - well_name = "{}well".format(self.well_type) if well_name in layer: - self.cell_well_width = max(self.active_width + 2 * self.well_enclose_active, - self.well_width) - self.cell_well_height = max(self.tx_width + 2 * self.well_enclose_active, - self.well_width) + well_width_rule = drc("minwidth_" + well_name) + well_enclose_active = drc(well_name + "_enclose_active") + self.well_width = max(self.active_width + 2 * well_enclose_active, + well_width_rule) + self.well_height = max(self.active_height + 2 * well_enclose_active, + well_width_rule) # We are going to shift the 0,0, so include that in the width and height - self.height = self.cell_well_height - self.active_offset.y - self.width = self.cell_well_width - self.active_offset.x + self.height = self.well_height - self.active_offset.y + self.width = self.well_width - self.active_offset.x else: - # If no well, use the boundary of the active and poly + # The well is not included in the height and width self.height = self.poly_height self.width = self.active_width - # The active offset is due to the well extension - self.active_offset = vector([self.well_enclose_active] * 2) - # This is the center of the first active contact offset (centered vertically) - self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5 * self.contact_width, + self.contact_offset = self.active_offset + vector(0.5 * self.active_contact.width, 0.5 * self.active_height) - # Min area results are just flagged for now. debug.check(self.active_width * self.active_height >= self.minarea_active, "Minimum active area violated.") @@ -231,8 +234,8 @@ class ptx(design.design): # This is the distance that we must route up or down from the center # of the contacts to avoid DRC violations to the other contacts - pin_offset = vector(0, 0.5 * self.active_contact.second_layer_height - + self.m1_space + 0.5 * self.m1_width) + pin_offset = vector(0, + 0.5 * self.active_contact.second_layer_height + self.m1_space + 0.5 * self.m1_width) # This is the width of a m1 extend the ends of the pin end_offset = vector(self.m1_width / 2.0, 0) @@ -328,26 +331,34 @@ class ptx(design.design): Add an (optional) well and implant for the type of transistor. """ well_name = "{}well".format(self.well_type) + if not (well_name in layer or "vtg" in layer): + return + + center_pos = self.active_offset + vector(self.width / 2.0, + self.height / 2.0) + well_ll = center_pos - vector(self.well_width / 2.0, + self.well_height / 2.0) + well_ll = well_ll - vector(0, + self.poly_extend_active) + if well_name in layer: self.add_rect(layer=well_name, - offset=(0,0), - width=self.cell_well_width, - height=self.cell_well_height) + offset=well_ll, + width=self.well_width, + height=self.well_height) if "vtg" in layer: self.add_rect(layer="vtg", - offset=(0,0), - width=self.cell_well_width, - height=self.cell_well_height) - + offset=well_ll, + width=self.well_width, + height=self.well_height) def calculate_num_contacts(self): - """ + """ Calculates the possible number of source/drain contacts in a finger. For now, it is hard set as 1. """ return 1 - def get_contact_positions(self): """ Create a list of the centers of drain and source contact positions. @@ -361,10 +372,10 @@ class ptx(design.design): for i in range(self.mults): if i%2: # It's a source... so offset from previous drain. - source_positions.append(drain_positions[-1] + vector(self.contact_pitch, 0)) + source_positions.append(drain_positions[-1] + vector(self.contact_spacing, 0)) else: # It's a drain... so offset from previous source. - drain_positions.append(source_positions[-1] + vector(self.contact_pitch, 0)) + drain_positions.append(source_positions[-1] + vector(self.contact_spacing, 0)) return [source_positions,drain_positions] @@ -379,7 +390,7 @@ class ptx(design.design): contact=self.add_via_center(layers=self.active_stack, offset=pos, size=(1, self.num_contacts), - directions=("H","V"), + directions=("V","V"), implant_type=self.implant_type, well_type=self.well_type) self.add_layout_pin_rect_center(text="S", @@ -393,7 +404,7 @@ class ptx(design.design): contact=self.add_via_center(layers=self.active_stack, offset=pos, size=(1, self.num_contacts), - directions=("H","V"), + directions=("V","V"), implant_type=self.implant_type, well_type=self.well_type) self.add_layout_pin_rect_center(text="D", diff --git a/compiler/pgates/pwrite_driver.py b/compiler/pgates/pwrite_driver.py index 2dc356c3..ed970f9b 100644 --- a/compiler/pgates/pwrite_driver.py +++ b/compiler/pgates/pwrite_driver.py @@ -40,12 +40,10 @@ class pwrite_driver(design.design): # Creates the netlist and layout # Since it has variable height, it is not a pgate. self.create_netlist() - if not OPTS.netlist_only: + if not OPTS.netlist_only: self.create_layout() self.DRC_LVS() - - def create_netlist(self): self.add_pins() self.add_modules() @@ -55,8 +53,6 @@ class pwrite_driver(design.design): self.place_modules() self.route_wires() self.route_supplies() - - def add_pins(self): self.add_pin("din", "INPUT") @@ -66,17 +62,19 @@ class pwrite_driver(design.design): self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - def add_modules(self): # Tristate inverter self.tri = factory.create(module_type="ptristate_inv", height="min") self.add_mod(self.tri) - debug.check(self.tri.width Date: Fri, 24 Jan 2020 17:45:24 +0000 Subject: [PATCH 054/103] Add fudge factor to pbitcell wells --- compiler/bitcells/pbitcell.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 6de87507..4f19c489 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -983,10 +983,12 @@ class pbitcell(bitcell_base.bitcell_base): self.read_nmos.well_height) well_height = max_nmos_well_height + self.port_ypos \ - self.nwell_enclose_active - self.gnd_position.y - offset = vector(self.leftmost_xpos, self.botmost_ypos) + # FIXME fudge factor xpos + well_width = self.width + 2*self.nwell_enclose_active + offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos) self.add_rect(layer="pwell", offset=offset, - width=self.width, + width=well_width, height=well_height) # extend nwell to encompass inverter_pmos @@ -1003,7 +1005,8 @@ class pbitcell(bitcell_base.bitcell_base): well_height = self.vdd_position.y - inverter_well_ypos \ + self.nwell_enclose_active + drc["minwidth_tx"] - offset = [inverter_well_xpos, inverter_well_ypos] + # FIXME fudge factor xpos + offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos] self.add_rect(layer="nwell", offset=offset, width=well_width, From 71cbe7401788736119e6827bede68c98b9f82e92 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 24 Jan 2020 18:00:28 +0000 Subject: [PATCH 055/103] Round middle position fix --- compiler/base/vector.py | 2 +- compiler/pgates/pgate.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/base/vector.py b/compiler/base/vector.py index 8bf09f7d..c9304f42 100644 --- a/compiler/base/vector.py +++ b/compiler/base/vector.py @@ -49,7 +49,7 @@ class vector(): else: self.x=float(value[0]) self.y=float(value[1]) - + def __getitem__(self, index): """ override getitem function diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index a964cbe4..e009d1bb 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -126,6 +126,8 @@ class pgate(design.design): def extend_wells(self, middle_position): """ Extend the n/p wells to cover whole cell """ + # FIXME: float rounding problem + middle_position = middle_position.snap_to_grid() # Add a rail width to extend the well to the top of the rail nwell_max_offset = max(self.find_highest_layer_coords("nwell").y, self.height + 0.5 * self.m1_width) From 877ea53b7f82c44b387e593659a591b1f7dd1ea3 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 24 Jan 2020 21:24:44 +0000 Subject: [PATCH 056/103] Fix conflicting boundary name --- compiler/base/geometry.py | 2 ++ compiler/base/hierarchy_layout.py | 23 +++++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 46cfe7c7..f354cf02 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -69,7 +69,9 @@ class geometry: """ Transform with offset, mirror and rotation to get the absolute pin location. We must then re-find the ll and ur. The master is the cell instance. """ if OPTS.netlist_only: + self.boundary = [vector(0,0), vector(0,0)] return + (ll, ur) = [vector(0, 0), vector(self.width, self.height)] if mirror == "MX": diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index bfb1c2d2..a1b18ef3 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -31,7 +31,7 @@ class layout(): self.name = name self.width = None self.height = None - self.boundary = None + self.bounding_box = None self.insts = [] # Holds module/cell layout instances self.objs = [] # Holds all other objects (labels, geometries, etc) self.pin_map = {} # Holds name->pin_layout map for all pins @@ -574,7 +574,7 @@ class layout(): # If it's not a premade cell # and we didn't add our own boundary, # we should add a boundary just for DRC in some technologies - if not self.is_library_cell and not self.boundary: + if not self.is_library_cell and not self.bounding_box: # If there is a boundary layer, and we didn't create one, add one. if "stdc" in techlayer.keys(): boundary_layer = "stdc" @@ -1063,20 +1063,23 @@ class layout(): def add_boundary(self, ll=vector(0, 0), ur=None): """ Add boundary for debugging dimensions """ + if OPTS.netlist_only: + return + if "stdc" in techlayer.keys(): boundary_layer = "stdc" else: boundary_layer = "boundary" if not ur: - self.boundary = self.add_rect(layer=boundary_layer, - offset=ll, - height=self.height, - width=self.width) + self.bounding_box = self.add_rect(layer=boundary_layer, + offset=ll, + height=self.height, + width=self.width) else: - self.boundary = self.add_rect(layer=boundary_layer, - offset=ll, - height=ur.y-ll.y, - width=ur.x-ll.x) + self.bounding_box = self.add_rect(layer=boundary_layer, + offset=ll, + height=ur.y-ll.y, + width=ur.x-ll.x) def add_enclosure(self, insts, layer="nwell"): """ Add a layer that surrounds the given instances. Useful From 3fb2b9c1c36bd27a7dd157c9dfad759ff64e9d0c Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 27 Jan 2020 11:53:29 +0100 Subject: [PATCH 057/103] Bitcell arrays: Create abstract base class a lot of functions of dummy- and bitcell-array are either copy-pasted or have just slight differences. Merge all of those into an abstract base class such that we don't have too much duplicate code. Signed-off-by: Bastian Koppelmann --- compiler/modules/base_array.py | 107 ++++++++++++++++++++++++++++++ compiler/modules/bitcell_array.py | 91 ++----------------------- compiler/modules/dummy_array.py | 94 ++------------------------ 3 files changed, 115 insertions(+), 177 deletions(-) create mode 100644 compiler/modules/base_array.py diff --git a/compiler/modules/base_array.py b/compiler/modules/base_array.py new file mode 100644 index 00000000..db2b1be6 --- /dev/null +++ b/compiler/modules/base_array.py @@ -0,0 +1,107 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +import design + +class bitcell_base_array(design.design): + """ + Abstract base class for bitcell-arrays -- bitcell, dummy + """ + def __init__(self, cols, rows, name): + design.design.__init__(self, name) + debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) + self.add_comment("rows: {0} cols: {1}".format(rows, cols)) + + self.column_size = cols + self.row_size = rows + + def add_pins(self): + row_list = self.cell.get_all_wl_names() + column_list = self.cell.get_all_bitline_names() + for col in range(self.column_size): + for cell_column in column_list: + self.add_pin(cell_column+"_{0}".format(col), "INOUT") + for row in range(self.row_size): + for cell_row in row_list: + self.add_pin(cell_row+"_{0}".format(row), "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def get_bitcell_pins(self, col, row): + """ Creates a list of connections in the bitcell, + indexed by column and row, for instance use in bitcell_array """ + + bitcell_pins = [] + + pin_names = self.cell.get_all_bitline_names() + for pin in pin_names: + bitcell_pins.append(pin+"_{0}".format(col)) + pin_names = self.cell.get_all_wl_names() + for pin in pin_names: + bitcell_pins.append(pin+"_{0}".format(row)) + bitcell_pins.append("vdd") + bitcell_pins.append("gnd") + + return bitcell_pins + + def add_layout_pins(self): + """ Add the layout pins """ + + row_list = self.cell.get_all_wl_names() + column_list = self.cell.get_all_bitline_names() + + for col in range(self.column_size): + for cell_column in column_list: + bl_pin = self.cell_inst[0,col].get_pin(cell_column) + self.add_layout_pin(text=cell_column+"_{0}".format(col), + layer=bl_pin.layer, + offset=bl_pin.ll().scale(1,0), + width=bl_pin.width(), + height=self.height) + + for row in range(self.row_size): + for cell_row in row_list: + wl_pin = self.cell_inst[row,0].get_pin(cell_row) + self.add_layout_pin(text=cell_row+"_{0}".format(row), + layer=wl_pin.layer, + offset=wl_pin.ll().scale(0,1), + width=self.width, + height=wl_pin.height()) + + # For every second row and column, add a via for gnd and vdd + for row in range(self.row_size): + for col in range(self.column_size): + inst = self.cell_inst[row,col] + for pin_name in ["vdd", "gnd"]: + for pin in inst.get_pins(pin_name): + self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) + + + + def place_array(self, name_template, row_offset=0): + # We increase it by a well enclosure so the precharges don't overlap our wells + self.height = self.row_size*self.cell.height + self.width = self.column_size*self.cell.width + + xoffset = 0.0 + for col in range(self.column_size): + yoffset = 0.0 + for row in range(self.row_size): + name = name_template.format(row, col) + + if (row + row_offset) % 2: + tempy = yoffset + self.cell.height + dir_key = "MX" + else: + tempy = yoffset + dir_key = "" + + self.cell_inst[row,col].place(offset=[xoffset, tempy], + mirror=dir_key) + yoffset += self.cell.height + xoffset += self.cell.width diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index bf43736b..4c1c798b 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -7,25 +7,21 @@ # import debug import design +from base_array import bitcell_base_array from tech import drc, spice from vector import vector from globals import OPTS from sram_factory import factory import logical_effort -class bitcell_array(design.design): +class bitcell_array(bitcell_base_array): """ Creates a rows x cols array of memory cells. Assumes bit-lines and word line is connected by abutment. Connects the word lines and bit lines. """ def __init__(self, cols, rows, name): - design.design.__init__(self, name) - debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) - self.add_comment("rows: {0} cols: {1}".format(rows, cols)) - - self.column_size = cols - self.row_size = rows + super().__init__(cols, rows, name) self.create_netlist() if not OPTS.netlist_only: @@ -44,27 +40,7 @@ class bitcell_array(design.design): def create_layout(self): - # We increase it by a well enclosure so the precharges don't overlap our wells - self.height = self.row_size*self.cell.height - self.width = self.column_size*self.cell.width - - xoffset = 0.0 - for col in range(self.column_size): - yoffset = 0.0 - for row in range(self.row_size): - name = "bit_r{0}_c{1}".format(row, col) - - if row % 2: - tempy = yoffset + self.cell.height - dir_key = "MX" - else: - tempy = yoffset - dir_key = "" - - self.cell_inst[row,col].place(offset=[xoffset, tempy], - mirror=dir_key) - yoffset += self.cell.height - xoffset += self.cell.width + self.place_array("bit_r{0}_c{1}") self.add_layout_pins() @@ -72,41 +48,13 @@ class bitcell_array(design.design): self.DRC_LVS() - def add_pins(self): - row_list = self.cell.get_all_wl_names() - column_list = self.cell.get_all_bitline_names() - for col in range(self.column_size): - for cell_column in column_list: - self.add_pin(cell_column+"_{0}".format(col), "INOUT") - for row in range(self.row_size): - for cell_row in row_list: - self.add_pin(cell_row+"_{0}".format(row), "INPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") def add_modules(self): """ Add the modules used in this design """ self.cell = factory.create(module_type="bitcell") self.add_mod(self.cell) - def get_bitcell_pins(self, col, row): - """ Creates a list of connections in the bitcell, - indexed by column and row, for instance use in bitcell_array """ - - bitcell_pins = [] - - pin_names = self.cell.get_all_bitline_names() - for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(col)) - pin_names = self.cell.get_all_wl_names() - for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(row)) - bitcell_pins.append("vdd") - bitcell_pins.append("gnd") - - return bitcell_pins - def create_instances(self): """ Create the module instances used in this design """ self.cell_inst = {} @@ -117,37 +65,6 @@ class bitcell_array(design.design): mod=self.cell) self.connect_inst(self.get_bitcell_pins(col, row)) - def add_layout_pins(self): - """ Add the layout pins """ - - row_list = self.cell.get_all_wl_names() - column_list = self.cell.get_all_bitline_names() - - for col in range(self.column_size): - for cell_column in column_list: - bl_pin = self.cell_inst[0,col].get_pin(cell_column) - self.add_layout_pin(text=cell_column+"_{0}".format(col), - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1,0), - width=bl_pin.width(), - height=self.height) - - for row in range(self.row_size): - for cell_row in row_list: - wl_pin = self.cell_inst[row,0].get_pin(cell_row) - self.add_layout_pin(text=cell_row+"_{0}".format(row), - layer=wl_pin.layer, - offset=wl_pin.ll().scale(0,1), - width=self.width, - height=wl_pin.height()) - - # For every second row and column, add a via for gnd and vdd - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row,col] - for pin_name in ["vdd", "gnd"]: - for pin in inst.get_pins(pin_name): - self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 1e5a2121..88717b56 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -5,23 +5,19 @@ # import debug import design +from base_array import bitcell_base_array from tech import drc import contact from sram_factory import factory from vector import vector from globals import OPTS -class dummy_array(design.design): +class dummy_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ def __init__(self, cols, rows, mirror=0, name=""): - design.design.__init__(self, name) - debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) - self.add_comment("rows: {0} cols: {1}".format(rows, cols)) - - self.column_size = cols - self.row_size = rows + super().__init__(cols, rows, name) self.mirror = mirror self.create_netlist() @@ -37,27 +33,7 @@ class dummy_array(design.design): def create_layout(self): - # We increase it by a well enclosure so the precharges don't overlap our wells - self.height = self.row_size*self.dummy_cell.height - self.width = self.column_size*self.dummy_cell.width - - xoffset = 0.0 - for col in range(self.column_size): - yoffset = 0.0 - for row in range(self.row_size): - name = "dummy_r{0}_c{1}".format(row, col) - - if (row+self.mirror) % 2: - tempy = yoffset + self.dummy_cell.height - dir_key = "MX" - else: - tempy = yoffset - dir_key = "" - - self.cell_inst[row,col].place(offset=[xoffset, tempy], - mirror=dir_key) - yoffset += self.dummy_cell.height - xoffset += self.dummy_cell.width + self.place_array("dummy_r{0}_c{1}", self.mirror) self.add_layout_pins() @@ -65,18 +41,6 @@ class dummy_array(design.design): self.DRC_LVS() - def add_pins(self): - row_list = self.cell.get_all_wl_names() - column_list = self.cell.get_all_bitline_names() - for col in range(self.column_size): - for cell_column in column_list: - self.add_pin(cell_column+"_{0}".format(col), "INOUT") - for row in range(self.row_size): - for cell_row in row_list: - self.add_pin(cell_row+"_{0}".format(row), "INPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - def add_modules(self): """ Add the modules used in this design """ self.dummy_cell = factory.create(module_type="dummy_bitcell") @@ -84,23 +48,6 @@ class dummy_array(design.design): self.cell = factory.create(module_type="bitcell") - def get_bitcell_pins(self, col, row): - """ Creates a list of connections in the bitcell, - indexed by column and row, for instance use in bitcell_array """ - - bitcell_pins = [] - - pin_names = self.cell.get_all_bitline_names() - for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(col)) - pin_names = self.cell.get_all_wl_names() - for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(row)) - bitcell_pins.append("vdd") - bitcell_pins.append("gnd") - - return bitcell_pins - def create_instances(self): """ Create the module instances used in this design """ @@ -111,39 +58,6 @@ class dummy_array(design.design): self.cell_inst[row,col]=self.add_inst(name=name, mod=self.dummy_cell) self.connect_inst(self.get_bitcell_pins(col, row)) - - def add_layout_pins(self): - """ Add the layout pins """ - - row_list = self.cell.get_all_wl_names() - column_list = self.cell.get_all_bitline_names() - - for col in range(self.column_size): - for cell_column in column_list: - bl_pin = self.cell_inst[0,col].get_pin(cell_column) - self.add_layout_pin(text=cell_column+"_{0}".format(col), - layer="m2", - offset=bl_pin.ll(), - width=bl_pin.width(), - height=self.height) - - for row in range(self.row_size): - for cell_row in row_list: - wl_pin = self.cell_inst[row,0].get_pin(cell_row) - self.add_layout_pin(text=cell_row+"_{0}".format(row), - layer="m1", - offset=wl_pin.ll(), - width=self.width, - height=wl_pin.height()) - - # For every second row and column, add a via for gnd and vdd - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row,col] - for pin_name in ["vdd", "gnd"]: - for pin in inst.get_pins(pin_name): - self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) - def input_load(self): wl_wire = self.gen_wl_wire() From 988df8ebb919d1147b7c09901db32fe73c7e9d89 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Tue, 28 Jan 2020 11:47:32 +0100 Subject: [PATCH 058/103] hierarchy_layout: Add methods to create via stacks this allows us to simplify add_power_pin() and gives a clean API to create vias through multiple layers. Signed-off-by: Bastian Koppelmann --- compiler/base/hierarchy_layout.py | 81 +++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index a1b18ef3..4dedd9a9 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -11,6 +11,7 @@ import gdsMill import debug from tech import drc, GDS from tech import layer as techlayer +from tech import layer_stacks import os from globals import OPTS from vector import vector @@ -508,6 +509,71 @@ class layout(): # We don't model the logical connectivity of wires/paths self.connect_inst([]) return inst + + def add_via_stack(self, offset, direction, from_layer, to_layer, + size=[1,1]): + """ + Punch a stack of vias from a start layer to a target layer. + """ + + return self.__add_via_stack_internal(offset=offset, + direction=direction, + from_layer=from_layer, + to_layer=to_layer, + via_func=self.add_via, + last_via=None, + size=size) + + def add_via_stack_center(self, offset, direction, from_layer, to_layer, + size=[1,1]): + """ + Punch a stack of vias from a start layer to a target layer by the center + coordinate accounting for mirroring and rotation. + """ + return self.__add_via_stack_internal(offset=offset, + direction=direction, + from_layer=from_layer, + to_layer=to_layer, + via_func=self.add_via_center, + last_via=None, + size=size) + + + def __add_via_stack_internal(self, offset, direction, from_layer, to_layer, + via_func, last_via, size): + """ + Punch a stack of vias from a start layer to a target layer. Here we + figure out whether to punch it up or down the stack. + """ + + if from_layer == to_layer: + return last_via + + from_id = int(from_layer[1]) + to_id = int(to_layer[1]) + + if from_id < to_id: # grow the stack up + search_id = 0 + next_id = 2 + else: # grow the stack down + search_id = 2 + next_id = 0 + + curr_stack = next(filter(lambda stack: stack[search_id] == from_layer, layer_stacks), None) + if curr_stack is None: + raise ValueError("Cannot create via from '{0}' to '{1}'." \ + "Layer '{0}' not defined" + .format(from_layer, to_layer)) + + via = via_func(layers=curr_stack, size=size, offset=offset, directions=direction) + return self.__add_via_stack_internal(offset=offset, + direction=direction, + from_layer=curr_stack[next_id], + to_layer=to_layer, + via_func=via_func, + last_via=via, + size=size) + def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): """Adds a ptx module to the design.""" @@ -1131,17 +1197,12 @@ class layout(): else: direction = ("H", "H") - if start_layer == "m1": - self.add_via_center(layers=self.m1_stack, - size=size, - offset=loc, - directions=direction) + via = self.add_via_stack_center(from_layer=start_layer, + to_layer="m3", + size=size, + offset=loc, + direction=direction) - if start_layer == "m1" or start_layer == "m2": - via = self.add_via_center(layers=self.m2_stack, - size=size, - offset=loc, - directions=direction) if start_layer == "m3": self.add_layout_pin_rect_center(text=name, layer="m3", From 90a4a72bba2b50cb1cf1d79b79d78b450b01ba13 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 23 Dec 2019 16:37:16 +0100 Subject: [PATCH 059/103] modules: Use add_power_pin API for all modules sense_amp_array, write_driver_array, and single_column_mux were the only offenders. Signed-off-by: Bastian Koppelmann --- compiler/modules/sense_amp_array.py | 21 +++++++++------------ compiler/modules/write_driver_array.py | 11 ++++------- compiler/pgates/single_level_column_mux.py | 12 ++++-------- 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 2447e4b8..ca5afc47 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -98,18 +98,15 @@ class sense_amp_array(design.design): for i in range(len(self.local_insts)): inst = self.local_insts[i] - gnd_pos = inst.get_pin("gnd").center() - self.add_via_center(layers=self.m2_stack, - offset=gnd_pos) - self.add_layout_pin_rect_center(text="gnd", - layer="m3", - offset=gnd_pos) - vdd_pos = inst.get_pin("vdd").center() - self.add_via_center(layers=self.m2_stack, - offset=vdd_pos) - self.add_layout_pin_rect_center(text="vdd", - layer="m3", - offset=vdd_pos) + self.add_power_pin(name = "gnd", + loc = inst.get_pin("gnd").center(), + start_layer="m2", + vertical=True) + + self.add_power_pin(name = "vdd", + loc = inst.get_pin("vdd").center(), + start_layer="m2", + vertical=True) bl_pin = inst.get_pin("bl") br_pin = inst.get_pin("br") diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index ac4dab00..900a8294 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -141,13 +141,10 @@ class write_driver_array(design.design): for n in ["vdd", "gnd"]: pin_list = self.driver_insts[i].get_pins(n) for pin in pin_list: - pin_pos = pin.center() - # Add the M2->M3 stack - self.add_via_center(layers=self.m2_stack, - offset=pin_pos) - self.add_layout_pin_rect_center(text=n, - layer="m3", - offset=pin_pos) + self.add_power_pin(name = n, + loc = pin.center(), + vertical=True, + start_layer = "m2") if self.write_size: for bit in range(self.num_wmasks): en_pin = self.driver_insts[bit*self.write_size].get_pin("en") diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 18b4b10f..66faf1da 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -180,14 +180,10 @@ class single_level_column_mux(pgate.pgate): implant_type="p", well_type="p") - # Add the M1->M2->M3 stack - self.add_via_center(layers=self.m1_stack, - offset=active_pos) - self.add_via_center(layers=self.m2_stack, - offset=active_pos) - self.add_layout_pin_rect_center(text="gnd", - layer="m3", - offset=active_pos) + # Add the M1->..->power_grid_layer stack + self.add_power_pin(name = "gnd", + loc = active_pos, + start_layer="m1") # Add well enclosure over all the tx and contact self.add_rect(layer="pwell", From 9749c522d1cc0e5003f9fc8a80dcf4601d368b18 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Tue, 28 Jan 2020 11:53:36 +0100 Subject: [PATCH 060/103] tech: Make power_grid configurable this is the first step to allow engineers, porting technologies, more room for routing their handmade cells. For now, we don't allow the specification of power_grids where the lower layer prefers to be routed vertically. This is due to the router not connecting some pins properly in that case. Signed-off-by: Bastian Koppelmann --- compiler/base/hierarchy_layout.py | 27 ++++++++++++++++++--------- compiler/router/router_tech.py | 17 +++++++++++++++-- compiler/sram/sram_base.py | 21 ++++++++++++++------- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 4dedd9a9..451d1356 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -39,6 +39,12 @@ class layout(): self.visited = [] # List of modules we have already visited self.is_library_cell = False # Flag for library cells self.gds_read() + try: + from tech import power_grid + self.pwr_grid_layer = power_grid[0] + except ImportError: + self.pwr_grid_layer = "m3" + ############################################################ # GDS layout @@ -1169,12 +1175,12 @@ class layout(): def copy_power_pins(self, inst, name): """ - This will copy a power pin if it is on M3. + This will copy a power pin if it is on the lowest power_grid layer. If it is on M1, it will add a power via too. """ pins = inst.get_pins(name) for pin in pins: - if pin.layer == "m3": + if pin.layer == self.pwr_grid_layer: self.add_layout_pin(name, pin.layer, pin.ll(), @@ -1183,14 +1189,17 @@ class layout(): elif pin.layer == "m1": self.add_power_pin(name, pin.center()) else: - debug.warning("{0} pins of {1} should be on metal3 or metal1 for supply router.".format(name,inst.name)) + debug.warning("{0} pins of {1} should be on {2} or metal1 for "\ + "supply router." + .format(name,inst.name,self.pwr_grid_layer)) def add_power_pin(self, name, loc, size=[1, 1], vertical=False, start_layer="m1"): """ - Add a single power pin from M3 down to M1 at the given center location. - The starting layer is specified to determine which vias are needed. + 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 + which vias are needed. """ if vertical: direction = ("V", "V") @@ -1198,18 +1207,18 @@ class layout(): direction = ("H", "H") via = self.add_via_stack_center(from_layer=start_layer, - to_layer="m3", + to_layer=self.pwr_grid_layer, size=size, offset=loc, direction=direction) - if start_layer == "m3": + if start_layer == self.pwr_grid_layer: self.add_layout_pin_rect_center(text=name, - layer="m3", + layer=self.pwr_grid_layer, offset=loc) else: self.add_layout_pin_rect_center(text=name, - layer="m3", + layer=self.pwr_grid_layer, offset=loc, width=via.width, height=via.height) diff --git a/compiler/router/router_tech.py b/compiler/router/router_tech.py index efae2708..e0f277e9 100644 --- a/compiler/router/router_tech.py +++ b/compiler/router/router_tech.py @@ -5,7 +5,7 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from tech import drc, layer +from tech import drc, layer, preferred_directions from contact import contact from vector import vector import debug @@ -26,6 +26,9 @@ class router_tech: self.rail_track_width = rail_track_width if len(self.layers) == 1: + if preferred_directions[self.layers[0]] != "H": + debug.warning("Using '{}' for horizontal routing, but it " \ + "prefers vertical routing".format(self.layers[0])) self.horiz_layer_name = self.vert_layer_name = self.layers[0] self.horiz_lpp = self.vert_lpp = layer[self.layers[0]] @@ -35,7 +38,17 @@ class router_tech: self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing else: - (self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers + (try_horiz_layer, self.via_layer_name, try_vert_layer) = self.layers + + # figure out wich of the two layers prefers horizontal/vertical + # routing + if preferred_directions[try_horiz_layer] == "H" and preferred_directions[try_vert_layer] == "V": + self.horiz_layer_name = try_horiz_layer + self.vert_layer_name = try_vert_layer + else: + raise ValueError("Layer '{}' and '{}' are using the wrong " \ + "preferred_directions '{}' and '{}'. Only "\ + "('H', 'V') are supported") via_connect = contact(self.layers, (1, 1)) max_via_size = max(via_connect.width,via_connect.height) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 65297889..c7c91e90 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -148,14 +148,21 @@ class sram_base(design, verilog, lef): if not OPTS.route_supplies: # Do not route the power supply (leave as must-connect pins) return - elif "m4" in tech.layer: - # Route a M3/M4 grid - from supply_grid_router import supply_grid_router as router - rtr=router(self.m3_stack, self) - elif "m3" in tech.layer: - from supply_tree_router import supply_tree_router as router - rtr=router(("m3",), self) + 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 + if "m4" in tech.layer: + # Route a M3/M4 grid + grid_stack = self.m3_stack + elif "m3" in tech.layer: + grid_stack =("m3",) + + from supply_grid_router import supply_grid_router as router + rtr=router(grid_stack, self) rtr.route() From 407bd026ee0a3ef64c53bb01c689fe4cf07185db Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 23 Dec 2019 17:36:57 +0100 Subject: [PATCH 061/103] tech: Make m3_stack the power_grid stack for FreePDK45/scn4m explicitly stating the power_grid makes people porting a new technology aware of this option. Signed-off-by: Bastian Koppelmann --- technology/freepdk45/tech/tech.py | 5 +++++ technology/scn4m_subm/tech/tech.py | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 8b769af9..00ab4b9b 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -71,6 +71,11 @@ preferred_directions = {"poly": "V", "m2": "V", "m3": "H", "m4": "V"} +################################################### +# Power grid +################################################### +# Use M3/M4 +power_grid = m3_stack ################################################### # GDS Layer Map diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 3000dc99..87c1d830 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -71,6 +71,12 @@ preferred_directions = {"poly": "V", "m3": "H", "m4": "V"} +################################################### +# Power grid +################################################### +# Use M3/M4 +power_grid = m3_stack + ################################################### ##GDS Layer Map ################################################### From df9f351a915d1af4bb77322c98514dbc5a59b5f1 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Tue, 28 Jan 2020 11:03:08 +0100 Subject: [PATCH 062/103] Add custom cell properties to technologies this is technology specific database to store data about the custom design cells. For now it only contains on which axis the bitcells are mirrored. This is a first step to support thin cells that need to be mirrored on the x and y axis. Signed-off-by: Bastian Koppelmann --- compiler/modules/base_array.py | 4 ++-- compiler/modules/custom_cell_properties.py | 28 ++++++++++++++++++++++ compiler/modules/replica_column.py | 4 ++-- technology/freepdk45/tech/tech.py | 7 ++++++ technology/scn3me_subm/tech/tech.py | 8 +++++++ technology/scn4m_subm/tech/tech.py | 7 ++++++ 6 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 compiler/modules/custom_cell_properties.py diff --git a/compiler/modules/base_array.py b/compiler/modules/base_array.py index db2b1be6..40481c59 100644 --- a/compiler/modules/base_array.py +++ b/compiler/modules/base_array.py @@ -7,6 +7,7 @@ # import debug import design +from tech import cell_properties class bitcell_base_array(design.design): """ @@ -93,8 +94,7 @@ class bitcell_base_array(design.design): yoffset = 0.0 for row in range(self.row_size): name = name_template.format(row, col) - - if (row + row_offset) % 2: + if cell_properties.bitcell.mirror.x and (row + row_offset) % 2: tempy = yoffset + self.cell.height dir_key = "MX" else: diff --git a/compiler/modules/custom_cell_properties.py b/compiler/modules/custom_cell_properties.py new file mode 100644 index 00000000..085527fc --- /dev/null +++ b/compiler/modules/custom_cell_properties.py @@ -0,0 +1,28 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2020 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +class _MirrorAxis: + def __init__(self, x, y): + self.x = x + self.y = y + +class _Bitcell: + def __init__(self, mirror): + self.mirror = mirror + +class CellProperties(): + """ + TODO + """ + def __init__(self): + self.names = {} + self._bitcell = _Bitcell(_MirrorAxis(True, False)) + + @property + def bitcell(self): + return self._bitcell diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 5552ed55..283787c2 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -92,7 +92,7 @@ class replica_column(design.design): self.connect_inst(self.get_bitcell_pins(0, row)) def place_instances(self): - + from tech import cell_properties # Flip the mirrors if we have an odd number of replica+dummy rows at the bottom # so that we will start with mirroring rather than not mirroring rbl_offset = (self.left_rbl+1)%2 @@ -100,7 +100,7 @@ class replica_column(design.design): for row in range(self.total_size): name = "bit_r{0}_{1}".format(row,"rbl") offset = vector(0,self.cell.height*(row+(row+rbl_offset)%2)) - if (row+rbl_offset)%2: + if cell_properties.bitcell.mirror.x and (row+rbl_offset)%2: dir_key = "MX" else: dir_key = "R0" diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 8b769af9..c21b1f97 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -8,6 +8,7 @@ import os from design_rules import * from module_type import * +from custom_cell_properties import CellProperties """ File containing the process technology parameters for FreePDK 45nm. @@ -24,6 +25,12 @@ File containing the process technology parameters for FreePDK 45nm. # For example: tech_modules['contact'] = 'contact_freepdk45' tech_modules = ModuleType() +################################################### +# Custom cell properties +################################################### +cell_properties = CellProperties() +cell_properties.bitcell.mirror.x = True +cell_properties.bitcell.mirror.y = False ################################################### # GDS file info diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index cb476df5..9e6f370b 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -1,6 +1,7 @@ import os from design_rules import * from module_type import * +from custom_cell_properties import CellProperties """ File containing the process technology parameters for SCMOS 3me, subm, 180nm. @@ -12,6 +13,13 @@ File containing the process technology parameters for SCMOS 3me, subm, 180nm. # For example: tech_modules['contact'] = 'contact_scn3me' tech_modules = ModuleType() +################################################### +# Custom cell properties +################################################### +cell_properties = CellProperties() +cell_properties.bitcell.mirror.x = True +cell_properties.bitcell.mirror.y = False + #GDS file info GDS={} # gds units diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 3000dc99..ce89e5af 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -8,6 +8,7 @@ import os from design_rules import * from module_type import * +from custom_cell_properties import CellProperties """ File containing the process technology parameters for SCMOS 4m, 0.35um @@ -24,6 +25,12 @@ File containing the process technology parameters for SCMOS 4m, 0.35um # For example: tech_modules['contact'] = 'contact_scn4m' tech_modules = ModuleType() +################################################### +# Custom cell properties +################################################### +cell_properties = CellProperties() +cell_properties.bitcell.mirror.x = True +cell_properties.bitcell.mirror.y = False ################################################### # GDS file info From dd1afe03133d70254711574152ff519e8a8f67a6 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 27 Jan 2020 17:17:06 +0100 Subject: [PATCH 063/103] Bitcell arrays: Allow mirroring on the y axis this allows for bitcells that need to be mirrored on the y axis, like thin cells. However, the portdata elements also need to be mirrored on the y axis. Otherwise the router will fail horribly when connecting bitlines. Signed-off-by: Bastian Koppelmann --- compiler/modules/base_array.py | 34 +++++++++++++++++---- compiler/modules/bitcell_array.py | 4 +-- compiler/modules/dummy_array.py | 4 +-- compiler/modules/replica_bitcell_array.py | 36 ++++++++++++++++++----- compiler/modules/replica_column.py | 27 ++++++++++++++--- 5 files changed, 84 insertions(+), 21 deletions(-) diff --git a/compiler/modules/base_array.py b/compiler/modules/base_array.py index 40481c59..08a52c21 100644 --- a/compiler/modules/base_array.py +++ b/compiler/modules/base_array.py @@ -13,13 +13,14 @@ class bitcell_base_array(design.design): """ Abstract base class for bitcell-arrays -- bitcell, dummy """ - def __init__(self, cols, rows, name): + def __init__(self, cols, rows, name, column_offset): design.design.__init__(self, name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) self.column_size = cols self.row_size = rows + self.column_offset = column_offset def add_pins(self): row_list = self.cell.get_all_wl_names() @@ -82,6 +83,23 @@ class bitcell_base_array(design.design): for pin in inst.get_pins(pin_name): self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer) + def _adjust_x_offset(self, xoffset, col, col_offset): + tempx = xoffset + dir_y = False + # If we mirror the current cell on the y axis adjust the x position + if cell_properties.bitcell.mirror.y and (col + col_offset) % 2: + tempx = xoffset + self.cell.width + dir_y = True + return (tempx, dir_y) + + def _adjust_y_offset(self, yoffset, row, row_offset): + tempy = yoffset + dir_x = False + # If we mirror the current cell on the x axis adjust the y position + if cell_properties.bitcell.mirror.x and (row + row_offset) % 2: + tempy = yoffset + self.cell.height + dir_x = True + return (tempy, dir_x) def place_array(self, name_template, row_offset=0): @@ -92,16 +110,22 @@ class bitcell_base_array(design.design): xoffset = 0.0 for col in range(self.column_size): yoffset = 0.0 + tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset) + for row in range(self.row_size): name = name_template.format(row, col) - if cell_properties.bitcell.mirror.x and (row + row_offset) % 2: - tempy = yoffset + self.cell.height + tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset) + + if dir_x and dir_y: + dir_key = "XY" + elif dir_x: dir_key = "MX" + elif dir_y: + dir_key = "MY" else: - tempy = yoffset dir_key = "" - self.cell_inst[row,col].place(offset=[xoffset, tempy], + self.cell_inst[row,col].place(offset=[tempx, tempy], mirror=dir_key) yoffset += self.cell.height xoffset += self.cell.width diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 4c1c798b..636408df 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -20,8 +20,8 @@ class bitcell_array(bitcell_base_array): and word line is connected by abutment. Connects the word lines and bit lines. """ - def __init__(self, cols, rows, name): - super().__init__(cols, rows, name) + def __init__(self, cols, rows, name, column_offset=0): + super().__init__(cols, rows, name, column_offset) self.create_netlist() if not OPTS.netlist_only: diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 88717b56..997330e1 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -16,8 +16,8 @@ class dummy_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, cols, rows, mirror=0, name=""): - super().__init__(cols, rows, name) + def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): + super().__init__(cols, rows, name, column_offset) self.mirror = mirror self.create_netlist() diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 74962ba2..9cf5c5a2 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -86,6 +86,7 @@ class replica_bitcell_array(design.design): # Bitcell array self.bitcell_array = factory.create(module_type="bitcell_array", + column_offset=1 + self.left_rbl, cols=self.column_size, rows=self.row_size) self.add_mod(self.bitcell_array) @@ -95,12 +96,17 @@ class replica_bitcell_array(design.design): for bit in range(self.left_rbl+self.right_rbl): if bit=self.total_size-right_rbl-1, @@ -96,14 +98,31 @@ class replica_column(design.design): # Flip the mirrors if we have an odd number of replica+dummy rows at the bottom # so that we will start with mirroring rather than not mirroring rbl_offset = (self.left_rbl+1)%2 - + + # if our bitcells are mirrored on the y axis, check if we are in global + # column that needs to be flipped. + dir_y = False + xoffset = 0 + if cell_properties.bitcell.mirror.y and self.column_offset % 2: + dir_y = True + xoffset = self.replica_cell.width + for row in range(self.total_size): + dir_x = False name = "bit_r{0}_{1}".format(row,"rbl") - offset = vector(0,self.cell.height*(row+(row+rbl_offset)%2)) if cell_properties.bitcell.mirror.x and (row+rbl_offset)%2: + dir_x = True + + offset = vector(xoffset,self.cell.height*(row+(row+rbl_offset)%2)) + + if dir_x and dir_y: + dir_key = "XY" + elif dir_x: dir_key = "MX" + elif dir_y: + dir_key = "MY" else: - dir_key = "R0" + dir_key = "" self.cell_inst[row].place(offset=offset, mirror=dir_key) From ed66fca031d0af78883b0348e845dac20e1e0b25 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 27 Jan 2020 17:20:48 +0100 Subject: [PATCH 064/103] write_driver/sense_amp/precharge arrays: Allow y axis mirroring since the bitlines alternate in the bitcell array we also need to mirror the port_data elements. Signed-off-by: Bastian Koppelmann --- compiler/modules/precharge_array.py | 14 ++++++++++++-- compiler/modules/sense_amp_array.py | 19 ++++++++++++++++--- compiler/modules/write_driver_array.py | 13 +++++++++++-- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index c2617f50..54ed2690 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -107,9 +107,19 @@ class precharge_array(design.design): def place_insts(self): """ Places precharge array by horizontally tiling the precharge cell""" + from tech import cell_properties + xoffset = 0 for i in range(self.columns): - offset = vector(self.pc_cell.width * i, 0) - self.local_insts[i].place(offset) + tempx = xoffset + if cell_properties.bitcell.mirror.y and (i + 1) % 2: + mirror = "MY" + tempx = tempx + self.pc_cell.width + else: + mirror = "" + + offset = vector(tempx, 0) + self.local_insts[i].place(offset=offset, mirror=mirror) + xoffset = xoffset + self.pc_cell.width def get_en_cin(self): """Get the relative capacitance of all the clk connections in the precharge array""" diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 2447e4b8..fcb68792 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -84,14 +84,27 @@ class sense_amp_array(design.design): "en", "vdd", "gnd"]) def place_sense_amp_array(self): - + from tech import cell_properties if self.bitcell.width > self.amp.width: amp_spacing = self.bitcell.width * self.words_per_row else: amp_spacing = self.amp.width * self.words_per_row + for i in range(0,self.word_size): - amp_position = vector(amp_spacing * i, 0) - self.local_insts[i].place(amp_position) + xoffset = amp_spacing * i + + # align the xoffset to the grid of bitcells. This way we + # know when to do the mirroring. + grid_x = int(xoffset / self.amp.width) + + if cell_properties.bitcell.mirror.y and grid_x % 2: + mirror = "MY" + xoffset = xoffset + self.amp.width + else: + mirror = "" + + amp_position = vector(xoffset, 0) + self.local_insts[i].place(offset=amp_position,mirror=mirror) def add_layout_pins(self): diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index ac4dab00..cac9b396 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -106,14 +106,23 @@ class write_driver_array(design.design): def place_write_array(self): + from tech import cell_properties if self.bitcell.width > self.driver.width: self.driver_spacing = self.bitcell.width else: self.driver_spacing = self.driver.width for i in range(0,self.columns,self.words_per_row): index = int(i/self.words_per_row) - base = vector(i * self.driver_spacing, 0) - self.driver_insts[index].place(base) + xoffset = i * self.driver_spacing + + if cell_properties.bitcell.mirror.y and i % 2: + mirror = "MY" + xoffset = xoffset + self.driver.width + else: + mirror = "" + + base = vector(xoffset, 0) + self.driver_insts[index].place(offset=base, mirror=mirror) def add_layout_pins(self): From b5701af8645118aefb58195079d57379dd72d664 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 27 Jan 2020 17:23:01 +0100 Subject: [PATCH 065/103] column_mux: Allow y axis mirroring since the bitlines alternate in the bitcell array we also need to mirror the port_data elements. Signed-off-by: Bastian Koppelmann --- .../modules/single_level_column_mux_array.py | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 09f21cfa..24510015 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -99,11 +99,19 @@ class single_level_column_mux_array(design.design): "gnd"]) def place_array(self): + from tech import cell_properties # For every column, add a pass gate for col_num in range(self.columns): + xoffset = col_num * self.mux.width + if cell_properties.bitcell.mirror.y and col_num % 2: + mirror = "MY" + xoffset = xoffset + self.mux.width + else: + mirror = "" + name = "XMUX{0}".format(col_num) - x_off = vector(col_num * self.mux.width, self.route_height) - self.mux_inst[col_num].place(x_off) + offset = vector(xoffset, self.route_height) + self.mux_inst[col_num].place(offset=offset, mirror=mirror) def add_layout_pins(self): @@ -161,6 +169,7 @@ class single_level_column_mux_array(design.design): def route_bitlines(self): """ Connect the output bit-lines to form the appropriate width mux """ + from tech import cell_properties for j in range(self.columns): bl_offset = self.mux_inst[j].get_pin("bl_out").bc() br_offset = self.mux_inst[j].get_pin("br_out").bc() @@ -171,23 +180,38 @@ class single_level_column_mux_array(design.design): bl_out_offset_end = bl_out_offset + vector(0,self.route_height) br_out_offset_end = br_out_offset + vector(0,self.route_height) + if cell_properties.bitcell.mirror.y and j % 2: + tmp_bl_out_end = br_out_offset_end + tmp_br_out_end = bl_out_offset_end + else: + tmp_bl_out_end = bl_out_offset_end + tmp_br_out_end = br_out_offset_end + if (j % self.words_per_row) == 0: # Create the metal1 to connect the n-way mux output from the pass gate # These will be located below the select lines. Yes, these are M2 width # to ensure vias are enclosed and M1 min width rules. width = self.m2_width + self.mux.width * (self.words_per_row - 1) - self.add_path("m1", [bl_out_offset, bl_out_offset+vector(width,0)]) - self.add_path("m1", [br_out_offset, br_out_offset+vector(width,0)]) + + if cell_properties.bitcell.mirror.y and (j % 2) == 0: + bl = self.mux.get_pin("bl") + br = self.mux.get_pin("br") + dist = abs(bl.ll().x - br.ll().x) + else: + dist = 0 + + self.add_path("m1", [bl_out_offset, bl_out_offset+vector(width+dist,0)]) + self.add_path("m1", [br_out_offset, br_out_offset+vector(width-dist,0)]) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)), layer="m2", start=bl_out_offset, - end=bl_out_offset_end) + end=tmp_bl_out_end) self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)), layer="m2", start=br_out_offset, - end=br_out_offset_end) + end=tmp_br_out_end) # This via is on the right of the wire @@ -200,8 +224,8 @@ class single_level_column_mux_array(design.design): else: - self.add_path("m2", [ bl_out_offset, bl_out_offset_end]) - self.add_path("m2", [ br_out_offset, br_out_offset_end]) + self.add_path("m2", [ bl_out_offset, tmp_bl_out_end]) + self.add_path("m2", [ br_out_offset, tmp_br_out_end]) # This via is on the right of the wire self.add_via_center(layers=self.m1_stack, From 79391b84da278637106cd04843e4747fb5aa4889 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 30 Jan 2020 01:45:33 +0000 Subject: [PATCH 066/103] Cleanup and rename vias. --- compiler/base/contact.py | 12 +++++-- compiler/base/design.py | 6 +++- compiler/bitcells/bitcell.py | 17 ++++++++-- .../custom_cell_properties.py | 12 ++++--- compiler/bitcells/pbitcell.py | 34 +++++++++---------- compiler/modules/bank_select.py | 2 +- compiler/modules/bitcell_array.py | 16 +++------ .../{base_array.py => bitcell_base_array.py} | 0 compiler/modules/hierarchical_predecode.py | 2 +- compiler/modules/module_type.py | 2 +- compiler/modules/multibank.py | 4 +-- compiler/modules/wordline_driver.py | 4 +-- compiler/pgates/pgate.py | 10 +++--- compiler/pgates/pinv.py | 7 ++-- compiler/pgates/pnand2.py | 14 ++++---- compiler/pgates/pnand3.py | 8 ++--- compiler/pgates/pnor2.py | 12 +++---- compiler/pgates/precharge.py | 6 ++-- compiler/pgates/ptristate_inv.py | 6 ++-- compiler/sram/sram_1bank.py | 6 ++-- technology/freepdk45/tech/tech.py | 7 ++-- technology/scn4m_subm/tech/tech.py | 6 ++-- 22 files changed, 106 insertions(+), 87 deletions(-) rename compiler/{modules => bitcells}/custom_cell_properties.py (66%) rename compiler/modules/{base_array.py => bitcell_base_array.py} (100%) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 1a5785aa..51efc498 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -36,6 +36,7 @@ class contact(hierarchy_design.hierarchy_design): hierarchy_design.hierarchy_design.__init__(self, name) debug.info(4, "create contact object {0}".format(name)) + self.add_comment("layers: {0}".format(layer_stack)) self.add_comment("dimensions: {0}".format(dimensions)) if implant_type or well_type: @@ -243,14 +244,19 @@ class contact(hierarchy_design.hierarchy_design): """ Get total power of a module """ return self.return_power() - + # Set up a static for each layer to be used for measurements for layer_stack in tech.layer_stacks: (layer1, via, layer2) = layer_stack cont = factory.create(module_type="contact", layer_stack=layer_stack) module = sys.modules[__name__] - # Create the contact as just the concat of the layer names - setattr(module, layer1 + layer2, cont) + # Also create a contact that is just the first layer + if layer1 == "poly" or layer1 == "active": + setattr(module, layer1 + "_contact", cont) + else: + setattr(module, layer1 + "_via", cont) + + diff --git a/compiler/base/design.py b/compiler/base/design.py index 9b595b5f..9db68877 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -61,8 +61,12 @@ class design(hierarchy_design): """ (layer1, via, layer2) = layer_stack - contact1 = getattr(contact, layer1 + layer2) + if layer1 == "poly" or layer1 == "active": + contact1 = getattr(contact, layer1 + "_contact") + else: + contact1 = getattr(contact, layer1 + "_via") max_contact = max(contact1.width, contact1.height) + layer1_space = getattr(self, layer1 + "_space") layer2_space = getattr(self, layer2 + "_space") pitch = max_contact + max(layer1_space, layer2_space) diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index b22c9a46..5b552832 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -7,7 +7,8 @@ # import debug import utils -from tech import GDS, layer +from tech import GDS, layer, parameter +from tech import cell_properties import bitcell_base @@ -19,7 +20,14 @@ class bitcell(bitcell_base.bitcell_base): library. """ - pin_names = ["bl", "br", "wl", "vdd", "gnd"] + # If we have a split WL bitcell, if not be backwards + # compatible in the tech file + + if cell_properties.bitcell.split_wl: + pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"] + else: + pin_names = ["bl", "br", "wl", "vdd", "gnd"] + storage_nets = ['Q', 'Qbar'] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] (width, height) = utils.get_libcell_size("cell_6t", @@ -40,7 +48,10 @@ class bitcell(bitcell_base.bitcell_base): def get_all_wl_names(self): """ Creates a list of all wordline pin names """ - row_pins = ["wl"] + if cell_properties.bitcell.split_wl: + row_pins = ["wl0", "wl1"] + else: + row_pins = ["wl"] return row_pins def get_all_bitline_names(self): diff --git a/compiler/modules/custom_cell_properties.py b/compiler/bitcells/custom_cell_properties.py similarity index 66% rename from compiler/modules/custom_cell_properties.py rename to compiler/bitcells/custom_cell_properties.py index 085527fc..e795f6d7 100644 --- a/compiler/modules/custom_cell_properties.py +++ b/compiler/bitcells/custom_cell_properties.py @@ -6,22 +6,24 @@ # All rights reserved. # -class _MirrorAxis: +class _mirror_axis: def __init__(self, x, y): self.x = x self.y = y -class _Bitcell: - def __init__(self, mirror): +class _bitcell: + def __init__(self, mirror, split_wl): self.mirror = mirror + self.split_wl = split_wl -class CellProperties(): +class cell_properties(): """ TODO """ def __init__(self): self.names = {} - self._bitcell = _Bitcell(_MirrorAxis(True, False)) + self._bitcell = _bitcell(mirror = _mirror_axis(True, False), + split_wl = False) @property def bitcell(self): diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 4f19c489..61bd9710 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -212,20 +212,20 @@ class pbitcell(bitcell_base.bitcell_base): # y-offset for the access transistor's gate contact self.gate_contact_yoffset = max_contact_extension + self.m2_space \ - + 0.5 * max(contact.polym1.height, contact.m1m2.height) + + 0.5 * max(contact.poly_contact.height, contact.m1_via.height) # y-position of access transistors - self.port_ypos = self.m1_space + 0.5 * contact.m1m2.height + self.gate_contact_yoffset + self.port_ypos = self.m1_space + 0.5 * contact.m1_via.height + self.gate_contact_yoffset # y-position of inverter nmos self.inverter_nmos_ypos = self.port_ypos # spacing between ports (same for read/write and write ports) self.bitline_offset = -0.5 * self.readwrite_nmos.active_width \ - + 0.5 * contact.m1m2.height \ + + 0.5 * contact.m1_via.height \ + self.m2_space + self.m2_width m2_constraint = self.bitline_offset + self.m2_space \ - + 0.5 * contact.m1m2.height \ + + 0.5 * contact.m1_via.height \ - 0.5 * self.readwrite_nmos.active_width self.write_port_spacing = max(self.active_space, self.m1_space, @@ -233,7 +233,7 @@ class pbitcell(bitcell_base.bitcell_base): self.read_port_spacing = self.bitline_offset + self.m2_space # spacing between cross coupled inverters - self.inverter_to_inverter_spacing = contact.polym1.width + self.m1_space + self.inverter_to_inverter_spacing = contact.poly_contact.width + self.m1_space # calculations related to inverter connections inverter_pmos_contact_extension = 0.5 * \ @@ -242,19 +242,19 @@ class pbitcell(bitcell_base.bitcell_base): (self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height) self.inverter_gap = max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ - + self.poly_to_contact + 2 * contact.polym1.width \ + + self.poly_to_contact + 2 * contact.poly_contact.width \ + self.m1_space + inverter_pmos_contact_extension self.cross_couple_lower_ypos = self.inverter_nmos_ypos \ + self.inverter_nmos.active_height \ + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ - + 0.5 * contact.polym1.width + + 0.5 * contact.poly_contact.width self.cross_couple_upper_ypos = self.inverter_nmos_ypos \ + self.inverter_nmos.active_height \ + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + self.poly_to_contact \ - + 1.5 * contact.polym1.width + + 1.5 * contact.poly_contact.width # spacing between wordlines (and gnd) self.m1_offset = -0.5 * self.m1_width @@ -290,7 +290,7 @@ class pbitcell(bitcell_base.bitcell_base): (self.write_nmos.active_width + self.write_port_spacing) \ - self.num_r_ports * \ (self.read_port_width + self.read_port_spacing) \ - - self.bitline_offset - 0.5 * contact.m1m2.width + - self.bitline_offset - 0.5 * contact.m1_via.width self.width = -2 * self.leftmost_xpos self.height = self.topmost_ypos - self.botmost_ypos @@ -375,14 +375,14 @@ class pbitcell(bitcell_base.bitcell_base): # add contacts to connect gate poly to drain/source # metal1 (to connect Q to Q_bar) contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x \ - + 0.5 * contact.polym1.height, + + 0.5 * contact.poly_contact.height, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, offset=contact_offset_left) contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \ - - 0.5*contact.polym1.height, + - 0.5*contact.poly_contact.height, self.cross_couple_lower_ypos) self.add_via_center(layers=self.poly_stack, offset=contact_offset_right) @@ -824,7 +824,7 @@ class pbitcell(bitcell_base.bitcell_base): offset=port_contact_offest) self.add_path("m2", - [port_contact_offest, bl_offset], width=contact.m1m2.height) + [port_contact_offest, bl_offset], width=contact.m1_via.height) for k in range(self.total_ports): port_contact_offest = right_port_transistors[k].get_pin("D").center() @@ -836,7 +836,7 @@ class pbitcell(bitcell_base.bitcell_base): offset=port_contact_offest) self.add_path("m2", - [port_contact_offest, br_offset], width=contact.m1m2.height) + [port_contact_offest, br_offset], width=contact.m1_via.height) def route_supply(self): """ Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """ @@ -853,9 +853,9 @@ class pbitcell(bitcell_base.bitcell_base): offset=position) if position.x > 0: - contact_correct = 0.5 * contact.m1m2.height + contact_correct = 0.5 * contact.m1_via.height else: - contact_correct = -0.5 * contact.m1m2.height + contact_correct = -0.5 * contact.m1_via.height supply_offset = vector(position.x + contact_correct, self.gnd_position.y) self.add_via_center(layers=self.m1_stack, @@ -921,13 +921,13 @@ class pbitcell(bitcell_base.bitcell_base): """ # add poly to metal1 contacts for gates of the inverters left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x \ - - self.poly_to_contact - 0.5*contact.polym1.width, + - self.poly_to_contact - 0.5*contact.poly_contact.width, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, offset=left_storage_contact) right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \ - + self.poly_to_contact + 0.5*contact.polym1.width, + + self.poly_to_contact + 0.5*contact.poly_contact.width, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, offset=right_storage_contact) diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index 0c15401e..c3307ece 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -256,7 +256,7 @@ class bank_select(design.design): # Connect the logic B input to bank_sel/bank_sel_bar - logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1m2.height,0) + logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1_via.height,0) input_pos = vector(xoffset_bank_signal, logic_pos.y) self.add_path("m2",[logic_pos, input_pos]) self.add_via_center(layers=self.m1_stack, diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 636408df..60461724 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -5,14 +5,11 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import debug -import design -from base_array import bitcell_base_array +from bitcell_base_array import bitcell_base_array from tech import drc, spice -from vector import vector from globals import OPTS from sram_factory import factory -import logical_effort + class bitcell_array(bitcell_base_array): """ @@ -29,7 +26,7 @@ class bitcell_array(bitcell_base_array): # We don't offset this because we need to align # the replica bitcell in the control logic - #self.offset_all_coordinates() + # self.offset_all_coordinates() def create_netlist(self): @@ -48,13 +45,11 @@ class bitcell_array(bitcell_base_array): self.DRC_LVS() - def add_modules(self): """ Add the modules used in this design """ self.cell = factory.create(module_type="bitcell") self.add_mod(self.cell) - def create_instances(self): """ Create the module instances used in this design """ self.cell_inst = {} @@ -65,7 +60,6 @@ class bitcell_array(bitcell_base_array): mod=self.cell) self.connect_inst(self.get_bitcell_pins(col, row)) - def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" from tech import drc, parameter @@ -77,10 +71,10 @@ class bitcell_array(bitcell_base_array): freq = spice["default_event_frequency"] bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) - #Calculate the bitcell power which currently only includes leakage + # Calculate the bitcell power which currently only includes leakage cell_power = self.cell.analytical_power(corner, load) - #Leakage power grows with entire array and bitlines. + # Leakage power grows with entire array and bitlines. total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, cell_power.leakage * self.column_size * self.row_size) return total_power diff --git a/compiler/modules/base_array.py b/compiler/modules/bitcell_base_array.py similarity index 100% rename from compiler/modules/base_array.py rename to compiler/modules/bitcell_base_array.py diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index dec04073..78e9af7f 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -178,7 +178,7 @@ class hierarchical_predecode(design.design): # route one signal next to each vdd/gnd rail since this is # typically where the p/n devices are and there are no # pins in the nand gates. - y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1m2.width + self.m1_space + y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space in_pin = "in_{}".format(num) a_pin = "A_{}".format(num) in_pos = vector(self.input_rails[in_pin].x,y_offset) diff --git a/compiler/modules/module_type.py b/compiler/modules/module_type.py index 1d8d0cba..419e6041 100644 --- a/compiler/modules/module_type.py +++ b/compiler/modules/module_type.py @@ -6,7 +6,7 @@ # All rights reserved. # -class ModuleType(): +class module_type(): """ This is a class that maps cell names to python classes implementing them. """ diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index 33076bfb..bf19954b 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -810,7 +810,7 @@ class multibank(design.design): self.add_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)]) # Bring it up to M2 for M2/M3 routing self.add_via(layers=self.m1_stack, - offset=in_pin + contact.m1m2.offset, + offset=in_pin + contact.m1_via.offset, rotate=90) self.add_via(layers=self.m2_stack, offset=in_pin + self.m2m3_via_offset, @@ -823,7 +823,7 @@ class multibank(design.design): rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y) self.add_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)]) self.add_via(layers=self.m1_stack, - offset=in_pin + contact.m1m2.offset, + offset=in_pin + contact.m1_via.offset, rotate=90) self.add_via(layers=self.m2_stack, offset=in_pin + self.m2m3_via_offset, diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 2af2c8c9..cac2eea5 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -177,7 +177,7 @@ class wordline_driver(design.design): up_or_down = self.m2_space if row % 2 else -self.m2_space input_offset = vector(0, b_pos.y + up_or_down) base_offset = vector(clk_offset.x, input_offset.y) - contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1m2.width, 0) + contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0) mid_via_offset = base_offset + contact_offset # must under the clk line in M1 @@ -191,7 +191,7 @@ class wordline_driver(design.design): # now connect to the nand2 B self.add_path("m2", [mid_via_offset, b_pos]) - contact_offset = b_pos - vector(0.5 * contact.m1m2.height, 0) + contact_offset = b_pos - vector(0.5 * contact.m1_via.height, 0) self.add_via_center(layers=self.m1_stack, offset=contact_offset, directions=("H", "H")) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index e009d1bb..43fc1c8c 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -87,16 +87,16 @@ class pgate(design.design): left_gate_offset = vector(nmos_gate_pin.lx(), ypos) # Center is completely symmetric. - contact_width = contact.polym1.width - contact_m1_width = contact.polym1.second_layer_width - contact_m1_height = contact.polym1.second_layer_height + contact_width = contact.poly_contact.width + contact_m1_width = contact.poly_contact.second_layer_width + contact_m1_height = contact.poly_contact.second_layer_height if position == "center": contact_offset = left_gate_offset \ + vector(0.5 * self.poly_width, 0) elif position == "farleft": contact_offset = left_gate_offset \ - - vector(0.5 * contact.polym1.width, 0) + - vector(0.5 * contact.poly_contact.width, 0) elif position == "left": contact_offset = left_gate_offset \ - vector(0.5 * contact_width - 0.5 * self.poly_width, 0) @@ -120,7 +120,7 @@ class pgate(design.design): + left_gate_offset.scale(0.5, 0) self.add_rect_center(layer="poly", offset=mid_point, - height=contact.polym1.first_layer_width, + height=contact.poly_contact.first_layer_width, width=left_gate_offset.x - contact_offset.x) def extend_wells(self, middle_position): diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 8a3a8326..8da648b0 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -89,14 +89,15 @@ class pinv(pgate.pgate): # Assume we need 3 metal 1 pitches (2 power rails, one # between the tx for the drain) # plus the tx height - nmos = factory.create(module_type="ptx", tx_type="nmos") + nmos = factory.create(module_type="ptx", + tx_type="nmos") pmos = factory.create(module_type="ptx", width=drc("minwidth_tx"), tx_type="pmos") tx_height = nmos.poly_height + pmos.poly_height # rotated m1 pitch or poly to active spacing - min_channel = max(contact.polym1.width + self.m1_space, - contact.polym1.width + 2 * self.poly_to_active) + min_channel = max(contact.poly_contact.width + self.m1_space, + contact.poly_contact.width + 2 * self.poly_to_active) # This is the extra space needed to ensure DRC rules # to the active contacts diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 8d3605a2..f5abdaaa 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -85,10 +85,10 @@ class pnand2(pgate.pgate): """ Pre-compute some handy layout parameters. """ # metal spacing to allow contacts on any layer - self.input_spacing = max(self.poly_space + contact.polym1.first_layer_width, - self.m1_space + contact.m1m2.first_layer_width, - self.m2_space + contact.m2m3.first_layer_width, - self.m3_space + contact.m2m3.second_layer_width) + self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width, + self.m1_space + contact.m1_via.first_layer_width, + self.m2_space + contact.m2_via.first_layer_width, + self.m3_space + contact.m2_via.second_layer_width) # Compute the other pmos2 location, @@ -98,7 +98,7 @@ class pnand2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. - self.well_width = 2 * self.pmos.active_width + contact.activem1.width \ + self.well_width = 2 * self.pmos.active_width + contact.active_contact.width \ + 2 * self.active_space \ + 2 * self.nwell_enclose_active @@ -245,8 +245,8 @@ class pnand2(pgate.pgate): self.add_layout_pin_rect_center(text="Z", layer="m1", offset=out_offset, - width=contact.m1m2.first_layer_height, - height=contact.m1m2.first_layer_width) + width=contact.m1_via.first_layer_height, + height=contact.m1_via.first_layer_width) def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index bb379db7..5c1e0db5 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -202,10 +202,10 @@ class pnand3(pgate.pgate): # wire space or wire and one contact space metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width, - self.m1_space + 0.5 *contact.polym1.width + 0.5 * self.m1_width) + self.m1_space + 0.5 *contact.poly_contact.width + 0.5 * self.m1_width) active_spacing = max(self.m1_space, - 0.5 * contact.polym1.first_layer_width + self.poly_to_active) + 0.5 * contact.poly_contact.first_layer_width + self.poly_to_active) inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing self.route_input_gate(self.pmos3_inst, self.nmos3_inst, @@ -256,8 +256,8 @@ class pnand3(pgate.pgate): self.add_layout_pin_rect_center(text="Z", layer="m1", offset=mid_offset, - width=contact.m1m2.first_layer_width, - height=contact.m1m2.first_layer_height) + width=contact.m1_via.first_layer_width, + height=contact.m1_via.first_layer_height) def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 9895e9d9..e28b6fc8 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -84,10 +84,10 @@ class pnor2(pgate.pgate): """ Pre-compute some handy layout parameters. """ # metal spacing to allow contacts on any layer - self.input_spacing = max(self.poly_space + contact.polym1.first_layer_width, - self.m1_space + contact.m1m2.first_layer_width, - self.m2_space + contact.m2m3.first_layer_width, - self.m3_space + contact.m2m3.second_layer_width) + self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width, + self.m1_space + contact.m1_via.first_layer_width, + self.m2_space + contact.m2_via.first_layer_width, + self.m3_space + contact.m2_via.second_layer_width) # Compute the other pmos2 location, but determining # offset to overlap the source and drain pins @@ -234,8 +234,8 @@ class pnor2(pgate.pgate): self.add_layout_pin_rect_center(text="Z", layer="m1", offset=mid3_offset, - width=contact.m1m2.first_layer_height, - height=contact.m1m2.first_layer_width) + width=contact.m1_via.first_layer_height, + height=contact.m1_via.first_layer_width) def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index d1e2f3af..888304ce 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -121,7 +121,7 @@ class precharge(design.design): self.lower_pmos_inst.place(self.lower_pmos_position) # adds the upper pmos(s) to layout - ydiff = self.pmos.height + 2 * self.m1_space + contact.polym1.width + ydiff = self.pmos.height + 2 * self.m1_space + contact.poly_contact.width self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff) self.upper_pmos1_inst.place(self.upper_pmos1_pos) @@ -175,7 +175,7 @@ class precharge(design.design): # adds the contact from active to metal1 well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ - + vector(0, self.upper_pmos1_inst.uy() + contact.activem1.height / 2 \ + + vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \ + self.nwell_extend_active) self.add_via_center(layers=self.active_stack, offset=well_contact_pos, @@ -183,7 +183,7 @@ class precharge(design.design): well_type="n") # leave an extra pitch for the height - self.height = well_contact_pos.y + contact.activem1.height + self.m1_pitch + self.height = well_contact_pos.y + contact.active_contact.height + self.m1_pitch # nwell should span the whole design since it is pmos only self.add_rect(layer="nwell", diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py index 764b87c0..9586e72b 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/pgates/ptristate_inv.py @@ -79,7 +79,7 @@ class ptristate_inv(pgate.pgate): # Height is an input parameter, so it is not recomputed. # Make sure we can put a well above and below - self.top_bottom_space = max(contact.activem1.width, contact.activem1.height) + self.top_bottom_space = max(contact.active_contact.width, contact.active_contact.height) def add_ptx(self): """ Create the PMOS and NMOS transistors. """ @@ -135,8 +135,8 @@ class ptristate_inv(pgate.pgate): """ pmos_yoff = self.height - self.pmos.active_height \ - - self.top_bottom_space - 0.5 * contact.activem1.height - nmos_yoff = self.top_bottom_space + 0.5 * contact.activem1.height + - self.top_bottom_space - 0.5 * contact.active_contact.height + nmos_yoff = self.top_bottom_space + 0.5 * contact.active_contact.height # Tristate transistors pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 151c8a70..93247fc1 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -17,7 +17,7 @@ from globals import OPTS, print_time from sram_base import sram_base from bank import bank -from contact import m2m3 +from contact import m2_via from dff_buf_array import dff_buf_array from dff_array import dff_array @@ -280,7 +280,7 @@ class sram_1bank(sram_base): mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y) # In some designs, the steiner via will be too close to the mid_pos via # so make the wire as wide as the contacts - self.add_path("m2",[mid_pos, clk_steiner_pos], width=max(m2m3.width,m2m3.height)) + self.add_path("m2",[mid_pos, clk_steiner_pos], width=max(m2_via.width,m2_via.height)) self.add_wire(("m3","via2","m2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos]) if self.write_size: @@ -289,7 +289,7 @@ class sram_1bank(sram_base): mid_pos = vector(clk_steiner_pos.x, wmask_dff_clk_pos.y) # In some designs, the steiner via will be too close to the mid_pos via # so make the wire as wide as the contacts - self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2m3.width, m2m3.height)) + self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height)) self.add_wire(("m3", "via2", "m2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos]) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index ffc981b8..c60abeb5 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -8,7 +8,7 @@ import os from design_rules import * from module_type import * -from custom_cell_properties import CellProperties +from custom_cell_properties import cell_properties """ File containing the process technology parameters for FreePDK 45nm. @@ -23,15 +23,16 @@ File containing the process technology parameters for FreePDK 45nm. # Using tech_modules['cellname'] you can override each class by providing a custom # implementation in '$OPENRAM_TECHDIR/modules/' # For example: tech_modules['contact'] = 'contact_freepdk45' -tech_modules = ModuleType() +tech_modules = module_type() ################################################### # Custom cell properties ################################################### -cell_properties = CellProperties() +cell_properties = cell_properties() cell_properties.bitcell.mirror.x = True cell_properties.bitcell.mirror.y = False + ################################################### # GDS file info ################################################### diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 965306a3..7c360d38 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -8,7 +8,7 @@ import os from design_rules import * from module_type import * -from custom_cell_properties import CellProperties +from custom_cell_properties import cell_properties """ File containing the process technology parameters for SCMOS 4m, 0.35um @@ -23,12 +23,12 @@ File containing the process technology parameters for SCMOS 4m, 0.35um # Using tech_modules['cellname'] you can override each class by providing a custom # implementation in '$OPENRAM_TECHDIR/modules/' # For example: tech_modules['contact'] = 'contact_scn4m' -tech_modules = ModuleType() +tech_modules = module_type() ################################################### # Custom cell properties ################################################### -cell_properties = CellProperties() +cell_properties = cell_properties() cell_properties.bitcell.mirror.x = True cell_properties.bitcell.mirror.y = False From 0880c393fda4fbfd2a9a4dc43f1eb7b945db5304 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 30 Jan 2020 01:58:30 +0000 Subject: [PATCH 067/103] Fix base bitcell syntax error. Remove some unused imports. --- compiler/modules/dummy_array.py | 9 ++------- compiler/modules/replica_bitcell_array.py | 5 +---- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 997330e1..de15d0ce 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -3,15 +3,11 @@ # Copyright (c) 2016-2019 Regents of the University of California # All rights reserved. # -import debug -import design -from base_array import bitcell_base_array -from tech import drc -import contact +from bitcell_base_array import bitcell_base_array from sram_factory import factory -from vector import vector from globals import OPTS + class dummy_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. @@ -23,7 +19,6 @@ class dummy_array(bitcell_base_array): self.create_netlist() if not OPTS.netlist_only: self.create_layout() - def create_netlist(self): """ Create and connect the netlist """ diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 9cf5c5a2..178c2f91 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -10,10 +10,7 @@ from tech import drc, spice from vector import vector from globals import OPTS from sram_factory import factory -import logical_effort -import bitcell_array -import replica_column -import dummy_array + class replica_bitcell_array(design.design): """ From 400cf0333aef3ef153ad42e4e4e98b7727ffe6ee Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 30 Jan 2020 03:34:04 +0000 Subject: [PATCH 068/103] Pgates are 8 M1 high by default. Port data is bitcell height. --- compiler/modules/hierarchical_decoder.py | 5 +-- compiler/modules/wordline_driver.py | 8 +++-- compiler/pgates/pgate.py | 42 ++++++++++++------------ 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index bb637ed1..f83035ce 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -21,7 +21,7 @@ class hierarchical_decoder(design.design): """ Dynamically generated hierarchical decoder. """ - def __init__(self, name, rows, height=None): + def __init__(self, name, rows): design.design.__init__(self, name) self.NAND_FORMAT = "DEC_NAND_{0}" @@ -30,7 +30,8 @@ class hierarchical_decoder(design.design): self.pre2x4_inst = [] self.pre3x8_inst = [] - self.cell_height = height + b = factory.create(module_type="bitcell") + self.cell_height = b.height self.rows = rows self.num_inputs = math.ceil(math.log(self.rows, 2)) (self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index cac2eea5..27671fd2 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -56,12 +56,16 @@ class wordline_driver(design.design): self.add_pin("gnd", "GROUND") def add_modules(self): + b = factory.create(module_type="bitcell") + self.inv = factory.create(module_type="pdriver", fanout=self.cols, - neg_polarity=True) + neg_polarity=True, + height=b.height) self.add_mod(self.inv) - self.nand2 = factory.create(module_type="pnand2") + self.nand2 = factory.create(module_type="pnand2", + height=b.height) self.add_mod(self.nand2) def route_vdd_gnd(self): diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 43fc1c8c..cac2ee9d 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -27,8 +27,8 @@ class pgate(design.design): if height: self.height = height elif not height: - b = factory.create(module_type="bitcell") - self.height = b.height + # By default, we make it 8 M1 pitch tall + self.height = 8*self.m1_pitch self.create_netlist() if not OPTS.netlist_only: @@ -128,37 +128,37 @@ class pgate(design.design): # FIXME: float rounding problem middle_position = middle_position.snap_to_grid() - # Add a rail width to extend the well to the top of the rail - nwell_max_offset = max(self.find_highest_layer_coords("nwell").y, - self.height + 0.5 * self.m1_width) - nwell_position = middle_position - nwell_height = nwell_max_offset - middle_position.y if "nwell" in layer: + # Add a rail width to extend the well to the top of the rail + nwell_max_offset = max(self.find_highest_layer_coords("nwell").y, + self.height + 0.5 * self.m1_width) + nwell_position = middle_position + nwell_height = nwell_max_offset - middle_position.y self.add_rect(layer="nwell", offset=middle_position, width=self.well_width, height=nwell_height) - if "vtg" in layer: - self.add_rect(layer="vtg", - offset=nwell_position, - width=self.well_width, - height=nwell_height) + if "vtg" in layer: + self.add_rect(layer="vtg", + offset=nwell_position, + width=self.well_width, + height=nwell_height) # Start this half a rail width below the cell - pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y, - -0.5 * self.m1_width) - pwell_position = vector(0, pwell_min_offset) - pwell_height = middle_position.y - pwell_position.y if "pwell" in layer: + pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y, + -0.5 * self.m1_width) + pwell_position = vector(0, pwell_min_offset) + pwell_height = middle_position.y - pwell_position.y self.add_rect(layer="pwell", offset=pwell_position, width=self.well_width, height=pwell_height) - if "vtg" in layer: - self.add_rect(layer="vtg", - offset=pwell_position, - width=self.well_width, - height=pwell_height) + if "vtg" in layer: + self.add_rect(layer="vtg", + offset=pwell_position, + width=self.well_width, + height=pwell_height) def add_nwell_contact(self, pmos, pmos_pos): """ Add an nwell contact next to the given pmos device. """ From 6cf20a03538bb318e7325e65d37a15ae6b23c376 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 30 Jan 2020 19:44:24 +0000 Subject: [PATCH 069/103] add technology based module customization --- compiler/modules/custom_module_properties.py | 27 ++++++++++++++++++++ compiler/modules/dff.py | 9 +++++-- 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 compiler/modules/custom_module_properties.py diff --git a/compiler/modules/custom_module_properties.py b/compiler/modules/custom_module_properties.py new file mode 100644 index 00000000..43c140b0 --- /dev/null +++ b/compiler/modules/custom_module_properties.py @@ -0,0 +1,27 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2020 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# + +class _dff: + def __init__(self, use_custom_ports, custom_port_list, custom_type_list): + self.use_custom_ports = use_custom_ports + self.custom_port_list = custom_port_list + self.custom_type_list = custom_type_list + +class module_properties(): + """ + TODO + """ + def __init__(self): + self.names = {} + self._dff = _dff(use_custom_ports = False, + custom_port_list = [], + custom_type_list = []) + + @property + def dff(self): + return self._dff diff --git a/compiler/modules/dff.py b/compiler/modules/dff.py index 8871872d..1078088e 100644 --- a/compiler/modules/dff.py +++ b/compiler/modules/dff.py @@ -7,6 +7,7 @@ # import design from tech import GDS, layer, spice, parameter +from tech import module_properties import utils @@ -14,9 +15,13 @@ class dff(design.design): """ Memory address flip-flop """ + if not module_properties.dff.use_custom_ports: + pin_names = ["D", "Q", "clk", "vdd", "gnd"] + type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] + else: + pin_names = module_properties.dff.custom_port_list + type_list = module_properties.dff.custom_type_list - pin_names = ["D", "Q", "clk", "vdd", "gnd"] - type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] (width, height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"]) From 34c9b3a0a552edc4d7bd9c9ef3031a5bfe351b1c Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 3 Feb 2020 17:37:53 +0000 Subject: [PATCH 070/103] Fix well offset computation for PMOS --- compiler/pgates/ptx.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 32bbeefe..3af456e0 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -154,17 +154,19 @@ class ptx(design.design): # Poly height must include poly extension over active self.poly_height = self.tx_width + 2 * self.poly_extend_active - - well_name = "{}well".format(self.well_type) # The active offset is due to the well extension - if well_name in layer: - well_enclose_active = drc(well_name + "_enclose_active") - self.active_offset = vector([well_enclose_active] * 2) - else: - self.active_offset = vector(0, 0) + # but we need to do the active offset for the other tx type too + # so that they align in pgates. + well_enclose_active = 0 + if self.well_type=="p" and "pwell" in layer: + well_enclose_active = max(drc("pwell_enclose_active"), well_enclose_active) + if self.well_type=="n" and "nwell" in layer: + well_enclose_active = max(drc("nwell_enclose_active"), well_enclose_active) + self.active_offset = vector([well_enclose_active] * 2) # Well enclosure of active, ensure minwidth as well + well_name = "{}well".format(self.well_type) if well_name in layer: well_width_rule = drc("minwidth_" + well_name) well_enclose_active = drc(well_name + "_enclose_active") @@ -179,7 +181,7 @@ class ptx(design.design): # The well is not included in the height and width self.height = self.poly_height self.width = self.active_width - + # This is the center of the first active contact offset (centered vertically) self.contact_offset = self.active_offset + vector(0.5 * self.active_contact.width, 0.5 * self.active_height) @@ -334,13 +336,10 @@ class ptx(design.design): if not (well_name in layer or "vtg" in layer): return - center_pos = self.active_offset + vector(self.width / 2.0, - self.height / 2.0) - well_ll = center_pos - vector(self.well_width / 2.0, - self.well_height / 2.0) - well_ll = well_ll - vector(0, - self.poly_extend_active) - + center_pos = self.active_offset + vector(0.5*self.active_width, + 0.5*self.active_height) + well_ll = center_pos - vector(0.5*self.well_width, + 0.5*self.well_height) if well_name in layer: self.add_rect(layer=well_name, offset=well_ll, From 5aed893725d9809688437f3c4e1245c38982af63 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 3 Feb 2020 18:41:06 +0000 Subject: [PATCH 071/103] Add nwell/pwell tap test --- compiler/tests/03_contact_test.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index f7a13118..d2f0c7f6 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -21,7 +21,7 @@ class contact_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from tech import poly_stack, beol_stacks + from tech import active_stack, poly_stack, beol_stacks # Don't do active because of nwell contact rules # Don't do metal3 because of min area rules @@ -67,6 +67,25 @@ class contact_test(openram_test): c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(3, 3)) self.local_drc_check(c) + # Test the well taps + # check vertical array with one in the middle and two ends + layer_stack = active_stack + stack_name = ":".join(map(str, layer_stack)) + + debug.info(2, "1 x 1 {} nwell".format(stack_name)) + c = factory.create(module_type="contact", + layer_stack=layer_stack, + implant_type="n", + well_type="n") + self.local_drc_check(c) + + debug.info(2, "1 x 1 {} pwell".format(stack_name)) + c = factory.create(module_type="contact", + layer_stack=layer_stack, + implant_type="p", + well_type="p") + self.local_drc_check(c) + globals.end_openram() From 53217e4030a0af582f3440a70440ea71c22b0ee2 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 4 Feb 2020 17:37:58 +0000 Subject: [PATCH 072/103] Check min well width in contact and redo position --- compiler/base/contact.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 51efc498..141ed979 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -231,10 +231,14 @@ class contact(hierarchy_design.hierarchy_design): # Optionally implant well if layer exists well_layer = "{}well".format(self.well_type) if well_layer in tech.layer: + well_width_rule = drc("minwidth_" + well_layer) well_enclose_active = drc(well_layer + "_enclose_active") - well_position = self.first_layer_position - [well_enclose_active] * 2 - well_width = self.first_layer_width + 2 * well_enclose_active - well_height = self.first_layer_height + 2 * well_enclose_active + well_width = max(self.first_layer_width + 2 * well_enclose_active, + well_width_rule) + well_height = max(self.first_layer_height + 2 * well_enclose_active, + well_width_rule) + center_pos = vector(0.5*self.width, 0.5*self.height) + well_position = center_pos - vector(0.5*well_width, 0.5*well_height) self.add_rect(layer=well_layer, offset=well_position, width=well_width, From 304971ff6086982d14a638439dd5407ba3da56ab Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 4 Feb 2020 17:38:35 +0000 Subject: [PATCH 073/103] Fix ptx so nmos and pmos have same active offset and gates align --- compiler/pgates/pgate.py | 4 +++- compiler/pgates/ptx.py | 18 +++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index cac2ee9d..ab30a0d0 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -75,8 +75,10 @@ class pgate(design.design): pmos_gate_pin = pmos_inst.get_pin("G") # Check if the gates are aligned and give an error if they aren't! + if nmos_gate_pin.ll().x != pmos_gate_pin.ll().x: + self.gds_write("unaliged_gates.gds") debug.check(nmos_gate_pin.ll().x == pmos_gate_pin.ll().x, - "Connecting unaligned gates not supported.") + "Connecting unaligned gates not supported. See unaligned_gates.gds.") # Pick point on the left of NMOS and connect down to PMOS nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5 * self.poly_width, 0) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 3af456e0..f331dc28 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -156,13 +156,17 @@ class ptx(design.design): self.poly_height = self.tx_width + 2 * self.poly_extend_active # The active offset is due to the well extension - # but we need to do the active offset for the other tx type too - # so that they align in pgates. - well_enclose_active = 0 - if self.well_type=="p" and "pwell" in layer: - well_enclose_active = max(drc("pwell_enclose_active"), well_enclose_active) - if self.well_type=="n" and "nwell" in layer: - well_enclose_active = max(drc("nwell_enclose_active"), well_enclose_active) + if "pwell" in layer: + pwell_enclose_active = drc("pwell_enclose_active") + else: + pwell_enclose_active = 0 + if "nwell" in layer: + nwell_enclose_active = drc("nwell_enclose_active") + else: + nwell_enclose_active = 0 + # Use the max of either so that the poly gates will align properly + well_enclose_active = max(pwell_enclose_active, + nwell_enclose_active) self.active_offset = vector([well_enclose_active] * 2) # Well enclosure of active, ensure minwidth as well From ed11145ca49a365cd46641d288fb1ef64b55b91b Mon Sep 17 00:00:00 2001 From: jcirimel Date: Tue, 4 Feb 2020 23:35:06 -0800 Subject: [PATCH 074/103] add custom module file, make dff clk pin dynamic --- compiler/modules/custom_module_properties.py | 8 +++++--- compiler/modules/dff.py | 2 ++ compiler/modules/dff_array.py | 15 ++++++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/compiler/modules/custom_module_properties.py b/compiler/modules/custom_module_properties.py index 43c140b0..e27741ea 100644 --- a/compiler/modules/custom_module_properties.py +++ b/compiler/modules/custom_module_properties.py @@ -7,10 +7,11 @@ # class _dff: - def __init__(self, use_custom_ports, custom_port_list, custom_type_list): + def __init__(self, use_custom_ports, custom_port_list, custom_type_list, clk_pin): self.use_custom_ports = use_custom_ports self.custom_port_list = custom_port_list self.custom_type_list = custom_type_list + self.clk_pin = clk_pin class module_properties(): """ @@ -19,8 +20,9 @@ class module_properties(): def __init__(self): self.names = {} self._dff = _dff(use_custom_ports = False, - custom_port_list = [], - custom_type_list = []) + custom_port_list = ["D", "Q", "clk", "vdd", "gnd"], + custom_type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"], + clk_pin= "clk") @property def dff(self): diff --git a/compiler/modules/dff.py b/compiler/modules/dff.py index 1078088e..45d20a62 100644 --- a/compiler/modules/dff.py +++ b/compiler/modules/dff.py @@ -18,9 +18,11 @@ class dff(design.design): if not module_properties.dff.use_custom_ports: pin_names = ["D", "Q", "clk", "vdd", "gnd"] type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] + clk_pin = "clk" else: pin_names = module_properties.dff.custom_port_list type_list = module_properties.dff.custom_type_list + clk_pin = module_properties.dff.clk_pin (width, height) = utils.get_libcell_size("dff", GDS["unit"], diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 6ba020c7..62464834 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -69,11 +69,12 @@ class dff_array(design.design): name = "dff_r{0}_c{1}".format(row,col) self.dff_insts[row,col]=self.add_inst(name=name, mod=self.dff) - self.connect_inst([self.get_din_name(row,col), - self.get_dout_name(row,col), - "clk", - "vdd", - "gnd"]) + instance_ports = [self.get_din_name(row,col), + self.get_dout_name(row,col)] + for port in self.dff.pin_names: + if port != 'D' and port != 'Q': + instance_ports.append(port) + self.connect_inst(instance_ports) def place_dff_array(self): for row in range(self.rows): @@ -142,7 +143,7 @@ class dff_array(design.design): # Create vertical spines to a single horizontal rail - clk_pin = self.dff_insts[0,0].get_pin("clk") + clk_pin = self.dff_insts[0,0].get_pin(self.dff.clk_pin) clk_ypos = 2*self.m3_pitch+self.m3_width debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2") self.add_layout_pin_segment_center(text="clk", @@ -150,7 +151,7 @@ class dff_array(design.design): start=vector(0,clk_ypos), end=vector(self.width,clk_ypos)) for col in range(self.columns): - clk_pin = self.dff_insts[0,col].get_pin("clk") + clk_pin = self.dff_insts[0,col].get_pin(self.dff.clk_pin) # Make a vertical strip for each column self.add_rect(layer="m2", offset=clk_pin.ll().scale(1,0), From 4526986de3bd0384a6b0d076a29e069fc0c15b20 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 5 Feb 2020 18:21:01 +0000 Subject: [PATCH 075/103] Update contact well support. Add asymmetric tap overlap support in DRC rules. Add static nwell and pwell contact in class for measurements. --- compiler/base/contact.py | 42 ++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 141ed979..1ffdc7bf 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -42,6 +42,8 @@ class contact(hierarchy_design.hierarchy_design): if implant_type or well_type: self.add_comment("implant type: {}\n".format(implant_type)) self.add_comment("well_type: {}\n".format(well_type)) + + self.is_well_contact = implant_type == well_type self.layer_stack = layer_stack self.dimensions = dimensions @@ -114,7 +116,12 @@ class contact(hierarchy_design.hierarchy_design): self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name)) self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name)) - self.first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)) + # If there's a different rule for active + # FIXME: Make this more elegant + if self.is_well_contact and self.first_layer_name == "active" and "tap_extend_contact" in drc.keys(): + self.first_layer_extend = drc("tap_extend_contact") + else: + self.first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)) self.second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name)) self.second_layer_enclosure = drc("{0}_enclose_{1}".format(self.second_layer_name, self.via_layer_name)) @@ -232,17 +239,17 @@ class contact(hierarchy_design.hierarchy_design): well_layer = "{}well".format(self.well_type) if well_layer in tech.layer: well_width_rule = drc("minwidth_" + well_layer) - well_enclose_active = drc(well_layer + "_enclose_active") - well_width = max(self.first_layer_width + 2 * well_enclose_active, - well_width_rule) - well_height = max(self.first_layer_height + 2 * well_enclose_active, - well_width_rule) + self.well_enclose_active = drc(well_layer + "_enclose_active") + self.well_width = max(self.first_layer_width + 2 * self.well_enclose_active, + well_width_rule) + self.well_height = max(self.first_layer_height + 2 * self.well_enclose_active, + well_width_rule) center_pos = vector(0.5*self.width, 0.5*self.height) - well_position = center_pos - vector(0.5*well_width, 0.5*well_height) + well_position = center_pos - vector(0.5*self.well_width, 0.5*self.well_height) self.add_rect(layer=well_layer, offset=well_position, - width=well_width, - height=well_height) + width=self.well_width, + height=self.well_height) def analytical_power(self, corner, load): """ Get total power of a module """ @@ -261,6 +268,21 @@ for layer_stack in tech.layer_stacks: else: setattr(module, layer1 + "_via", cont) - +# Set up a static for each well contact for measurements +if "nwell" in tech.layer: + cont = factory.create(module_type="contact", + layer_stack=tech.active_stack, + implant_type="n", + well_type="n") + module = sys.modules[__name__] + setattr(module, "nwell_contact", cont) + +if "pwell" in tech.layer: + cont = factory.create(module_type="contact", + layer_stack=tech.active_stack, + implant_type="p", + well_type="p") + module = sys.modules[__name__] + setattr(module, "pwell_contact", cont) From 04d254542dde8004f363a2a113ee253cc98dfb1b Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 5 Feb 2020 18:22:22 +0000 Subject: [PATCH 076/103] Add general well_extend_active DRC in design class. --- compiler/base/design.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/compiler/base/design.py b/compiler/base/design.py index 9db68877..c2ef8340 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -113,6 +113,15 @@ class design(hierarchy_design): if match: setattr(self, match.group(0), drc(match.group(0))) + # Create the maximum well extend active that gets used + # by cells to extend the wells for interaction with other cells + from tech import layer + self.well_extend_active = 0 + if "nwell" in layer: + self.well_extend_active = max(self.well_extend_active, self.nwell_extend_active) + if "pwell" in layer: + self.well_extend_active = max(self.well_extend_active, self.pwell_extend_active) + # These are for debugging previous manual rules if False: print("poly_width", self.poly_width) From 596302d9a927743ba929b358cecdf8f32b0deb09 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 5 Feb 2020 18:22:45 +0000 Subject: [PATCH 077/103] Update pgate well and well contacts. Extend well left and right past a cell boundary. Use asymmetric well contacts. --- compiler/pgates/pgate.py | 8 +++----- compiler/pgates/pinv.py | 18 ++++++++++-------- compiler/pgates/pnand2.py | 6 +++--- compiler/pgates/pnand3.py | 10 +++++----- compiler/pgates/pnor2.py | 8 ++++---- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index ab30a0d0..c13939f1 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -134,10 +134,10 @@ class pgate(design.design): # Add a rail width to extend the well to the top of the rail nwell_max_offset = max(self.find_highest_layer_coords("nwell").y, self.height + 0.5 * self.m1_width) - nwell_position = middle_position + nwell_position = middle_position - vector(self.well_extend_active, 0) nwell_height = nwell_max_offset - middle_position.y self.add_rect(layer="nwell", - offset=middle_position, + offset=nwell_position, width=self.well_width, height=nwell_height) if "vtg" in layer: @@ -150,7 +150,7 @@ class pgate(design.design): if "pwell" in layer: pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y, -0.5 * self.m1_width) - pwell_position = vector(0, pwell_min_offset) + pwell_position = vector(-self.well_extend_active, pwell_min_offset) pwell_height = middle_position.y - pwell_position.y self.add_rect(layer="pwell", offset=pwell_position, @@ -183,7 +183,6 @@ class pgate(design.design): 0.5 * pmos.active_contact.first_layer_height) self.nwell_contact = self.add_via_center(layers=layer_stack, offset=contact_offset, - directions=("H", "V"), implant_type="n", well_type="n") self.add_rect_center(layer="m1", @@ -237,7 +236,6 @@ class pgate(design.design): 0.5 * nmos.active_contact.first_layer_height) self.pwell_contact= self.add_via_center(layers=layer_stack, offset=contact_offset, - directions=("H", "V"), implant_type="p", well_type="p") self.add_rect_center(layer="m1", diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 8da648b0..c0336956 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -52,10 +52,10 @@ class pinv(pgate.pgate): def create_layout(self): """ Calls all functions related to the generation of the layout """ self.setup_layout_constants() - self.route_supply_rails() self.place_ptx() self.add_well_contacts() self.extend_wells(self.well_pos) + self.route_supply_rails() self.connect_rails() self.route_input_gate(self.pmos_inst, self.nmos_inst, @@ -104,8 +104,9 @@ class pinv(pgate.pgate): extra_contact_space = max(-nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, - self.poly_extend_active, self.poly_space) + self.poly_extend_active + self.poly_space) total_height = tx_height + min_channel + 2 * self.top_bottom_space + debug.check(self.height > total_height, "Cell height {0} too small for simple min height {1}.".format(self.height, total_height)) @@ -148,14 +149,15 @@ class pinv(pgate.pgate): def setup_layout_constants(self): """ - Pre-compute some handy layout parameters. + Compute the width and height """ - # the well width is determined the multi-finger PMOS device width plus - # the well contact width and half well enclosure on both sides - self.well_width = self.pmos.active_width + self.pmos.active_contact.width \ - + self.active_space + 2*self.nwell_enclose_active - self.width = self.well_width + # the width is determined the multi-finger PMOS device width plus + # the well contact width, spacing between them + self.width = self.pmos.active_offset.x + self.pmos.active_width \ + + contact.nwell_contact.width + self.active_space + 0.5 * self.nwell_enclose_active + # This includes full enclosures on each end + self.well_width = self.width + 2*self.nwell_enclose_active # Height is an input parameter, so it is not recomputed. def add_ptx(self): diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index f5abdaaa..2f539c16 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -98,11 +98,11 @@ class pnand2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. - self.well_width = 2 * self.pmos.active_width + contact.active_contact.width \ + self.width = 2 * self.pmos.active_width + contact.active_contact.width \ + 2 * self.active_space \ - + 2 * self.nwell_enclose_active + + 0.5 * self.nwell_enclose_active - self.width = self.well_width + self.well_width = self.width + 2 * self.nwell_enclose_active # Height is an input parameter, so it is not recomputed. # This is the extra space needed to ensure DRC rules diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 5c1e0db5..d5b07069 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -91,10 +91,10 @@ class pnand3(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. - self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \ - + 2 * self.active_space + 2 * self.nwell_enclose_active \ + self.width = 3 * self.pmos.active_width + self.pmos.active_contact.width \ + + 2 * self.active_space + 0.5 * self.nwell_enclose_active \ - self.overlap_offset.x - self.width = self.well_width + self.well_width = self.width + 2 * self.nwell_enclose_active # Height is an input parameter, so it is not recomputed. # This is the extra space needed to ensure DRC rules @@ -245,10 +245,10 @@ class pnand3(pgate.pgate): offset=nmos3_pin.center()) # PMOS3 and NMOS3 are drain aligned - self.add_path("m2", [pmos3_pin.bc(), nmos3_pin.uc()]) + self.add_path("m2", [pmos3_pin.center(), nmos3_pin.uc()]) # Route in the A input track (top track) mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) - self.add_path("m2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) + self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) # This extends the output to the edge of the cell self.add_via_center(layers=self.m1_stack, diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index e28b6fc8..7ceab71a 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -95,11 +95,11 @@ class pnor2(pgate.pgate): # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. - self.well_width = 2 * self.pmos.active_width \ + self.width = 2 * self.pmos.active_width \ + self.pmos.active_contact.width \ + 2 * self.active_space \ - + 2 * self.nwell_enclose_active - self.width = self.well_width + + 0.5 * self.nwell_enclose_active + self.well_width = self.width + 2 * self.nwell_enclose_active # Height is an input parameter, so it is not recomputed. # This is the extra space needed to ensure DRC rules @@ -225,7 +225,7 @@ class pnor2(pgate.pgate): # PMOS1 to mid-drain to NMOS2 drain self.add_path("m2", - [pmos_pin.bc(), mid2_offset, mid3_offset]) + [pmos_pin.center(), mid2_offset, mid3_offset]) self.add_path("m2", [nmos_pin.rc(), mid1_offset, mid2_offset]) # This extends the output to the edge of the cell From 18573c0e42c4b48ff24716d230fb1c454b6304dc Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 5 Feb 2020 22:25:35 +0000 Subject: [PATCH 078/103] add module properties to other technologies --- technology/freepdk45/tech/tech.py | 3 ++- technology/scn4m_subm/tech/tech.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index c60abeb5..f91be16c 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -9,7 +9,7 @@ import os from design_rules import * from module_type import * from custom_cell_properties import cell_properties - +from custom_module_properties import module_properties """ File containing the process technology parameters for FreePDK 45nm. """ @@ -25,6 +25,7 @@ File containing the process technology parameters for FreePDK 45nm. # For example: tech_modules['contact'] = 'contact_freepdk45' tech_modules = module_type() +module_properties = module_properties() ################################################### # Custom cell properties ################################################### diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 7c360d38..bb379478 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -9,6 +9,7 @@ import os from design_rules import * from module_type import * from custom_cell_properties import cell_properties +from custom_module_properties import module_properties """ File containing the process technology parameters for SCMOS 4m, 0.35um @@ -24,6 +25,7 @@ File containing the process technology parameters for SCMOS 4m, 0.35um # implementation in '$OPENRAM_TECHDIR/modules/' # For example: tech_modules['contact'] = 'contact_scn4m' tech_modules = module_type() +module_properties = module_properties() ################################################### # Custom cell properties From 3a06141030c2f247d8184cef1db3955d16fbf01f Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 6 Feb 2020 12:10:49 +0000 Subject: [PATCH 079/103] add simple sram sizing for netlist only --- compiler/base/utils.py | 13 +++++++++---- compiler/modules/bank.py | 1 + compiler/sram/sram_config.py | 15 +++++++++++++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/compiler/base/utils.py b/compiler/base/utils.py index f02b2b85..16fbc51f 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -96,8 +96,10 @@ def get_libcell_size(name, units, lpp): Open a GDS file and return the library cell size from either the bounding box or a border layer. """ - cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" - return(get_gds_size(name, cell_gds, units, lpp)) + if not OPTS.netlist_only: + cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" + return(get_gds_size(name, cell_gds, units, lpp)) + return (0,0,) def get_gds_pins(pin_names, name, gds_filename, units): @@ -128,8 +130,11 @@ def get_libcell_pins(pin_list, name, units): Open a GDS file and find the pins in pin_list as text on a given layer. Return these as a rectangle layer pair for each pin. """ - cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" - return(get_gds_pins(pin_list, name, cell_gds, units)) + if not OPTS.netlist_only: + cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" + return(get_gds_pins(pin_list, name, cell_gds, units)) + else: + return diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 7ed0c9a3..4a674c23 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -57,6 +57,7 @@ class bank(design.design): def create_netlist(self): + self.compute_sizes() self.add_modules() self.add_pins() # Must create the replica bitcell array first diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index 376bf42b..c086a57a 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -23,9 +23,12 @@ class sram_config: # This will get over-written when we determine the organization self.words_per_row = words_per_row + if not OPTS.netlist_only: + self.compute_sizes() + else: + self.compute_simple_sram_sizes() - self.compute_sizes() - + def set_local_config(self, module): """ Copy all of the member variables to the given module for convenience """ @@ -35,6 +38,14 @@ class sram_config: # Copy all the variables to the local module for member in members: setattr(module,member,getattr(self,member)) + def compute_simple_sram_sizes(self): + self.row_addr_size = int(log(OPTS.num_words, 2)) + self.col_addr_size = int(log(OPTS.word_size, 2)) + self.words_per_row = 1 + self.num_rows = OPTS.num_words + self.num_cols = OPTS.word_size + self.bank_addr_size = self.col_addr_size + self.row_addr_size + self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) def compute_sizes(self): """ Computes the organization of the memory using bitcell size by trying to make it square.""" From b1079346726ae5f4028bba0f0d5272488d972f82 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 6 Feb 2020 12:15:52 +0000 Subject: [PATCH 080/103] fix styling --- compiler/example_configs/example_config_scn4m_subm.py | 1 + compiler/sram/sram_config.py | 1 + 2 files changed, 2 insertions(+) diff --git a/compiler/example_configs/example_config_scn4m_subm.py b/compiler/example_configs/example_config_scn4m_subm.py index 71ef328b..a55122a2 100644 --- a/compiler/example_configs/example_config_scn4m_subm.py +++ b/compiler/example_configs/example_config_scn4m_subm.py @@ -9,6 +9,7 @@ temperatures = [25] route_supplies = True check_lvsdrc = True +netlist_only = True output_path = "temp" output_name = "sram_{0}_{1}_{2}".format(word_size, diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index c086a57a..20c8299e 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -38,6 +38,7 @@ class sram_config: # Copy all the variables to the local module for member in members: setattr(module,member,getattr(self,member)) + def compute_simple_sram_sizes(self): self.row_addr_size = int(log(OPTS.num_words, 2)) self.col_addr_size = int(log(OPTS.word_size, 2)) From f0ecf385e883420ddea567543f308de041e9a4c0 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 6 Feb 2020 16:20:09 +0000 Subject: [PATCH 081/103] Nwell fixes in pgates. Fix minor PEP8 format fixes. Fix nwell to be 55% of cell height. Move contact in hierarchical decoder for DRC error. --- compiler/modules/hierarchical_decoder.py | 14 +++----------- compiler/modules/hierarchical_predecode.py | 10 ++++------ compiler/pgates/pgate.py | 18 ++++++++++-------- compiler/pgates/pinv.py | 10 +++++----- compiler/pgates/pnand2.py | 7 ++----- compiler/pgates/pnand3.py | 11 +++-------- compiler/pgates/pnor2.py | 5 +---- compiler/pgates/precharge.py | 2 +- compiler/pgates/pwrite_driver.py | 5 ++--- 9 files changed, 31 insertions(+), 51 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index f83035ce..242ef476 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -5,18 +5,14 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from tech import drc import debug import design -from math import log -from math import sqrt -from math import ceil import math -import contact from sram_factory import factory from vector import vector from globals import OPTS + class hierarchical_decoder(design.design): """ Dynamically generated hierarchical decoder. @@ -34,13 +30,12 @@ class hierarchical_decoder(design.design): self.cell_height = b.height self.rows = rows self.num_inputs = math.ceil(math.log(self.rows, 2)) - (self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) + (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) self.create_netlist() if not OPTS.netlist_only: self.create_layout() - def create_netlist(self): self.add_modules() self.setup_netlist_constants() @@ -308,7 +303,6 @@ class hierarchical_decoder(design.design): base= vector(-self.pre2_4.width, num * self.pre2_4.height) self.pre2x4_inst[num].place(base) - def place_pre3x8(self,num): """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ @@ -321,7 +315,6 @@ class hierarchical_decoder(design.design): self.pre3x8_inst[num].place(offset) - def create_row_decoder(self): """ Create the row-decoder by placing NAND2/NAND3 and Inverters and add the primary decoder output pins. """ @@ -329,7 +322,6 @@ class hierarchical_decoder(design.design): self.create_decoder_nand_array() self.create_decoder_inv_array() - def create_decoder_nand_array(self): """ Add a column of NAND gates for final decode """ @@ -556,7 +548,7 @@ class hierarchical_decoder(design.design): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ # The vias will be placed in the center and right of the cells, respectively. - xoffset = self.nand_inst[0].cx() + xoffset = self.nand_inst[0].rx() for num in range(0,self.rows): for pin_name in ["vdd", "gnd"]: # The nand and inv are the same height rows... diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 78e9af7f..88bae534 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -8,12 +8,11 @@ import debug import design import math -from tech import drc import contact from vector import vector -from globals import OPTS from sram_factory import factory + class hierarchical_predecode(design.design): """ Pre 2x4 and 3x8 decoder shared code. @@ -42,7 +41,7 @@ class hierarchical_predecode(design.design): self.add_nand(self.number_of_inputs) self.add_mod(self.nand) - def add_nand(self,inputs): + def add_nand(self, inputs): """ Create the NAND for the predecode input stage """ if inputs==2: self.nand = factory.create(module_type="pnand2", @@ -51,7 +50,7 @@ class hierarchical_predecode(design.design): self.nand = factory.create(module_type="pnand3", height=self.cell_height) else: - debug.error("Invalid number of predecode inputs: {}".format(inputs),-1) + debug.error("Invalid number of predecode inputs: {}".format(inputs), -1) def setup_layout_constraints(self): @@ -89,7 +88,6 @@ class hierarchical_predecode(design.design): names=decode_names, length=self.height - 2*self.m1_width) - def create_input_inverters(self): """ Create the input inverters to invert input signals for the decode stage. """ self.in_inst = [] @@ -266,7 +264,7 @@ class hierarchical_predecode(design.design): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ # Find the x offsets for where the vias/pins should be placed - in_xoffset = self.in_inst[0].rx() + in_xoffset = self.in_inst[0].rx() + self.m1_space out_xoffset = self.inv_inst[0].lx() - self.m1_space for num in range(0,self.number_of_outputs): # this will result in duplicate polygons for rails, but who cares diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index c13939f1..e0401726 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -11,7 +11,6 @@ import debug from tech import layer from vector import vector from globals import OPTS -from sram_factory import factory class pgate(design.design): @@ -29,7 +28,7 @@ class pgate(design.design): elif not height: # By default, we make it 8 M1 pitch tall self.height = 8*self.m1_pitch - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -125,17 +124,20 @@ class pgate(design.design): height=contact.poly_contact.first_layer_width, width=left_gate_offset.x - contact_offset.x) - def extend_wells(self, middle_position): + def extend_wells(self): """ Extend the n/p wells to cover whole cell """ + # This should match the cells in the cell library + nwell_y_offset = 0.48 * self.height + full_height = self.height + 0.5*self.m1_width + # FIXME: float rounding problem - middle_position = middle_position.snap_to_grid() if "nwell" in layer: # Add a rail width to extend the well to the top of the rail nwell_max_offset = max(self.find_highest_layer_coords("nwell").y, - self.height + 0.5 * self.m1_width) - nwell_position = middle_position - vector(self.well_extend_active, 0) - nwell_height = nwell_max_offset - middle_position.y + full_height) + nwell_position = vector(0, nwell_y_offset) - vector(self.well_extend_active, 0) + nwell_height = nwell_max_offset - nwell_y_offset self.add_rect(layer="nwell", offset=nwell_position, width=self.well_width, @@ -151,7 +153,7 @@ class pgate(design.design): pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y, -0.5 * self.m1_width) pwell_position = vector(-self.well_extend_active, pwell_min_offset) - pwell_height = middle_position.y - pwell_position.y + pwell_height = nwell_y_offset - pwell_position.y self.add_rect(layer="pwell", offset=pwell_position, width=self.well_width, diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index c0336956..c0f1f955 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -54,7 +54,7 @@ class pinv(pgate.pgate): self.setup_layout_constants() self.place_ptx() self.add_well_contacts() - self.extend_wells(self.well_pos) + self.extend_wells() self.route_supply_rails() self.connect_rails() self.route_input_gate(self.pmos_inst, @@ -154,8 +154,11 @@ class pinv(pgate.pgate): # the width is determined the multi-finger PMOS device width plus # the well contact width, spacing between them + # space is for power supply contact to nwell m1 spacing self.width = self.pmos.active_offset.x + self.pmos.active_width \ - + contact.nwell_contact.width + self.active_space + 0.5 * self.nwell_enclose_active + + self.active_space + contact.nwell_contact.width \ + + 0.5 * self.nwell_enclose_active \ + + self.m1_space # This includes full enclosures on each end self.well_width = self.width + 2*self.nwell_enclose_active # Height is an input parameter, so it is not recomputed. @@ -225,9 +228,6 @@ class pinv(pgate.pgate): nmos_drain_pos = self.nmos_inst.get_pin("D").ul() self.output_pos = vector(0, 0.5 * (pmos_drain_pos.y + nmos_drain_pos.y)) - # This will help with the wells - self.well_pos = self.output_pos - def route_outputs(self): """ Route the output (drains) together. diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 2f539c16..8d3e0582 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -53,7 +53,7 @@ class pnand2(pgate.pgate): self.place_ptx() self.connect_rails() self.add_well_contacts() - self.extend_wells(self.well_pos) + self.extend_wells() self.route_inputs() self.route_output() @@ -110,7 +110,7 @@ class pnand2(pgate.pgate): extra_contact_space = max(-self.nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space, - self.poly_extend_active, self.poly_space) + self.poly_extend_active + self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ @@ -170,9 +170,6 @@ class pnand2(pgate.pgate): self.output_pos = vector(0, 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height)) - # This will help with the wells - self.well_pos = self.output_pos - def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index d5b07069..88b02d92 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -61,7 +61,7 @@ class pnand3(pgate.pgate): self.place_ptx() self.connect_rails() self.add_well_contacts() - self.extend_wells(self.well_pos) + self.extend_wells() self.route_inputs() self.route_output() @@ -102,10 +102,8 @@ class pnand3(pgate.pgate): nmos = factory.create(module_type="ptx", tx_type="nmos") extra_contact_space = max(-nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell - self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space \ - + extra_contact_space, - self.poly_extend_active, - self.poly_space) + self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space, + self.poly_extend_active + self.poly_space) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ @@ -179,9 +177,6 @@ class pnand3(pgate.pgate): # This will help with the wells and the input/output placement self.output_pos = vector(0, 0.5*self.height) - # This should be placed at the top of the NMOS well - self.well_pos = self.output_pos - def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 7ceab71a..d6605d68 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -52,7 +52,7 @@ class pnor2(pgate.pgate): self.place_ptx() self.connect_rails() self.add_well_contacts() - self.extend_wells(self.well_pos) + self.extend_wells() self.route_inputs() self.route_output() @@ -168,9 +168,6 @@ class pnor2(pgate.pgate): self.output_pos = vector(0, 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height)) - # This will help with the wells - self.well_pos = self.output_pos - def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 888304ce..28b4d85a 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -8,7 +8,7 @@ import contact import design import debug -from tech import drc, parameter +from tech import parameter from vector import vector from globals import OPTS from sram_factory import factory diff --git a/compiler/pgates/pwrite_driver.py b/compiler/pgates/pwrite_driver.py index ed970f9b..16830e62 100644 --- a/compiler/pgates/pwrite_driver.py +++ b/compiler/pgates/pwrite_driver.py @@ -6,14 +6,13 @@ #All rights reserved. # import design -from tech import drc, parameter, spice +from tech import parameter import debug -import math -from tech import drc from vector import vector from globals import OPTS from sram_factory import factory + class pwrite_driver(design.design): """ The pwrite_driver is two tristate inverters that drive the bitlines. From 5e514215d5dad40b977bcea6ef3c2ebfa6b0daeb Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 6 Feb 2020 16:44:19 +0000 Subject: [PATCH 082/103] Force vertical vias on pnand3 --- compiler/pgates/pnand3.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 88b02d92..9ec9ff60 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -233,11 +233,14 @@ class pnand3(pgate.pgate): # Go up to metal2 for ease on all output pins self.add_via_center(layers=self.m1_stack, - offset=pmos1_pin.center()) + offset=pmos1_pin.center(), + directions=("V", "V")) self.add_via_center(layers=self.m1_stack, - offset=pmos3_pin.center()) + offset=pmos3_pin.center(), + directions=("V", "V")) self.add_via_center(layers=self.m1_stack, - offset=nmos3_pin.center()) + offset=nmos3_pin.center(), + directions=("V", "V")) # PMOS3 and NMOS3 are drain aligned self.add_path("m2", [pmos3_pin.center(), nmos3_pin.uc()]) From 4b06ab9eafa24dcd6589fb8caaffcda465ddbad1 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 6 Feb 2020 19:46:10 +0000 Subject: [PATCH 083/103] Move port 2 column address bus down. PEP 8 cleanup. --- compiler/pgates/pnand2.py | 4 ++-- compiler/sram/sram_1bank.py | 15 ++------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 8d3e0582..dd04ccb9 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -224,11 +224,11 @@ class pnand2(pgate.pgate): # Non-preferred active contacts self.add_via_center(layers=self.m1_stack, - directions=("V","H"), + directions=("V", "H"), offset=pmos_pin.center()) # Non-preferred active contacts self.add_via_center(layers=self.m1_stack, - directions=("V","H"), + directions=("V", "H"), offset=nmos_pin.center()) self.add_via_center(layers=self.m1_stack, offset=out_offset) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 93247fc1..2b02923f 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -5,21 +5,10 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import sys -from tech import drc, spice import debug -from math import log,sqrt,ceil -import datetime -import getpass -import numpy as np from vector import vector -from globals import OPTS, print_time - from sram_base import sram_base -from bank import bank from contact import m2_via -from dff_buf_array import dff_buf_array -from dff_array import dff_array class sram_1bank(sram_base): @@ -328,10 +317,10 @@ class sram_1bank(sram_base): offset=flop_pos) def route_col_addr_dff(self): - """ Connect the output of the row flops to the bank pins """ + """ Connect the output of the col flops to the bank pins """ for port in self.all_ports: if port%2: - offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) + offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.word_size+4)*self.m1_pitch) else: offset = self.col_addr_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) From 2ff058f5d563a1a8a1fcbda30fe4b1a3e64871bc Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 6 Feb 2020 22:59:30 +0000 Subject: [PATCH 084/103] PEP8 Cleanup and reverse pitch offset of col addr routing --- compiler/modules/bank.py | 34 ++++++++++++++++------------------ compiler/sram/sram_1bank.py | 2 +- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 7ed0c9a3..915aa064 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -5,19 +5,15 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import sys -from tech import drc, parameter import debug import design -import math -from math import log,sqrt,ceil -import contact -import pgates from sram_factory import factory +from math import log +from tech import drc from vector import vector - from globals import OPTS + class bank(design.design): """ Dynamically generated a single bank including bitcell array, @@ -499,6 +495,8 @@ class bank(design.design): Create a 2:4 or 3:8 column address decoder. """ + # Height is a multiple of DFF so that it can be staggered + # and rows do not align with the control logic module self.dff = factory.create(module_type="dff") if self.col_addr_size == 0: @@ -867,7 +865,6 @@ class bank(design.design): route_map = list(zip(decode_pins, column_mux_pins)) self.create_vertical_channel_route(route_map, offset, self.m1_stack) - def add_lvs_correspondence_points(self): """ This adds some points for easier debugging if LVS goes wrong. These should probably be turned off by default though, since extraction @@ -912,7 +909,6 @@ class bank(design.design): layer="m1", offset=data_pin.center()) - def route_control_lines(self, port): """ Route the control lines of the entire bank """ @@ -920,23 +916,25 @@ class bank(design.design): # From control signal to the module pin # Connection from the central bus to the main control block crosses # pre-decoder and this connection is in metal3 - write_inst = 0 - read_inst = 0 - connection = [] - connection.append((self.prefix+"p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar").lc())) + connection.append((self.prefix + "p_en_bar{}".format(port), + self.port_data_inst[port].get_pin("p_en_bar").lc())) rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port]) - connection.append((self.prefix+"wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc())) + connection.append((self.prefix + "wl_en{}".format(port), + self.bitcell_array_inst.get_pin(rbl_wl_name).lc())) if port in self.write_ports: if port % 2: - connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").rc())) + connection.append((self.prefix + "w_en{}".format(port), + self.port_data_inst[port].get_pin("w_en").rc())) else: - connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").lc())) + connection.append((self.prefix + "w_en{}".format(port), + self.port_data_inst[port].get_pin("w_en").lc())) if port in self.read_ports: - connection.append((self.prefix+"s_en{}".format(port), self.port_data_inst[port].get_pin("s_en").lc())) + connection.append((self.prefix + "s_en{}".format(port), + self.port_data_inst[port].get_pin("s_en").lc())) for (control_signal, pin_pos) in connection: control_mid_pos = self.bus_xoffset[port][control_signal] @@ -960,7 +958,7 @@ class bank(design.design): self.add_via_center(layers=self.m1_stack, offset=control_pos) - def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): + def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): """Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline""" #Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption stage_effort_list = [] diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 2b02923f..3b8d969c 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -320,7 +320,7 @@ class sram_1bank(sram_base): """ Connect the output of the col flops to the bank pins """ for port in self.all_ports: if port%2: - offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.word_size+4)*self.m1_pitch) + offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) else: offset = self.col_addr_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) From 4d85640a0025154b511ee6be0a8d094c546b746a Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 7 Feb 2020 22:20:16 +0000 Subject: [PATCH 085/103] Change col addr spacing to col addr size --- compiler/sram/sram_1bank.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 3b8d969c..2f37d927 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -320,7 +320,7 @@ class sram_1bank(sram_base): """ Connect the output of the col flops to the bank pins """ for port in self.all_ports: if port%2: - offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) + offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size+2)*self.m1_pitch) else: offset = self.col_addr_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) From b212b3e85a31b279fe2265c8889a8fb3cb330586 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Sun, 9 Feb 2020 21:37:09 -0800 Subject: [PATCH 086/103] s8 gdsless netlist only working up to dff array --- compiler/base/utils.py | 16 +++++++--------- compiler/bitcells/bitcell.py | 7 +++++-- compiler/bitcells/replica_bitcell.py | 18 ++++++++++++++---- compiler/modules/replica_bitcell_array.py | 4 ++-- compiler/modules/sense_amp.py | 9 +++++++-- compiler/modules/write_driver.py | 9 +++++++-- compiler/sram/sram_config.py | 14 +------------- 7 files changed, 43 insertions(+), 34 deletions(-) diff --git a/compiler/base/utils.py b/compiler/base/utils.py index 16fbc51f..b6a62788 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -96,10 +96,10 @@ def get_libcell_size(name, units, lpp): Open a GDS file and return the library cell size from either the bounding box or a border layer. """ - if not OPTS.netlist_only: - cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" - return(get_gds_size(name, cell_gds, units, lpp)) - return (0,0,) + + cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" + return(get_gds_size(name, cell_gds, units, lpp)) + def get_gds_pins(pin_names, name, gds_filename, units): @@ -130,11 +130,9 @@ def get_libcell_pins(pin_list, name, units): Open a GDS file and find the pins in pin_list as text on a given layer. Return these as a rectangle layer pair for each pin. """ - if not OPTS.netlist_only: - cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" - return(get_gds_pins(pin_list, name, cell_gds, units)) - else: - return + + cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" + return(get_gds_pins(pin_list, name, cell_gds, units)) diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index 5b552832..a2ae66bf 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -81,8 +81,11 @@ class bitcell(bitcell_base.bitcell_base): def get_wl_name(self, port=0): """Get wl name""" - debug.check(port == 0, "One port for bitcell only.") - return "wl" + if cell_properties.bitcell.split_wl: + return "wl{}".format(port) + else: + debug.check(port == 0, "One port for bitcell only.") + return "wl" def build_graph(self, graph, inst_name, port_nets): """ diff --git a/compiler/bitcells/replica_bitcell.py b/compiler/bitcells/replica_bitcell.py index 2f804bf0..e67ce118 100644 --- a/compiler/bitcells/replica_bitcell.py +++ b/compiler/bitcells/replica_bitcell.py @@ -8,7 +8,8 @@ import design import debug import utils -from tech import GDS,layer,drc,parameter +from tech import GDS,layer,drc,parameter,cell_properties +from globals import OPTS class replica_bitcell(design.design): """ @@ -17,10 +18,19 @@ class replica_bitcell(design.design): is a hand-made cell, so the layout and netlist should be available in the technology library. """ - pin_names = ["bl", "br", "wl", "vdd", "gnd"] + if cell_properties.bitcell.split_wl: + pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"] + else: + pin_names = ["bl", "br", "wl", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - (width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"]) + + if not OPTS.netlist_only: + (width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"]) + else: + (width,height) = (0,0) + pin_map = [] def __init__(self, name=""): # Ignore the name argument diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 178c2f91..a8b3ec30 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -158,7 +158,7 @@ class replica_bitcell_array(design.design): # Left port WLs (one dummy for each port when we allow >1 port) for port in range(self.left_rbl): # Make names for all RBLs - wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.all_ports))] + wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.cell.get_all_wl_names()))] # Keep track of the pin that is the RBL self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] self.replica_col_wl_names.extend(wl_names) @@ -167,7 +167,7 @@ class replica_bitcell_array(design.design): # Right port WLs (one dummy for each port when we allow >1 port) for port in range(self.left_rbl,self.left_rbl+self.right_rbl): # Make names for all RBLs - wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.all_ports))] + wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.cell.get_all_wl_names()))] # Keep track of the pin that is the RBL self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] self.replica_col_wl_names.extend(wl_names) diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index e77d577f..ac3a859c 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -9,6 +9,7 @@ import design import debug import utils from tech import GDS,layer, parameter,drc +from globals import OPTS import logical_effort class sense_amp(design.design): @@ -21,8 +22,12 @@ class sense_amp(design.design): pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"] type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - (width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"]) + if not OPTS.netlist_only: + (width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"]) + else: + (width, height) = (0,0) + pin_map = [] def __init__(self, name): design.design.__init__(self, name) diff --git a/compiler/modules/write_driver.py b/compiler/modules/write_driver.py index 85a58fd5..11b14f00 100644 --- a/compiler/modules/write_driver.py +++ b/compiler/modules/write_driver.py @@ -8,6 +8,7 @@ import debug import design import utils +from globals import OPTS from tech import GDS,layer class write_driver(design.design): @@ -20,8 +21,12 @@ class write_driver(design.design): pin_names = ["din", "bl", "br", "en", "vdd", "gnd"] type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - (width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"]) + if not OPTS.netlist_only: + (width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"]) + else: + (width,height) = (0,0) + pin_map = [] def __init__(self, name): design.design.__init__(self, name) diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index 20c8299e..3a7e6d39 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -23,10 +23,7 @@ class sram_config: # This will get over-written when we determine the organization self.words_per_row = words_per_row - if not OPTS.netlist_only: - self.compute_sizes() - else: - self.compute_simple_sram_sizes() + self.compute_sizes() @@ -38,15 +35,6 @@ class sram_config: # Copy all the variables to the local module for member in members: setattr(module,member,getattr(self,member)) - - def compute_simple_sram_sizes(self): - self.row_addr_size = int(log(OPTS.num_words, 2)) - self.col_addr_size = int(log(OPTS.word_size, 2)) - self.words_per_row = 1 - self.num_rows = OPTS.num_words - self.num_cols = OPTS.word_size - self.bank_addr_size = self.col_addr_size + self.row_addr_size - self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) def compute_sizes(self): """ Computes the organization of the memory using bitcell size by trying to make it square.""" From 7038fad9301e3229364feb084233bb074cafdd91 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Sun, 9 Feb 2020 23:10:33 -0800 Subject: [PATCH 087/103] s8 gdsless netlist only working up to pdriver --- compiler/modules/custom_module_properties.py | 27 ++++++++++++++++++++ compiler/modules/dff_buf.py | 9 +++++-- compiler/modules/dff_buf_array.py | 14 +++++++--- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/compiler/modules/custom_module_properties.py b/compiler/modules/custom_module_properties.py index e27741ea..768a8c18 100644 --- a/compiler/modules/custom_module_properties.py +++ b/compiler/modules/custom_module_properties.py @@ -13,17 +13,44 @@ class _dff: self.custom_type_list = custom_type_list self.clk_pin = clk_pin +class _dff_buff: + def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts): + self.use_custom_ports = use_custom_ports + self.buf_ports = custom_buff_ports + self.add_body_contacts = add_body_contacts + +class _dff_buff_array: + def __init__(self, use_custom_ports, add_body_contacts): + self.use_custom_ports = use_custom_ports + self.add_body_contacts = add_body_contacts + class module_properties(): """ TODO """ def __init__(self): self.names = {} + self._dff = _dff(use_custom_ports = False, custom_port_list = ["D", "Q", "clk", "vdd", "gnd"], custom_type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"], clk_pin= "clk") + + self._dff_buff = _dff_buff(use_custom_ports = False, + custom_buff_ports = ["D", "qint", "clk", "vdd", "gnd"], + add_body_contacts = False) + + self._dff_buff_array = _dff_buff_array(use_custom_ports = False, + add_body_contacts = False) @property def dff(self): return self._dff + + @property + def dff_buff(self): + return self._dff_buff + + @property + def dff_buff_array(self): + return self._dff_buff_array \ No newline at end of file diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 9e2ff0aa..d2698b34 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -7,7 +7,7 @@ # import debug import design -from tech import drc,parameter +from tech import drc,parameter,module_properties from math import log from vector import vector from globals import OPTS @@ -82,10 +82,15 @@ class dff_buf(design.design): self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") + if module_properties.dff_buff.add_body_contacts: + self.add_pin("vpb", "INPUT") + self.add_pin("vpn", "INPUT") + def create_instances(self): self.dff_inst=self.add_inst(name="dff_buf_dff", mod=self.dff) - self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) + self.connect_inst(module_properties.dff_buff.buf_ports) + #self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) self.inv1_inst=self.add_inst(name="dff_buf_inv1", mod=self.inv1) diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 8b8e21dc..326fdda1 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -7,7 +7,7 @@ # import debug import design -from tech import drc +from tech import drc, module_properties from math import log from vector import vector from globals import OPTS @@ -64,6 +64,10 @@ class dff_buf_array(design.design): self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") + if module_properties.dff_buff_array.add_body_contacts: + self.add_pin("vpb", "INPUT") + self.add_pin("vnb", "INPUT") + def add_modules(self): self.dff = factory.create(module_type="dff_buf", inv1_size=self.inv1_size, @@ -77,12 +81,16 @@ class dff_buf_array(design.design): name = "dff_r{0}_c{1}".format(row,col) self.dff_insts[row,col]=self.add_inst(name=name, mod=self.dff) - self.connect_inst([self.get_din_name(row,col), + inst_ports = [self.get_din_name(row,col), self.get_dout_name(row,col), self.get_dout_bar_name(row,col), "clk", "vdd", - "gnd"]) + "gnd"] + if module_properties.dff_buff_array.add_body_contacts: + inst_ports.append("vpb") + inst_ports.append("vnb") + self.connect_inst(inst_ports) def place_dff_array(self): for row in range(self.rows): From 27eced1fbe0729c764117379c992cae405d14ada Mon Sep 17 00:00:00 2001 From: jcirimel Date: Sun, 9 Feb 2020 23:51:01 -0800 Subject: [PATCH 088/103] netlist_only done --- compiler/bitcells/bitcell.py | 5 +++-- compiler/bitcells/replica_bitcell.py | 4 ++-- compiler/modules/control_logic.py | 8 ++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index a2ae66bf..49fdf19f 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -25,11 +25,12 @@ class bitcell(bitcell_base.bitcell_base): if cell_properties.bitcell.split_wl: pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] else: pin_names = ["bl", "br", "wl", "vdd", "gnd"] - + type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] storage_nets = ['Q', 'Qbar'] - type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] + (width, height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) diff --git a/compiler/bitcells/replica_bitcell.py b/compiler/bitcells/replica_bitcell.py index e67ce118..a869e4b7 100644 --- a/compiler/bitcells/replica_bitcell.py +++ b/compiler/bitcells/replica_bitcell.py @@ -20,10 +20,10 @@ class replica_bitcell(design.design): if cell_properties.bitcell.split_wl: pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT" , "POWER", "GROUND"] else: pin_names = ["bl", "br", "wl", "vdd", "gnd"] - - type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] if not OPTS.netlist_only: (width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"]) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index dbb7c3b7..fa11e30b 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -7,7 +7,7 @@ # from math import log import design -from tech import drc, parameter +from tech import drc, parameter, module_properties import debug import contact from sram_factory import factory @@ -742,7 +742,11 @@ class control_logic(design.design): def create_dffs(self): self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs", mod=self.ctrl_dff_array) - self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list) + inst_pins = self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list + if module_properties.dff_buff_array.add_body_contacts: + inst_pins.append("vpb") + inst_pins.append("vnb") + self.connect_inst(inst_pins) def place_dffs(self): self.ctrl_dff_inst.place(vector(0,0)) From 101eb281126f6dd3fd1897ed0a4ebb3ac2f6372b Mon Sep 17 00:00:00 2001 From: jcirimel Date: Sun, 9 Feb 2020 23:52:11 -0800 Subject: [PATCH 089/103] revert example scn4m to non netlist only --- compiler/example_configs/example_config_scn4m_subm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/example_configs/example_config_scn4m_subm.py b/compiler/example_configs/example_config_scn4m_subm.py index a55122a2..71ef328b 100644 --- a/compiler/example_configs/example_config_scn4m_subm.py +++ b/compiler/example_configs/example_config_scn4m_subm.py @@ -9,7 +9,6 @@ temperatures = [25] route_supplies = True check_lvsdrc = True -netlist_only = True output_path = "temp" output_name = "sram_{0}_{1}_{2}".format(word_size, From f7915ec55e687adade51973fd22576563c2c3639 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 10 Feb 2020 17:12:39 +0000 Subject: [PATCH 090/103] Route to top of NMOS to prevent poly overlap nmos --- compiler/pgates/pgate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index e0401726..6d385d09 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -79,8 +79,8 @@ class pgate(design.design): debug.check(nmos_gate_pin.ll().x == pmos_gate_pin.ll().x, "Connecting unaligned gates not supported. See unaligned_gates.gds.") - # Pick point on the left of NMOS and connect down to PMOS - nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5 * self.poly_width, 0) + # Pick point on the left of NMOS and up to PMOS + nmos_gate_pos = nmos_gate_pin.ul() + vector(0.5 * self.poly_width, 0) pmos_gate_pos = vector(nmos_gate_pos.x, pmos_gate_pin.bc().y) self.add_path("poly", [nmos_gate_pos, pmos_gate_pos]) From 6bf33a980fd357f94516dd446f19e2239a21ed4b Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 10 Feb 2020 19:28:30 +0000 Subject: [PATCH 091/103] Add conservative well spacing between library FF and our pgates. --- compiler/modules/dff_buf.py | 10 +++++++--- compiler/modules/dff_buf_array.py | 10 ++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 9e2ff0aa..82f567e6 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -49,10 +49,11 @@ class dff_buf(design.design): self.create_instances() def create_layout(self): - self.width = self.dff.width + self.inv1.width + self.inv2.width + self.place_instances() + self.width = self.inv2_inst.rx() self.height = self.dff.height - self.place_instances() + self.route_wires() self.add_layout_pins() self.add_boundary() @@ -100,7 +101,10 @@ class dff_buf(design.design): self.dff_inst.place(vector(0,0)) # Add INV1 to the right - self.inv1_inst.place(vector(self.dff_inst.rx(),0)) + well_spacing = max(self.nwell_space, + self.pwell_space, + self.pwell_to_nwell) + self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active,0)) # Add INV2 to the right self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 8b8e21dc..67c7522f 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -70,6 +70,12 @@ class dff_buf_array(design.design): inv2_size=self.inv2_size) self.add_mod(self.dff) + well_spacing = max(self.nwell_space, + self.pwell_space, + self.pwell_to_nwell) + + self.dff_pitch = self.dff.width + well_spacing + self.well_extend_active + def create_dff_array(self): self.dff_insts={} for row in range(self.rows): @@ -89,10 +95,10 @@ class dff_buf_array(design.design): for col in range(self.columns): name = "Xdff_r{0}_c{1}".format(row,col) if (row % 2 == 0): - base = vector(col*self.dff.width,row*self.dff.height) + base = vector(col*self.dff_pitch,row*self.dff.height) mirror = "R0" else: - base = vector(col*self.dff.width,(row+1)*self.dff.height) + base = vector(col*self.dff_pitch,(row+1)*self.dff.height) mirror = "MX" self.dff_insts[row,col].place(offset=base, mirror=mirror) From 0ef06ec1e14c07948c54d5012d050988e1ce6f3f Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 10 Feb 2020 20:06:34 +0000 Subject: [PATCH 092/103] Fix dff_buf width in netlist_only mode --- compiler/modules/dff_buf_array.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 67c7522f..4f2140c4 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -70,11 +70,6 @@ class dff_buf_array(design.design): inv2_size=self.inv2_size) self.add_mod(self.dff) - well_spacing = max(self.nwell_space, - self.pwell_space, - self.pwell_to_nwell) - - self.dff_pitch = self.dff.width + well_spacing + self.well_extend_active def create_dff_array(self): self.dff_insts={} @@ -91,14 +86,21 @@ class dff_buf_array(design.design): "gnd"]) def place_dff_array(self): + + well_spacing = max(self.nwell_space, + self.pwell_space, + self.pwell_to_nwell) + + dff_pitch = self.dff.width + well_spacing + self.well_extend_active + for row in range(self.rows): for col in range(self.columns): name = "Xdff_r{0}_c{1}".format(row,col) if (row % 2 == 0): - base = vector(col*self.dff_pitch,row*self.dff.height) + base = vector(col*dff_pitch,row*self.dff.height) mirror = "R0" else: - base = vector(col*self.dff_pitch,(row+1)*self.dff.height) + base = vector(col*dff_pitch,(row+1)*self.dff.height) mirror = "MX" self.dff_insts[row,col].place(offset=base, mirror=mirror) From aedbc5f9689ba1b8fd4577b77f4f41b9a04b67d9 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 12 Feb 2020 04:09:40 +0000 Subject: [PATCH 093/103] merge custom cell and module properties --- .../custom_cell_properties.py} | 23 ++++++++++++-- compiler/bitcells/custom_cell_properties.py | 30 ------------------- compiler/modules/control_logic.py | 5 ++-- compiler/modules/dff.py | 10 +++---- compiler/modules/dff_buf.py | 7 +++-- compiler/modules/dff_buf_array.py | 7 +++-- technology/freepdk45/tech/tech.py | 4 +-- technology/scn4m_subm/tech/tech.py | 2 -- 8 files changed, 38 insertions(+), 50 deletions(-) rename compiler/{modules/custom_module_properties.py => base/custom_cell_properties.py} (79%) delete mode 100644 compiler/bitcells/custom_cell_properties.py diff --git a/compiler/modules/custom_module_properties.py b/compiler/base/custom_cell_properties.py similarity index 79% rename from compiler/modules/custom_module_properties.py rename to compiler/base/custom_cell_properties.py index 768a8c18..ed00d443 100644 --- a/compiler/modules/custom_module_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -5,6 +5,15 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # +class _mirror_axis: + def __init__(self, x, y): + self.x = x + self.y = y + +class _bitcell: + def __init__(self, mirror, split_wl): + self.mirror = mirror + self.split_wl = split_wl class _dff: def __init__(self, use_custom_ports, custom_port_list, custom_type_list, clk_pin): @@ -24,13 +33,16 @@ class _dff_buff_array: self.use_custom_ports = use_custom_ports self.add_body_contacts = add_body_contacts -class module_properties(): +class cell_properties(): """ TODO """ def __init__(self): self.names = {} - + + self._bitcell = _bitcell(mirror = _mirror_axis(True, False), + split_wl = False) + self._dff = _dff(use_custom_ports = False, custom_port_list = ["D", "Q", "clk", "vdd", "gnd"], custom_type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"], @@ -43,6 +55,10 @@ class module_properties(): self._dff_buff_array = _dff_buff_array(use_custom_ports = False, add_body_contacts = False) + @property + def bitcell(self): + return self._bitcell + @property def dff(self): return self._dff @@ -53,4 +69,5 @@ class module_properties(): @property def dff_buff_array(self): - return self._dff_buff_array \ No newline at end of file + return self._dff_buff_array + diff --git a/compiler/bitcells/custom_cell_properties.py b/compiler/bitcells/custom_cell_properties.py deleted file mode 100644 index e795f6d7..00000000 --- a/compiler/bitcells/custom_cell_properties.py +++ /dev/null @@ -1,30 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2020 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# - -class _mirror_axis: - def __init__(self, x, y): - self.x = x - self.y = y - -class _bitcell: - def __init__(self, mirror, split_wl): - self.mirror = mirror - self.split_wl = split_wl - -class cell_properties(): - """ - TODO - """ - def __init__(self): - self.names = {} - self._bitcell = _bitcell(mirror = _mirror_axis(True, False), - split_wl = False) - - @property - def bitcell(self): - return self._bitcell diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index fa11e30b..e22d05b3 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -7,7 +7,8 @@ # from math import log import design -from tech import drc, parameter, module_properties +from tech import drc, parameter +from tech import cell_properties as props import debug import contact from sram_factory import factory @@ -743,7 +744,7 @@ class control_logic(design.design): self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs", mod=self.ctrl_dff_array) inst_pins = self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list - if module_properties.dff_buff_array.add_body_contacts: + if props.dff_buff_array.add_body_contacts: inst_pins.append("vpb") inst_pins.append("vnb") self.connect_inst(inst_pins) diff --git a/compiler/modules/dff.py b/compiler/modules/dff.py index 45d20a62..e319459e 100644 --- a/compiler/modules/dff.py +++ b/compiler/modules/dff.py @@ -7,7 +7,7 @@ # import design from tech import GDS, layer, spice, parameter -from tech import module_properties +from tech import cell_properties as props import utils @@ -15,14 +15,14 @@ class dff(design.design): """ Memory address flip-flop """ - if not module_properties.dff.use_custom_ports: + if not props.dff.use_custom_ports: pin_names = ["D", "Q", "clk", "vdd", "gnd"] type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] clk_pin = "clk" else: - pin_names = module_properties.dff.custom_port_list - type_list = module_properties.dff.custom_type_list - clk_pin = module_properties.dff.clk_pin + pin_names = props.dff.custom_port_list + type_list = props.dff.custom_type_list + clk_pin = props.dff.clk_pin (width, height) = utils.get_libcell_size("dff", GDS["unit"], diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index d2698b34..1fba563b 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -7,7 +7,8 @@ # import debug import design -from tech import drc,parameter,module_properties +from tech import drc,parameter +from tech import cell_properties as props from math import log from vector import vector from globals import OPTS @@ -82,14 +83,14 @@ class dff_buf(design.design): self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - if module_properties.dff_buff.add_body_contacts: + if props.dff_buff.add_body_contacts: self.add_pin("vpb", "INPUT") self.add_pin("vpn", "INPUT") def create_instances(self): self.dff_inst=self.add_inst(name="dff_buf_dff", mod=self.dff) - self.connect_inst(module_properties.dff_buff.buf_ports) + self.connect_inst(props.dff_buff.buf_ports) #self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) self.inv1_inst=self.add_inst(name="dff_buf_inv1", diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 326fdda1..770675bf 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -7,7 +7,8 @@ # import debug import design -from tech import drc, module_properties +from tech import drc +from tech import cell_properties as props from math import log from vector import vector from globals import OPTS @@ -64,7 +65,7 @@ class dff_buf_array(design.design): self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - if module_properties.dff_buff_array.add_body_contacts: + if props.dff_buff_array.add_body_contacts: self.add_pin("vpb", "INPUT") self.add_pin("vnb", "INPUT") @@ -87,7 +88,7 @@ class dff_buf_array(design.design): "clk", "vdd", "gnd"] - if module_properties.dff_buff_array.add_body_contacts: + if props.dff_buff_array.add_body_contacts: inst_ports.append("vpb") inst_ports.append("vnb") self.connect_inst(inst_ports) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index f91be16c..c2bf135f 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -9,7 +9,7 @@ import os from design_rules import * from module_type import * from custom_cell_properties import cell_properties -from custom_module_properties import module_properties + """ File containing the process technology parameters for FreePDK 45nm. """ @@ -25,7 +25,7 @@ File containing the process technology parameters for FreePDK 45nm. # For example: tech_modules['contact'] = 'contact_freepdk45' tech_modules = module_type() -module_properties = module_properties() + ################################################### # Custom cell properties ################################################### diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index bb379478..7c360d38 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -9,7 +9,6 @@ import os from design_rules import * from module_type import * from custom_cell_properties import cell_properties -from custom_module_properties import module_properties """ File containing the process technology parameters for SCMOS 4m, 0.35um @@ -25,7 +24,6 @@ File containing the process technology parameters for SCMOS 4m, 0.35um # implementation in '$OPENRAM_TECHDIR/modules/' # For example: tech_modules['contact'] = 'contact_scn4m' tech_modules = module_type() -module_properties = module_properties() ################################################### # Custom cell properties From 64bf93e4e584fc4590eb63e23198961ee3cc6546 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Wed, 12 Feb 2020 13:19:41 +0100 Subject: [PATCH 094/103] bank: Connect instances by their individual bl/br names each module should be able to state how their bl/br lines are named. Here we always connect port_data with the bitcell_array, so port_data needs function that return the names of bl/br. Signed-off-by: Bastian Koppelmann --- compiler/modules/bank.py | 10 +++++++--- compiler/modules/port_data.py | 14 ++++++++++++++ compiler/pgates/precharge.py | 6 ++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 133a66c2..f5f3feb6 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -679,9 +679,13 @@ class bank(design.design): inst1 = self.bitcell_array_inst inst1_bl_name = self.bl_names[port]+"_{}" inst1_br_name = self.br_names[port]+"_{}" + + inst2_bl_name = inst2.mod.get_bl_names()+"_{}" + inst2_br_name = inst2.mod.get_br_names()+"_{}" self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, - inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name) + inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, + inst2_bl_name=inst2_bl_name, inst2_br_name=inst2_br_name) # Connect the replica bitlines rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) @@ -786,8 +790,8 @@ class bank(design.design): def connect_bitlines(self, inst1, inst2, num_bits, - inst1_bl_name="bl_{}", inst1_br_name="br_{}", - inst2_bl_name="bl_{}", inst2_br_name="br_{}"): + inst1_bl_name, inst1_br_name, + inst2_bl_name, inst2_br_name): """ Connect the bl and br of two modules. """ diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 9e7f4048..0355fd3c 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -38,6 +38,15 @@ class port_data(design.design): self.create_layout() self.add_boundary() + def get_bl_names(self): + # bl lines are connect from the precharger + return self.precharge.get_bl_names() + + def get_br_names(self): + # br lines are connect from the precharger + return self.precharge.get_br_names() + + def create_netlist(self): self.precompute_constants() @@ -221,6 +230,11 @@ class port_data(design.design): self.bl_names = self.bitcell.get_all_bl_names() self.br_names = self.bitcell.get_all_br_names() self.wl_names = self.bitcell.get_all_wl_names() + # used for bl/br names + self.precharge = factory.create(module_type="precharge", + bitcell_bl = self.bl_names[0], + bitcell_br = self.br_names[0]) + def create_precharge_array(self): """ Creating Precharge """ diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 28b4d85a..acb2062f 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -38,6 +38,12 @@ class precharge(design.design): self.create_layout() self.DRC_LVS() + def get_bl_names(self): + return "bl" + + def get_br_names(self): + return "br" + def create_netlist(self): self.add_pins() self.add_ptx() From f9babcf6667aafda662c51447b2a83b9a42ab9ea Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Wed, 12 Feb 2020 14:04:05 +0100 Subject: [PATCH 095/103] port_data: Each submodule now specifies their bl/br names before the names of bl/br from the bitcell were assumed. If we want to allow renaming of bl/br from bitcells, we have to seperate the other modules from that. Note, that we don't touch every occurence of bl/br, but only the once necessary that pin renaming of the bitcell works. Signed-off-by: Bastian Koppelmann --- compiler/modules/port_data.py | 53 +++++++++++++------ compiler/modules/precharge_array.py | 15 ++++++ compiler/modules/sense_amp.py | 6 +++ compiler/modules/sense_amp_array.py | 14 +++++ .../modules/single_level_column_mux_array.py | 15 ++++++ compiler/modules/write_driver.py | 6 +++ compiler/modules/write_driver_array.py | 13 +++++ compiler/pgates/single_level_column_mux.py | 6 +++ 8 files changed, 112 insertions(+), 16 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 0355fd3c..d1c2c671 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -94,8 +94,10 @@ class port_data(design.design): self.add_pin("rbl_bl","INOUT") self.add_pin("rbl_br","INOUT") for bit in range(self.num_cols): - self.add_pin("{0}_{1}".format(self.bl_names[self.port], bit),"INOUT") - self.add_pin("{0}_{1}".format(self.br_names[self.port], bit),"INOUT") + bl_name = self.precharge_array.get_bl_name(self.port) + br_name = self.precharge_array.get_br_name(self.port) + self.add_pin("{0}_{1}".format(bl_name, bit),"INOUT") + self.add_pin("{0}_{1}".format(br_name, bit),"INOUT") if self.port in self.read_ports: for bit in range(self.word_size): self.add_pin("dout_{}".format(bit),"OUTPUT") @@ -234,6 +236,13 @@ class port_data(design.design): self.precharge = factory.create(module_type="precharge", bitcell_bl = self.bl_names[0], bitcell_br = self.br_names[0]) + # We create a dummy here to get bl/br names to add those pins to this + # module, which happens before we create the real precharge_array + self.precharge_array = factory.create(module_type="precharge_array", + columns=self.num_cols + 1, + bitcell_bl=self.bl_names[self.port], + bitcell_br=self.br_names[self.port]) + def create_precharge_array(self): @@ -244,6 +253,8 @@ class port_data(design.design): self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port), mod=self.precharge_array) + bl_name = self.precharge_array.get_bl_name(self.port) + br_name = self.precharge_array.get_br_name(self.port) temp = [] # Use left BLs for RBL @@ -251,8 +262,9 @@ class port_data(design.design): temp.append("rbl_bl") temp.append("rbl_br") for bit in range(self.num_cols): - temp.append(self.bl_names[self.port]+"_{0}".format(bit)) - temp.append(self.br_names[self.port]+"_{0}".format(bit)) + temp.append("{0}_{1}".format(bl_name, bit)) + temp.append("{0}_{1}".format(br_name, bit)) + # Use right BLs for RBL if self.port==1: temp.append("rbl_bl") @@ -273,15 +285,19 @@ class port_data(design.design): self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port), mod=self.column_mux_array) + bl_name = self.column_mux_array.get_bl_name(self.port) + br_name = self.column_mux_array.get_br_name(self.port) temp = [] for col in range(self.num_cols): - temp.append(self.bl_names[self.port]+"_{0}".format(col)) - temp.append(self.br_names[self.port]+"_{0}".format(col)) + temp.append("{0}_{1}".format(bl_name, col)) + temp.append("{0}_{1}".format(br_name, col)) + for word in range(self.words_per_row): temp.append("sel_{}".format(word)) for bit in range(self.word_size): - temp.append(self.bl_names[self.port]+"_out_{0}".format(bit)) - temp.append(self.br_names[self.port]+"_out_{0}".format(bit)) + temp.append("{0}_out_{1}".format(bl_name, bit)) + temp.append("{0}_out_{1}".format(br_name, bit)) + temp.append("gnd") self.connect_inst(temp) @@ -299,15 +315,18 @@ class port_data(design.design): self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port), mod=self.sense_amp_array) + bl_name = self.sense_amp_array.get_bl_name(self.port) + br_name = self.sense_amp_array.get_br_name(self.port) temp = [] for bit in range(self.word_size): temp.append("dout_{}".format(bit)) if self.words_per_row == 1: - temp.append(self.bl_names[self.port]+"_{0}".format(bit)) - temp.append(self.br_names[self.port]+"_{0}".format(bit)) + temp.append("{0}_{1}".format(bl_name, bit)) + temp.append("{0}_{1}".format(br_name, bit)) else: - temp.append(self.bl_names[self.port]+"_out_{0}".format(bit)) - temp.append(self.br_names[self.port]+"_out_{0}".format(bit)) + temp.append("{0}_out_{1}".format(bl_name, bit)) + temp.append("{0}_out_{1}".format(br_name, bit)) + temp.extend(["s_en", "vdd", "gnd"]) self.connect_inst(temp) @@ -322,6 +341,8 @@ class port_data(design.design): """ Creating Write Driver """ self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port), mod=self.write_driver_array) + bl_name = self.write_driver_array.get_bl_name(self.port) + br_name = self.write_driver_array.get_br_name(self.port) temp = [] for bit in range(self.word_size): @@ -329,11 +350,11 @@ class port_data(design.design): for bit in range(self.word_size): if (self.words_per_row == 1): - temp.append(self.bl_names[self.port] + "_{0}".format(bit)) - temp.append(self.br_names[self.port] + "_{0}".format(bit)) + temp.append("{0}_{1}".format(bl_name, bit)) + temp.append("{0}_{1}".format(br_name, bit)) else: - temp.append(self.bl_names[self.port] + "_out_{0}".format(bit)) - temp.append(self.br_names[self.port] + "_out_{0}".format(bit)) + temp.append("{0}_out_{1}".format(bl_name, bit)) + temp.append("{0}_out_{1}".format(br_name, bit)) if self.write_size is not None: for i in range(self.num_wmasks): diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 54ed2690..5ac97b4d 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -32,6 +32,21 @@ class precharge_array(design.design): if not OPTS.netlist_only: self.create_layout() + def get_bl_name(self, port=0): + bl_name = self.pc_cell.get_bl_names() + if len(self.all_ports) == 1: + return bl_name + else: + return bl_name + "{}".format(port) + + def get_br_name(self, port=0): + br_name = self.pc_cell.get_br_names() + if len(self.all_ports) == 1: + return br_name + else: + return br_name + "{}".format(port) + + def add_pins(self): """Adds pins for spice file""" for i in range(self.columns): diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index ac3a859c..eb624111 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -29,6 +29,12 @@ class sense_amp(design.design): (width, height) = (0,0) pin_map = [] + def get_bl_names(self): + return "bl" + + def get_br_names(self): + return "br" + def __init__(self, name): design.design.__init__(self, name) debug.info(2, "Create sense_amp") diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 44639017..8ac5146c 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -34,6 +34,20 @@ class sense_amp_array(design.design): self.create_layout() + def get_bl_name(self, port=0): + bl_name = self.amp.get_bl_names() + if len(self.all_ports) == 1: + return bl_name + else: + return bl_name + "{}".format(port) + + def get_br_name(self, port=0): + br_name = self.amp.get_br_names() + if len(self.all_ports) == 1: + return br_name + else: + return br_name + "{}".format(port) + def create_netlist(self): self.add_modules() self.add_pins() diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 24510015..1c181574 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -37,6 +37,21 @@ class single_level_column_mux_array(design.design): if not OPTS.netlist_only: self.create_layout() + def get_bl_name(self, port=0): + bl_name = self.mux.get_bl_names() + if len(self.all_ports) == 1: + return bl_name + else: + return bl_name + "{}".format(port) + + def get_br_name(self, port=0): + br_name = self.mux.get_br_names() + if len(self.all_ports) == 1: + return br_name + else: + return br_name + "{}".format(port) + + def create_netlist(self): self.add_modules() self.add_pins() diff --git a/compiler/modules/write_driver.py b/compiler/modules/write_driver.py index 11b14f00..db08bcf4 100644 --- a/compiler/modules/write_driver.py +++ b/compiler/modules/write_driver.py @@ -37,6 +37,12 @@ class write_driver(design.design): self.pin_map = write_driver.pin_map self.add_pin_types(self.type_list) + def get_bl_names(self): + return "bl" + + def get_br_names(self): + return "br" + def get_w_en_cin(self): """Get the relative capacitance of a single input""" # This is approximated from SCMOS. It has roughly 5 3x transistor gates. diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index e9b0ac1b..dd7430a9 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -37,6 +37,19 @@ class write_driver_array(design.design): if not OPTS.netlist_only: self.create_layout() + def get_bl_name(self, port=0): + bl_name = self.driver.get_bl_names() + if len(self.all_ports) == 1: + return bl_name + else: + return bl_name + "{}".format(port) + + def get_br_name(self, port=0): + br_name = self.driver.get_br_names() + if len(self.all_ports) == 1: + return br_name + else: + return br_name + "{}".format(port) def create_netlist(self): self.add_modules() diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 66faf1da..90645326 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -31,6 +31,12 @@ class single_level_column_mux(pgate.pgate): pgate.pgate.__init__(self, name) + def get_bl_names(self): + return "bl" + + def get_br_names(self): + return "br" + def create_netlist(self): self.add_modules() self.add_pins() From f6302caeac85bcaf8b332cdc007363d093d60f57 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Wed, 12 Feb 2020 14:17:29 +0100 Subject: [PATCH 096/103] replica_bitcell_array: Connect bitcells based on bitcell bl/br/wl names this allows us to override the bl/br/wl names of each bitcell. Signed-off-by: Bastian Koppelmann --- compiler/modules/bitcell_base_array.py | 26 ++++++++++++++ compiler/modules/replica_bitcell_array.py | 41 ++++++++++++----------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 08a52c21..45bcfe0c 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -22,6 +22,32 @@ class bitcell_base_array(design.design): self.row_size = rows self.column_offset = column_offset + def get_all_bitline_names(self): + + res = list() + bitline_names = self.cell.get_all_bitline_names() + + # We have to keep the order of self.pins, otherwise we connect + # it wrong in the spice netlist + for pin in self.pins: + for bl_name in bitline_names: + if bl_name in pin: + res.append(pin) + return res + + def get_all_wordline_names(self): + + res = list() + wordline_names = self.cell.get_all_wl_names() + + # We have to keep the order of self.pins, otherwise we connect + # it wrong in the spice netlist + for pin in self.pins: + for wl_name in wordline_names: + if wl_name in pin: + res.append(pin) + return res + def add_pins(self): row_list = self.cell.get_all_wl_names() column_list = self.cell.get_all_bitline_names() diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index a8b3ec30..d5865658 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -138,11 +138,10 @@ class replica_bitcell_array(design.design): def add_pins(self): + self.bitcell_array_wl_names = self.bitcell_array.get_all_wordline_names() + self.bitcell_array_bl_names = self.bitcell_array.get_all_bitline_names() - self.bitcell_array_wl_names = [x for x in self.bitcell_array.pins if x.startswith("w")] - self.bitcell_array_bl_names = [x for x in self.bitcell_array.pins if x.startswith("b")] - - # These are the non-indexed names + # These are the non-indexed names self.dummy_cell_wl_names = ["dummy_"+x for x in self.cell.get_all_wl_names()] self.dummy_cell_bl_names = ["dummy_"+x for x in self.cell.get_all_bitline_names()] self.dummy_row_bl_names = self.bitcell_array_bl_names @@ -316,22 +315,24 @@ class replica_bitcell_array(design.design): # Main array wl and bl/br pin_names = self.bitcell_array.get_pin_names() for pin_name in pin_names: - if pin_name.startswith("wl"): - pin_list = self.bitcell_array_inst.get_pins(pin_name) - for pin in pin_list: - self.add_layout_pin(text=pin_name, - layer=pin.layer, - offset=pin.ll().scale(0,1), - width=self.width, - height=pin.height()) - elif pin_name.startswith("bl") or pin_name.startswith("br"): - pin_list = self.bitcell_array_inst.get_pins(pin_name) - for pin in pin_list: - self.add_layout_pin(text=pin_name, - layer=pin.layer, - offset=pin.ll().scale(1,0), - width=pin.width(), - height=self.height) + for wl in self.bitcell_array_wl_names: + if wl in pin_name: + pin_list = self.bitcell_array_inst.get_pins(pin_name) + for pin in pin_list: + self.add_layout_pin(text=pin_name, + layer=pin.layer, + offset=pin.ll().scale(0,1), + width=self.width, + height=pin.height()) + for bitline in self.bitcell_array_bl_names: + if bitline in pin_name: + pin_list = self.bitcell_array_inst.get_pins(pin_name) + for pin in pin_list: + self.add_layout_pin(text=pin_name, + layer=pin.layer, + offset=pin.ll().scale(1,0), + width=pin.width(), + height=self.height) # Replica wordlines From c97bad72db9522f1fc624ea1249a63ea9fef4ca5 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Wed, 12 Feb 2020 15:03:47 +0100 Subject: [PATCH 097/103] custom_cell_properties: Add bitcell pin name API this allows users to overrride the pin names to match the names of their GDS. Signed-off-by: Bastian Koppelmann --- compiler/base/custom_cell_properties.py | 69 +++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index ed00d443..45887561 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -5,15 +5,75 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # + +class _pins: + def __init__(self, pin_dict): + # make the pins elements of the class to allow "." access. + # For example: props.bitcell.cell_6t.pin.bl = "foobar" + for k,v in pin_dict.items(): + self.__dict__[k] = v + +class _cell: + def __init__(self, pin_dict): + pin_dict.update(self._default_power_pins()) + self._pins = _pins(pin_dict) + + @property + def pin(self): + return self._pins + + def _default_power_pins(self): + return { 'vdd' : 'vdd', 'gnd' : 'gnd' } + class _mirror_axis: def __init__(self, x, y): self.x = x self.y = y class _bitcell: - def __init__(self, mirror, split_wl): + def __init__(self, mirror, split_wl, cell_6t, cell_1rw1r, cell_1w1r): self.mirror = mirror self.split_wl = split_wl + self._6t = cell_6t + self._1rw1r = cell_1rw1r + self._1w1r = cell_1w1r + + def _default(): + axis = _mirror_axis(True, False) + cell_6t = _cell({'bl' : 'bl', + 'br' : 'br', + 'wl' : 'wl'}) + + cell_1rw1r = _cell({'bl0' : 'bl0', + 'br0' : 'br0', + 'bl1' : 'bl1', + 'br1' : 'br1', + 'wl0' : 'wl0', + 'wl1' : 'wl1'}) + cell_1w1r = _cell({'bl0' : 'bl0', + 'br0' : 'br0', + 'bl1' : 'bl1', + 'br1' : 'br1', + 'wl0' : 'wl0', + 'wl1' : 'wl1'}) + return _bitcell(cell_6t=cell_6t, + cell_1rw1r=cell_1rw1r, + cell_1w1r=cell_1w1r, + split_wl = False, + mirror=axis) + + @property + def cell_6t(self): + return self._6t + + @property + def cell_1rw1r(self): + return self._1rw1r + + @property + def cell_1w1r(self): + return self._1w1r + class _dff: def __init__(self, use_custom_ports, custom_port_list, custom_type_list, clk_pin): @@ -35,13 +95,14 @@ class _dff_buff_array: class cell_properties(): """ - TODO + This contains meta information about the custom designed cells. For + instance, pin names, or the axis on which they need to be mirrored. These + can be overriden in the tech.py file. """ def __init__(self): self.names = {} - self._bitcell = _bitcell(mirror = _mirror_axis(True, False), - split_wl = False) + self._bitcell = _bitcell._default() self._dff = _dff(use_custom_ports = False, custom_port_list = ["D", "Q", "clk", "vdd", "gnd"], From 87b5a48f9ecc52d2862e6e68933731aa83b832f7 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Wed, 12 Feb 2020 14:48:58 +0100 Subject: [PATCH 098/103] bitcell: Remove hardcoded signal pins use names provided by the tech file, which can be overriden by the technology. Signed-off-by: Bastian Koppelmann --- compiler/bitcells/bitcell.py | 49 +++++----- compiler/bitcells/bitcell_1rw_1r.py | 100 ++++++++++++-------- compiler/bitcells/bitcell_1w_1r.py | 96 +++++++++++-------- compiler/bitcells/dummy_bitcell.py | 7 +- compiler/bitcells/dummy_bitcell_1rw_1r.py | 11 ++- compiler/bitcells/dummy_bitcell_1w_1r.py | 10 +- compiler/bitcells/replica_bitcell.py | 11 ++- compiler/bitcells/replica_bitcell_1rw_1r.py | 24 +++-- compiler/bitcells/replica_bitcell_1w_1r.py | 22 +++-- 9 files changed, 207 insertions(+), 123 deletions(-) diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index 49fdf19f..68e70e7e 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -8,7 +8,7 @@ import debug import utils from tech import GDS, layer, parameter -from tech import cell_properties +from tech import cell_properties as props import bitcell_base @@ -22,15 +22,19 @@ class bitcell(bitcell_base.bitcell_base): # If we have a split WL bitcell, if not be backwards # compatible in the tech file - - if cell_properties.bitcell.split_wl: + + if props.bitcell.split_wl: pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] else: - pin_names = ["bl", "br", "wl", "vdd", "gnd"] + pin_names = [props.bitcell.cell_6t.pin.bl, + props.bitcell.cell_6t.pin.br, + props.bitcell.cell_6t.pin.wl, + props.bitcell.cell_6t.pin.vdd, + props.bitcell.cell_6t.pin.gnd] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] storage_nets = ['Q', 'Qbar'] - + (width, height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) @@ -46,48 +50,47 @@ class bitcell(bitcell_base.bitcell_base): self.pin_map = bitcell.pin_map self.add_pin_types(self.type_list) self.nets_match = self.do_nets_exist(self.storage_nets) - + def get_all_wl_names(self): """ Creates a list of all wordline pin names """ - if cell_properties.bitcell.split_wl: + if props.bitcell.split_wl: row_pins = ["wl0", "wl1"] else: - row_pins = ["wl"] + row_pins = [props.bitcell.cell_6t.pin.wl] return row_pins - + def get_all_bitline_names(self): """ Creates a list of all bitline pin names (both bl and br) """ - column_pins = ["bl", "br"] + pin = props.bitcell.cell_6t.pin + column_pins = [pin.bl, pin.br] return column_pins - + def get_all_bl_names(self): """ Creates a list of all bl pins names """ - column_pins = ["bl"] - return column_pins - + return [props.bitcell.cell_6t.pin.bl] + def get_all_br_names(self): """ Creates a list of all br pins names """ - column_pins = ["br"] - return column_pins - + return [props.bitcell.cell_6t.pin.br] + def get_bl_name(self, port=0): """Get bl name""" debug.check(port == 0, "One port for bitcell only.") - return "bl" - + return props.bitcell.cell_6t.pin.bl + def get_br_name(self, port=0): """Get bl name""" debug.check(port == 0, "One port for bitcell only.") - return "br" + return props.bitcell.cell_6t.pin.br def get_wl_name(self, port=0): """Get wl name""" - if cell_properties.bitcell.split_wl: + if props.bitcell.split_wl: return "wl{}".format(port) else: debug.check(port == 0, "One port for bitcell only.") - return "wl" - + return props.bitcell.cell_6t.pin.wl + def build_graph(self, graph, inst_name, port_nets): """ Adds edges based on inputs/outputs. diff --git a/compiler/bitcells/bitcell_1rw_1r.py b/compiler/bitcells/bitcell_1rw_1r.py index 0280f3cb..e5eb3043 100644 --- a/compiler/bitcells/bitcell_1rw_1r.py +++ b/compiler/bitcells/bitcell_1rw_1r.py @@ -8,6 +8,7 @@ import debug import utils from tech import GDS, layer, parameter, drc +from tech import cell_properties as props import logical_effort import bitcell_base @@ -20,7 +21,15 @@ class bitcell_1rw_1r(bitcell_base.bitcell_base): library. """ - pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + pin_names = [props.bitcell.cell_1rw1r.pin.bl0, + props.bitcell.cell_1rw1r.pin.br0, + props.bitcell.cell_1rw1r.pin.bl1, + props.bitcell.cell_1rw1r.pin.br1, + props.bitcell.cell_1rw1r.pin.wl0, + props.bitcell.cell_1rw1r.pin.wl1, + props.bitcell.cell_1rw1r.pin.vdd, + props.bitcell.cell_1rw1r.pin.gnd] + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] storage_nets = ['Q', 'Q_bar'] @@ -39,85 +48,92 @@ class bitcell_1rw_1r(bitcell_base.bitcell_base): self.pin_map = bitcell_1rw_1r.pin_map self.add_pin_types(self.type_list) self.nets_match = self.do_nets_exist(self.storage_nets) - + + pin_names = bitcell_1rw_1r.pin_names + self.bl_names = [pin_names[0], pin_names[2]] + self.br_names = [pin_names[1], pin_names[3]] + self.wl_names = [pin_names[4], pin_names[5]] + def get_bitcell_pins(self, col, row): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ - bitcell_pins = ["bl0_{0}".format(col), - "br0_{0}".format(col), - "bl1_{0}".format(col), - "br1_{0}".format(col), - "wl0_{0}".format(row), - "wl1_{0}".format(row), + pin_name = props.bitcell.cell_1rw1r.pin + bitcell_pins = ["{0}_{1}".format(pin_name.bl0, col), + "{0}_{1}".format(pin_name.br0, col), + "{0}_{1}".format(pin_name.bl1, col), + "{0}_{1}".format(pin_name.br1, col), + "{0}_{1}".format(pin_name.wl0, row), + "{0}_{1}".format(pin_name.wl1, row), "vdd", "gnd"] return bitcell_pins - + def get_all_wl_names(self): """ Creates a list of all wordline pin names """ - row_pins = ["wl0", "wl1"] - return row_pins - + return [props.bitcell.cell_1rw1r.pin.wl0, + props.bitcell.cell_1rw1r.pin.wl1] + def get_all_bitline_names(self): """ Creates a list of all bitline pin names (both bl and br) """ - column_pins = ["bl0", "br0", "bl1", "br1"] - return column_pins - + return [props.bitcell.cell_1rw1r.pin.bl0, + props.bitcell.cell_1rw1r.pin.br0, + props.bitcell.cell_1rw1r.pin.bl1, + props.bitcell.cell_1rw1r.pin.br1] + def get_all_bl_names(self): """ Creates a list of all bl pins names """ - column_pins = ["bl0", "bl1"] - return column_pins - + return [props.bitcell.cell_1rw1r.pin.bl0, + props.bitcell.cell_1rw1r.pin.bl1] + def get_all_br_names(self): """ Creates a list of all br pins names """ - column_pins = ["br0", "br1"] - return column_pins - + return [props.bitcell.cell_1rw1r.pin.br0, + props.bitcell.cell_1rw1r.pin.br1] + def get_read_bl_names(self): """ Creates a list of bl pin names associated with read ports """ - column_pins = ["bl0", "bl1"] - return column_pins - + return [props.bitcell.cell_1rw1r.pin.bl0, + props.bitcell.cell_1rw1r.pin.bl1] + def get_read_br_names(self): """ Creates a list of br pin names associated with read ports """ - column_pins = ["br0", "br1"] - return column_pins - + return [props.bitcell.cell_1rw1r.pin.br0, + props.bitcell.cell_1rw1r.pin.br1] + def get_write_bl_names(self): """ Creates a list of bl pin names associated with write ports """ - column_pins = ["bl0"] - return column_pins - + return [props.bitcell.cell_1rw1r.pin.bl0] + def get_write_br_names(self): """ Creates a list of br pin names asscociated with write ports""" - column_pins = ["br0"] - return column_pins - + return [props.bitcell.cell_1rw1r.pin.br1] + def get_bl_name(self, port=0): """Get bl name by port""" debug.check(port < 2, "Two ports for bitcell_1rw_1r only.") - return "bl{}".format(port) - + return self.bl_names[port] + def get_br_name(self, port=0): """Get bl name by port""" debug.check(port < 2, "Two ports for bitcell_1rw_1r only.") - return "br{}".format(port) + return self.br_names[port] def get_wl_name(self, port=0): """Get wl name by port""" debug.check(port < 2, "Two ports for bitcell_1rw_1r only.") - return "wl{}".format(port) - + return self.wl_names[port] + def build_graph(self, graph, inst_name, port_nets): """Adds edges to graph. Multiport bitcell timing graph is too complex to use the add_graph_edges function.""" pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} # Edges hardcoded here. Essentially wl->bl/br for both ports. # Port 0 edges - graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self) - graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self) + pins = props.bitcell.cell_1rw1r.pin + graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.bl0], self) + graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self) # Port 1 edges - graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) - graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) + graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self) + graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self) diff --git a/compiler/bitcells/bitcell_1w_1r.py b/compiler/bitcells/bitcell_1w_1r.py index a92dc75d..56bd0b45 100644 --- a/compiler/bitcells/bitcell_1w_1r.py +++ b/compiler/bitcells/bitcell_1w_1r.py @@ -8,6 +8,7 @@ import debug import utils from tech import GDS, layer +from tech import cell_properties as props import bitcell_base @@ -19,7 +20,14 @@ class bitcell_1w_1r(bitcell_base.bitcell_base): library. """ - pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + pin_names = [props.bitcell.cell_1w1r.pin.bl0, + props.bitcell.cell_1w1r.pin.br0, + props.bitcell.cell_1w1r.pin.bl1, + props.bitcell.cell_1w1r.pin.br1, + props.bitcell.cell_1w1r.pin.wl0, + props.bitcell.cell_1w1r.pin.wl1, + props.bitcell.cell_1w1r.pin.vdd, + props.bitcell.cell_1w1r.pin.gnd] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] storage_nets = ['Q', 'Q_bar'] @@ -39,80 +47,88 @@ class bitcell_1w_1r(bitcell_base.bitcell_base): self.add_pin_types(self.type_list) self.nets_match = self.do_nets_exist(self.storage_nets) + pin_names = bitcell_1w_1r.pin_names + self.bl_names = [pin_names[0], pin_names[2]] + self.br_names = [pin_names[1], pin_names[3]] + self.wl_names = [pin_names[4], pin_names[5]] + + def get_bitcell_pins(self, col, row): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ - bitcell_pins = ["bl0_{0}".format(col), - "br0_{0}".format(col), - "bl1_{0}".format(col), - "br1_{0}".format(col), - "wl0_{0}".format(row), - "wl1_{0}".format(row), + pin_name = props.bitcell.cell_1w1r.pin + bitcell_pins = ["{0}_{1}".format(pin_name.bl0, col), + "{0}_{1}".format(pin_name.br0, col), + "{0}_{1}".format(pin_name.bl1, col), + "{0}_{1}".format(pin_name.br1, col), + "{0}_{1}".format(pin_name.wl0, row), + "{0}_{1}".format(pin_name.wl1, row), "vdd", "gnd"] return bitcell_pins - + def get_all_wl_names(self): """ Creates a list of all wordline pin names """ - row_pins = ["wl0", "wl1"] - return row_pins - + return [props.bitcell.cell_1w1r.pin.wl0, + props.bitcell.cell_1w1r.pin.wl1] + def get_all_bitline_names(self): """ Creates a list of all bitline pin names (both bl and br) """ - column_pins = ["bl0", "br0", "bl1", "br1"] - return column_pins - + return [props.bitcell.cell_1w1r.pin.bl0, + props.bitcell.cell_1w1r.pin.br0, + props.bitcell.cell_1w1r.pin.bl1, + props.bitcell.cell_1w1r.pin.br1] + def get_all_bl_names(self): """ Creates a list of all bl pins names """ - column_pins = ["bl0", "bl1"] - return column_pins - + return [props.bitcell.cell_1w1r.pin.bl0, + props.bitcell.cell_1w1r.pin.bl1] + def get_all_br_names(self): """ Creates a list of all br pins names """ - column_pins = ["br0", "br1"] - return column_pins - + return [props.bitcell.cell_1w1r.pin.br0, + props.bitcell.cell_1w1r.pin.br1] + def get_read_bl_names(self): """ Creates a list of bl pin names associated with read ports """ - column_pins = ["bl0", "bl1"] - return column_pins - + return [props.bitcell.cell_1w1r.pin.bl0, + props.bitcell.cell_1w1r.pin.bl1] + def get_read_br_names(self): """ Creates a list of br pin names associated with read ports """ - column_pins = ["br0", "br1"] - return column_pins - + return [props.bitcell.cell_1w1r.pin.br0, + props.bitcell.cell_1w1r.pin.br1] + def get_write_bl_names(self): """ Creates a list of bl pin names associated with write ports """ - column_pins = ["bl0"] - return column_pins - + return [props.bitcell.cell_1w1r.pin.bl0] + def get_write_br_names(self): """ Creates a list of br pin names asscociated with write ports""" - column_pins = ["br0"] - return column_pins - + return [props.bitcell.cell_1w1r.pin.br1] + def get_bl_name(self, port=0): """Get bl name by port""" - return "bl{}".format(port) - + return self.bl_names[port] + def get_br_name(self, port=0): """Get bl name by port""" - return "br{}".format(port) - + return self.br_names[port] + def get_wl_name(self, port=0): """Get wl name by port""" debug.check(port < 2, "Two ports for bitcell_1rw_1r only.") - return "wl{}".format(port) - + return self.wl_names[port] + def build_graph(self, graph, inst_name, port_nets): """Adds edges to graph. Multiport bitcell timing graph is too complex to use the add_graph_edges function.""" pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} + pins = props.bitcell.cell_1w1r.pin # Edges hardcoded here. Essentially wl->bl/br for both ports. # Port 0 edges - graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) - graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) + graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self) + graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self) # Port 1 is a write port, so its timing is not considered here. diff --git a/compiler/bitcells/dummy_bitcell.py b/compiler/bitcells/dummy_bitcell.py index 98da96e2..116ea3ed 100644 --- a/compiler/bitcells/dummy_bitcell.py +++ b/compiler/bitcells/dummy_bitcell.py @@ -8,6 +8,7 @@ import debug import utils from tech import GDS, layer +from tech import cell_properties as props import bitcell_base @@ -18,8 +19,12 @@ class dummy_bitcell(bitcell_base.bitcell_base): the layout and netlist should be available in the technology library. """ + pin_names = [props.bitcell.cell_6t.pin.bl, + props.bitcell.cell_6t.pin.br, + props.bitcell.cell_6t.pin.wl, + props.bitcell.cell_6t.pin.vdd, + props.bitcell.cell_6t.pin.gnd] - pin_names = ["bl", "br", "wl", "vdd", "gnd"] (width, height) = utils.get_libcell_size("dummy_cell_6t", GDS["unit"], layer["boundary"]) diff --git a/compiler/bitcells/dummy_bitcell_1rw_1r.py b/compiler/bitcells/dummy_bitcell_1rw_1r.py index 401e9f85..d29c804f 100644 --- a/compiler/bitcells/dummy_bitcell_1rw_1r.py +++ b/compiler/bitcells/dummy_bitcell_1rw_1r.py @@ -8,6 +8,7 @@ import debug import utils from tech import GDS, layer +from tech import cell_properties as props import bitcell_base @@ -18,7 +19,15 @@ class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base): is a hand-made cell, so the layout and netlist should be available in the technology library. """ - pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + pin_names = [props.bitcell.cell_1rw1r.pin.bl0, + props.bitcell.cell_1rw1r.pin.br0, + props.bitcell.cell_1rw1r.pin.bl1, + props.bitcell.cell_1rw1r.pin.br1, + props.bitcell.cell_1rw1r.pin.wl0, + props.bitcell.cell_1rw1r.pin.wl1, + props.bitcell.cell_1rw1r.pin.vdd, + props.bitcell.cell_1rw1r.pin.gnd] + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] (width, height) = utils.get_libcell_size("dummy_cell_1rw_1r", diff --git a/compiler/bitcells/dummy_bitcell_1w_1r.py b/compiler/bitcells/dummy_bitcell_1w_1r.py index 54192f71..758a5b16 100644 --- a/compiler/bitcells/dummy_bitcell_1w_1r.py +++ b/compiler/bitcells/dummy_bitcell_1w_1r.py @@ -8,6 +8,7 @@ import debug import utils from tech import GDS, layer +from tech import cell_properties as props import bitcell_base @@ -18,7 +19,14 @@ class dummy_bitcell_1w_1r(bitcell_base.bitcell_base): is a hand-made cell, so the layout and netlist should be available in the technology library. """ - pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + pin_names = [props.bitcell.cell_1w1r.pin.bl0, + props.bitcell.cell_1w1r.pin.br0, + props.bitcell.cell_1w1r.pin.bl1, + props.bitcell.cell_1w1r.pin.br1, + props.bitcell.cell_1w1r.pin.wl0, + props.bitcell.cell_1w1r.pin.wl1, + props.bitcell.cell_1w1r.pin.vdd, + props.bitcell.cell_1w1r.pin.gnd] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] (width, height) = utils.get_libcell_size("dummy_cell_1w_1r", diff --git a/compiler/bitcells/replica_bitcell.py b/compiler/bitcells/replica_bitcell.py index a869e4b7..479883d9 100644 --- a/compiler/bitcells/replica_bitcell.py +++ b/compiler/bitcells/replica_bitcell.py @@ -9,6 +9,8 @@ import design import debug import utils from tech import GDS,layer,drc,parameter,cell_properties +from tech import cell_properties as props + from globals import OPTS class replica_bitcell(design.design): @@ -22,7 +24,12 @@ class replica_bitcell(design.design): pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT" , "POWER", "GROUND"] else: - pin_names = ["bl", "br", "wl", "vdd", "gnd"] + pin_names = [props.bitcell.cell_6t.pin.bl, + props.bitcell.cell_6t.pin.br, + props.bitcell.cell_6t.pin.wl, + props.bitcell.cell_6t.pin.vdd, + props.bitcell.cell_6t.pin.gnd] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] if not OPTS.netlist_only: @@ -66,4 +73,4 @@ class replica_bitcell(design.design): def build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" - self.add_graph_edges(graph, port_nets) \ No newline at end of file + self.add_graph_edges(graph, port_nets) diff --git a/compiler/bitcells/replica_bitcell_1rw_1r.py b/compiler/bitcells/replica_bitcell_1rw_1r.py index 0f56319e..79f16a47 100644 --- a/compiler/bitcells/replica_bitcell_1rw_1r.py +++ b/compiler/bitcells/replica_bitcell_1rw_1r.py @@ -9,6 +9,7 @@ import design import debug import utils from tech import GDS,layer,drc,parameter +from tech import cell_properties as props class replica_bitcell_1rw_1r(design.design): """ @@ -17,7 +18,15 @@ class replica_bitcell_1rw_1r(design.design): is a hand-made cell, so the layout and netlist should be available in the technology library. """ - pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + pin_names = [props.bitcell.cell_1rw1r.pin.bl0, + props.bitcell.cell_1rw1r.pin.br0, + props.bitcell.cell_1rw1r.pin.bl1, + props.bitcell.cell_1rw1r.pin.br1, + props.bitcell.cell_1rw1r.pin.wl0, + props.bitcell.cell_1rw1r.pin.wl1, + props.bitcell.cell_1rw1r.pin.vdd, + props.bitcell.cell_1rw1r.pin.gnd] + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] (width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"]) @@ -47,14 +56,15 @@ class replica_bitcell_1rw_1r(design.design): access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin - def build_graph(self, graph, inst_name, port_nets): + def build_graph(self, graph, inst_name, port_nets): """Adds edges to graph. Multiport bitcell timing graph is too complex to use the add_graph_edges function.""" - pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + pins = props.bitcell.cell_1rw1r.pin #Edges hardcoded here. Essentially wl->bl/br for both ports. # Port 0 edges - graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self) - graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self) + graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.bl0], self) + graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self) # Port 1 edges - graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) - graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) \ No newline at end of file + graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self) + graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self) diff --git a/compiler/bitcells/replica_bitcell_1w_1r.py b/compiler/bitcells/replica_bitcell_1w_1r.py index b903e0ad..52bea519 100644 --- a/compiler/bitcells/replica_bitcell_1w_1r.py +++ b/compiler/bitcells/replica_bitcell_1w_1r.py @@ -9,6 +9,7 @@ import design import debug import utils from tech import GDS,layer,drc,parameter +from tech import cell_properties as props class replica_bitcell_1w_1r(design.design): """ @@ -17,7 +18,15 @@ class replica_bitcell_1w_1r(design.design): is a hand-made cell, so the layout and netlist should be available in the technology library. """ - pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] + pin_names = [props.bitcell.cell_1w1r.pin.bl0, + props.bitcell.cell_1w1r.pin.br0, + props.bitcell.cell_1w1r.pin.bl1, + props.bitcell.cell_1w1r.pin.br1, + props.bitcell.cell_1w1r.pin.wl0, + props.bitcell.cell_1w1r.pin.wl1, + props.bitcell.cell_1w1r.pin.vdd, + props.bitcell.cell_1w1r.pin.gnd] + type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] (width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"]) @@ -47,13 +56,14 @@ class replica_bitcell_1w_1r(design.design): access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"] return 2*access_tx_cin - def build_graph(self, graph, inst_name, port_nets): + def build_graph(self, graph, inst_name, port_nets): """Adds edges to graph. Multiport bitcell timing graph is too complex to use the add_graph_edges function.""" debug.info(1,'Adding edges for {}'.format(inst_name)) - pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + pins = props.bitcell.cell_1w1r.pin #Edges hardcoded here. Essentially wl->bl/br for the read port. # Port 1 edges - graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) - graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) - # Port 0 is a write port, so its timing is not considered here. \ No newline at end of file + graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self) + graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self) + # Port 0 is a write port, so its timing is not considered here. From 125bcafb3edd6ffba578ca529632b9ae51fec3ae Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Sat, 15 Feb 2020 15:00:30 -0800 Subject: [PATCH 099/103] fixed purposes for gdsMill --- compiler/gdsMill/gdsMill/gds2reader.py | 101 +++++++++++----------- compiler/gdsMill/gdsMill/gds2writer.py | 95 ++++++++++---------- compiler/gdsMill/gdsMill/gdsPrimitives.py | 27 +++--- 3 files changed, 114 insertions(+), 109 deletions(-) diff --git a/compiler/gdsMill/gdsMill/gds2reader.py b/compiler/gdsMill/gdsMill/gds2reader.py index b4a40c35..2f9976a5 100644 --- a/compiler/gdsMill/gdsMill/gds2reader.py +++ b/compiler/gdsMill/gdsMill/gds2reader.py @@ -12,14 +12,14 @@ class Gds2reader: self.fileHandle = None self.layoutObject = layoutObject self.debugToTerminal=debugToTerminal - + #do we dump debug data to the screen - + def print64AsBinary(self,number): for index in range(0,64): print((number>>(63-index))&0x1,eol='') print("\n") - + def stripNonASCII(self,bytestring): string = bytestring.decode('utf-8') return string @@ -29,20 +29,20 @@ class Gds2reader: #(1)sign (7)exponent (56)mantissa #exponent is excess 64, mantissa has no implied 1 #a normal IEEE double is like this: - #(1)sign (11)exponent (52)mantissa + #(1)sign (11)exponent (52)mantissa data = struct.unpack('>q',ibmData)[0] sign = (data >> 63)&0x01 exponent = (data >> 56) & 0x7f mantissa = data<<8 #chop off sign and exponent - + if mantissa == 0: newFloat = 0.0 - else: + else: exponent = ((exponent-64)*4)+1023 #convert to double exponent #re normalize - while mantissa & 0x8000000000000000 == 0: + while mantissa & 0x8000000000000000 == 0: mantissa<<=1 - exponent-=1 + exponent-=1 mantissa<<=1 #remove the assumed high bit exponent-=1 #check for underflow error -- should handle these properly! @@ -56,7 +56,7 @@ class Gds2reader: #convert back to double newFloat = struct.unpack('>d',asciiDouble)[0] return newFloat - + def ieeeFloatCheck(self,aFloat): asciiDouble = struct.pack('>d',aFloat) data = struct.unpack('>q',asciiDouble)[0] @@ -70,12 +70,12 @@ class Gds2reader: asciiDouble = struct.pack('>q',(sign<<63)|(exponent+1023<<52)|(mantissa>>12)) newFloat = struct.unpack('>d',asciiDouble)[0] print("Check:"+str(newFloat)) - + def readNextRecord(self): global offset recordLengthAscii = self.fileHandle.read(2) #first 2 bytes tell us the length of the record if len(recordLengthAscii)==0: - return + return recordLength = struct.unpack(">h",recordLengthAscii) #gives us a tuple with a short int inside offset_int = int(recordLength[0]) # extract length offset += offset_int # count offset @@ -96,20 +96,20 @@ class Gds2reader: else: print("Invalid GDSII Header") return -1 - + #read records until we hit the UNITS section... this is the last part of the header while 1: record = self.readNextRecord() idBits = record[0:2] ## Modified Date if idBits==b'\x01\x02' and len(record)==26: - modYear = struct.unpack(">h",record[2:4])[0] + modYear = struct.unpack(">h",record[2:4])[0] modMonth = struct.unpack(">h",record[4:6])[0] modDay = struct.unpack(">h",record[6:8])[0] modHour = struct.unpack(">h",record[8:10])[0] modMinute = struct.unpack(">h",record[10:12])[0] modSecond = struct.unpack(">h",record[12:14])[0] - lastAccessYear = struct.unpack(">h",record[14:16])[0] + lastAccessYear = struct.unpack(">h",record[14:16])[0] lastAccessMonth = struct.unpack(">h",record[16:18])[0] lastAccessDay = struct.unpack(">h",record[18:20])[0] lastAccessHour = struct.unpack(">h",record[20:22])[0] @@ -169,14 +169,14 @@ class Gds2reader: if(self.debugToTerminal==1): print("Units: 1 user unit="+str(userUnits)+" database units, 1 database unit="+str(dbUnits)+" meters.") break; - if(self.debugToTerminal==1): + if(self.debugToTerminal==1): print("End of GDSII Header Found") return 1 - + def readBoundary(self): ##reads in a boundary type structure = a filled polygon if(self.debugToTerminal==1): - print("\t\t\tBeginBoundary") + print("\t\t\tBeginBoundary") thisBoundary=GdsBoundary() while 1: record = self.readNextRecord() @@ -198,9 +198,9 @@ class Gds2reader: self.layoutObject.layerNumbersInUse += [drawingLayer] if(self.debugToTerminal==1): print("\t\tDrawing Layer: "+str(drawingLayer)) - elif(idBits==b'\x16\x02'): #Purpose + elif(idBits==b'\x0E\x02'): #Purpose DATATYPE purposeLayer = struct.unpack(">h",record[2:4])[0] - thisBoundary.purposeLayer=purposeLayer + thisBoundary.purposeLayer=purposeLayer if(self.debugToTerminal==1): print("\t\tPurpose Layer: "+str(purposeLayer)) elif(idBits==b'\x10\x03'): #XY Data Points @@ -217,11 +217,11 @@ class Gds2reader: print("\t\t\tEndBoundary") break; return thisBoundary - + def readPath(self): #reads in a path structure if(self.debugToTerminal==1): print("\t\t\tBeginPath") - + thisPath=GdsPath() while 1: record = self.readNextRecord() @@ -245,7 +245,7 @@ class Gds2reader: print("\t\t\tDrawing Layer: "+str(drawingLayer)) elif(idBits==b'\x16\x02'): #Purpose purposeLayer = struct.unpack(">h",record[2:4])[0] - thisPath.purposeLayer=purposeLayer + thisPath.purposeLayer=purposeLayer if(self.debugToTerminal==1): print("\t\tPurpose Layer: "+str(purposeLayer)) elif(idBits==b'\x21\x02'): #Path type @@ -277,7 +277,7 @@ class Gds2reader: print("\t\t\tEndPath") break; return thisPath - + def readSref(self): #reads in a reference to another structure if(self.debugToTerminal==1): print("\t\t\tBeginSref") @@ -318,7 +318,7 @@ class Gds2reader: print("\t\t\tMagnification:"+str(magFactor)) elif(idBits==b'\x1C\x05'): #Rotate Angle rotateAngle=self.ieeeDoubleFromIbmData(record[2:10]) - thisSref.rotateAngle=rotateAngle + thisSref.rotateAngle=rotateAngle if(self.debugToTerminal==1): print("\t\t\tRotate Angle (CCW):"+str(rotateAngle)) elif(idBits==b'\x10\x03'): #XY Data Points @@ -333,11 +333,11 @@ class Gds2reader: print("\t\t\tEndSref") break; return thisSref - + def readAref(self): #an array of references if(self.debugToTerminal==1): print("\t\t\tBeginAref") - + thisAref = GdsAref() while 1: record = self.readNextRecord() @@ -374,7 +374,7 @@ class Gds2reader: print("\t\t\tMagnification:"+str(magFactor)) elif(idBits==b'\x1C\x05'): #Rotate Angle rotateAngle=self.ieeeDoubleFromIbmData(record[2:10]) - thisAref.rotateAngle=rotateAngle + thisAref.rotateAngle=rotateAngle if(self.debugToTerminal==1): print("\t\t\tRotate Angle (CCW):"+str(rotateAngle)) elif(idBits==b'\x10\x03'): #XY Data Points @@ -393,11 +393,11 @@ class Gds2reader: print("\t\t\tEndAref") break; return thisAref - + def readText(self): if(self.debugToTerminal==1): print("\t\t\tBeginText") - + thisText=GdsText() while 1: record = self.readNextRecord() @@ -419,9 +419,9 @@ class Gds2reader: self.layoutObject.layerNumbersInUse += [drawingLayer] if(self.debugToTerminal==1): print("\t\tDrawing Layer: "+str(drawingLayer)) - elif(idBits==b'\x16\x02'): #Purpose + elif(idBits==b'\x16\x02'): #Purpose TEXTTYPE purposeLayer = struct.unpack(">h",record[2:4])[0] - thisText.purposeLayer=purposeLayer + thisText.purposeLayer=purposeLayer if(self.debugToTerminal==1): print("\t\tPurpose Layer: "+str(purposeLayer)) elif(idBits==b'\x1A\x01'): #Transformation @@ -497,11 +497,11 @@ class Gds2reader: print("\t\t\tEndText") break; return thisText - + def readNode(self): if(self.debugToTerminal==1): print("\t\t\tBeginNode") - + ##reads in a node type structure = an electrical net thisNode = GdsNode() while 1: @@ -543,11 +543,11 @@ class Gds2reader: print("\t\t\tEndNode") break; return thisNode - + def readBox(self): if(self.debugToTerminal==1): print("\t\t\tBeginBox") - + ##reads in a gds BOX structure thisBox = GdsBox() while 1: @@ -570,9 +570,9 @@ class Gds2reader: self.layoutObject.layerNumbersInUse += [drawingLayer] if(self.debugToTerminal==1): print("\t\tDrawing Layer: "+str(drawingLayer)) - elif(idBits==b'\x16\x02'): #Purpose + elif(idBits==b'\x16\x02'): #Purpose TEXTYPE purposeLayer = struct.unpack(">h",record[2:4])[0] - thisBox.purposeLayer=purposeLayer + thisBox.purposeLayer=purposeLayer if(self.debugToTerminal==1): print("\t\tPurpose Layer: "+str(purposeLayer)) elif(idBits==b'\x2D\x00'): #Box @@ -594,14 +594,14 @@ class Gds2reader: print("\t\t\tEndBox") break; return thisBox - + def readNextStructure(self): - thisStructure = GdsStructure() + thisStructure = GdsStructure() record = self.readNextRecord() idBits = record[0:2] # Begin structure if(idBits==b'\x05\x02' and len(record)==26): - createYear = struct.unpack(">h",record[2:4])[0] + createYear = struct.unpack(">h",record[2:4])[0] createMonth = struct.unpack(">h",record[4:6])[0] createDay = struct.unpack(">h",record[6:8])[0] createHour = struct.unpack(">h",record[8:10])[0] @@ -628,7 +628,7 @@ class Gds2reader: idBits = record[0:2] if idBits==b'\x07\x00': break; #we've reached the end of the structure elif(idBits==b'\x06\x06'): - structName = self.stripNonASCII(record[2::]) + structName = self.stripNonASCII(record[2::]) thisStructure.name = structName if(self.debugToTerminal==1): print("\tStructure Name: "+structName) @@ -646,11 +646,11 @@ class Gds2reader: thisStructure.nodes+=[self.readNode()] elif(idBits==b'\x2E\x02'): thisStructure.boxes+=[self.readBox()] - if(self.debugToTerminal==1): + if(self.debugToTerminal==1): print("\tEnd of Structure.") self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object return 1 - + def readGds2(self): if(self.readHeader()): #did the header read ok? record = self.readNextStructure() @@ -667,7 +667,7 @@ class Gds2reader: print("There was an error reading the structure list.") else: print("There was an error parsing the GDS header. Aborting...") - + def loadFromFile(self, fileName): self.fileHandle = open(fileName,"rb") self.readGds2() @@ -695,11 +695,11 @@ class Gds2reader: def findStruct_readNextStruct(self,findStructName): self.debugToTerminal=0 - thisStructure = GdsStructure() + thisStructure = GdsStructure() record = self.readNextRecord() idBits = record[0:2] if(idBits==('\x05','\x02') and len(record)==26): - createYear = struct.unpack(">h",record[2]+record[3])[0] + createYear = struct.unpack(">h",record[2]+record[3])[0] createMonth = struct.unpack(">h",record[4]+record[5])[0] createDay = struct.unpack(">h",record[6]+record[7])[0] createHour = struct.unpack(">h",record[8]+record[9])[0] @@ -743,7 +743,7 @@ class Gds2reader: thisStructure.nodes+=[self.readNode()] elif(idBits==('\x2E','\x02')): thisStructure.boxes+=[self.readBox()] - if(self.debugToTerminal==1): + if(self.debugToTerminal==1): print("\tEnd of Structure.") self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object if(wantedStruct == 0): @@ -771,11 +771,11 @@ class Gds2reader: def findLabel_readNextStruct(self,findLabelName): self.debugToTerminal=0 - thisStructure = GdsStructure() + thisStructure = GdsStructure() record = self.readNextRecord() idBits = record[0:2] if(idBits==('\x05','\x02') and len(record)==26): - createYear = struct.unpack(">h",record[2]+record[3])[0] + createYear = struct.unpack(">h",record[2]+record[3])[0] createMonth = struct.unpack(">h",record[4]+record[5])[0] createDay = struct.unpack(">h",record[6]+record[7])[0] createHour = struct.unpack(">h",record[8]+record[9])[0] @@ -825,7 +825,7 @@ class Gds2reader: thisStructure.nodes+=[self.readNode()] elif(idBits==('\x2E','\x02')): thisStructure.boxes+=[self.readBox()] - if(self.debugToTerminal==1): + if(self.debugToTerminal==1): print("\tEnd of Structure.") self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object if(wantedLabel == 0): @@ -833,4 +833,3 @@ class Gds2reader: else: #print("\tDone with collectting bound. Return") return [0,wantedtexts] - diff --git a/compiler/gdsMill/gdsMill/gds2writer.py b/compiler/gdsMill/gdsMill/gds2writer.py index 6e63533b..875de831 100644 --- a/compiler/gdsMill/gdsMill/gds2writer.py +++ b/compiler/gdsMill/gdsMill/gds2writer.py @@ -5,37 +5,37 @@ from .gdsPrimitives import * class Gds2writer: """Class to take a populated layout class and write it to a file in GDSII format""" ## Based on info from http://www.rulabinsky.com/cavd/text/chapc.html - + def __init__(self,layoutObject): self.fileHandle = 0 self.layoutObject = layoutObject self.debugToTerminal=0 #do we dump debug data to the screen - + def print64AsBinary(self,number): #debugging method for binary inspection for index in range(0,64): print((number>>(63-index))&0x1,eol='') print("\n") - + def ieeeDoubleFromIbmData(self,ibmData): #the GDS double is in IBM 370 format like this: #(1)sign (7)exponent (56)mantissa #exponent is excess 64, mantissa has no implied 1 #a normal IEEE double is like this: - #(1)sign (11)exponent (52)mantissa + #(1)sign (11)exponent (52)mantissa data = struct.unpack('>q',ibmData)[0] sign = (data >> 63)&0x01 exponent = (data >> 56) & 0x7f mantissa = data<<8 #chop off sign and exponent - + if mantissa == 0: newFloat = 0.0 else: exponent = ((exponent-64)*4)+1023 #convert to double exponent #re normalize - while mantissa & 0x8000000000000000 == 0: + while mantissa & 0x8000000000000000 == 0: mantissa<<=1 - exponent-=1 + exponent-=1 mantissa<<=1 #remove the assumed high bit exponent-=1 #check for underflow error -- should handle these properly! @@ -49,12 +49,12 @@ class Gds2writer: #convert back to double newFloat = struct.unpack('>d',asciiDouble)[0] return newFloat - + def ibmDataFromIeeeDouble(self,ieeeDouble): asciiDouble = struct.pack('>d',ieeeDouble) data = struct.unpack('>q',asciiDouble)[0] sign = (data >> 63) & 0x01 - exponent = ((data >> 52) & 0x7ff)-1023 + exponent = ((data >> 52) & 0x7ff)-1023 mantissa = data << 12 #chop off sign and exponent if(ieeeDouble == 0): mantissa = 0 @@ -70,14 +70,14 @@ class Gds2writer: for index in range (0,-exponent&3): mantissa >>= 1 mantissa = mantissa & 0x7fffffffffffffff - - exponent = (exponent+3) >> 2 + + exponent = (exponent+3) >> 2 exponent+=64 - - newFloat =(sign<<63)|(exponent<<56)|((mantissa>>8)&0xffffffffffffff) - asciiDouble = struct.pack('>q',newFloat) + + newFloat =(sign<<63)|(exponent<<56)|((mantissa>>8)&0xffffffffffffff) + asciiDouble = struct.pack('>q',newFloat) return asciiDouble - + def ieeeFloatCheck(self,aFloat): #debugging method for float construction asciiDouble = struct.pack('>d',aFloat) @@ -90,7 +90,7 @@ class Gds2writer: asciiDouble = struct.pack('>q',(sign<<63)|(exponent+1023<<52)|(mantissa>>12)) newFloat = struct.unpack('>d',asciiDouble)[0] print("Check:"+str(newFloat)) - + def writeRecord(self,record): recordLength = len(record)+2 #make sure to include this in the length recordLengthAscii=struct.pack(">h",recordLength) @@ -127,7 +127,7 @@ class Gds2writer: libraryName = self.layoutObject.info["libraryName"].encode() + "\0" else: libraryName = self.layoutObject.info["libraryName"].encode() - self.writeRecord(idBits+libraryName) + self.writeRecord(idBits+libraryName) ## reference libraries if("referenceLibraries" in self.layoutObject.info): idBits=b'\x1F\x06' @@ -158,11 +158,11 @@ class Gds2writer: mask = self.layoutObject.info["mask"] self.writeRecord(idBits+mask) if("units" in self.layoutObject.info): - idBits=b'\x03\x05' + idBits=b'\x03\x05' userUnits=self.ibmDataFromIeeeDouble(self.layoutObject.info["units"][0]) dbUnits=self.ibmDataFromIeeeDouble((self.layoutObject.info["units"][0]*1e-6/self.layoutObject.info["units"][1])*self.layoutObject.info["units"][1]) - #User Units are hardcoded, since the floating point implementation of gdsMill is not adequate, + #User Units are hardcoded, since the floating point implementation of gdsMill is not adequate, #resulting in a different value being written in output stream. Hardcoded to sram compiler's outputed gds units. #db="39225c17d04dad2a" #uu="3e20c49ba5e353f8" @@ -172,17 +172,17 @@ class Gds2writer: #dbUnits="39225c17d04dad2a".decode("hex") #db=39225c17d04dad2a - - + + self.writeRecord(idBits+userUnits+dbUnits) - if(self.debugToTerminal==1): + if(self.debugToTerminal==1): print("writer: userUnits %s"%(userUnits.encode("hex"))) print("writer: dbUnits %s"%(dbUnits.encode("hex"))) #self.ieeeFloatCheck(1.3e-6) - + print("End of GDSII Header Written") return 1 - + def writeBoundary(self,thisBoundary): idBits=b'\x08\x00' #record Type self.writeRecord(idBits) @@ -216,7 +216,7 @@ class Gds2writer: idBits=b'\x11\x00' #End Of Element coordinateRecord = idBits self.writeRecord(coordinateRecord) - + def writePath(self,thisPath): #writes out a path structure idBits=b'\x09\x00' #record Type self.writeRecord(idBits) @@ -260,7 +260,7 @@ class Gds2writer: idBits=b'\x11\x00' #End Of Element coordinateRecord = idBits self.writeRecord(coordinateRecord) - + def writeSref(self,thisSref): #reads in a reference to another structure idBits=b'\x0A\x00' #record Type self.writeRecord(idBits) @@ -296,7 +296,7 @@ class Gds2writer: magFactor=self.ibmDataFromIeeeDouble(thisSref.magFactor) self.writeRecord(idBits+magFactor) if(thisSref.rotateAngle!=""): - idBits=b'\x1C\x05' + idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisSref.rotateAngle) self.writeRecord(idBits+rotateAngle) if(thisSref.coordinates!=""): @@ -312,7 +312,7 @@ class Gds2writer: idBits=b'\x11\x00' #End Of Element coordinateRecord = idBits self.writeRecord(coordinateRecord) - + def writeAref(self,thisAref): #an array of references idBits=b'\x0B\x00' #record Type self.writeRecord(idBits) @@ -348,7 +348,7 @@ class Gds2writer: magFactor=self.ibmDataFromIeeeDouble(thisAref.magFactor) self.writeRecord(idBits+magFactor) if(thisAref.rotateAngle!=""): - idBits=b'\x1C\x05' + idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisAref.rotateAngle) self.writeRecord(idBits+rotateAngle) if(thisAref.coordinates): @@ -363,7 +363,7 @@ class Gds2writer: idBits=b'\x11\x00' #End Of Element coordinateRecord = idBits self.writeRecord(coordinateRecord) - + def writeText(self,thisText): idBits=b'\x0C\x00' #record Type self.writeRecord(idBits) @@ -379,9 +379,8 @@ class Gds2writer: idBits=b'\x0D\x02' #drawing layer drawingLayer = struct.pack(">h",thisText.drawingLayer) self.writeRecord(idBits+drawingLayer) - # TextType is always a 0 per GDS specification - idBits=b'\x16\x02' #purpose layer - purposeLayer = struct.pack(">h",0) + idBits=b'\x16\x02' #purpose layer TEXTTYPE + purposeLayer = struct.pack(">h",thisText.purposeLayer) self.writeRecord(idBits+purposeLayer) if(thisText.transFlags != ""): idBits=b'\x1A\x01' @@ -400,7 +399,7 @@ class Gds2writer: magFactor=self.ibmDataFromIeeeDouble(thisText.magFactor) self.writeRecord(idBits+magFactor) if(thisText.rotateAngle!=""): - idBits=b'\x1C\x05' + idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisText.rotateAngle) self.writeRecord(idBits+rotateAngle) if(thisText.pathType !=""): @@ -412,12 +411,12 @@ class Gds2writer: pathWidth = struct.pack(">i",thisText.pathWidth) self.writeRecord(idBits+pathWidth) if(thisText.presentationFlags!=""): - idBits=b'\x1A\x01' + idBits=b'\x1A\x01' font = thisText.presentationFlags[0]<<4 verticalFlags = int(thisText.presentationFlags[1])<<2 horizontalFlags = int(thisText.presentationFlags[2]) presentationFlags = struct.pack(">H",font|verticalFlags|horizontalFlags) - self.writeRecord(idBits+transFlags) + self.writeRecord(idBits+transFlags) if(thisText.coordinates!=""): idBits=b'\x10\x03' #XY Data Points coordinateRecord = idBits @@ -431,11 +430,11 @@ class Gds2writer: idBits=b'\x19\x06' textString = thisText.textString self.writeRecord(idBits+textString.encode()) - + idBits=b'\x11\x00' #End Of Element coordinateRecord = idBits self.writeRecord(coordinateRecord) - + def writeNode(self,thisNode): idBits=b'\x15\x00' #record Type self.writeRecord(idBits) @@ -450,11 +449,11 @@ class Gds2writer: if(thisNode.drawingLayer!=""): idBits=b'\x0D\x02' #drawig layer drawingLayer = struct.pack(">h",thisNode.drawingLayer) - self.writeRecord(idBits+drawingLayer) + self.writeRecord(idBits+drawingLayer) if(thisNode.nodeType!=""): idBits=b'\x2A\x02' nodeType = struct.pack(">h",thisNode.nodeType) - self.writeRecord(idBits+nodeType) + self.writeRecord(idBits+nodeType) if(thisText.coordinates!=""): idBits=b'\x10\x03' #XY Data Points coordinateRecord = idBits @@ -464,11 +463,11 @@ class Gds2writer: coordinateRecord+=x coordinateRecord+=y self.writeRecord(coordinateRecord) - + idBits=b'\x11\x00' #End Of Element coordinateRecord = idBits self.writeRecord(coordinateRecord) - + def writeBox(self,thisBox): idBits=b'\x2E\x02' #record Type self.writeRecord(idBits) @@ -491,7 +490,7 @@ class Gds2writer: if(thisBox.boxValue!=""): idBits=b'\x2D\x00' boxValue = struct.pack(">h",thisBox.boxValue) - self.writeRecord(idBits+boxValue) + self.writeRecord(idBits+boxValue) if(thisBox.coordinates!=""): idBits=b'\x10\x03' #XY Data Points coordinateRecord = idBits @@ -501,11 +500,11 @@ class Gds2writer: coordinateRecord+=x coordinateRecord+=y self.writeRecord(coordinateRecord) - + idBits=b'\x11\x00' #End Of Element coordinateRecord = idBits self.writeRecord(coordinateRecord) - + def writeNextStructure(self,structureName): #first put in the structure head thisStructure = self.layoutObject.structures[structureName] @@ -532,7 +531,7 @@ class Gds2writer: structureName = structureName + '\x00' self.writeRecord(idBits+structureName.encode()) #now go through all the structure elements and write them in - + for boundary in thisStructure.boundaries: self.writeBoundary(boundary) for path in thisStructure.paths: @@ -550,7 +549,7 @@ class Gds2writer: #put in the structure tail idBits=b'\x07\x00' self.writeRecord(idBits) - + def writeGds2(self): self.writeHeader(); #first, put the header in #go through each structure in the layout and write it to the file @@ -559,7 +558,7 @@ class Gds2writer: #at the end, put in the END LIB record idBits=b'\x04\x00' self.writeRecord(idBits) - + def writeToFile(self,fileName): self.fileHandle = open(fileName,"wb") self.writeGds2() diff --git a/compiler/gdsMill/gdsMill/gdsPrimitives.py b/compiler/gdsMill/gdsMill/gdsPrimitives.py index 886f9c13..13b8acf9 100644 --- a/compiler/gdsMill/gdsMill/gdsPrimitives.py +++ b/compiler/gdsMill/gdsMill/gdsPrimitives.py @@ -1,5 +1,12 @@ import math +from globals import OPTS +# default purpose layer is used for addText() in vlsiLayout.py +if OPTS.tech_name == "s8": + purposeLayer=20 +else: + purposeLayer=0 + class GdsStructure: """Class represent a GDS Structure Object""" def __init__(self): @@ -9,7 +16,7 @@ class GdsStructure: #these are the primitives defined in GDS2, and we will maintain lists of them all self.boundaries=[] self.paths=[] - self.srefs=[] + self.srefs=[] self.arefs=[] self.texts=[] self.nodes=[] @@ -23,7 +30,7 @@ class GdsBoundary: self.drawingLayer="" self.purposeLayer=0 self.coordinates="" - + class GdsPath: """Class represent a GDS Path Object""" def __init__(self): @@ -35,7 +42,7 @@ class GdsPath: self.dataType=None self.pathWidth="" self.coordinates="" - + def equivalentBoundaryCoordinates(self): """Convert the path to a set of boundary coordinates that define it""" halfWidth = (self.pathWidth/2) @@ -62,7 +69,7 @@ class GdsPath: nextX = None; nextY = None; if lastX==None: #start of the path - if nextX>x:#moving right + if nextX>x:#moving right boundaryEquivalent+=[(x,y+halfWidth)] if nextX nextX): boundaryEquivalent+=[(x+halfWidth,y-halfWidth)] - - if nextX == None: #end of path, put in the last 2 points - if lastXx:#moving left boundaryEquivalent+=[(x,y-halfWidth)] @@ -140,7 +147,7 @@ class GdsText: self.elementFlags="" self.plex="" self.drawingLayer="" - self.purposeLayer=0 + self.purposeLayer=purposeLayer self.transFlags=[0,0,0] self.magFactor="" self.rotateAngle="" @@ -149,7 +156,7 @@ class GdsText: self.presentationFlags="" self.coordinates="" self.textString = "" - + class GdsNode: """Class represent a GDS Node Object""" def __init__(self): @@ -158,7 +165,7 @@ class GdsNode: self.drawingLayer="" self.nodeType="" self.coordinates="" - + class GdsBox: """Class represent a GDS Box Object""" def __init__(self): From 843fce41d73e7dd34531e950aa8ecc3872dd662e Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 19 Feb 2020 03:06:11 -0800 Subject: [PATCH 100/103] Fixed issues with sen control logic for read ports. --- compiler/characterizer/delay.py | 2 +- compiler/modules/control_logic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 2a8d5293..94e38ff2 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -650,7 +650,7 @@ class delay(simulation): debug.error("Timed out, could not find a feasible period.",2) # Clear any write target ports and set read port - self.targ_write_ports = [port] + self.targ_write_ports = [] self.targ_read_ports = [port] debug.info(1, "Trying feasible period: {0}ns on Port {1}".format(feasible_period, port)) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index e22d05b3..458b9b96 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -647,7 +647,7 @@ class control_logic(design.design): if self.port_type=="rw": input_name = "we_bar" else: - input_name = "cs_bar" + input_name = "cs" # GATE FOR S_EN self.s_en_gate_inst = self.add_inst(name="buf_s_en_and", mod=self.sen_and3) From e4fef73e3f3d6e80e1e0cd801e223533a293737b Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 19 Feb 2020 15:34:31 -0800 Subject: [PATCH 101/103] Fixed issues with bitcell measurements variable names, made target write ports required during characterization --- compiler/characterizer/delay.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 94e38ff2..a9f9542c 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -180,30 +180,30 @@ class delay(simulation): def create_read_bit_measures(self): """ Adds bit measurements for read0 and read1 cycles """ - self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} + self.read_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE) for cycle in meas_cycles: meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) for polarity,meas in single_bit_meas.items(): meas.meta_str = cycle - self.bit_meas[polarity].append(meas) + self.read_bit_meas[polarity].append(meas) # Dictionary values are lists, reduce to a single list of measurements - return [meas for meas_list in self.bit_meas.values() for meas in meas_list] + return [meas for meas_list in self.read_bit_meas.values() for meas in meas_list] def create_write_bit_measures(self): """ Adds bit measurements for write0 and write1 cycles """ - self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} + self.write_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE) for cycle in meas_cycles: meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) for polarity,meas in single_bit_meas.items(): meas.meta_str = cycle - self.bit_meas[polarity].append(meas) + self.write_bit_meas[polarity].append(meas) # Dictionary values are lists, reduce to a single list of measurements - return [meas for meas_list in self.bit_meas.values() for meas in meas_list] + return [meas for meas_list in self.write_bit_meas.values() for meas in meas_list] def get_bit_measures(self, meas_tag, probe_address, probe_data): """ @@ -649,8 +649,9 @@ class delay(simulation): if (time_out <= 0): debug.error("Timed out, could not find a feasible period.",2) - # Clear any write target ports and set read port - self.targ_write_ports = [] + # Write ports are assumed non-critical to timing, so the first available is used + self.targ_write_ports = [self.write_ports[0]] + # Set target read port for simulation self.targ_read_ports = [port] debug.info(1, "Trying feasible period: {0}ns on Port {1}".format(feasible_period, port)) @@ -733,7 +734,8 @@ class delay(simulation): # First, check that the memory has the right values at the right times - if not self.check_bit_measures(): + if not self.check_bit_measures(self.read_bit_meas) or \ + not self.check_bit_measures(self.write_bit_meas): return(False,{}) for port in self.targ_write_ports: @@ -824,13 +826,13 @@ class delay(simulation): return dout_success - def check_bit_measures(self): + def check_bit_measures(self, bit_measures): """ Checks the measurements which represent the internal storage voltages at the end of the read cycle. """ success = False - for polarity, meas_list in self.bit_meas.items(): + for polarity, meas_list in bit_measures.items(): for meas in meas_list: val = meas.retrieve_measure() debug.info(2,"{}={}".format(meas.name, val)) @@ -965,7 +967,8 @@ class delay(simulation): # Binary search algorithm to find the min period (max frequency) of input port time_out = 25 - self.targ_write_ports = [port] + # Write ports are assumed non-critical to timing, so the first available is used + self.targ_write_ports = [self.write_ports[0]] self.targ_read_ports = [port] while True: time_out -= 1 @@ -1253,8 +1256,8 @@ class delay(simulation): """ # Using this requires setting at least one port to target for simulation. - if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0: - debug.error("No port selected for characterization.",1) + if len(self.targ_write_ports) == 0 or len(self.targ_read_ports) == 0: + debug.error("Write and read port must be specified for characterization.",1) self.set_stimulus_variables() # Get any available read/write port in case only a single write or read ports is being characterized. From df2f981a34a38ea1760e3c02b3847ec7cf4e069c Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 19 Feb 2020 15:59:26 -0800 Subject: [PATCH 102/103] Adds checks to prevent characterization of redundant corners. --- compiler/characterizer/lib.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 6baff201..6d6c6ce5 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -81,17 +81,25 @@ class lib: self.lib_files = [] # Nominal corner - self.add_corner(nom_process, nom_supply, nom_temperature) + corner_set = set() + nom_corner = (nom_process, nom_supply, nom_temperature) + corner_set.add(nom_corner) if not OPTS.nominal_corner_only: # Temperature corners - self.add_corner(nom_process, nom_supply, min_temperature) - self.add_corner(nom_process, nom_supply, max_temperature) + corner_set.add((nom_process, nom_supply, min_temperature)) + corner_set.add((nom_process, nom_supply, max_temperature)) # Supply corners - self.add_corner(nom_process, min_supply, nom_temperature) - self.add_corner(nom_process, max_supply, nom_temperature) + corner_set.add((nom_process, min_supply, nom_temperature)) + corner_set.add((nom_process, max_supply, nom_temperature)) # Process corners - self.add_corner(min_process, nom_supply, nom_temperature) - self.add_corner(max_process, nom_supply, nom_temperature) + corner_set.add((min_process, nom_supply, nom_temperature)) + corner_set.add((max_process, nom_supply, nom_temperature)) + + # Enforce that nominal corner is the first to be characterized + self.add_corner(*nom_corner) + corner_set.remove(nom_corner) + for corner_tuple in corner_set: + self.add_corner(*corner_tuple) def add_corner(self, proc, volt, temp): self.corner_name = "{0}_{1}_{2}V_{3}C".format(self.sram.name, From c1cb6bf51278ec1525e5ee675b08eb9096ed97c8 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 19 Feb 2020 23:32:11 -0800 Subject: [PATCH 103/103] Changed layout input names of s_en AND gate to match the schematic --- compiler/modules/control_logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 458b9b96..1612938d 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -670,7 +670,7 @@ class control_logic(design.design): if self.port_type=="rw": input_name = "we_bar" else: - input_name = "cs_bar" + input_name = "cs" sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name]) self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.rail_offsets)