From 1d5a41df2dfcc874dba4cb80dc943d1fb11ea5cd Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 29 Aug 2018 08:52:45 -0700 Subject: [PATCH 01/31] fixed issue with read ports that caused extra transistors to appear --- compiler/pgates/pbitcell.py | 91 ++++++++++++++----------------------- 1 file changed, 33 insertions(+), 58 deletions(-) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index ac6ace3b..490c3de4 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -35,8 +35,6 @@ class pbitcell(pgate.pgate): # FIXME: Why is this static set here? pbitcell.width = self.width pbitcell.height = self.height - - def create_netlist(self): self.add_pins() @@ -89,7 +87,6 @@ class pbitcell(pgate.pgate): self.add_pin("vdd") self.add_pin("gnd") - def add_modules(self): # if there are any read/write ports, then the inverter nmos is sized based the number of them @@ -133,7 +130,6 @@ class pbitcell(pgate.pgate): tx_type="nmos") self.add_mod(self.read_nmos) - def calculate_spacing(self): """ Calculate transistor spacings """ @@ -267,6 +263,31 @@ class pbitcell(pgate.pgate): self.width = -2*self.leftmost_xpos self.height = self.topmost_ypos - self.botmost_ypos - array_vdd_overlap + + def create_storage(self): + """ + Creates the crossed coupled inverters that act as storage for the bitcell. + The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". + """ + + # create active for nmos + self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", + mod=self.inverter_nmos) + self.connect_inst(["Q_bar", "Q", "gnd", "gnd"]) + + self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", + mod=self.inverter_nmos) + self.connect_inst(["gnd", "Q_bar", "Q", "gnd"]) + + # create active for pmos + self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", + mod=self.inverter_pmos) + self.connect_inst(["Q_bar", "Q", "vdd", "vdd"]) + + self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", + mod=self.inverter_pmos) + self.connect_inst(["vdd", "Q_bar", "Q", "vdd"]) + def place_storage(self): """ @@ -318,31 +339,6 @@ class pbitcell(pgate.pgate): # update furthest left and right transistor edges (this will propagate to further transistor offset calculations) self.left_building_edge = -self.inverter_tile_width self.right_building_edge = self.inverter_tile_width - - - def create_storage(self): - """ - Creates the crossed coupled inverters that act as storage for the bitcell. - The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". - """ - - # create active for nmos - self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", - mod=self.inverter_nmos) - self.connect_inst(["Q_bar", "Q", "gnd", "gnd"]) - - self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right", - mod=self.inverter_nmos) - self.connect_inst(["gnd", "Q_bar", "Q", "gnd"]) - - # create active for pmos - self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left", - mod=self.inverter_pmos) - self.connect_inst(["Q_bar", "Q", "vdd", "vdd"]) - - self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right", - mod=self.inverter_pmos) - self.connect_inst(["vdd", "Q_bar", "Q", "vdd"]) def route_rails(self): @@ -816,15 +812,6 @@ class pbitcell(pgate.pgate): # calculate offset to overlap the drain of the read-access transistor with the source of the read transistor overlap_offset = self.read_nmos.get_pin("D").ll() - self.read_nmos.get_pin("S").ll() - # define read transistor variables as empty arrays based on the number of read ports - self.read_nmos_left = [None] * self.num_read - self.read_nmos_right = [None] * self.num_read - self.read_access_nmos_left = [None] * self.num_read - self.read_access_nmos_right = [None] * self.num_read - self.rwl_positions = [None] * self.num_read - self.rbl_positions = [None] * self.num_read - self.rbl_bar_positions = [None] * self.num_read - # iterate over the number of read ports for k in range(0,self.num_read): # Add transistors @@ -840,30 +827,18 @@ class pbitcell(pgate.pgate): + read_rotation_correct # add read-access transistors - self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), - mod=self.read_nmos, - offset=[left_read_transistor_xpos,0], - rotate=90) - self.connect_inst(["RA_to_R_left{}".format(k), " Q_bar", "gnd", "gnd"]) + self.read_access_nmos_left[k].place(offset=[left_read_transistor_xpos,0], + rotate=90) - self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right{}".format(k), - mod=self.read_nmos, - offset=[right_read_transistor_xpos,0], - rotate=90) - self.connect_inst(["RA_to_R_right{}".format(k), "Q", "gnd", "gnd"]) + self.read_access_nmos_right[k].place(offset=[right_read_transistor_xpos,0], + rotate=90) # add read transistors - self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k), - mod=self.read_nmos, - offset=[left_read_transistor_xpos,overlap_offset.x], - rotate=90) - self.connect_inst(["rbl{}".format(k), "rwl{}".format(k), "RA_to_R_left{}".format(k), "gnd"]) + self.read_nmos_left[k].place(offset=[left_read_transistor_xpos,overlap_offset.x], + rotate=90) - self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k), - mod=self.read_nmos, - offset=[right_read_transistor_xpos,overlap_offset.x], - rotate=90) - self.connect_inst(["rbl_bar{}".format(k), "rwl{}".format(k), "RA_to_R_right{}".format(k), "gnd"]) + self.read_nmos_right[k].place(offset=[right_read_transistor_xpos,overlap_offset.x], + rotate=90) # Add RWL lines # calculate RWL position From 0182309f922b5e097fac3147ae9d4b11e2e736e5 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 29 Aug 2018 14:51:50 -0700 Subject: [PATCH 02/31] Editting comment on rule 5.5.b in scmos tech file. Adding complimentary rule to freepdk45 tech file. --- technology/freepdk45/tech/tech.py | 2 ++ technology/scn3me_subm/tech/tech.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index f232a2a7..a6ec5e51 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -106,6 +106,8 @@ drc["poly_to_active"] = 0.05 drc["poly_to_field_poly"] = 0.075 # Not a rule drc["minarea_poly"] = 0.0 +# Not a rule +drc["poly_to_contactpoly"] = 0.075 # ACTIVE.2 Minimum spacing of active drc["active_to_body_active"] = 0.08 diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index 18ec5bca..db157089 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -83,7 +83,7 @@ drc["minwidth_poly"] = 0.6 drc["poly_to_poly"] = 0.9 # 3.3 Minimum gate extension of active drc["poly_extend_active"] = 0.6 -# ?? +# 5.5.b Minimum spacing between poly contact and other poly (alternative rules) drc["poly_to_polycontact"] = 1.2 # ?? drc["active_enclosure_gate"] = 0.0 From 1f53a82d5662e18701877e30deb0c0d7512145ac Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 29 Aug 2018 15:04:17 -0700 Subject: [PATCH 03/31] Fixed name for poly_to_polycontact rule. Previously said poly_to_contactpoly in error. --- technology/freepdk45/tech/tech.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index a6ec5e51..e3217252 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -107,7 +107,7 @@ drc["poly_to_field_poly"] = 0.075 # Not a rule drc["minarea_poly"] = 0.0 # Not a rule -drc["poly_to_contactpoly"] = 0.075 +drc["poly_to_polycontact"] = 0.075 # ACTIVE.2 Minimum spacing of active drc["active_to_body_active"] = 0.08 From 807a4d7767ff61642d710c6db27971b6572df34e Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 29 Aug 2018 15:30:50 -0700 Subject: [PATCH 04/31] Fixed drcs error in magic. Pbitcell should now pass unit tests in calibre and magic. --- compiler/pgates/pbitcell.py | 42 ++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 490c3de4..e6e3b981 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -149,15 +149,15 @@ class pbitcell(pgate.pgate): # readwrite to readwrite transistor spacing (also acts as readwrite to write transistor spacing) if(self.readwrite_nmos_contact_extension > self.gate_contact_thres): - self.readwrite_to_readwrite_spacing = drc["minwidth_metal2"] + self.readwrite_nmos_contact_extension + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + self.readwrite_to_readwrite_spacing = drc["minwidth_metal2"] + self.readwrite_nmos_contact_extension + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] else: - self.readwrite_to_readwrite_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + self.readwrite_to_readwrite_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] # write to write transistor spacing if(self.write_nmos_contact_extension > self.gate_contact_thres): - self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] else: - self.write_to_write_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"] + self.write_to_write_spacing = drc["poly_to_active"] + contact.poly.width + drc["poly_to_polycontact"] + drc["poly_extend_active"] # read to read transistor spacing if(self.read_nmos_contact_extension > self.gate_contact_thres): @@ -183,7 +183,7 @@ class pbitcell(pgate.pgate): else: read_portion = drc["poly_to_active"] - self.write_to_read_spacing = write_portion + read_portion + 2*contact.poly.width + drc["poly_to_field_poly"] + self.write_to_read_spacing = write_portion + read_portion + 2*contact.poly.width + drc["poly_to_polycontact"] """ calculations for transistor tiling (transistor + spacing) """ self.inverter_tile_width = self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing @@ -192,13 +192,13 @@ class pbitcell(pgate.pgate): self.read_tile_width = self.read_to_read_spacing + self.read_nmos.active_height """ calculation for row line tiling """ - self.rail_tile_height = drc["active_to_body_active"] + 0.5*(drc["minwidth_tx"] - drc["minwidth_metal1"]) + drc["minwidth_metal1"] + self.rail_tile_height = drc["active_to_body_active"] + contact.well.width #0.5*(drc["minwidth_tx"] - drc["minwidth_metal1"]) + drc["minwidth_metal1"] self.rowline_tile_height = drc["minwidth_metal1"] + contact.m1m2.width """ calculations related to inverter connections """ - self.inverter_gap = drc["poly_to_active"] + drc["poly_to_field_poly"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.inverter_pmos_contact_extension + self.inverter_gap = drc["poly_to_active"] + drc["poly_to_polycontact"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.inverter_pmos_contact_extension self.cross_couple_lower_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + 0.5*contact.poly.width - self.cross_couple_upper_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + drc["poly_to_field_poly"] + 1.5*contact.poly.width + self.cross_couple_upper_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + drc["poly_to_polycontact"] + 1.5*contact.poly.width def calculate_postions(self): @@ -531,12 +531,12 @@ class pbitcell(pgate.pgate): # this path only needs to be drawn once on the last iteration of the loop if(k == self.num_readwrite-1): # add contacts to connect gate of inverters to drain of read/write transistors - left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) + left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=left_storage_contact, rotate=90) - right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) + right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=right_storage_contact, rotate=90) @@ -725,12 +725,12 @@ class pbitcell(pgate.pgate): # this path only needs to be drawn once on the last iteration of the loop if(k == self.num_write-1): # add contacts to connect gate of inverters to drain of write transistors - left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) + left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=left_storage_contact, rotate=90) - right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) + right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), offset=right_storage_contact, rotate=90) @@ -1072,23 +1072,17 @@ class pbitcell(pgate.pgate): offset = vector(0, self.gnd_position.y + 0.5*contact.well.second_layer_width) self.add_contact_center(layers=("active", "contact", "metal1"), offset=offset, - rotate=90) - - self.add_rect_center(layer="pimplant", - offset=offset, - width=drc["minwidth_tx"], - height=drc["minwidth_tx"]) + rotate=90, + implant_type="p", + well_type="p") # connect nimplants to vdd offset = vector(0, self.vdd_position.y + 0.5*drc["minwidth_metal1"]) self.add_contact_center(layers=("active", "contact", "metal1"), offset=offset, - rotate=90) - - self.add_rect_center(layer="nimplant", - offset=offset, - width=drc["minwidth_tx"], - height=drc["minwidth_tx"]) + rotate=90, + implant_type="n", + well_type="n") def list_bitcell_pins(self, col, row): From 29da8a5209d5997d93b8282a94cb93aa2f261000 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 29 Aug 2018 15:54:49 -0700 Subject: [PATCH 05/31] Further changes to pbitcell so that it passes unit tests for bitcell_array --- compiler/pgates/pbitcell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index e6e3b981..ddd998b6 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -242,7 +242,7 @@ class pbitcell(pgate.pgate): - self.read_port_flag*self.write_to_read_spacing \ - self.read_port_flag*(self.read_nmos.active_height + (self.num_read-1)*self.read_tile_width) \ - end_connection \ - - 0.5*drc["minwidth_metal2"] + - 0.5*drc["poly_to_polycontact"] self.rightmost_xpos = -self.leftmost_xpos @@ -259,7 +259,7 @@ class pbitcell(pgate.pgate): + self.rail_tile_height # calculations for the cell dimensions - array_vdd_overlap = 0.5*drc["minwidth_metal1"] + array_vdd_overlap = 0.5*contact.well.width self.width = -2*self.leftmost_xpos self.height = self.topmost_ypos - self.botmost_ypos - array_vdd_overlap From 7ef7c084cd54d098bbedef1ac554fb6ed67e7e10 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 29 Aug 2018 16:01:25 -0700 Subject: [PATCH 06/31] fixed typo that added two '/' characters to path sys command (i.e. from tech//SCN3ME_SUBM.30 to tech/SCN3ME_SUBM.30) --- compiler/verify/magic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index cf3508f4..98e345d4 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -34,7 +34,7 @@ def write_magic_script(cell_name, gds_name, extract=False): f = open(run_file, "w") f.write("#!/bin/sh\n") f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1])) - f.write("path sys +{}/tech\n".format(OPTS.openram_tech)) + f.write("path sys +{}tech\n".format(OPTS.openram_tech)) f.write("tech load SCN3ME_SUBM.30\n") #gf.write("scalegrid 1 8\n") #f.write("gds rescale no\n") From aeaab13d28c1b6ec44144753200123098235d351 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Wed, 29 Aug 2018 16:05:13 -0700 Subject: [PATCH 07/31] Unit tests for pbitcell now passing, so commenting out skip line. Also gave pbitcell_array useful names in unit test for easier debugging --- compiler/tests/04_pbitcell_test.py | 2 +- compiler/tests/05_pbitcell_array_test.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 41b2ab83..bcf78f8c 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -13,7 +13,7 @@ import debug OPTS = globals.OPTS -@unittest.skip("SKIPPING 04_pbitcell_test") +#@unittest.skip("SKIPPING 04_pbitcell_test") class pbitcell_test(openram_test): def runTest(self): diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 92e58673..01cb967d 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 05_pbitcell_array_test") +#@unittest.skip("SKIPPING 05_pbitcell_array_test") class pbitcell_array_test(openram_test): def runTest(self): @@ -26,7 +26,7 @@ class pbitcell_array_test(openram_test): OPTS.rw_ports = 2 OPTS.r_ports = 2 OPTS.w_ports = 2 - a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) + a = bitcell_array.bitcell_array(name="pbitcell_array_Rport_edge", cols=4, rows=4) self.local_check(a) debug.info(2, "Testing 4x4 array for multiport bitcell, with write ports at the edge of the bit cell") @@ -34,7 +34,7 @@ class pbitcell_array_test(openram_test): OPTS.rw_ports = 2 OPTS.r_ports = 0 OPTS.w_ports = 2 - a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) + a = bitcell_array.bitcell_array(name="pbitcell_array_Wport_edge", cols=4, rows=4) self.local_check(a) debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell") @@ -42,7 +42,7 @@ class pbitcell_array_test(openram_test): OPTS.rw_ports = 2 OPTS.r_ports = 0 OPTS.w_ports = 0 - a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) + a = bitcell_array.bitcell_array(name="pbitcell_array_RWport_edge", cols=4, rows=4) self.local_check(a) globals.end_openram() From e36452622cac26c05c68dcd765611f5c3ee35670 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 29 Aug 2018 16:12:06 -0700 Subject: [PATCH 08/31] Preserve same order of design rules in each tech file --- technology/freepdk45/tech/tech.py | 4 ++-- technology/scn3me_subm/tech/tech.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index e3217252..50d8977c 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -98,6 +98,8 @@ drc["minwidth_poly"] = 0.05 drc["poly_to_poly"] = 0.14 # POLY.3 Minimum poly extension beyond active drc["poly_extend_active"] = 0.055 +# Not a rule +drc["poly_to_polycontact"] = 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 @@ -106,8 +108,6 @@ drc["poly_to_active"] = 0.05 drc["poly_to_field_poly"] = 0.075 # Not a rule drc["minarea_poly"] = 0.0 -# Not a rule -drc["poly_to_polycontact"] = 0.075 # ACTIVE.2 Minimum spacing of active drc["active_to_body_active"] = 0.08 diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index db157089..d69a7d7f 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -87,10 +87,10 @@ drc["poly_extend_active"] = 0.6 drc["poly_to_polycontact"] = 1.2 # ?? drc["active_enclosure_gate"] = 0.0 -# 3.2.a Minimum spacing over field poly -drc["poly_to_field_poly"] = 0.9 # 3.5 Minimum field poly to active drc["poly_to_active"] = 0.3 +# 3.2.a Minimum spacing over field poly +drc["poly_to_field_poly"] = 0.9 # Not a rule drc["minarea_poly"] = 0.0 From 563ff77d44e5075ab72d81cc47e008123e0552c9 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 31 Aug 2018 12:03:28 -0700 Subject: [PATCH 09/31] Add sram_config class. Rename port variables for better description. --- compiler/globals.py | 18 +- compiler/modules/bank.py | 285 +++++++++++----------- compiler/openram.py | 14 +- compiler/options.py | 6 +- compiler/pgates/pbitcell.py | 190 +++++++-------- compiler/sram.py | 30 +-- compiler/sram_1bank.py | 5 +- compiler/sram_2bank.py | 4 +- compiler/sram_4bank.py | 4 +- compiler/sram_base.py | 58 +++-- compiler/sram_config.py | 36 +++ compiler/tests/04_pbitcell_test.py | 20 +- compiler/tests/04_precharge_test.py | 6 +- compiler/tests/05_pbitcell_array_test.py | 18 +- compiler/tests/08_precharge_array_test.py | 6 +- compiler/tests/19_multi_bank_test.py | 21 +- compiler/tests/19_psingle_bank_test.py | 84 ++++--- compiler/tests/19_single_bank_test.py | 26 +- compiler/tests/20_sram_1bank_test.py | 33 ++- compiler/tests/20_sram_2bank_test.py | 19 +- compiler/tests/20_sram_4bank_test.py | 15 +- compiler/tests/23_lib_sram_model_test.py | 15 +- compiler/tests/23_lib_sram_prune_test.py | 13 +- compiler/tests/23_lib_sram_test.py | 11 +- compiler/tests/24_lef_sram_test.py | 11 +- compiler/tests/25_verilog_sram_test.py | 11 +- 26 files changed, 548 insertions(+), 411 deletions(-) create mode 100644 compiler/sram_config.py diff --git a/compiler/globals.py b/compiler/globals.py index 9acfc945..991ec1ad 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -204,26 +204,30 @@ def read_config(config_file, is_unit_test=True): # Note that if we re-read a config file, nothing will get read again! if not k in OPTS.__dict__ or k=="tech_name": OPTS.__dict__[k]=v - + + # Massage the output path to be an absolute one if not OPTS.output_path.endswith('/'): OPTS.output_path += "/" if not OPTS.output_path.startswith('/'): OPTS.output_path = os.getcwd() + "/" + OPTS.output_path debug.info(1, "Output saved in " + OPTS.output_path) + # Remember if we are running unit tests to reduce output OPTS.is_unit_test=is_unit_test # If we are only generating a netlist, we can't do DRC/LVS if OPTS.netlist_only: OPTS.check_lvsdrc=False - + # If config didn't set output name, make a reasonable default. if (OPTS.output_name == ""): - OPTS.output_name = "sram_{0}rw_{1}b_{2}w_{3}bank_{4}".format(OPTS.rw_ports, - OPTS.word_size, - OPTS.num_words, - OPTS.num_banks, - OPTS.tech_name) + OPTS.output_name = "sram_{0}b_{1}w_{2}bank_{3}rw_{4}w_{5}r_{6}".format(OPTS.word_size, + OPTS.num_words, + OPTS.num_banks, + OPTS.num_rw_ports, + OPTS.num_w_ports, + OPTS.num_r_ports, + OPTS.tech_name) # Don't delete the output dir, it may have other files! # make the directory if it doesn't exist diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 9e711d22..f39fb424 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -20,22 +20,16 @@ class bank(design.design): write driver and sense amplifiers. """ - def __init__(self, word_size, num_words, words_per_row, num_banks=1, name=""): + def __init__(self, sram_config, name=""): - if name == "": - name = "bank_{0}_{1}".format(word_size, num_words) - design.design.__init__(self, name) - debug.info(2, "create sram of size {0} with {1} words".format(word_size,num_words)) - - self.word_size = word_size - self.num_words = num_words - self.words_per_row = words_per_row - self.num_banks = num_banks + sram_config.set_local_config(self) - self.total_write = OPTS.rw_ports + OPTS.w_ports - self.total_read = OPTS.rw_ports + OPTS.r_ports - self.total_ports = OPTS.rw_ports + OPTS.w_ports + OPTS.r_ports + if name == "": + name = "bank_{0}_{1}".format(self.word_size, self.num_words) + design.design.__init__(self, name) + debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words)) + # The local control signals are gated when we have bank select logic, # so this prefix will be added to all of the input signals to create # the internal gated signals. @@ -70,24 +64,24 @@ class bank(design.design): def add_pins(self): """ Adding pins for Bank module""" - for k in range(self.total_read): - for i in range(self.word_size): - self.add_pin("dout{0}[{1}]".format(k,i),"OUT") - for k in range(self.total_write): - for i in range(self.word_size): - self.add_pin("din{0}[{1}]".format(k,i),"IN") - for k in range(self.total_ports): - for i in range(self.addr_size): - self.add_pin("addr{0}[{1}]".format(k,i),"INPUT") + for port in range(self.total_read): + for bit in range(self.word_size): + self.add_pin("dout{0}[{1}]".format(port,bit),"OUT") + for port in range(self.total_write): + for bit in range(self.word_size): + self.add_pin("din{0}[{1}]".format(port,bit),"IN") + for port in range(self.total_ports): + for bit in range(self.addr_size): + self.add_pin("addr{0}[{1}]".format(port,bit),"INPUT") # For more than one bank, we have a bank select and name # the signals gated_*. if self.num_banks > 1: self.add_pin("bank_sel","INPUT") - for k in range(self.total_read): - self.add_pin("s_en{0}".format(k), "INPUT") - for k in range(self.total_write): - self.add_pin("w_en{0}".format(k), "INPUT") + for port in range(self.total_read): + self.add_pin("s_en{0}".format(port), "INPUT") + for port in range(self.total_write): + self.add_pin("w_en{0}".format(port), "INPUT") for pin in ["clk_buf_bar","clk_buf"]: self.add_pin(pin,"INPUT") self.add_pin("vdd","POWER") @@ -228,9 +222,9 @@ class bank(design.design): self.total_bitline_list = self.bitcell.list_all_bitline_names() self.precharge_array = [] - for k in range(self.total_read): - self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.read_bl_list[k], bitcell_br=self.read_br_list[k])) - self.add_mod(self.precharge_array[k]) + for port in range(self.total_read): + self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.read_bl_list[port], bitcell_br=self.read_br_list[port])) + self.add_mod(self.precharge_array[port]) if self.col_addr_size > 0: self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols, @@ -287,13 +281,13 @@ class bank(design.design): """ Creating Precharge """ self.precharge_array_inst = [] - for k in range(self.total_read): - self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(k), - mod=self.precharge_array[k])) + for port in range(self.total_read): + self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(port), + mod=self.precharge_array[port])) temp = [] for i in range(self.num_cols): - temp.append(self.read_bl_list[k]+"[{0}]".format(i)) - temp.append(self.read_br_list[k]+"[{0}]".format(i)) + temp.append(self.read_bl_list[port]+"[{0}]".format(i)) + temp.append(self.read_br_list[port]+"[{0}]".format(i)) temp.extend([self.prefix+"clk_buf_bar", "vdd"]) self.connect_inst(temp) @@ -301,11 +295,11 @@ class bank(design.design): """ Placing Precharge """ # FIXME: place for multiport - for k in range(self.total_read): + for port in range(self.total_read): # The wells must be far enough apart # The enclosure is for the well and the spacing is to the bitcell wells y_offset = self.bitcell_array.height + self.m2_gap - self.precharge_array_inst[k].place(vector(0,y_offset)) + self.precharge_array_inst[port].place(vector(0,y_offset)) def create_column_mux_array(self): """ Creating Column Mux when words_per_row > 1 . """ @@ -313,19 +307,19 @@ class bank(design.design): return self.col_mux_array_inst = [] - for k in range(self.total_ports): - self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(k), + for port in range(self.total_ports): + self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(port), mod=self.column_mux_array)) temp = [] - for i in range(self.num_cols): - temp.append(self.total_bl_list[k]+"[{0}]".format(i)) - temp.append(self.total_br_list[k]+"[{0}]".format(i)) - for h in range(self.words_per_row): - temp.append("sel{0}[{1}]".format(k,h)) - for j in range(self.word_size): - temp.append(self.total_bl_list[k]+"_out[{0}]".format(j)) - temp.append(self.total_br_list[k]+"_out[{0}]".format(j)) + for col in range(self.num_cols): + temp.append(self.total_bl_list[port]+"[{0}]".format(col)) + temp.append(self.total_br_list[port]+"[{0}]".format(col)) + for word in range(self.words_per_row): + temp.append("sel{0}[{1}]".format(port,word)) + for bit in range(self.word_size): + temp.append(self.total_bl_list[port]+"_out[{0}]".format(bit)) + temp.append(self.total_br_list[port]+"_out[{0}]".format(bit)) temp.append("gnd") self.connect_inst(temp) @@ -337,68 +331,68 @@ class bank(design.design): self.column_mux_height = 0 return - for k in range(self.total_ports): + for port in range(self.total_ports): y_offset = self.column_mux_height - self.col_mux_array_inst[k].place(vector(0,y_offset).scale(-1,-1)) + self.col_mux_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) def create_sense_amp_array(self): """ Creating Sense amp """ self.sense_amp_array_inst = [] - for k in range(self.total_read): - self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(k), + for port in range(self.total_read): + self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(port), mod=self.sense_amp_array)) temp = [] - for i in range(self.word_size): - temp.append("dout{0}[{1}]".format(k,i)) + for bit in range(self.word_size): + temp.append("dout{0}[{1}]".format(port,bit)) if self.words_per_row == 1: - temp.append(self.read_bl_list[k]+"[{0}]".format(i)) - temp.append(self.read_br_list[k]+"[{0}]".format(i)) + temp.append(self.read_bl_list[port]+"[{0}]".format(bit)) + temp.append(self.read_br_list[port]+"[{0}]".format(bit)) else: - temp.append(self.read_bl_list[k]+"_out[{0}]".format(i)) - temp.append(self.read_br_list[k]+"_out[{0}]".format(i)) + temp.append(self.read_bl_list[port]+"_out[{0}]".format(bit)) + temp.append(self.read_br_list[port]+"_out[{0}]".format(bit)) - temp.extend([self.prefix+"s_en{0}".format(k), "vdd", "gnd"]) + temp.extend([self.prefix+"s_en{0}".format(port), "vdd", "gnd"]) self.connect_inst(temp) def place_sense_amp_array(self): """ Placing Sense amp """ # FIXME: place for multiport - for k in range(self.total_read): + for port in range(self.total_read): y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap - self.sense_amp_array_inst[k].place(vector(0,y_offset).scale(-1,-1)) + self.sense_amp_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) def create_write_driver_array(self): """ Creating Write Driver """ self.write_driver_array_inst = [] - for k in range(self.total_write): - self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(k), + for port in range(self.total_write): + self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port), mod=self.write_driver_array)) temp = [] - for i in range(self.word_size): - temp.append("din{0}[{1}]".format(k,i)) - for i in range(self.word_size): + for bit in range(self.word_size): + temp.append("din{0}[{1}]".format(port,bit)) + for bit in range(self.word_size): if (self.words_per_row == 1): - temp.append(self.write_bl_list[k]+"[{0}]".format(i)) - temp.append(self.write_br_list[k]+"[{0}]".format(i)) + temp.append(self.write_bl_list[port]+"[{0}]".format(bit)) + temp.append(self.write_br_list[port]+"[{0}]".format(bit)) else: - temp.append(self.write_bl_list[k]+"_out[{0}]".format(i)) - temp.append(self.write_br_list[k]+"_out[{0}]".format(i)) - temp.extend([self.prefix+"w_en{0}".format(k), "vdd", "gnd"]) + temp.append(self.write_bl_list[port]+"_out[{0}]".format(bit)) + temp.append(self.write_br_list[port]+"_out[{0}]".format(bit)) + temp.extend([self.prefix+"w_en{0}".format(port), "vdd", "gnd"]) self.connect_inst(temp) def place_write_driver_array(self): """ Placing Write Driver """ # FIXME: place for multiport - for k in range(self.total_write): + for port in range(self.total_write): y_offset = self.sense_amp_array.height + self.column_mux_height \ + self.m2_gap + self.write_driver_array.height - self.write_driver_array_inst[k].place(vector(0,y_offset).scale(-1,-1)) + self.write_driver_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) @@ -406,15 +400,15 @@ class bank(design.design): """ Create the hierarchical row decoder """ self.row_decoder_inst = [] - for k in range(self.total_ports): - self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(k), + for port in range(self.total_ports): + self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(port), mod=self.row_decoder)) temp = [] - for i in range(self.row_addr_size): - temp.append("addr{0}[{1}]".format(k,i+self.col_addr_size)) - for j in range(self.num_rows): - temp.append("dec_out{0}[{1}]".format(k,j)) + for bit in range(self.row_addr_size): + temp.append("addr{0}[{1}]".format(port,bit+self.col_addr_size)) + for row in range(self.num_rows): + temp.append("dec_out{0}[{1}]".format(port,row)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -428,24 +422,24 @@ class bank(design.design): # The address flop and decoder are aligned in the x coord. # FIXME: place for multiport - for k in range(self.total_ports): + for port in range(self.total_ports): x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) - self.row_decoder_inst[k].place(vector(x_offset,0)) + self.row_decoder_inst[port].place(vector(x_offset,0)) def create_wordline_driver(self): """ Create the Wordline Driver """ self.wordline_driver_inst = [] - for k in range(self.total_ports): - self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(k), + for port in range(self.total_ports): + self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(port), mod=self.wordline_driver)) temp = [] - for i in range(self.num_rows): - temp.append("dec_out{0}[{1}]".format(k,i)) - for i in range(self.num_rows): - temp.append(self.total_wl_list[k]+"[{0}]".format(i)) + for row in range(self.num_rows): + temp.append("dec_out{0}[{1}]".format(port,row)) + for row in range(self.num_rows): + temp.append(self.total_wl_list[port]+"[{0}]".format(row)) temp.append(self.prefix+"clk_buf") temp.append("vdd") temp.append("gnd") @@ -455,10 +449,10 @@ class bank(design.design): """ Place the Wordline Driver """ # FIXME: place for multiport - for k in range(self.total_ports): + for port in range(self.total_ports): # The wordline driver is placed to the right of the main decoder width. x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch - self.wordline_driver_inst[k].place(vector(x_offset,0)) + self.wordline_driver_inst[port].place(vector(x_offset,0)) def create_column_decoder(self): @@ -480,15 +474,15 @@ class bank(design.design): debug.error("Invalid column decoder?",-1) self.col_decoder_inst = [] - for k in range(self.total_ports): - self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(k), + for port in range(self.total_ports): + self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(port), mod=self.col_decoder)) temp = [] - for i in range(self.col_addr_size): - temp.append("addr{0}[{1}]".format(k,i)) - for j in range(self.num_col_addr_lines): - temp.append("sel{0}[{1}]".format(k,j)) + for bit in range(self.col_addr_size): + temp.append("addr{0}[{1}]".format(port,bit)) + for bit in range(self.num_col_addr_lines): + temp.append("sel{0}[{1}]".format(port,bit)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) @@ -500,11 +494,11 @@ class bank(design.design): return # FIXME: place for multiport - for k in range(self.total_ports): + for port in range(self.total_ports): # Place the col decoder right aligned with row decoder x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) - self.col_decoder_inst[k].place(vector(x_off,y_off)) + self.col_decoder_inst[port].place(vector(x_off,y_off)) def create_bank_select(self): @@ -514,7 +508,7 @@ class bank(design.design): return self.bank_select_inst = [] - for k in range(self.total_ports): + for port in range(self.total_ports): self.bank_select_inst.append(self.add_inst(name="bank_select", mod=self.bank_select)) @@ -532,7 +526,7 @@ class bank(design.design): return # FIXME: place for multiport - for k in range(self.total_ports): + for port in range(self.total_ports): x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) if self.col_addr_size > 0: y_off = min(self.col_decoder_inst[0].by(), self.col_mux_array_inst[0].by()) @@ -540,7 +534,7 @@ class bank(design.design): y_off = self.row_decoder_inst[0].by() y_off -= (self.bank_select.height + drc["well_to_well"]) self.bank_select_pos = vector(x_off,y_off) - self.bank_select_inst[k].place(self.bank_select_pos) + self.bank_select_inst[port].place(self.bank_select_pos) def route_vdd_gnd(self): @@ -549,19 +543,19 @@ class bank(design.design): # These are the instances that every bank has top_instances = [self.bitcell_array_inst] - for k in range(self.total_ports): - top_instances.extend([self.precharge_array_inst[k], - self.sense_amp_array_inst[k], - self.write_driver_array_inst[k], - self.row_decoder_inst[k], - self.wordline_driver_inst[k]]) + for port in range(self.total_ports): + top_instances.extend([self.precharge_array_inst[port], + self.sense_amp_array_inst[port], + self.write_driver_array_inst[port], + self.row_decoder_inst[port], + self.wordline_driver_inst[port]]) # Add these if we use the part... if self.col_addr_size > 0: - top_instances.append(self.col_decoder_inst[k]) - top_instances.append(self.col_mux_array_inst[k]) + top_instances.append(self.col_decoder_inst[port]) + top_instances.append(self.col_mux_array_inst[port]) if self.num_banks > 1: - top_instances.append(self.bank_select_inst[k]) + top_instances.append(self.bank_select_inst[port]) for inst in top_instances: @@ -575,13 +569,13 @@ class bank(design.design): def route_bank_select(self): """ Route the bank select logic. """ - for k in range(self.total_ports): + for port in range(self.total_ports): for input_name in self.input_control_signals+["bank_sel"]: - self.copy_layout_pin(self.bank_select_inst[k], input_name) + self.copy_layout_pin(self.bank_select_inst[port], input_name) for gated_name in self.control_signals: # Connect the inverter output to the central bus - out_pos = self.bank_select_inst[k].get_pin(gated_name).rc() + out_pos = self.bank_select_inst[port].get_pin(gated_name).rc() bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y) self.add_path("metal3",[out_pos, bus_pos]) self.add_via_center(layers=("metal2", "via2", "metal3"), @@ -659,12 +653,12 @@ class bank(design.design): """ Routing of BL and BR between pre-charge and bitcell array """ # FIXME: Update for multiport - for k in range(self.total_read): - for i in range(self.num_cols): - precharge_bl = self.precharge_array_inst[k].get_pin("bl[{}]".format(i)).bc() - precharge_br = self.precharge_array_inst[k].get_pin("br[{}]".format(i)).bc() - bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[k]+"[{}]".format(i)).uc() - bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_list[k]+"[{}]".format(i)).uc() + for port in range(self.total_read): + for col in range(self.num_cols): + precharge_bl = self.precharge_array_inst[port].get_pin("bl[{}]".format(col)).bc() + precharge_br = self.precharge_array_inst[port].get_pin("br[{}]".format(col)).bc() + bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"[{}]".format(col)).uc() + bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"[{}]".format(col)).uc() yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), @@ -681,12 +675,12 @@ class bank(design.design): return # FIXME: Update for multiport - for k in range(self.total_ports): - for i in range(self.num_cols): - col_mux_bl = self.col_mux_array_inst[k].get_pin("bl[{}]".format(i)).uc() - col_mux_br = self.col_mux_array_inst[k].get_pin("br[{}]".format(i)).uc() - bitcell_bl = self.bitcell_array_inst.get_pin(self.total_bl_list[k]+"[{}]".format(i)).bc() - bitcell_br = self.bitcell_array_inst.get_pin(self.total_br_list[k]+"[{}]".format(i)).bc() + for port in range(self.total_ports): + for col in range(self.num_cols): + col_mux_bl = self.col_mux_array_inst[port].get_pin("bl[{}]".format(col)).uc() + col_mux_br = self.col_mux_array_inst[port].get_pin("br[{}]".format(col)).uc() + bitcell_bl = self.bitcell_array_inst.get_pin(self.total_bl_list[port]+"[{}]".format(col)).bc() + bitcell_br = self.bitcell_array_inst.get_pin(self.total_br_list[port]+"[{}]".format(col)).bc() yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y) self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), @@ -697,19 +691,19 @@ class bank(design.design): def route_sense_amp_to_col_mux_or_bitcell_array(self): """ Routing of BL and BR between sense_amp and column mux or bitcell array """ - for k in range(self.total_read): - for i in range(self.word_size): - sense_amp_bl = self.sense_amp_array_inst[k].get_pin("bl[{}]".format(i)).uc() - sense_amp_br = self.sense_amp_array_inst[k].get_pin("br[{}]".format(i)).uc() + for port in range(self.total_read): + for bit in range(self.word_size): + sense_amp_bl = self.sense_amp_array_inst[port].get_pin("bl[{}]".format(bit)).uc() + sense_amp_br = self.sense_amp_array_inst[port].get_pin("br[{}]".format(bit)).uc() if self.col_addr_size>0: # Sense amp is connected to the col mux - connect_bl = self.col_mux_array_inst[k].get_pin("bl_out[{}]".format(i)).bc() - connect_br = self.col_mux_array_inst[k].get_pin("br_out[{}]".format(i)).bc() + connect_bl = self.col_mux_array_inst[port].get_pin("bl_out[{}]".format(bit)).bc() + connect_br = self.col_mux_array_inst[port].get_pin("br_out[{}]".format(bit)).bc() else: # Sense amp is directly connected to the bitcell array - connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[k]+"[{}]".format(i)).bc() - connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[k]+"[{}]".format(i)).bc() + connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"[{}]".format(bit)).bc() + connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"[{}]".format(bit)).bc() yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) @@ -721,9 +715,11 @@ class bank(design.design): def route_sense_amp_out(self): """ Add pins for the sense amp output """ - for i in range(self.word_size): - data_pin = self.sense_amp_array_inst[0].get_pin("data[{}]".format(i)) - self.add_layout_pin_rect_center(text="dout0[{}]".format(i), + + # FIXME: Update for multiport + for bit in range(self.word_size): + data_pin = self.sense_amp_array_inst[0].get_pin("data[{}]".format(bit)) + self.add_layout_pin_rect_center(text="dout0[{}]".format(bit), layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), @@ -733,10 +729,11 @@ class bank(design.design): def route_row_decoder(self): """ Routes the row decoder inputs and supplies """ + # FIXME: Update for multiport # Create inputs for the row address lines - for i in range(self.row_addr_size): - addr_idx = i + self.col_addr_size - decoder_name = "addr[{}]".format(i) + for row in range(self.row_addr_size): + addr_idx = row + self.col_addr_size + decoder_name = "addr[{}]".format(row) addr_name = "addr0[{}]".format(addr_idx) self.copy_layout_pin(self.row_decoder_inst[0], decoder_name, addr_name) @@ -744,9 +741,9 @@ class bank(design.design): def route_write_driver(self): """ Connecting write driver """ - for i in range(self.word_size): - data_name = "data[{}]".format(i) - din_name = "din0[{}]".format(i) + for row in range(self.word_size): + data_name = "data[{}]".format(row) + din_name = "din0[{}]".format(row) self.copy_layout_pin(self.write_driver_array_inst[0], data_name, din_name) @@ -754,17 +751,17 @@ class bank(design.design): def route_wordline_driver(self): """ Connecting Wordline driver output to Bitcell WL connection """ - for i in range(self.num_rows): + for row in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs - decoder_out_pos = self.row_decoder_inst[0].get_pin("decode[{}]".format(i)).rc() - driver_in_pos = self.wordline_driver_inst[0].get_pin("in[{}]".format(i)).lc() + decoder_out_pos = self.row_decoder_inst[0].get_pin("decode[{}]".format(row)).rc() + driver_in_pos = self.wordline_driver_inst[0].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]) # The mid guarantees we exit the input cell to the right. - driver_wl_pos = self.wordline_driver_inst[0].get_pin("wl[{}]".format(i)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[0]+"[{}]".format(i)).lc() + driver_wl_pos = self.wordline_driver_inst[0].get_pin("wl[{}]".format(row)).rc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[0]+"[{}]".format(row)).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]) diff --git a/compiler/openram.py b/compiler/openram.py index 9045e8e7..a6db154d 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -38,7 +38,8 @@ report_status() # Start importing design modules after we have the config file import verify -import sram +from sram import sram +from sram_config import sram_config output_extensions = ["sp","v","lib"] if not OPTS.netlist_only: @@ -51,11 +52,14 @@ print(*output_files,sep="\n") start_time = datetime.datetime.now() print_time("Start",start_time) +c = sram_config(word_size=OPTS.word_size, + num_words=OPTS.num_words, + num_rw_ports=OPTS.num_rw_ports, + num_w_ports=OPTS.num_w_ports, + num_r_ports=OPTS.num_r_ports) + # import SRAM test generation -s = sram.sram(word_size=OPTS.word_size, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name=OPTS.output_name) +s = sram(c, OPTS.output_name) # Output the files for the resulting SRAM s.save() diff --git a/compiler/options.py b/compiler/options.py index 04d546d4..a81f7fae 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -52,9 +52,9 @@ class options(optparse.Values): purge_temp = True # These are the configuration parameters - rw_ports = 1 - r_ports = 0 - w_ports = 0 + num_rw_ports = 1 + num_r_ports = 0 + num_w_ports = 0 # These will get initialized by the the file supply_voltages = "" temperatures = "" diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index ddd998b6..0b2ec27d 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -16,18 +16,18 @@ class pbitcell(pgate.pgate): width = None height = None - unique_id = 1 - - def __init__(self, num_readwrite=OPTS.rw_ports, num_write=OPTS.w_ports, num_read=OPTS.r_ports): - name = "pbitcell_{0}RW_{1}W_{2}R_{3}".format(num_readwrite, num_write, num_read, pbitcell.unique_id) - pbitcell.unique_id += 1 - pgate.pgate.__init__(self, name) - debug.info(2, "create a multi-port bitcell with {0} write ports and {1} read ports".format(num_write, num_read)) + def __init__(self, num_rw_ports=OPTS.num_rw_ports, num_w_ports=OPTS.num_w_ports, num_r_ports=OPTS.num_r_ports): - self.num_readwrite = num_readwrite - self.num_write = num_write - self.num_read = num_read + name = "pbitcell_{0}RW_{1}W_{2}R".format(num_rw_ports, num_w_ports, num_r_ports) + pgate.pgate.__init__(self, name) + debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(num_rw_ports, + num_w_ports, + num_r_ports)) + self.num_rw_ports = num_rw_ports + self.num_w_ports = num_w_ports + self.num_r_ports = num_r_ports + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -41,11 +41,11 @@ class pbitcell(pgate.pgate): self.add_modules() self.create_storage() - if(self.num_readwrite > 0): + if(self.num_rw_ports > 0): self.create_readwrite_ports() - if(self.num_write > 0): + if(self.num_w_ports > 0): self.create_write_ports() - if(self.num_read > 0): + if(self.num_r_ports > 0): self.create_read_ports() def create_layout(self): @@ -56,11 +56,11 @@ class pbitcell(pgate.pgate): self.route_storage() self.route_rails() - if(self.num_readwrite > 0): + if(self.num_rw_ports > 0): self.place_readwrite_ports() - if(self.num_write > 0): + if(self.num_w_ports > 0): self.place_write_ports() - if(self.num_read > 0): + if(self.num_r_ports > 0): self.place_read_ports() self.extend_well() @@ -68,21 +68,21 @@ class pbitcell(pgate.pgate): self.DRC_LVS() def add_pins(self): - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): self.add_pin("rwbl{}".format(k)) self.add_pin("rwbl_bar{}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): self.add_pin("wbl{}".format(k)) self.add_pin("wbl_bar{}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): self.add_pin("rbl{}".format(k)) self.add_pin("rbl_bar{}".format(k)) - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): self.add_pin("rwwl{}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): self.add_pin("wwl{}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): self.add_pin("rwl{}".format(k)) self.add_pin("vdd") @@ -90,8 +90,8 @@ class pbitcell(pgate.pgate): def add_modules(self): # if there are any read/write ports, then the inverter nmos is sized based the number of them - if(self.num_readwrite > 0): - inverter_nmos_width = self.num_readwrite*3*parameter["min_tx_size"] + if(self.num_rw_ports > 0): + inverter_nmos_width = self.num_rw_ports*3*parameter["min_tx_size"] inverter_pmos_width = parameter["min_tx_size"] readwrite_nmos_width = 1.5*parameter["min_tx_size"] write_nmos_width = parameter["min_tx_size"] @@ -167,7 +167,7 @@ class pbitcell(pgate.pgate): # write to read transistor spacing (also acts as readwrite to read transistor spacing) # calculation is dependent on whether the read transistor is adjacent to a write transistor or a readwrite transistor - if(self.num_write > 0): + if(self.num_w_ports > 0): if(self.write_nmos_contact_extension > self.gate_contact_thres): write_portion = drc["minwidth_metal2"] + self.write_nmos_contact_extension else: @@ -206,23 +206,23 @@ class pbitcell(pgate.pgate): Calculate positions that describe the edges of the cell """ # create flags for excluding readwrite, write, or read port calculations if they are not included in the bitcell - if(self.num_readwrite > 0): + if(self.num_rw_ports > 0): self.readwrite_port_flag = True else: self.readwrite_port_flag = False - if(self.num_write > 0): + if(self.num_w_ports > 0): self.write_port_flag = True else: self.write_port_flag = False - if(self.num_read > 0): + if(self.num_r_ports > 0): self.read_port_flag = True else: self.read_port_flag = False # determine the distance of the leftmost/rightmost transistor gate connection - if (self.num_read > 0): + if (self.num_r_ports > 0): if(self.read_nmos_contact_extension > self.gate_contact_thres): end_connection = drc["minwidth_metal2"] + self.read_nmos_contact_extension + contact.m1m2.height else: @@ -236,11 +236,11 @@ class pbitcell(pgate.pgate): # leftmost position = storage width + read/write ports width + write ports width + read ports width + end transistor gate connections + metal spacing necessary for tiling the bitcell self.leftmost_xpos = -self.inverter_tile_width \ - self.inverter_to_write_spacing \ - - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-1)*self.readwrite_tile_width) \ + - self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_rw_ports-1)*self.readwrite_tile_width) \ - self.write_port_flag*self.readwrite_port_flag*self.write_to_write_spacing \ - - self.write_port_flag*(self.write_nmos.active_height + (self.num_write-1)*self.write_tile_width) \ + - self.write_port_flag*(self.write_nmos.active_height + (self.num_w_ports-1)*self.write_tile_width) \ - self.read_port_flag*self.write_to_read_spacing \ - - self.read_port_flag*(self.read_nmos.active_height + (self.num_read-1)*self.read_tile_width) \ + - self.read_port_flag*(self.read_nmos.active_height + (self.num_r_ports-1)*self.read_tile_width) \ - end_connection \ - 0.5*drc["poly_to_polycontact"] @@ -249,9 +249,9 @@ class pbitcell(pgate.pgate): # bottommost position = gnd height + rwwl height + wwl height + rwl height + space needed between tiled bitcells array_tiling_offset = 0.5*drc["minwidth_metal2"] self.botmost_ypos = -self.rail_tile_height \ - - self.num_readwrite*self.rowline_tile_height \ - - self.num_write*self.rowline_tile_height \ - - self.num_read*self.rowline_tile_height \ + - self.num_rw_ports*self.rowline_tile_height \ + - self.num_w_ports*self.rowline_tile_height \ + - self.num_r_ports*self.rowline_tile_height \ - array_tiling_offset # topmost position = height of the inverter + height of vdd @@ -390,11 +390,11 @@ class pbitcell(pgate.pgate): """ # define write transistor variables as empty arrays based on the number of write ports - self.readwrite_nmos_left = [None] * self.num_readwrite - self.readwrite_nmos_right = [None] * self.num_readwrite + self.readwrite_nmos_left = [None] * self.num_rw_ports + self.readwrite_nmos_right = [None] * self.num_rw_ports # iterate over the number of read/write ports - for k in range(0,self.num_readwrite): + for k in range(0,self.num_rw_ports): # add read/write transistors self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), mod=self.readwrite_nmos) @@ -411,15 +411,15 @@ class pbitcell(pgate.pgate): """ # Define variables relevant to write transistors - self.rwwl_positions = [None] * self.num_readwrite - self.rwbl_positions = [None] * self.num_readwrite - self.rwbl_bar_positions = [None] * self.num_readwrite + self.rwwl_positions = [None] * self.num_rw_ports + self.rwbl_positions = [None] * self.num_rw_ports + self.rwbl_bar_positions = [None] * self.num_rw_ports # define offset correction due to rotation of the ptx module readwrite_rotation_correct = self.readwrite_nmos.active_height # iterate over the number of read/write ports - for k in range(0,self.num_readwrite): + for k in range(0,self.num_rw_ports): # Add transistors # calculate read/write transistor offsets left_readwrite_transistor_xpos = self.left_building_edge \ @@ -529,7 +529,7 @@ class pbitcell(pgate.pgate): # Drain/Storage connections # this path only needs to be drawn once on the last iteration of the loop - if(k == self.num_readwrite-1): + if(k == self.num_rw_ports-1): # add contacts to connect gate of inverters to drain of read/write transistors left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), @@ -580,11 +580,11 @@ class pbitcell(pgate.pgate): write_rotation_correct = self.write_nmos.active_height # define write transistor variables as empty arrays based on the number of write ports - self.write_nmos_left = [None] * self.num_write - self.write_nmos_right = [None] * self.num_write + self.write_nmos_left = [None] * self.num_w_ports + self.write_nmos_right = [None] * self.num_w_ports # iterate over the number of write ports - for k in range(0,self.num_write): + for k in range(0,self.num_w_ports): # add write transistors self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k), mod=self.write_nmos) @@ -601,15 +601,15 @@ class pbitcell(pgate.pgate): """ # Define variables relevant to write transistors - self.wwl_positions = [None] * self.num_write - self.wbl_positions = [None] * self.num_write - self.wbl_bar_positions = [None] * self.num_write + self.wwl_positions = [None] * self.num_w_ports + self.wbl_positions = [None] * self.num_w_ports + self.wbl_bar_positions = [None] * self.num_w_ports # define offset correction due to rotation of the ptx module write_rotation_correct = self.write_nmos.active_height # iterate over the number of write ports - for k in range(0,self.num_write): + for k in range(0,self.num_w_ports): # Add transistors # calculate write transistor offsets left_write_transistor_xpos = self.left_building_edge \ @@ -634,7 +634,7 @@ class pbitcell(pgate.pgate): # Add WWL lines # calculate WWL position wwl_ypos = self.gnd_position.y \ - - self.num_readwrite*self.rowline_tile_height \ + - self.num_rw_ports*self.rowline_tile_height \ - (k+1)*self.rowline_tile_height self.wwl_positions[k] = vector(self.leftmost_xpos, wwl_ypos) @@ -723,7 +723,7 @@ class pbitcell(pgate.pgate): # Drain/Storage connections # this path only needs to be drawn once on the last iteration of the loop - if(k == self.num_write-1): + if(k == self.num_w_ports-1): # add contacts to connect gate of inverters to drain of write transistors left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos) self.add_contact_center(layers=("poly", "contact", "metal1"), @@ -771,13 +771,13 @@ class pbitcell(pgate.pgate): """ # define read transistor variables as empty arrays based on the number of read ports - self.read_nmos_left = [None] * self.num_read - self.read_nmos_right = [None] * self.num_read - self.read_access_nmos_left = [None] * self.num_read - self.read_access_nmos_right = [None] * self.num_read + self.read_nmos_left = [None] * self.num_r_ports + self.read_nmos_right = [None] * self.num_r_ports + self.read_access_nmos_left = [None] * self.num_r_ports + self.read_access_nmos_right = [None] * self.num_r_ports # iterate over the number of read ports - for k in range(0,self.num_read): + for k in range(0,self.num_r_ports): # add read-access transistors self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), mod=self.read_nmos) @@ -802,9 +802,9 @@ class pbitcell(pgate.pgate): """ # Define variables relevant to read transistors - self.rwl_positions = [None] * self.num_read - self.rbl_positions = [None] * self.num_read - self.rbl_bar_positions = [None] * self.num_read + self.rwl_positions = [None] * self.num_r_ports + self.rbl_positions = [None] * self.num_r_ports + self.rbl_bar_positions = [None] * self.num_r_ports # define offset correction due to rotation of the ptx module read_rotation_correct = self.read_nmos.active_height @@ -813,7 +813,7 @@ class pbitcell(pgate.pgate): overlap_offset = self.read_nmos.get_pin("D").ll() - self.read_nmos.get_pin("S").ll() # iterate over the number of read ports - for k in range(0,self.num_read): + for k in range(0,self.num_r_ports): # Add transistors # calculate transistor offsets left_read_transistor_xpos = self.left_building_edge \ @@ -843,8 +843,8 @@ class pbitcell(pgate.pgate): # Add RWL lines # calculate RWL position rwl_ypos = self.gnd_position.y \ - - self.num_readwrite*self.rowline_tile_height \ - - self.num_write*self.rowline_tile_height \ + - self.num_rw_ports*self.rowline_tile_height \ + - self.num_w_ports*self.rowline_tile_height \ - (k+1)*self.rowline_tile_height self.rwl_positions[k] = vector(self.leftmost_xpos, rwl_ypos) @@ -1003,7 +1003,7 @@ class pbitcell(pgate.pgate): # extend pwell over read/write and write transistors to the # height of the write transistor well (read/write and write # transistors are the same height) - if(self.num_write > 0): + if(self.num_w_ports > 0): # calculate the edge of the write transistor well closest to the center left_write_well_xpos = self.write_nmos_left[0].offset.x + drc["well_enclosure_active"] right_write_well_xpos = self.write_nmos_right[0].offset.x - self.write_nmos.active_height - drc["well_enclosure_active"] @@ -1029,7 +1029,7 @@ class pbitcell(pgate.pgate): height=write_well_height) # extend pwell over the read transistors to the height of the bitcell - if(self.num_read > 0): + if(self.num_r_ports > 0): # calculate the edge of the read transistor well clostest to the center left_read_well_xpos = self.read_nmos_left[0].offset.x + drc["well_enclosure_active"] right_read_well_xpos = self.read_nmos_right[0].offset.x - self.read_nmos.active_height - drc["well_enclosure_active"] @@ -1088,20 +1088,20 @@ class pbitcell(pgate.pgate): def list_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 = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): bitcell_pins.append("rwbl{0}[{1}]".format(k,col)) bitcell_pins.append("rwbl_bar{0}[{1}]".format(k,col)) - for k in range(self.num_write): + for k in range(self.num_w_ports): bitcell_pins.append("wbl{0}[{1}]".format(k,col)) bitcell_pins.append("wbl_bar{0}[{1}]".format(k,col)) - for k in range(self.num_read): + for k in range(self.num_r_ports): bitcell_pins.append("rbl{0}[{1}]".format(k,col)) bitcell_pins.append("rbl_bar{0}[{1}]".format(k,col)) - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): bitcell_pins.append("rwwl{0}[{1}]".format(k,row)) - for k in range(self.num_write): + for k in range(self.num_w_ports): bitcell_pins.append("wwl{0}[{1}]".format(k,row)) - for k in range(self.num_read): + for k in range(self.num_r_ports): bitcell_pins.append("rwl{0}[{1}]".format(k,row)) bitcell_pins.append("vdd") bitcell_pins.append("gnd") @@ -1111,11 +1111,11 @@ class pbitcell(pgate.pgate): def list_all_wl_names(self): """ Creates a list of all wordline pin names """ row_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): row_pins.append("rwwl{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): row_pins.append("wwl{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): row_pins.append("rwl{0}".format(k)) return row_pins @@ -1123,9 +1123,9 @@ class pbitcell(pgate.pgate): def list_read_wl_names(self): """ Creates a list of wordline pin names associated with read ports """ row_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): row_pins.append("rwwl{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): row_pins.append("rwl{0}".format(k)) return row_pins @@ -1133,9 +1133,9 @@ class pbitcell(pgate.pgate): def list_write_wl_names(self): """ Creates a list of wordline pin names associated with write ports """ row_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): row_pins.append("rwwl{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): row_pins.append("wwl{0}".format(k)) return row_pins @@ -1144,13 +1144,13 @@ class pbitcell(pgate.pgate): def list_all_bitline_names(self): """ Creates a list of all bitline pin names (both bl and br) """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl{0}".format(k)) column_pins.append("rwbl_bar{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): column_pins.append("wbl{0}".format(k)) column_pins.append("wbl_bar{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): column_pins.append("rbl{0}".format(k)) column_pins.append("rbl_bar{0}".format(k)) @@ -1159,11 +1159,11 @@ class pbitcell(pgate.pgate): def list_all_bl_names(self): """ Creates a list of all bl pins names """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): column_pins.append("wbl{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): column_pins.append("rbl{0}".format(k)) return column_pins @@ -1171,11 +1171,11 @@ class pbitcell(pgate.pgate): def list_all_br_names(self): """ Creates a list of all br pins names """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl_bar{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): column_pins.append("wbl_bar{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): column_pins.append("rbl_bar{0}".format(k)) return column_pins @@ -1183,9 +1183,9 @@ class pbitcell(pgate.pgate): def list_read_bl_names(self): """ Creates a list of bl pin names associated with read ports """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): column_pins.append("rbl{0}".format(k)) return column_pins @@ -1193,9 +1193,9 @@ class pbitcell(pgate.pgate): def list_read_br_names(self): """ Creates a list of br pin names associated with read ports """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl_bar{0}".format(k)) - for k in range(self.num_read): + for k in range(self.num_r_ports): column_pins.append("rbl_bar{0}".format(k)) return column_pins @@ -1203,9 +1203,9 @@ class pbitcell(pgate.pgate): def list_write_bl_names(self): """ Creates a list of bl pin names associated with write ports """ column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): column_pins.append("wbl{0}".format(k)) return column_pins @@ -1213,9 +1213,9 @@ class pbitcell(pgate.pgate): def list_write_br_names(self): """ Creates a list of br pin names asscociated with write ports""" column_pins = [] - for k in range(self.num_readwrite): + for k in range(self.num_rw_ports): column_pins.append("rwbl_bar{0}".format(k)) - for k in range(self.num_write): + for k in range(self.num_w_ports): column_pins.append("wbl_bar{0}".format(k)) return column_pins diff --git a/compiler/sram.py b/compiler/sram.py index 653a3a03..3891150b 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -3,7 +3,7 @@ import datetime import getpass import debug from globals import OPTS, print_time - +from sram_config import sram_config class sram(): """ @@ -12,31 +12,31 @@ class sram(): results. We can later add visualizer and other high-level functions as needed. """ - def __init__(self, word_size, num_words, num_banks, name): + def __init__(self, sram_config, name="sram"): + sram_config.set_local_config(self) + # reset the static duplicate name checker for unit tests # in case we create more than one SRAM from design import design design.name_map=[] - debug.info(2, "create sram of size {0} with {1} num of words".format(word_size, - num_words)) + debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size, + self.num_words, + self.num_banks)) start_time = datetime.datetime.now() self.name = name - - if num_banks == 1: - from sram_1bank import sram_1bank - self.s=sram_1bank(word_size, num_words, name) - elif num_banks == 2: - from sram_2bank import sram_2bank - self.s=sram_2bank(word_size, num_words, name) - elif num_banks == 4: - from sram_4bank import sram_4bank - self.s=sram_4bank(word_size, num_words, name) + if self.num_banks == 1: + from sram_1bank import sram_1bank as sram + elif self.num_banks == 2: + from sram_2bank import sram_2bank as sram + elif self.num_banks == 4: + from sram_4bank import sram_4bank as sram else: debug.error("Invalid number of banks.",-1) - + + self.s = sram(sram_config, name) self.s.create_netlist() if not OPTS.netlist_only: self.s.create_layout() diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 93903551..20dab3c7 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -18,8 +18,9 @@ class sram_1bank(sram_base): """ Procedures specific to a one bank SRAM. """ - def __init__(self, word_size, num_words, name): - sram_base.__init__(self, word_size, num_words, 1, name) + def __init__(self, sram_config, name): + sram_base.__init__(self, sram_config, name) + def create_netlist(self): self.compute_sizes() diff --git a/compiler/sram_2bank.py b/compiler/sram_2bank.py index ba10525c..ff0d7868 100644 --- a/compiler/sram_2bank.py +++ b/compiler/sram_2bank.py @@ -16,8 +16,8 @@ class sram_2bank(sram_base): """ Procedures specific to a two bank SRAM. """ - def __init__(self, word_size, num_words, name): - sram_base.__init__(self, word_size, num_words, 2, name) + def __init__(self, sram_config, name): + sram_base.__init__(self, sram_config, name) def compute_bank_offsets(self): """ Compute the overall offsets for a two bank SRAM """ diff --git a/compiler/sram_4bank.py b/compiler/sram_4bank.py index 14e597d5..64163e9f 100644 --- a/compiler/sram_4bank.py +++ b/compiler/sram_4bank.py @@ -16,8 +16,8 @@ class sram_4bank(sram_base): """ Procedures specific to a four bank SRAM. """ - def __init__(self, word_size, num_words, name): - sram_base.__init__(self, word_size, num_words, 4, name) + def __init__(self, sram_config, name): + sram_base.__init__(self, sram_config, name) def compute_bank_offsets(self): """ Compute the overall offsets for a four bank SRAM """ diff --git a/compiler/sram_base.py b/compiler/sram_base.py index da6403db..7ead2e48 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -2,6 +2,7 @@ import sys import datetime import getpass import debug +from importlib import reload from math import log,sqrt,ceil from vector import vector from globals import OPTS, print_time @@ -13,25 +14,18 @@ class sram_base(design): Dynamically generated SRAM by connecting banks to control logic. The number of banks should be 1 , 2 or 4 """ - def __init__(self, word_size, num_words, num_banks, name): + def __init__(self, sram_config, name): design.__init__(self, name) - - from importlib import reload - c = reload(__import__(OPTS.control_logic)) - self.mod_control_logic = getattr(c, OPTS.control_logic) + # This is used to compute the sizes of the SRAM + # and must be loaded before netlist creation c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() - c = reload(__import__(OPTS.ms_flop)) - self.mod_ms_flop = getattr(c, OPTS.ms_flop) - self.ms_flop = self.mod_ms_flop() + self.sram_config = sram_config + self.sram_config.set_local_config(self) - self.word_size = word_size - self.num_words = num_words - self.num_banks = num_banks - self.bank_insts = [] def compute_sizes(self): @@ -41,18 +35,22 @@ class sram_base(design): self.num_words_per_bank = self.num_words/self.num_banks self.num_bits_per_bank = self.word_size*self.num_words_per_bank + + # If this was hard coded, don't dynamically compute it! + if self.sram_config.words_per_row: + self.words_per_row = self.sram_config.words_per_row + else: + # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) + self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank + self.bank_side_length = sqrt(self.bank_area) - # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) - self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank - self.bank_side_length = sqrt(self.bank_area) + # Estimate the words per row given the height of the bitcell and the square side length + self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width) + self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size) - # Estimate the words per row given the height of the bitcell and the square side length - self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width) - self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size) - - # Estimate the number of rows given the tentative words per row - self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) - self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) + # Estimate the number of rows given the tentative words per row + self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) + self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) # Fix the number of columns and rows self.num_cols = int(self.words_per_row*self.word_size) @@ -63,7 +61,8 @@ class sram_base(design): self.col_addr_size = int(log(self.words_per_row, 2)) 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)) - + + self.sram_config.words_per_row = self.words_per_row debug.info(1,"Words per row: {}".format(self.words_per_row)) def estimate_words_per_row(self,tentative_num_cols, word_size): @@ -271,7 +270,15 @@ class sram_base(design): def add_modules(self): """ Create all the modules that will be used """ + + c = reload(__import__(OPTS.control_logic)) + self.mod_control_logic = getattr(c, OPTS.control_logic) + c = reload(__import__(OPTS.ms_flop)) + self.mod_ms_flop = getattr(c, OPTS.ms_flop) + self.ms_flop = self.mod_ms_flop() + + from control_logic import control_logic # Create the control logic module self.control_logic = self.mod_control_logic(num_rows=self.num_rows) @@ -293,10 +300,7 @@ class sram_base(design): # Create the bank module (up to four are instantiated) from bank import bank - self.bank = bank(word_size=self.word_size, - num_words=self.num_words_per_bank, - words_per_row=self.words_per_row, - num_banks=self.num_banks, + self.bank = bank(self.sram_config, name="bank") self.add_mod(self.bank) diff --git a/compiler/sram_config.py b/compiler/sram_config.py new file mode 100644 index 00000000..e7c58905 --- /dev/null +++ b/compiler/sram_config.py @@ -0,0 +1,36 @@ +from globals import OPTS + +class sram_config: + """ This is a structure that is used to hold the SRAM configuration options. """ + + def __init__(self, word_size, num_words, num_banks=1, num_rw_ports=OPTS.num_rw_ports, num_w_ports=OPTS.num_w_ports, num_r_ports=OPTS.num_r_ports): + self.word_size = word_size + self.num_words = num_words + self.num_banks = num_banks + self.num_rw_ports = num_rw_ports + self.num_w_ports = num_w_ports + self.num_r_ports = num_r_ports + + # This will get over-written when we determine the organization + self.num_banks = 1 + self.words_per_row = None + + self.total_write = num_rw_ports + num_w_ports + self.total_read = num_rw_ports + num_r_ports + self.total_ports = num_rw_ports + num_w_ports + num_r_ports + + + def set_local_config(self, module): + module.word_size = self.word_size + module.num_words = self.num_words + module.num_banks = self.num_banks + module.num_rw_ports = self.num_rw_ports + module.num_w_ports = self.num_w_ports + module.num_r_ports = self.num_r_ports + + module.words_per_row = self.words_per_row + + module.total_write = self.total_write + module.total_read = self.total_read + module.total_ports = self.total_ports + diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 0f74b42a..070637f9 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -22,43 +22,43 @@ class pbitcell_test(openram_test): import tech debug.info(2, "Bitcell with 1 of each port: read/write, write, and read") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=1) + tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=1,num_r_ports=1) self.local_check(tx) debug.info(2, "Bitcell with 0 read/write ports") - tx = pbitcell.pbitcell(num_readwrite=0,num_write=1,num_read=1) + tx = pbitcell.pbitcell(num_rw_ports=0,num_w_ports=1,num_r_ports=1) self.local_check(tx) debug.info(2, "Bitcell with 0 write ports") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=1) + tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=0,num_r_ports=1) self.local_check(tx) debug.info(2, "Bitcell with 0 read ports") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=1,num_read=0) + tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=1,num_r_ports=0) self.local_check(tx) debug.info(2, "Bitcell with 0 read ports and 0 write ports") - tx = pbitcell.pbitcell(num_readwrite=1,num_write=0,num_read=0) + tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=0,num_r_ports=0) self.local_check(tx) debug.info(2, "Bitcell with 2 of each port: read/write, write, and read") - tx = pbitcell.pbitcell(num_readwrite=2,num_write=2,num_read=2) + tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=2,num_r_ports=2) self.local_check(tx) debug.info(2, "Bitcell with 0 read/write ports") - tx = pbitcell.pbitcell(num_readwrite=0,num_write=2,num_read=2) + tx = pbitcell.pbitcell(num_rw_ports=0,num_w_ports=2,num_r_ports=2) self.local_check(tx) debug.info(2, "Bitcell with 0 write ports") - tx = pbitcell.pbitcell(num_readwrite=2,num_write=0,num_read=2) + tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=0,num_r_ports=2) self.local_check(tx) debug.info(2, "Bitcell with 0 read ports") - tx = pbitcell.pbitcell(num_readwrite=2,num_write=2,num_read=0) + tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=2,num_r_ports=0) self.local_check(tx) debug.info(2, "Bitcell with 0 read ports and 0 write ports") - tx = pbitcell.pbitcell(num_readwrite=2,num_write=0,num_read=0) + tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=0,num_r_ports=0) self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index bcf11473..8e44aa52 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -24,9 +24,9 @@ class precharge_test(openram_test): debug.info(2, "Checking precharge for pbitcell") OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 2 - OPTS.w_ports = 2 + OPTS.num_rw_ports = 2 + OPTS.num_r_ports = 2 + OPTS.num_w_ports = 2 tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="rwbl0", bitcell_br="rwbl_bar0") self.local_check(tx) diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index ad13cfbd..4fc75ac5 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -20,25 +20,25 @@ class pbitcell_array_test(openram_test): debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell") OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 2 - OPTS.w_ports = 2 + OPTS.num_rw_ports = 2 + OPTS.num_r_ports = 2 + OPTS.num_w_ports = 2 a = bitcell_array.bitcell_array(name="pbitcell_array_Rport_edge", cols=4, rows=4) self.local_check(a) debug.info(2, "Testing 4x4 array for multiport bitcell, with write ports at the edge of the bit cell") OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 0 - OPTS.w_ports = 2 + OPTS.num_rw_ports = 2 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 2 a = bitcell_array.bitcell_array(name="pbitcell_array_Wport_edge", cols=4, rows=4) self.local_check(a) debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell") OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 0 - OPTS.w_ports = 0 + OPTS.num_rw_ports = 2 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 a = bitcell_array.bitcell_array(name="pbitcell_array_RWport_edge", cols=4, rows=4) self.local_check(a) diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index 9592b6c5..49530410 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -24,9 +24,9 @@ class precharge_test(openram_test): debug.info(2, "Checking precharge for pbitcell") OPTS.bitcell = "pbitcell" - OPTS.rw_ports = 2 - OPTS.r_ports = 2 - OPTS.w_ports = 2 + OPTS.num_rw_ports = 2 + OPTS.num_r_ports = 2 + OPTS.num_w_ports = 2 pc = precharge_array.precharge_array(columns=3, bitcell_bl="rwbl0", bitcell_br="rwbl_bar0") self.local_check(pc) diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index 2169dcf1..9bab2a4f 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -16,21 +16,34 @@ class multi_bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from bank import bank + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=16) + c.num_banks=2 + + c.words_per_row=1 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=2, name="bank1_multi") + a = bank(c, name="bank1_multi") self.local_check(a) + c.num_words=32 + c.words_per_row=2 debug.info(1, "Two way column mux") - a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="bank2_multi") + a = bank(c, name="bank2_multi") self.local_check(a) + c.num_words=64 + c.words_per_row=4 debug.info(1, "Four way column mux") - a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="bank3_multi") + a = bank(c, name="bank3_multi") self.local_check(a) + c.word_size=2 + c.num_words=128 + c.words_per_row=8 debug.info(1, "Eight way column mux") - a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4_multi") + a = bank(c, name="bank4_multi") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index fad2b100..b98c2ff1 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -21,64 +21,92 @@ class psingle_bank_test(openram_test): from bank import bank OPTS.bitcell = "pbitcell" - - # testing all port configurations (with no column mux) to verify layout between bitcell array and peripheral circuitry - OPTS.rw_ports = 2 - OPTS.w_ports = 2 - OPTS.r_ports = 2 + from sram_config import sram_config + # testing all port configurations (with no column mux) to verify layout between bitcell array and peripheral circuitry + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + c = sram_config(word_size=4, + num_words=16) + + c.words_per_row=1 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_2w_2r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) """ - OPTS.rw_ports = 0 - OPTS.w_ports = 2 - OPTS.r_ports = 2 + OPTS.num_rw_ports = c.num_rw_ports = 2 + OPTS.num_w_ports = c.num_w_ports = 2 + OPTS.num_r_ports = c.num_r_ports = 2 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_0rw_2w_2r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) + self.local_check(a) + + OPTS.num_rw_ports = c.num_rw_ports = 0 + OPTS.num_w_ports = c.num_w_ports = 2 + OPTS.num_r_ports = c.num_r_ports = 2 + + debug.info(1, "No column mux") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) - OPTS.rw_ports = 2 - OPTS.w_ports = 0 - OPTS.r_ports = 2 + OPTS.num_rw_ports = c.num_rw_ports = 2 + OPTS.num_w_ports = c.num_w_ports = 0 + OPTS.num_r_ports = c.num_r_ports = 2 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_0w_2r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) - OPTS.rw_ports = 2 - OPTS.w_ports = 2 - OPTS.r_ports = 0 + OPTS.num_rw_ports = c.num_rw_ports = 2 + OPTS.num_w_ports = c.num_w_ports = 2 + OPTS.num_r_ports = c.num_r_ports = 0 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_2w_0r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) - OPTS.rw_ports = 2 - OPTS.w_ports = 0 - OPTS.r_ports = 0 + OPTS.num_rw_ports = c.num_rw_ports = 2 + OPTS.num_w_ports = c.num_w_ports = 0 + OPTS.num_r_ports = c.num_r_ports = 0 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_0w_0r_single") + name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) # testing with various column muxes - OPTS.rw_ports = 2 - OPTS.w_ports = 2 - OPTS.r_ports = 2 + OPTS.num_rw_ports = c.num_rw_ports = 2 + OPTS.num_w_ports = c.num_w_ports = 2 + OPTS.num_r_ports = c.num_r_ports = 2 + c.num_words=32 + c.words_per_row=2 debug.info(1, "Two way column mux") - a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single") + name = "bank2_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) + c.num_words=64 + c.words_per_row=4 debug.info(1, "Four way column mux") - a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single") + name = "bank3_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) # Eight way has a short circuit of one column mux select to gnd rail + c.word_size=2 + c.num_words=128 + c.words_per_row=8 debug.info(1, "Eight way column mux") - a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single") + name = "bank4_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) + a = bank(c, name=name) self.local_check(a) """ diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index 25567e82..3c32b30d 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -16,22 +16,34 @@ class single_bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from bank import bank + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=16) + + c.words_per_row=1 debug.info(1, "No column mux") - a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_single") - self.local_check(a) - - debug.info(1, "Two way column mux") - a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single") + a = bank(c, name="bank1_single") self.local_check(a) + c.num_words=32 + c.words_per_row=2 + debug.info(1, "Two way column mux") + a = bank(c, name="bank2_single") + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 debug.info(1, "Four way column mux") - a = bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3_single") + a = bank(c, name="bank3_single") self.local_check(a) # Eight way has a short circuit of one column mux select to gnd rail + c.word_size=2 + c.num_words=128 + c.words_per_row=8 debug.info(1, "Eight way column mux") - a = bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single") + a = bank(c, name="bank4_single") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/20_sram_1bank_test.py b/compiler/tests/20_sram_1bank_test.py index 7079b715..ad5732a6 100755 --- a/compiler/tests/20_sram_1bank_test.py +++ b/compiler/tests/20_sram_1bank_test.py @@ -16,22 +16,33 @@ class sram_1bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from sram import sram - + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=16, + num_banks=1) + debug.info(1, "Single bank, no column mux with control logic") - a = sram(word_size=4, num_words=16, num_banks=1, name="sram1") + a = sram(c, name="sram1") self.local_check(a, final_verification=True) - # debug.info(1, "Single bank two way column mux with control logic") - # a = sram(word_size=4, num_words=32, num_banks=1, name="sram2") - # self.local_check(a, final_verification=True) + c.num_words=32 + c.words_per_row=2 + debug.info(1, "Single bank two way column mux with control logic") + a = sram(c, name="sram2") + self.local_check(a, final_verification=True) - # debug.info(1, "Single bank, four way column mux with control logic") - # a = sram(word_size=4, num_words=64, num_banks=1, name="sram3") - # self.local_check(a, final_verification=True) + c.num_words=64 + c.words_per_row=4 + debug.info(1, "Single bank, four way column mux with control logic") + a = sram(c, name="sram3") + self.local_check(a, final_verification=True) - # debug.info(1, "Single bank, eight way column mux with control logic") - # a = sram(word_size=2, num_words=128, num_banks=1, name="sram4") - # self.local_check(a, final_verification=True) + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + debug.info(1, "Single bank, eight way column mux with control logic") + a = sram(c, name="sram4") + self.local_check(a, final_verification=True) globals.end_openram() diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index 00be14df..fd701860 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -17,21 +17,32 @@ class sram_2bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from sram import sram + from sram_config import sram_config + c = sram_config(word_size=16, + num_words=32, + num_banks=2) debug.info(1, "Two bank, no column mux with control logic") - a = sram(word_size=16, num_words=32, num_banks=2, name="sram1") + a = sram(c, name="sram1") self.local_check(a, final_verification=True) + c.num_words=64 + c.words_per_row=2 debug.info(1, "Two bank two way column mux with control logic") - a = sram(word_size=16, num_words=64, num_banks=2, name="sram2") + a = sram(c, name="sram2") self.local_check(a, final_verification=True) + c.num_words=128 + c.words_per_row=4 debug.info(1, "Two bank, four way column mux with control logic") - a = sram(word_size=16, num_words=128, num_banks=2, name="sram3") + a = sram(c, name="sram3") self.local_check(a, final_verification=True) + c.word_size=2 + c.num_words=256 + c.words_per_row=8 debug.info(1, "Two bank, eight way column mux with control logic") - a = sram(word_size=2, num_words=256, num_banks=2, name="sram4") + a = sram(c, name="sram4") self.local_check(a, final_verification=True) globals.end_openram() diff --git a/compiler/tests/20_sram_4bank_test.py b/compiler/tests/20_sram_4bank_test.py index e4a28090..19937d04 100755 --- a/compiler/tests/20_sram_4bank_test.py +++ b/compiler/tests/20_sram_4bank_test.py @@ -17,19 +17,30 @@ class sram_4bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from sram import sram + from sram_config import sram_config + c = sram_config(word_size=16, + num_words=64, + num_banks=4) debug.info(1, "Four bank, no column mux with control logic") - a = sram(word_size=16, num_words=64, num_banks=4, name="sram1") + a = sram(c, name="sram1") self.local_check(a, final_verification=True) + c.num_words=128 + c.words_per_row=2 debug.info(1, "Four bank two way column mux with control logic") - a = sram(word_size=16, num_words=128, num_banks=4, name="sram2") + a = sram(c, name="sram2") self.local_check(a, final_verification=True) + c.num_words=256 + c.words_per_row=4 debug.info(1, "Four bank, four way column mux with control logic") a = sram(word_size=16, num_words=256, num_banks=4, name="sram3") self.local_check(a, final_verification=True) + c.word_size=2 + c.num_words=256 + c.words_per_row=8 debug.info(1, "Four bank, eight way column mux with control logic") a = sram.sram(word_size=2, num_words=256, num_banks=4, name="sram4") self.local_check(a, final_verification=True) diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index 5d1d641c..cec8b85a 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -16,15 +16,16 @@ class lib_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import sram from characterizer import lib + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) + + debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) - debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") - s = sram.sram(word_size=2, - num_words=16, - num_banks=1, - name="sram_2_16_1_{0}".format(OPTS.tech_name)) - tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index 35de23aa..37974bf7 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -26,13 +26,14 @@ class lib_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) - debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") - s = sram.sram(word_size=2, - num_words=16, - num_banks=1, - name="sram_2_16_1_{0}".format(OPTS.tech_name)) + debug.info(1, "Testing pruned timing for sample 2 bit, 16 words SRAM with 1 bank") + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index 8dc5c7a0..5e6aa060 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -26,13 +26,14 @@ class lib_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") - s = sram.sram(word_size=2, - num_words=16, - num_banks=1, - name="sram_2_16_1_{0}".format(OPTS.tech_name)) + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/24_lef_sram_test.py b/compiler/tests/24_lef_sram_test.py index 99f13d2d..b2566b84 100755 --- a/compiler/tests/24_lef_sram_test.py +++ b/compiler/tests/24_lef_sram_test.py @@ -17,13 +17,14 @@ class lef_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import sram + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank") - s = sram.sram(word_size=2, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name="sram_2_16_1_{0}".format(OPTS.tech_name)) + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) gdsfile = s.name + ".gds" leffile = s.name + ".lef" diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index fe0be3d9..9412e8e8 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -16,13 +16,14 @@ class verilog_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import sram + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank") - s = sram.sram(word_size=2, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name="sram_2_16_1_{0}".format(OPTS.tech_name)) + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) vfile = s.name + ".v" vname = OPTS.openram_temp + vfile From 9d8d2b65e44b09499faf9853ba204901ef5250c2 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 31 Aug 2018 13:01:17 -0700 Subject: [PATCH 10/31] Fix delay test with new sram_config. Merge dev changes. --- compiler/tests/21_hspice_delay_test.py | 14 +++++++------- compiler/tests/21_ngspice_delay_test.py | 13 +++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index dd2d14de..cfcf3173 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -27,14 +27,14 @@ class timing_sram_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram - import tech - debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") - s = sram.sram(word_size=OPTS.word_size, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name="sram1") + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=1, + num_words=16, + num_banks=1) + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") + s = sram(c, name="sram1") tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index cd41c798..02557d96 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -27,13 +27,14 @@ class timing_sram_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram - import tech + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=1, + num_words=16, + num_banks=1) + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") - s = sram.sram(word_size=OPTS.word_size, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name="sram1") + s = sram(c, name="sram1") tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) From f3cca7eea0e2f2e209e590f147343524dcf2d8f4 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Fri, 31 Aug 2018 23:28:06 -0700 Subject: [PATCH 11/31] Altering sense amp array and write driver array so spacing between amps/drivers accomodates multiport. Also altering sense amp array and write driver array tests to include multiport cases. --- compiler/modules/sense_amp_array.py | 15 +++++++++++++-- compiler/modules/write_driver_array.py | 19 +++++++++++++++++-- compiler/tests/09_sense_amp_array_test.py | 14 ++++++++++++++ compiler/tests/10_write_driver_array_test.py | 17 ++++++++++++++++- 4 files changed, 60 insertions(+), 5 deletions(-) mode change 100755 => 100644 compiler/tests/09_sense_amp_array_test.py mode change 100755 => 100644 compiler/tests/10_write_driver_array_test.py diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 47bcf023..a02ffd9d 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -30,7 +30,11 @@ class sense_amp_array(design.design): def create_layout(self): self.height = self.amp.height - self.width = self.amp.width * self.word_size * self.words_per_row + + if self.bitcell.width > self.amp.width: + self.width = self.bitcell.width * self.word_size * self.words_per_row + else: + self.width = self.amp.width * self.word_size * self.words_per_row self.place_sense_amp_array() self.add_layout_pins() @@ -53,6 +57,10 @@ class sense_amp_array(design.design): self.amp = self.mod_sense_amp("sense_amp") self.add_mod(self.amp) + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() + self.add_mod(self.bitcell) def create_sense_amp_array(self): self.local_insts = [] @@ -68,7 +76,10 @@ class sense_amp_array(design.design): def place_sense_amp_array(self): - amp_spacing = self.amp.width * self.words_per_row + 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) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 965f1735..88d40c86 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -30,7 +30,12 @@ class write_driver_array(design.design): self.create_write_array() def create_layout(self): - self.width = self.columns * self.driver.width + + if self.bitcell.width > self.driver.width: + self.width = self.columns * self.bitcell.width + else: + self.width = self.columns * self.driver.width + self.height = self.driver.height self.place_write_array() @@ -53,6 +58,11 @@ class write_driver_array(design.design): self.mod_write_driver = getattr(c, OPTS.write_driver) self.driver = self.mod_write_driver("write_driver") self.add_mod(self.driver) + + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() + self.add_mod(self.bitcell) def create_write_array(self): self.driver_insts = {} @@ -69,9 +79,14 @@ class write_driver_array(design.design): def place_write_array(self): + if self.bitcell.width > self.driver.width: + driver_spacing = self.bitcell.width + else: + 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.width,0) + base = vector(i * driver_spacing,0) self.driver_insts[index].place(base) diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py old mode 100755 new mode 100644 index 51620495..ca07f884 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -17,7 +17,21 @@ class sense_amp_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import sense_amp_array + # check sense amp array in single port + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") + a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2) + self.local_check(a) + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4") + a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4) + self.local_check(a) + + # check sense amp array in multi-port + OPTS.bitcell = "pbitcell" + OPTS.rw_ports = 1 + OPTS.w_ports = 1 + OPTS.r_ports = 1 + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2) self.local_check(a) diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py old mode 100755 new mode 100644 index 27538d0b..f4a484cc --- a/compiler/tests/10_write_driver_array_test.py +++ b/compiler/tests/10_write_driver_array_test.py @@ -17,6 +17,7 @@ class write_driver_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import write_driver_array + # check write driver array in single port debug.info(2, "Testing write_driver_array for columns=8, word_size=8") a = write_driver_array.write_driver_array(columns=8, word_size=8) self.local_check(a) @@ -25,7 +26,21 @@ class write_driver_test(openram_test): a = write_driver_array.write_driver_array(columns=16, word_size=8) self.local_check(a) - globals.end_openram() + # check write driver array in multi-port + OPTS.bitcell = "pbitcell" + OPTS.rw_ports = 1 + OPTS.w_ports = 1 + OPTS.r_ports = 1 + + debug.info(2, "Testing write_driver_array for columns=8, word_size=8") + a = write_driver_array.write_driver_array(columns=8, word_size=8) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=8") + a = write_driver_array.write_driver_array(columns=16, word_size=8) + self.local_check(a) + + #globals.end_openram() # instantiate a copy of the class to actually run the test if __name__ == "__main__": From d3441c7ba4373193bf4608b5b6805fcf8941bea9 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Mon, 3 Sep 2018 17:31:12 -0700 Subject: [PATCH 12/31] Altering wordline driver to size for any bitcell. Editting multi-port test cases for sense amp array, write driver array, and wordline driver to least number of ports as a better test of spacing betwwen amps/drivers --- compiler/modules/wordline_driver.py | 20 +++++++++++++++----- compiler/tests/08_wordline_driver_test.py | 11 +++++++++++ compiler/tests/09_sense_amp_array_test.py | 8 ++++---- compiler/tests/10_write_driver_array_test.py | 10 +++++----- 4 files changed, 35 insertions(+), 14 deletions(-) mode change 100755 => 100644 compiler/tests/08_wordline_driver_test.py diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 709e3e45..87b0430e 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -58,6 +58,12 @@ class wordline_driver(design.design): self.nand2 = pnand2() self.add_mod(self.nand2) + + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() + self.add_mod(self.bitcell) def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ @@ -125,16 +131,20 @@ class wordline_driver(design.design): nand2_xoffset = inv1_xoffset + self.inv.width inv2_xoffset = nand2_xoffset + self.nand2.width - self.width = inv2_xoffset + self.inv.width - self.height = self.inv.height * self.rows - + self.width = inv2_xoffset + self.inv.height + if self.bitcell.height > self.inv.height: + self.height = self.bitcell.height * self.rows + driver_height = self.bitcell.height + else: + self.height = self.inv.height * self.rows + driver_height = self.inv.height for row in range(self.rows): if (row % 2): - y_offset = self.inv.height*(row + 1) + y_offset = driver_height*(row + 1) inst_mirror = "MX" else: - y_offset = self.inv.height*row + y_offset = driver_height*row inst_mirror = "R0" inv1_offset = [inv1_xoffset, y_offset] diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/08_wordline_driver_test.py old mode 100755 new mode 100644 index 9c91c7dd..1e0ae018 --- a/compiler/tests/08_wordline_driver_test.py +++ b/compiler/tests/08_wordline_driver_test.py @@ -20,10 +20,21 @@ class wordline_driver_test(openram_test): import wordline_driver import tech + # check wordline driver array in single port debug.info(2, "Checking driver") tx = wordline_driver.wordline_driver(rows=8) self.local_check(tx) + # check wordline driver array in multi-port + OPTS.bitcell = "pbitcell" + OPTS.rw_ports = 1 + OPTS.w_ports = 0 + OPTS.r_ports = 0 + + debug.info(2, "Checking driver (multi-port case)") + tx = wordline_driver.wordline_driver(rows=8) + self.local_check(tx) + globals.end_openram() # instantiate a copy of the class to actually run the test diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index ca07f884..a85a1ea3 100644 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -29,14 +29,14 @@ class sense_amp_test(openram_test): # check sense amp array in multi-port OPTS.bitcell = "pbitcell" OPTS.rw_ports = 1 - OPTS.w_ports = 1 - OPTS.r_ports = 1 + OPTS.w_ports = 0 + OPTS.r_ports = 0 - debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)") a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2) self.local_check(a) - debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4") + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)") a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4) self.local_check(a) diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py index f4a484cc..9c68cf5f 100644 --- a/compiler/tests/10_write_driver_array_test.py +++ b/compiler/tests/10_write_driver_array_test.py @@ -29,18 +29,18 @@ class write_driver_test(openram_test): # check write driver array in multi-port OPTS.bitcell = "pbitcell" OPTS.rw_ports = 1 - OPTS.w_ports = 1 - OPTS.r_ports = 1 + OPTS.w_ports = 0 + OPTS.r_ports = 0 - debug.info(2, "Testing write_driver_array for columns=8, word_size=8") + debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)") a = write_driver_array.write_driver_array(columns=8, word_size=8) self.local_check(a) - debug.info(2, "Testing write_driver_array for columns=16, word_size=8") + debug.info(2, "Testing write_driver_array for columns=16, word_size=8 (multi-port case)") a = write_driver_array.write_driver_array(columns=16, word_size=8) self.local_check(a) - #globals.end_openram() + globals.end_openram() # instantiate a copy of the class to actually run the test if __name__ == "__main__": From 1e5924d1b7cfe04b115425e0530215ddf4284c17 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Mon, 3 Sep 2018 17:35:00 -0700 Subject: [PATCH 13/31] Adding multiported bank_sel pins --- compiler/modules/bank.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 9e711d22..1bc78fd6 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -83,7 +83,8 @@ class bank(design.design): # For more than one bank, we have a bank select and name # the signals gated_*. if self.num_banks > 1: - self.add_pin("bank_sel","INPUT") + for port in range(self.total_ports): + self.add_pin("bank_sel{}".format(port),"INPUT") for k in range(self.total_read): self.add_pin("s_en{0}".format(k), "INPUT") for k in range(self.total_write): @@ -514,13 +515,13 @@ class bank(design.design): return self.bank_select_inst = [] - for k in range(self.total_ports): - self.bank_select_inst.append(self.add_inst(name="bank_select", + for port in range(self.total_ports): + self.bank_select_inst.append(self.add_inst(name="bank_select{}".format(port), mod=self.bank_select)) temp = [] temp.extend(self.input_control_signals) - temp.append("bank_sel") + temp.append("bank_sel{}".format(port)) temp.extend(self.control_signals) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) From 341a3ee68d7d95738878740f3f0e080d41030edc Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Mon, 3 Sep 2018 17:44:32 -0700 Subject: [PATCH 14/31] Adding multiport pin names to sram_base for netlist only use --- compiler/sram_base.py | 75 +++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/compiler/sram_base.py b/compiler/sram_base.py index da6403db..ae4f1abe 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -32,6 +32,10 @@ class sram_base(design): self.num_words = num_words self.num_banks = num_banks + self.total_write = OPTS.rw_ports + OPTS.w_ports + self.total_read = OPTS.rw_ports + OPTS.r_ports + self.total_ports = OPTS.rw_ports + OPTS.w_ports + OPTS.r_ports + self.bank_insts = [] def compute_sizes(self): @@ -97,11 +101,14 @@ class sram_base(design): def add_pins(self): """ Add pins for entire SRAM. """ - - for i in range(self.word_size): - self.add_pin("DIN[{0}]".format(i),"INPUT") - for i in range(self.addr_size): - self.add_pin("ADDR[{0}]".format(i),"INPUT") + + for port in range(self.total_write): + for bit in range(self.word_size): + self.add_pin("DIN{0}[{1}]".format(port,bit),"INPUT") + + for port in range(self.total_ports): + for bit in range(self.addr_size): + self.add_pin("ADDR{0}[{1}]".format(port,bit),"INPUT") # These are used to create the physical pins too self.control_logic_inputs=self.control_logic.get_inputs() @@ -109,8 +116,9 @@ class sram_base(design): self.add_pin_list(self.control_logic_inputs,"INPUT") - for i in range(self.word_size): - self.add_pin("DOUT[{0}]".format(i),"OUTPUT") + for port in range(self.total_read): + for bit in range(self.word_size): + self.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT") self.add_pin("vdd","POWER") self.add_pin("gnd","GROUND") @@ -279,16 +287,16 @@ class sram_base(design): # 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 = dff_array(name="row_addr_dff", rows=self.row_addr_size*self.total_ports, 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 = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size*self.total_ports) 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 = dff_array(name="data_dff", rows=1, columns=self.word_size*self.total_ports) self.add_mod(self.data_dff) # Create the bank module (up to four are instantiated) @@ -312,20 +320,28 @@ class sram_base(design): def create_bank(self,bank_num): - """ Create a bank """ + """ Create a bank """ self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num), mod=self.bank)) temp = [] - for i in range(self.word_size): - temp.append("DOUT[{0}]".format(i)) - for i in range(self.word_size): - temp.append("BANK_DIN[{0}]".format(i)) - for i in range(self.bank_addr_size): - temp.append("A[{0}]".format(i)) + for port in range(self.total_read): + for bit in range(self.word_size): + temp.append("DOUT{0}[{1}]".format(port,bit)) + for port in range(self.total_write): + for bit in range(self.word_size): + temp.append("BANK_DIN{0}[{1}]".format(port,bit)) + for port in range(self.total_ports): + for bit in range(self.bank_addr_size): + temp.append("A{0}[{1}]".format(port,bit)) if(self.num_banks > 1): - temp.append("bank_sel[{0}]".format(bank_num)) - temp.extend(["s_en0", "w_en0", "clk_buf_bar","clk_buf" , "vdd", "gnd"]) + for port in range(self.total_ports): + temp.append("bank_sel{0}[{1}]".format(port,bank_num)) + for port in range(self.total_read): + temp.append("s_en{0}".format(port)) + for port in range(self.total_write): + temp.append("w_en{0}".format(port)) + temp.extend(["clk_buf_bar","clk_buf" , "vdd", "gnd"]) self.connect_inst(temp) return self.bank_insts[-1] @@ -370,9 +386,10 @@ class sram_base(design): # inputs, outputs/output/bar inputs = [] outputs = [] - for i in range(self.row_addr_size): - inputs.append("ADDR[{}]".format(i+self.col_addr_size)) - outputs.append("A[{}]".format(i+self.col_addr_size)) + for k in range(self.total_ports): + for i in range(self.row_addr_size): + inputs.append("ADDR{}[{}]".format(k,i+self.col_addr_size)) + outputs.append("A{}[{}]".format(k,i+self.col_addr_size)) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) return inst @@ -385,9 +402,10 @@ class sram_base(design): # inputs, outputs/output/bar inputs = [] outputs = [] - for i in range(self.col_addr_size): - inputs.append("ADDR[{}]".format(i)) - outputs.append("A[{}]".format(i)) + for k in range(self.total_ports): + for i in range(self.col_addr_size): + inputs.append("ADDR{}[{}]".format(k,i)) + outputs.append("A{}[{}]".format(k,i)) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) return inst @@ -400,9 +418,10 @@ class sram_base(design): # inputs, outputs/output/bar inputs = [] outputs = [] - for i in range(self.word_size): - inputs.append("DIN[{}]".format(i)) - outputs.append("BANK_DIN[{}]".format(i)) + for k in range(self.total_write): + for i in range(self.word_size): + inputs.append("DIN{}[{}]".format(k,i)) + outputs.append("BANK_DIN{}[{}]".format(k,i)) self.connect_inst(inputs + outputs + ["clk_buf", "vdd", "gnd"]) return inst From 774c14ad750fbbdb3b32be19a9a565a610a1dbe0 Mon Sep 17 00:00:00 2001 From: Michael Timothy Grimes Date: Mon, 3 Sep 2018 17:47:29 -0700 Subject: [PATCH 15/31] changing 19_psingle_bank_test to test layout for a single bank using pbitcell with 1 RW port (equivalent to using 6T cell) --- compiler/tests/19_psingle_bank_test.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) mode change 100755 => 100644 compiler/tests/19_psingle_bank_test.py diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py old mode 100755 new mode 100644 index fad2b100..45745b2f --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -22,14 +22,29 @@ class psingle_bank_test(openram_test): from bank import bank OPTS.bitcell = "pbitcell" + # testing layout of bank using pbitcell with 1 RW port (a 6T-cell equivalent) + OPTS.rw_ports = 1 + OPTS.w_ports = 0 + OPTS.r_ports = 0 + + debug.info(1, "No column mux") + a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_1rw_0w_0r_single") + self.local_check(a) + + """ # testing all port configurations (with no column mux) to verify layout between bitcell array and peripheral circuitry OPTS.rw_ports = 2 OPTS.w_ports = 2 OPTS.r_ports = 2 - + + # multiport can't generate layout yet on the bank level + OPTS.netlist_only = True + debug.info(1, "No column mux") a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_2rw_2w_2r_single") self.local_check(a) + """ + """ OPTS.rw_ports = 0 OPTS.w_ports = 2 From a346bddd880ae172a642a4aa2f9b66a40312743c Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 10:47:24 -0700 Subject: [PATCH 16/31] Cleanup some items with new sram_config. Update unit tests accordingly. --- compiler/globals.py | 52 ++++++++---- compiler/modules/bank.py | 4 + compiler/modules/hierarchical_decoder.py | 2 +- compiler/openram.py | 9 +- compiler/options.py | 8 +- compiler/pgates/pbitcell.py | 18 ++-- compiler/sram.py | 9 +- compiler/sram_1bank.py | 5 +- compiler/sram_2bank.py | 4 +- compiler/sram_4bank.py | 4 +- compiler/sram_base.py | 83 ++----------------- compiler/sram_config.py | 100 ++++++++++++++++++----- compiler/tests/04_pbitcell_test.py | 53 +++++++++--- compiler/tests/19_psingle_bank_test.py | 1 - compiler/tests/20_sram_1bank_test.py | 9 +- compiler/tests/20_sram_2bank_test.py | 9 +- compiler/tests/20_sram_4bank_test.py | 8 +- compiler/tests/21_hspice_delay_test.py | 2 +- compiler/tests/21_ngspice_delay_test.py | 2 +- compiler/tests/23_lib_sram_model_test.py | 3 +- compiler/tests/23_lib_sram_prune_test.py | 2 +- compiler/tests/23_lib_sram_test.py | 2 +- compiler/tests/24_lef_sram_test.py | 2 +- compiler/tests/25_verilog_sram_test.py | 2 +- 24 files changed, 222 insertions(+), 171 deletions(-) diff --git a/compiler/globals.py b/compiler/globals.py index 991ec1ad..3bdc5f92 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -116,6 +116,11 @@ def init_openram(config_file, is_unit_test=True): import_tech() + init_paths() + + # This depends on the tech, so do it after tech is loaded + init_config() + # Reset the static duplicate name checker for unit tests. import hierarchy_design hierarchy_design.hierarchy_design.name_map=[] @@ -229,15 +234,6 @@ def read_config(config_file, is_unit_test=True): OPTS.num_r_ports, OPTS.tech_name) - # Don't delete the output dir, it may have other files! - # make the directory if it doesn't exist - try: - os.makedirs(OPTS.output_path, 0o750) - except OSError as e: - if e.errno == 17: # errno.EEXIST - os.chmod(OPTS.output_path, 0o750) - except: - debug.error("Unable to make output directory.",-1) def end_openram(): @@ -299,12 +295,6 @@ def setup_paths(): cleanup_paths() - # make the directory if it doesn't exist - try: - os.makedirs(OPTS.openram_temp, 0o750) - except OSError as e: - if e.errno == 17: # errno.EEXIST - os.chmod(OPTS.openram_temp, 0o750) def is_exe(fpath): @@ -320,7 +310,37 @@ def find_exe(check_exe): if is_exe(exe): return exe return None - + +def init_paths(): + """ Create the temp and output directory if it doesn't exist """ + + # make the directory if it doesn't exist + try: + os.makedirs(OPTS.openram_temp, 0o750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(OPTS.openram_temp, 0o750) + + # Don't delete the output dir, it may have other files! + # make the directory if it doesn't exist + try: + os.makedirs(OPTS.output_path, 0o750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(OPTS.output_path, 0o750) + except: + debug.error("Unable to make output directory.",-1) + +def init_config(): + """ Initialize the SRAM configurations. """ + # Create the SRAM configuration + from sram_config import sram_config + OPTS.sram_config = sram_config(OPTS.word_size, + OPTS.num_words, + OPTS.num_banks) + + + # imports correct technology directories for testing def import_tech(): global OPTS diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index f39fb424..3af935a3 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -29,6 +29,10 @@ class bank(design.design): design.design.__init__(self, name) debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words)) + + self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports + self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports + self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports # The local control signals are gated when we have bank select logic, # so this prefix will be added to all of the input signals to create diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 38136b62..7c90b1f1 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -130,7 +130,7 @@ class hierarchical_decoder(design.design): self.total_number_of_predecoder_outputs = 4*self.no_of_pre2x4 + 8*self.no_of_pre3x8 else: self.total_number_of_predecoder_outputs = 0 - debug.error("Not enough rows for a hierarchical decoder. Non-hierarchical not supported yet.",-1) + debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),-1) # Calculates height and width of pre-decoder, if self.no_of_pre3x8 > 0: diff --git a/compiler/openram.py b/compiler/openram.py index a6db154d..e4ab593e 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -52,14 +52,13 @@ print(*output_files,sep="\n") start_time = datetime.datetime.now() print_time("Start",start_time) +# Configure the SRAM organization c = sram_config(word_size=OPTS.word_size, - num_words=OPTS.num_words, - num_rw_ports=OPTS.num_rw_ports, - num_w_ports=OPTS.num_w_ports, - num_r_ports=OPTS.num_r_ports) + num_words=OPTS.num_words) # import SRAM test generation -s = sram(c, OPTS.output_name) +s = sram(sram_config=c, + name=OPTS.output_name) # Output the files for the resulting SRAM s.save() diff --git a/compiler/options.py b/compiler/options.py index a81f7fae..5298c00f 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -55,11 +55,17 @@ class options(optparse.Values): num_rw_ports = 1 num_r_ports = 0 num_w_ports = 0 + # These will get initialized by the the file supply_voltages = "" temperatures = "" process_corners = "" - + + # These are the main configuration parameters that should be over-ridden + # in a configuration file. + #num_words = 0 + #num_banks = 1 + #word_size = 0 # These are the default modules that can be over-riden decoder = "hierarchical_decoder" diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 0b2ec27d..3ac0fa74 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -16,18 +16,18 @@ class pbitcell(pgate.pgate): width = None height = None - def __init__(self, num_rw_ports=OPTS.num_rw_ports, num_w_ports=OPTS.num_w_ports, num_r_ports=OPTS.num_r_ports): + def __init__(self): - name = "pbitcell_{0}RW_{1}W_{2}R".format(num_rw_ports, num_w_ports, num_r_ports) + self.num_rw_ports = OPTS.num_rw_ports + self.num_w_ports = OPTS.num_w_ports + self.num_r_ports = OPTS.num_r_ports + + name = "pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports) pgate.pgate.__init__(self, name) - debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(num_rw_ports, - num_w_ports, - num_r_ports)) + debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, + self.num_w_ports, + self.num_r_ports)) - self.num_rw_ports = num_rw_ports - self.num_w_ports = num_w_ports - self.num_r_ports = num_r_ports - self.create_netlist() if not OPTS.netlist_only: self.create_layout() diff --git a/compiler/sram.py b/compiler/sram.py index 3891150b..0feea1b3 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -12,8 +12,9 @@ class sram(): results. We can later add visualizer and other high-level functions as needed. """ - def __init__(self, sram_config, name="sram"): + def __init__(self, sram_config, name): + sram_config.compute_sizes() sram_config.set_local_config(self) # reset the static duplicate name checker for unit tests @@ -27,6 +28,8 @@ class sram(): start_time = datetime.datetime.now() self.name = name + + if self.num_banks == 1: from sram_1bank import sram_1bank as sram elif self.num_banks == 2: @@ -35,8 +38,8 @@ class sram(): from sram_4bank import sram_4bank as sram else: debug.error("Invalid number of banks.",-1) - - self.s = sram(sram_config, name) + + self.s = sram(name, sram_config) self.s.create_netlist() if not OPTS.netlist_only: self.s.create_layout() diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 20dab3c7..e27eb2b3 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -18,12 +18,11 @@ class sram_1bank(sram_base): """ Procedures specific to a one bank SRAM. """ - def __init__(self, sram_config, name): - sram_base.__init__(self, sram_config, name) + def __init__(self, name, sram_config): + sram_base.__init__(self, name, sram_config) def create_netlist(self): - self.compute_sizes() sram_base.create_netlist(self) self.create_modules() diff --git a/compiler/sram_2bank.py b/compiler/sram_2bank.py index ff0d7868..daf02563 100644 --- a/compiler/sram_2bank.py +++ b/compiler/sram_2bank.py @@ -16,8 +16,8 @@ class sram_2bank(sram_base): """ Procedures specific to a two bank SRAM. """ - def __init__(self, sram_config, name): - sram_base.__init__(self, sram_config, name) + def __init__(self, name, sram_config): + sram_base.__init__(self, name, sram_config) def compute_bank_offsets(self): """ Compute the overall offsets for a two bank SRAM """ diff --git a/compiler/sram_4bank.py b/compiler/sram_4bank.py index 64163e9f..44b8ba87 100644 --- a/compiler/sram_4bank.py +++ b/compiler/sram_4bank.py @@ -16,8 +16,8 @@ class sram_4bank(sram_base): """ Procedures specific to a four bank SRAM. """ - def __init__(self, sram_config, name): - sram_base.__init__(self, sram_config, name) + def __init__(self, name, sram_config): + sram_base.__init__(self, name, sram_config) def compute_bank_offsets(self): """ Compute the overall offsets for a four bank SRAM """ diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 7ead2e48..af21d704 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -3,7 +3,6 @@ import datetime import getpass import debug from importlib import reload -from math import log,sqrt,ceil from vector import vector from globals import OPTS, print_time @@ -14,85 +13,14 @@ class sram_base(design): Dynamically generated SRAM by connecting banks to control logic. The number of banks should be 1 , 2 or 4 """ - def __init__(self, sram_config, name): + def __init__(self, name, sram_config): design.__init__(self, name) - - # This is used to compute the sizes of the SRAM - # and must be loaded before netlist creation - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell = self.mod_bitcell() - - self.sram_config = sram_config - self.sram_config.set_local_config(self) + self.sram_config = sram_config + sram_config.set_local_config(self) + self.bank_insts = [] - def compute_sizes(self): - """ Computes the organization of the memory using bitcell size by trying to make it square.""" - - debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.") - - self.num_words_per_bank = self.num_words/self.num_banks - self.num_bits_per_bank = self.word_size*self.num_words_per_bank - - # If this was hard coded, don't dynamically compute it! - if self.sram_config.words_per_row: - self.words_per_row = self.sram_config.words_per_row - else: - # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) - self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank - self.bank_side_length = sqrt(self.bank_area) - - # Estimate the words per row given the height of the bitcell and the square side length - self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width) - self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size) - - # Estimate the number of rows given the tentative words per row - self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) - self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) - - # Fix the number of columns and rows - self.num_cols = int(self.words_per_row*self.word_size) - self.num_rows = int(self.num_words_per_bank/self.words_per_row) - - # Compute the address and bank sizes - self.row_addr_size = int(log(self.num_rows, 2)) - self.col_addr_size = int(log(self.words_per_row, 2)) - 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)) - - self.sram_config.words_per_row = self.words_per_row - debug.info(1,"Words per row: {}".format(self.words_per_row)) - - def estimate_words_per_row(self,tentative_num_cols, word_size): - """ - This provides a heuristic rounded estimate for the number of words - per row. - """ - - if tentative_num_cols < 1.5*word_size: - return 1 - elif tentative_num_cols > 3*word_size: - return 4 - else: - return 2 - - def amend_words_per_row(self,tentative_num_rows, words_per_row): - """ - This picks the number of words per row more accurately by limiting - it to a minimum and maximum. - """ - # Recompute the words per row given a hard max - if(tentative_num_rows > 512): - debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048") - return int(words_per_row*tentative_num_rows/512) - # Recompute the words per row given a hard min - if(tentative_num_rows < 16): - debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows)) - return int(words_per_row*tentative_num_rows/16) - - return words_per_row def add_pins(self): """ Add pins for entire SRAM. """ @@ -270,6 +198,9 @@ class sram_base(design): def add_modules(self): """ Create all the modules that will be used """ + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() c = reload(__import__(OPTS.control_logic)) self.mod_control_logic = getattr(c, OPTS.control_logic) diff --git a/compiler/sram_config.py b/compiler/sram_config.py index e7c58905..e7c80fd8 100644 --- a/compiler/sram_config.py +++ b/compiler/sram_config.py @@ -1,36 +1,96 @@ +import debug +from math import log,sqrt,ceil +from importlib import reload from globals import OPTS class sram_config: """ This is a structure that is used to hold the SRAM configuration options. """ - def __init__(self, word_size, num_words, num_banks=1, num_rw_ports=OPTS.num_rw_ports, num_w_ports=OPTS.num_w_ports, num_r_ports=OPTS.num_r_ports): + def __init__(self, word_size, num_words, num_banks=1): self.word_size = word_size self.num_words = num_words self.num_banks = num_banks - self.num_rw_ports = num_rw_ports - self.num_w_ports = num_w_ports - self.num_r_ports = num_r_ports # This will get over-written when we determine the organization - self.num_banks = 1 self.words_per_row = None - - self.total_write = num_rw_ports + num_w_ports - self.total_read = num_rw_ports + num_r_ports - self.total_ports = num_rw_ports + num_w_ports + num_r_ports + # Move the module names to this? + def set_local_config(self, module): - module.word_size = self.word_size - module.num_words = self.num_words - module.num_banks = self.num_banks - module.num_rw_ports = self.num_rw_ports - module.num_w_ports = self.num_w_ports - module.num_r_ports = self.num_r_ports + """ Copy all of the member variables to the given module for convenience """ - module.words_per_row = self.words_per_row - - module.total_write = self.total_write - module.total_read = self.total_read - module.total_ports = self.total_ports + members = [attr for attr in dir(self) if not callable(getattr(self, attr)) and not attr.startswith("__")] + # Copy all the variables to the local module + for member in members: + setattr(module,member,getattr(self,member)) + + def compute_sizes(self): + """ Computes the organization of the memory using bitcell size by trying to make it square.""" + + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + # pass a copy of myself for the port numbers + self.bitcell = self.mod_bitcell() + + + debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.") + + self.num_words_per_bank = self.num_words/self.num_banks + self.num_bits_per_bank = self.word_size*self.num_words_per_bank + + # If this was hard coded, don't dynamically compute it! + if not self.words_per_row: + # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) + self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank + self.bank_side_length = sqrt(self.bank_area) + + # Estimate the words per row given the height of the bitcell and the square side length + self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width) + self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size) + + # Estimate the number of rows given the tentative words per row + self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) + self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) + + # Fix the number of columns and rows + self.num_cols = int(self.words_per_row*self.word_size) + self.num_rows = int(self.num_words_per_bank/self.words_per_row) + + # Compute the address and bank sizes + self.row_addr_size = int(log(self.num_rows, 2)) + self.col_addr_size = int(log(self.words_per_row, 2)) + 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)) + + debug.info(1,"Words per row: {}".format(self.words_per_row)) + + def estimate_words_per_row(self,tentative_num_cols, word_size): + """ + This provides a heuristic rounded estimate for the number of words + per row. + """ + + if tentative_num_cols < 1.5*word_size: + return 1 + elif tentative_num_cols > 3*word_size: + return 4 + else: + return 2 + + def amend_words_per_row(self,tentative_num_rows, words_per_row): + """ + This picks the number of words per row more accurately by limiting + it to a minimum and maximum. + """ + # Recompute the words per row given a hard max + if(not OPTS.is_unit_test and tentative_num_rows > 512): + debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048") + return int(words_per_row*tentative_num_rows/512) + # Recompute the words per row given a hard min + if(not OPTS.is_unit_test and tentative_num_rows < 16): + debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows)) + return int(words_per_row*tentative_num_rows/16) + + return words_per_row diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 070637f9..70f51a99 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -18,47 +18,76 @@ class pbitcell_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - import pbitcell + from pbitcell import pbitcell import tech - + OPTS.sram_config.num_rw_ports=1 + OPTS.sram_config.num_w_ports=1 + OPTS.sram_config.num_r_ports=1 debug.info(2, "Bitcell with 1 of each port: read/write, write, and read") - tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=1,num_r_ports=1) + tx = pbitcell() self.local_check(tx) + OPTS.sram_config.num_rw_ports=0 + OPTS.sram_config.num_w_ports=1 + OPTS.sram_config.num_r_ports=1 debug.info(2, "Bitcell with 0 read/write ports") - tx = pbitcell.pbitcell(num_rw_ports=0,num_w_ports=1,num_r_ports=1) + tx = pbitcell() self.local_check(tx) + OPTS.sram_config.num_rw_ports=1 + OPTS.sram_config.num_w_ports=0 + OPTS.sram_config.num_r_ports=1 debug.info(2, "Bitcell with 0 write ports") - tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=0,num_r_ports=1) + tx = pbitcell() self.local_check(tx) + OPTS.sram_config.num_rw_ports=1 + OPTS.sram_config.num_w_ports=1 + OPTS.sram_config.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports") - tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=1,num_r_ports=0) + tx = pbitcell() self.local_check(tx) + OPTS.sram_config.num_rw_ports=1 + OPTS.sram_config.num_w_ports=0 + OPTS.sram_config.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports and 0 write ports") - tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=0,num_r_ports=0) + tx = pbitcell() self.local_check(tx) + OPTS.sram_config.num_rw_ports=2 + OPTS.sram_config.num_w_ports=2 + OPTS.sram_config.num_r_ports=2 debug.info(2, "Bitcell with 2 of each port: read/write, write, and read") - tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=2,num_r_ports=2) + tx = pbitcell() self.local_check(tx) + OPTS.sram_config.num_rw_ports=0 + OPTS.sram_config.num_w_ports=2 + OPTS.sram_config.num_r_ports=2 debug.info(2, "Bitcell with 0 read/write ports") - tx = pbitcell.pbitcell(num_rw_ports=0,num_w_ports=2,num_r_ports=2) + tx = pbitcell() self.local_check(tx) + OPTS.sram_config.num_rw_ports=2 + OPTS.sram_config.num_w_ports=0 + OPTS.sram_config.num_r_ports=2 debug.info(2, "Bitcell with 0 write ports") - tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=0,num_r_ports=2) + tx = pbitcell() self.local_check(tx) + OPTS.sram_config.num_rw_ports=2 + OPTS.sram_config.num_w_ports=2 + OPTS.sram_config.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports") - tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=2,num_r_ports=0) + tx = pbitcell() self.local_check(tx) + OPTS.sram_config.num_rw_ports=2 + OPTS.sram_config.num_w_ports=0 + OPTS.sram_config.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports and 0 write ports") - tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=0,num_r_ports=0) + tx = pbitcell() self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index b98c2ff1..fd68409a 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -29,7 +29,6 @@ class psingle_bank_test(openram_test): OPTS.num_r_ports = 1 c = sram_config(word_size=4, num_words=16) - c.words_per_row=1 debug.info(1, "No column mux") name = "bank1_{0}rw_{1}w_{2}r_single".format(c.num_rw_ports, c.num_w_ports, c.num_r_ports) diff --git a/compiler/tests/20_sram_1bank_test.py b/compiler/tests/20_sram_1bank_test.py index ad5732a6..ce482b6d 100755 --- a/compiler/tests/20_sram_1bank_test.py +++ b/compiler/tests/20_sram_1bank_test.py @@ -21,27 +21,28 @@ class sram_1bank_test(openram_test): num_words=16, num_banks=1) + c.words_per_row=1 debug.info(1, "Single bank, no column mux with control logic") - a = sram(c, name="sram1") + a = sram(c, "sram1") self.local_check(a, final_verification=True) c.num_words=32 c.words_per_row=2 debug.info(1, "Single bank two way column mux with control logic") - a = sram(c, name="sram2") + a = sram(c, "sram2") self.local_check(a, final_verification=True) c.num_words=64 c.words_per_row=4 debug.info(1, "Single bank, four way column mux with control logic") - a = sram(c, name="sram3") + a = sram(c, "sram3") self.local_check(a, final_verification=True) c.word_size=2 c.num_words=128 c.words_per_row=8 debug.info(1, "Single bank, eight way column mux with control logic") - a = sram(c, name="sram4") + a = sram(c, "sram4") self.local_check(a, final_verification=True) globals.end_openram() diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index fd701860..ff9fbaea 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -22,27 +22,28 @@ class sram_2bank_test(openram_test): num_words=32, num_banks=2) + c.words_per_row=1 debug.info(1, "Two bank, no column mux with control logic") - a = sram(c, name="sram1") + a = sram(c, "sram1") self.local_check(a, final_verification=True) c.num_words=64 c.words_per_row=2 debug.info(1, "Two bank two way column mux with control logic") - a = sram(c, name="sram2") + a = sram(c, "sram2") self.local_check(a, final_verification=True) c.num_words=128 c.words_per_row=4 debug.info(1, "Two bank, four way column mux with control logic") - a = sram(c, name="sram3") + a = sram(c, "sram3") self.local_check(a, final_verification=True) c.word_size=2 c.num_words=256 c.words_per_row=8 debug.info(1, "Two bank, eight way column mux with control logic") - a = sram(c, name="sram4") + a = sram(c, "sram4") self.local_check(a, final_verification=True) globals.end_openram() diff --git a/compiler/tests/20_sram_4bank_test.py b/compiler/tests/20_sram_4bank_test.py index 19937d04..fb34d3b0 100755 --- a/compiler/tests/20_sram_4bank_test.py +++ b/compiler/tests/20_sram_4bank_test.py @@ -23,26 +23,26 @@ class sram_4bank_test(openram_test): num_banks=4) debug.info(1, "Four bank, no column mux with control logic") - a = sram(c, name="sram1") + a = sram(c, "sram1") self.local_check(a, final_verification=True) c.num_words=128 c.words_per_row=2 debug.info(1, "Four bank two way column mux with control logic") - a = sram(c, name="sram2") + a = sram(c, "sram2") self.local_check(a, final_verification=True) c.num_words=256 c.words_per_row=4 debug.info(1, "Four bank, four way column mux with control logic") - a = sram(word_size=16, num_words=256, num_banks=4, name="sram3") + a = sram(c, "sram3") self.local_check(a, final_verification=True) c.word_size=2 c.num_words=256 c.words_per_row=8 debug.info(1, "Four bank, eight way column mux with control logic") - a = sram.sram(word_size=2, num_words=256, num_banks=4, name="sram4") + a = sram.sram(c, "sram4") self.local_check(a, final_verification=True) globals.end_openram() diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index cfcf3173..93fc8413 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -32,7 +32,7 @@ class timing_sram_test(openram_test): c = sram_config(word_size=1, num_words=16, num_banks=1) - + c.words_per_row=1 debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") s = sram(c, name="sram1") diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 02557d96..d54cdf54 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -32,7 +32,7 @@ class timing_sram_test(openram_test): c = sram_config(word_size=1, num_words=16, num_banks=1) - + c.words_per_row=1 debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") s = sram(c, name="sram1") diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index cec8b85a..8d5064d5 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -22,10 +22,9 @@ class lib_test(openram_test): c = sram_config(word_size=2, num_words=16, num_banks=1) - + c.words_per_row=1 debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) - tempspice = OPTS.openram_temp + "temp.sp" s.sp_write(tempspice) diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index 37974bf7..952072aa 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -31,7 +31,7 @@ class lib_test(openram_test): c = sram_config(word_size=2, num_words=16, num_banks=1) - + c.words_per_row=1 debug.info(1, "Testing pruned timing for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index 5e6aa060..1b2d317c 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -31,7 +31,7 @@ class lib_test(openram_test): c = sram_config(word_size=2, num_words=16, num_banks=1) - + c.words_per_row=1 debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) diff --git a/compiler/tests/24_lef_sram_test.py b/compiler/tests/24_lef_sram_test.py index b2566b84..f66104b6 100755 --- a/compiler/tests/24_lef_sram_test.py +++ b/compiler/tests/24_lef_sram_test.py @@ -22,7 +22,7 @@ class lef_test(openram_test): c = sram_config(word_size=2, num_words=16, num_banks=1) - + c.words_per_row=1 debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index 9412e8e8..48ba29e8 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -21,7 +21,7 @@ class verilog_test(openram_test): c = sram_config(word_size=2, num_words=16, num_banks=1) - + c.words_per_row=1 debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank") s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) From de6f22aa3c1f6ea0bb750ccaaa10183d3e393bac Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 10:48:37 -0700 Subject: [PATCH 17/31] Fix unit test permissions --- compiler/tests/08_wordline_driver_test.py | 0 compiler/tests/09_sense_amp_array_test.py | 0 compiler/tests/10_write_driver_array_test.py | 0 compiler/tests/19_psingle_bank_test.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 compiler/tests/08_wordline_driver_test.py mode change 100644 => 100755 compiler/tests/09_sense_amp_array_test.py mode change 100644 => 100755 compiler/tests/10_write_driver_array_test.py mode change 100644 => 100755 compiler/tests/19_psingle_bank_test.py diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/08_wordline_driver_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py old mode 100644 new mode 100755 From 0adfe664296ccf8087689709c8d1291bb5bde62a Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 11:15:18 -0700 Subject: [PATCH 18/31] Add total_ port variables to sram base class. --- compiler/sram_base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 23412f05..ca3351b5 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -19,6 +19,10 @@ class sram_base(design): self.sram_config = sram_config sram_config.set_local_config(self) + self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports + self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports + self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + self.bank_insts = [] From 6963a1092f2074fae0b2b27a7d9e2b109f626ede Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 11:55:22 -0700 Subject: [PATCH 19/31] Make bitcell width/height not static. Update modules to use it for pbitcell. --- compiler/modules/delay_chain.py | 5 ----- compiler/modules/hierarchical_decoder.py | 3 ++- compiler/modules/sense_amp_array.py | 3 ++- compiler/modules/wordline_driver.py | 12 +++++++----- compiler/modules/write_driver_array.py | 5 +++-- compiler/pgates/pbitcell.py | 17 ++++++----------- compiler/pgates/pgate.py | 11 ++++++++++- compiler/pgates/pinv.py | 8 ++------ compiler/pgates/pinvbuf.py | 8 ++------ compiler/pgates/pnand2.py | 9 ++------- compiler/pgates/pnand3.py | 10 ++-------- compiler/pgates/pnor2.py | 9 ++------- compiler/pgates/single_level_column_mux.py | 2 ++ compiler/tests/19_psingle_bank_test.py | 2 +- 14 files changed, 43 insertions(+), 61 deletions(-) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index b8a57f15..90175743 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -53,11 +53,6 @@ class delay_chain(design.design): self.add_pin("gnd") def add_modules(self): - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell = self.mod_bitcell() - self.inv = pinv(route_output=False) self.add_mod(self.inv) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 7c90b1f1..0b44c8fc 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -24,7 +24,8 @@ class hierarchical_decoder(design.design): from importlib import reload c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell_height = self.mod_bitcell.height + b = self.mod_bitcell() + self.bitcell_height = b.height self.NAND_FORMAT = "DEC_NAND[{0}]" self.INV_FORMAT = "DEC_INV_[{0}]" diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index a02ffd9d..c48d280d 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -57,10 +57,11 @@ class sense_amp_array(design.design): self.amp = self.mod_sense_amp("sense_amp") self.add_mod(self.amp) + # This is just used for measurements, + # so don't add the module c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() - self.add_mod(self.bitcell) def create_sense_amp_array(self): self.local_insts = [] diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 87b0430e..3deac4e1 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -50,6 +50,13 @@ class wordline_driver(design.design): def add_modules(self): + # This is just used for measurements, + # so don't add the module + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + self.mod_bitcell = getattr(c, OPTS.bitcell) + self.bitcell = self.mod_bitcell() + self.inv = pinv() self.add_mod(self.inv) @@ -59,11 +66,6 @@ class wordline_driver(design.design): self.nand2 = pnand2() self.add_mod(self.nand2) - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell = self.mod_bitcell() - self.add_mod(self.bitcell) def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 88d40c86..eff0c8d8 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -58,11 +58,12 @@ class write_driver_array(design.design): self.mod_write_driver = getattr(c, OPTS.write_driver) self.driver = self.mod_write_driver("write_driver") self.add_mod(self.driver) - + + # This is just used for measurements, + # so don't add the module c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() - self.add_mod(self.bitcell) def create_write_array(self): self.driver_insts = {} diff --git a/compiler/pgates/pbitcell.py b/compiler/pgates/pbitcell.py index 3ac0fa74..6f579c06 100644 --- a/compiler/pgates/pbitcell.py +++ b/compiler/pgates/pbitcell.py @@ -1,5 +1,4 @@ import contact -import pgate import design import debug from tech import drc, parameter, spice @@ -7,14 +6,11 @@ from vector import vector from ptx import ptx from globals import OPTS -class pbitcell(pgate.pgate): +class pbitcell(design.design): """ This module implements a parametrically sized multi-port bitcell, with a variable number of read/write, write, and read ports """ - - width = None - height = None def __init__(self): @@ -23,18 +19,17 @@ class pbitcell(pgate.pgate): self.num_r_ports = OPTS.num_r_ports name = "pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports) - pgate.pgate.__init__(self, name) + # This is not a pgate because pgates depend on the bitcell height! + design.design.__init__(self, name) debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports)) self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() + # We must always create the bitcell layout because + # some transistor sizes in the other netlists depend on it + self.create_layout() - # FIXME: Why is this static set here? - pbitcell.width = self.width - pbitcell.height = self.height def create_netlist(self): self.add_pins() diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 4e47934d..ec3bed0c 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -11,10 +11,19 @@ class pgate(design.design): This is a module that implements some shared functions for parameterized gates. """ - def __init__(self, name): + def __init__(self, name, height=None): """ Creates a generic cell """ design.design.__init__(self, name) + if height: + self.height = height + elif not height: + from importlib import reload + c = reload(__import__(OPTS.bitcell)) + bitcell = getattr(c, OPTS.bitcell) + b = bitcell() + self.height = b.height + def connect_pin_to_rail(self,inst,pin,supply): """ Conencts a ptx pin to a supply rail. """ diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index e2e87053..f0df378d 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -17,26 +17,22 @@ class pinv(pgate.pgate): from center of rail to rail.. The route_output will route the output to the right side of the cell for easier access. """ - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - bitcell = getattr(c, OPTS.bitcell) unique_id = 1 - def __init__(self, size=1, beta=parameter["beta"], height=bitcell.height, route_output=True): + def __init__(self, size=1, beta=parameter["beta"], height=None, route_output=True): # We need to keep unique names because outputting to GDSII # will use the last record with a given name. I.e., you will # over-write a design in GDS if one has and the other doesn't # have poly connected, for example. name = "pinv_{}".format(pinv.unique_id) pinv.unique_id += 1 - pgate.pgate.__init__(self, name) + pgate.pgate.__init__(self, name, height) debug.info(2, "create pinv structure {0} with size of {1}".format(name, size)) self.nmos_size = size self.pmos_size = beta*size self.beta = beta - self.height = height # Maybe minimize height if not defined in future? self.route_output = False diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index e55fb649..328836dc 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -11,13 +11,9 @@ class pinvbuf(design.design): This is a simple inverter/buffer used for driving loads. It is used in the column decoder for 1:2 decoding and as the clock buffer. """ - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - bitcell = getattr(c, OPTS.bitcell) - unique_id = 1 - def __init__(self, driver_size=4, height=bitcell.height, name=""): + def __init__(self, driver_size=4, height=None, name=""): self.stage_effort = 4 self.row_height = height @@ -32,7 +28,7 @@ class pinvbuf(design.design): name = "pinvbuf_{0}_{1}_{2}".format(self.predriver_size, self.driver_size, pinvbuf.unique_id) pinvbuf.unique_id += 1 - design.design.__init__(self, name) + design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) self.create_netlist() diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 99ae9f02..d38c7de4 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -12,24 +12,19 @@ class pnand2(pgate.pgate): This model use ptx to generate a 2-input nand within a cetrain height. """ - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - bitcell = getattr(c, OPTS.bitcell) - unique_id = 1 - def __init__(self, size=1, height=bitcell.height): + def __init__(self, size=1, height=None): """ Creates a cell for a simple 2 input nand """ name = "pnand2_{0}".format(pnand2.unique_id) pnand2.unique_id += 1 - pgate.pgate.__init__(self, name) + pgate.pgate.__init__(self, name, height) debug.info(2, "create pnand2 structure {0} with size of {1}".format(name, size)) self.nmos_size = 2*size self.pmos_size = parameter["beta"]*size self.nmos_width = self.nmos_size*drc["minwidth_tx"] self.pmos_width = self.pmos_size*drc["minwidth_tx"] - self.height = height # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnand2 is only supported now.") diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 984ee417..b4e11b32 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -12,18 +12,13 @@ class pnand3(pgate.pgate): This model use ptx to generate a 2-input nand within a cetrain height. """ - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - mod_bitcell = getattr(c, OPTS.bitcell) - bitcell = mod_bitcell() - unique_id = 1 - def __init__(self, size=1, height=bitcell.height): + def __init__(self, size=1, height=None): """ Creates a cell for a simple 3 input nand """ name = "pnand3_{0}".format(pnand3.unique_id) pnand3.unique_id += 1 - pgate.pgate.__init__(self, name) + pgate.pgate.__init__(self, name, height) debug.info(2, "create pnand3 structure {0} with size of {1}".format(name, size)) # We have trouble pitch matching a 3x sizes to the bitcell... @@ -32,7 +27,6 @@ class pnand3(pgate.pgate): self.pmos_size = parameter["beta"]*size self.nmos_width = self.nmos_size*drc["minwidth_tx"] self.pmos_width = self.pmos_size*drc["minwidth_tx"] - self.height = height # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnand3 is only supported now.") diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index e19f03cf..8f7dcea4 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -12,17 +12,13 @@ class pnor2(pgate.pgate): This model use ptx to generate a 2-input nor within a cetrain height. """ - from importlib import reload - c = reload(__import__(OPTS.bitcell)) - bitcell = getattr(c, OPTS.bitcell) - unique_id = 1 - def __init__(self, size=1, height=bitcell.height): + def __init__(self, size=1, height=None): """ Creates a cell for a simple 2 input nor """ name = "pnor2_{0}".format(pnor2.unique_id) pnor2.unique_id += 1 - pgate.pgate.__init__(self, name) + pgate.pgate.__init__(self, name, height) debug.info(2, "create pnor2 structure {0} with size of {1}".format(name, size)) self.nmos_size = size @@ -30,7 +26,6 @@ class pnor2(pgate.pgate): self.pmos_size = 1.5*parameter["beta"]*size self.nmos_width = self.nmos_size*drc["minwidth_tx"] self.pmos_width = self.pmos_size*drc["minwidth_tx"] - self.height = height # FIXME: Allow these to be sized debug.check(size==1,"Size 1 pnor2 is only supported now.") diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 604c9312..015b434a 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -39,6 +39,8 @@ class single_level_column_mux(design.design): self.add_wells() def add_modules(self): + # This is just used for measurements, + # so don't add the module from importlib import reload c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index bdb64f9e..f377a3db 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -11,7 +11,7 @@ import globals from globals import OPTS import debug -@unittest.skip("SKIPPING 19_psingle_bank_test") +#@unittest.skip("SKIPPING 19_psingle_bank_test") class psingle_bank_test(openram_test): def runTest(self): From 4fc9278b73c654e01d65ef8b2ff15fabe41766e9 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 13:05:21 -0700 Subject: [PATCH 20/31] Convert bounding box layer for SCMOS to bb, gds layer 63. --- technology/scn3me_subm/gds_lib/cell_6t.gds | Bin 6144 -> 5916 bytes technology/scn3me_subm/gds_lib/dff.gds | Bin 16622 -> 16622 bytes technology/scn3me_subm/gds_lib/ms_flop.gds | Bin 18934 -> 18934 bytes .../scn3me_subm/gds_lib/replica_cell_6t.gds | Bin 6060 -> 6060 bytes technology/scn3me_subm/gds_lib/sense_amp.gds | Bin 10240 -> 8312 bytes technology/scn3me_subm/gds_lib/tri_gate.gds | Bin 4576 -> 4576 bytes .../scn3me_subm/gds_lib/write_driver.gds | Bin 11804 -> 11804 bytes technology/scn3me_subm/mag_lib/cell_6t.gds | Bin 5916 -> 0 bytes technology/scn3me_subm/mag_lib/cell_6t.mag | 10 +++++----- technology/scn3me_subm/mag_lib/dff.mag | 4 ++-- technology/scn3me_subm/mag_lib/ms_flop.mag | 4 ++-- .../scn3me_subm/mag_lib/replica_cell_6t.mag | 10 +++++----- technology/scn3me_subm/mag_lib/sense_amp.mag | 4 ++-- technology/scn3me_subm/mag_lib/tri_gate.mag | 4 ++-- .../scn3me_subm/mag_lib/write_driver.mag | 4 ++-- .../scn3me_subm/tech/SCN3ME_SUBM.30.tech | 12 +++++++++++- technology/scn3me_subm/tech/tech.py | 4 ++-- technology/scn3me_subm/tf/layers.map | 2 +- 18 files changed, 34 insertions(+), 24 deletions(-) delete mode 100644 technology/scn3me_subm/mag_lib/cell_6t.gds diff --git a/technology/scn3me_subm/gds_lib/cell_6t.gds b/technology/scn3me_subm/gds_lib/cell_6t.gds index ec9264568959cc70edee2dc6cb55ac528ede4993..20e5367ece732f8208aab30adb0584859e15655f 100644 GIT binary patch literal 5916 zcmbuDJ#3X_6vq#@x0eD1+sa2v3e;4PmQo5-sUJ~H*kEvAAcjPvH8M1Y7=w%q4w^79 zFqp`|K%xVK149Rg4#qGtI5;pcFgQ3k5EnnL=XcNh_kHfg^Eya!`Q84X=YP)g{XX}# zjm;Skaf7Cs`A`(Y&VefnCKU(|EW)Da1h0klg@@43vw=MB^yT89> zw&h*3iH(|jE{869dE&p>;U~X{A6?A-F+SvA{KT0Dp^L7k{U;ATts6)m z(8U4&k$V2k4qh`mdBbdespiV*&_ypV^B4YZ_s?(4j$DlWBQ8D)U39(CPwv)*XahWQ zQE`J_p7_mnjD?T=OB_f$(8VS{`-FOU#l=Ul|3uf*{P^+izgm+xid&VZe#Rev`N5Aa zdRgL+@gWD}Chor)y6Af1k33Zydw%tW{PYRt{>Z}~9lI2J74Di0UG(xa|F-q0J9bHo zIK+%kPfz{RGf|IrQy+4Mpo>lUr)MHBdyTcso*|CnR^?^>rE>oqx@)%lUF>!7)J`*W z(e>0n`q*szrr9&E)qM6BGj!3*Q~$nm;X81rCO&jA_jkL0{TR77Ez}&p8M^3gOY_rD z+Rj-g(jRoO$v^s7Z4k|93;hrF^fdpzbKyI1M+_hR$6u8v{_wERs4E`57rN+r;*UP# zqp#x8d!dU>{=HAaM|<%@eCT46pK->=_>1_^#oQnLBp3Y`HKwBLY5&QCkNhHiKoR^s@|M}w>t-Tx6c@Ul`q?);x1T>>lf3BS!1KE=Kl9ER ziC-ihy6AeEpL*P%cb}gU;A&NcJEK13G>=D#mL`O3Ls?wAX7 z(aX#HCkETkbC3EW&pp~N4m|$~f1E$`oAXCJcrA2sp#OzG&KZ2{d69AB{1Kb{Bg>I% z>vb{O!kEyesyyxgn8ulz6`=wg$fb~4wTlOnOuMc0%5$N2DD zf$SvF$=NWgAexr-7r+(Hcdmg_? zobpU@J@s?Oa_6W0BK<%YT~GbAlX~PAX$QLKdZVAb+}lOsC@yTu&pKn?87qYny$)|{ETt< z%illv8T(+by!4;!Kfl7?x*(eA+zK}N>-Ot+AY;aR2ihNO_OtFdC%7|+_|QeyllI4+ zp`WZD5g&U%bUpR|zwu+8#kkEciIJCeNB*k3mwzS%hzz-2i|@8^VVa1 LTE*Jzy=dFtRZEf{ literal 6144 zcmd6rJ&YAq6vxlL_jY)=&*kk3>@HaZf(ZKoEbJ+D^l^W4=FiNmo_WT~O_m?|-}9ey?#J9a zcP+D)Wi~s~vPo;(4x6x!_0#`HZQRDJXQ!;!%2qGE{l*7t@0)FJkG}BYrzc+e@vHmK zon1eA^UK%H*z9P7H_P6-w6^y8v&q}(nq`l-Y~Hf*mYFT~+k?UHH_i4va6bQo*|C#m zqV#3e0q-bysheADE(%$dk2F*zlLUG>t=g@5~b&C@OwP|9n)s> zOC@<;@!Zfqw(fa|<+p!Xl-`uT7L(`4PaXaowb#b4*8c-n&1Puz?`5WULz=Uewf>9ODQ9=zjZ?UP@Wo*3l!+KHo9eAFOHALb_>KjTGyQF>ySpKlKG zK;rp!5T(cYecTS-apGg#MCrr)a~=0nH~B^B>3P-qkCgN8=Ue-lbM&sCU!wH(waCx6 zr>}R&S@_heC3zo;_8`CS735>jU<{{kl$4$r`nxNhAMQMB_Sk17$zQZL`tcFR8sK|V zl%Ck=CmuhwvyY0>6GK0-`Xx7hQF>zNXOCr!A>&WYqV&W@Kk@ikugsq)Ju&oiUJwsi zclbr=iJ@QP23hyapGg0U_C`PP#F3ZtMwFgd^&c(QzrQbdd2h(goQcvCLqF#Obwa*@ zm^)GWrunPopWN`h4>I1=A+jeI?Roy5-~WzTwv+o`HZ{V(yZE=)Z~NYnYRzV{kJf4x zCx>o~^w8gSddk-_JEl{5&T5Zq{w-aP+tPI(HzoZ2%hH?t9y5B^$w>2+FG}C^#@F(* zQxN}8JBIYcuzp6DTLt9ngZ!fO#L&;|(r3tQF@B=-#6~~y_?ZLV98r2==&#;#iRaA~ z$zQaGepV&@zZrj#=M?Q>{j59UAvZ++FG^1g{p>WvL2jyyzbL)Quezab zx+4CfJ;?9lrkgc&Gk&797Nw`>Rr4RO?qAb4{MN(z^;@ne{cks}n!mf^ zaqRB+*)yEdoBhl&J2PYsIb}rYi9!9IWB(y1K5~oFW51sW+>9nS%(yA>E{W20W9s>d zu^gQk{I%!!DksL9?|H?;9DGw1j^d0N=3t+G=2z>^WFM}srbCLqL=N-t<&b`o=bd-| zttWSE@^l~dzdZjAT=libm}}iRrRS{9UmCyC{~ylJnx8$AcLtJ|^Fx%L80Kfc;eCVn z*#AW7o8F&V{_6RSkMqLm{V&>s{KV+jebkJiAjZC#{S8>rt#U<^C;c Lt)$oMfBf?h%e|6$ diff --git a/technology/scn3me_subm/gds_lib/dff.gds b/technology/scn3me_subm/gds_lib/dff.gds index b96235c7500aba5314ed95369b44d5000c60b5ff..07c37298b1293dad95cbcfb12e4c3222dddffa7f 100644 GIT binary patch delta 96 zcmaFY$oQ_2QHg<#fr){cL57iup^Sl(frWvGL6Jca&NgHan5d+zj#YwzgN-dEEscSJ ag@b{CjhBhRe)1c8i;XD__LEf{P5}Txb`Avq delta 93 zcmaFY$oQ_2QHg<#fr){cL57iup$y0pVBlvEXAnkWi%nEgmcc5)z`@3rl9sk{VY594 T2Ll5eFB3!X#2kysDh{UrF7FL_ diff --git a/technology/scn3me_subm/gds_lib/ms_flop.gds b/technology/scn3me_subm/gds_lib/ms_flop.gds index 0e06e5328c1ef9dfee97b69b81905929d2c06d86..e1c071bea7fc0075f13ae6143e1c4c103f6f6f98 100644 GIT binary patch delta 100 zcmex1nep3XMkNL|1||k(1{p>shB5|D1{MY$21N!fINOjxcA}EHHdYA+9yYez;`p?j e`~n6B77hjmHeMzM`^hC977KGcHm~vUVgvxOLl2Yy delta 98 zcmex1nep3XMkNL|1||k(1{p>shB5{g25tsk1}z2yB(}vwC3OX?5)3?SY`MkpX*u}? a8&@)WaBwg%u<_E)I000%)8u7la7=}OE+e^_amRf;gMHH1ED?gpEKcX@ZZ8; zd^LZ`g>c|&`{QrX|5x=F)qOrE52|0$w^#kUtA5W5W}kQm^B0!i%NbcgFsbClAGVMBkqD zsgr)FU)<&UkX-aU={LOg8)us>URLs+-+aP67kzuxzpGk*?}XXm&1Um{Df!h!Gjh?l zC;dD2njJqF{fq23a?$go-|chv_D5#7oG5uHkGhL~zP;Lio6m8M&pe2Dek&8KhU|x80UoVMAF6Mmdd}loB7vJrR`bE#P_2W^uh)4Zm z&Zka1>KF0I#hlN&@#t`@cj99QV?7Z)Pu9=6%W<}-QSwAjtjD5nPx{Q?i+=2||HU1b zWBx?XlRoo=$NY(SJ&gVL@tYvo1!IDFxas3c| zd$Rr+hr(kWq#cMyF6R6>e}u<+hY#_{#hlM|3yto~hWPu9;kId>Sp$eK!?m544 zpY>8a#};=F{h=gVC2rJLID0NxyTS*`Z&<7iouF^gQdY?2G3mk#^KB^gQWL z?=k!E&$#avKi+6YE_$Bzd2Zu*RiquY3v17BZS%)GZ#*CO`y$V+xT1K`wfp^+yKHu3v2?(vI4No@f2FK3}bi zW+Lqr-XuLw`pgsFf^#LA2XZmz)6acBM*Sk~k&B)u`{%hIkN!nGaxv#~d=HOD|03r> zaokVR^JM?b8_$l+zeqdeqUTAU=Qcd*6?yMKF6MmtVeP~hX-9Px+pGOAt*&3JGvE5L z$66;_rc9^zKgt1A{RZ+`l~O6FRoc1zUX<< zpTE!ykKn6up z??g$CUvjZ_{I>b*cYLm&A|APz^LbCk8qNC}k-o`A&#U8KUe4c@D}SGg>m2usvS#qU5Yi60=y|e#?(;ZKS!YDOcN9HO`rPO7 z{*r!0JaV!2{<3ZUIZnCGvJQ!O??@g&+9NQc9i?#Qs z|EV93`bFwcT`=d<{}UJE`Cp_R)m3cI*1u^>-0zCCqjsU^NuT|%`wo%gK*vR~J?S%U z)_v}`Md~0IYwLg8{BfVaI?eT0)O~~Kd9r`jE$$oeMcN@3YxfUr{r`{qk2YWLxfuuV zt93u5x{B?|`l%a_YrnW^ZH!0sJn7Ru#~9aJk#>mSdVT)#xFU%D<9+mrosp07U@ z^DXjy61nJk(r28^bN%=yXFSFBq|Z39c?MVgHG&ndGA9NuT=oVgHHL zp>Y-4lRowF!~Ww3;%i*R_DR1L-+%l1Gn4PXo3p0y?;QSZ-PE=AuOs{%x0b#a_xrA9 ze{Xvkhio<~RJ`^Z%Z6p7*)E z?`I8bv4+hY(_)?0>c8za+hz^@-(?eRiuG7WOQW~%#jSgH>@n*a+xFnYhnBDX`OMY( zSD#;W>iE+=HnT15ozQ6P?%TP$@A<7S_nWo0n>B7~u?5zc&|+pilg9er($D^bW(zNy ziKR=elMDa+x}SJ{eryfDDBW51-(B{14wxO?YzDu)VmABNg3_Hy|DA7}ojPm=zglUw zU~xg|&ZNJ6nc15kn!!Ue&2BqgP`Wege`b65`#lHzqI74{Py7bIG4P4=W_N5VDBYR# zk8|%dzqxR!*Kzaog3_HyfA=rphxq4SEQr5(z3g9HzJF7$nw{KY2EXw9r>-t2-I?^? zx-R?>Km8!$Z(dLOY4_NHX#e=7XkV1>O#1nJ;vhcS7p3R?H+YWk9yNpST{9c+HHgxk z$@V)3!VmFr|3v)F>q-C2+2M!yX1!VvfAf0MPu_1`i2UE_iu^_C&ZM9Gh=cgZUzDEn zlOJ&qANh;YbAHx~jmu))c>J*0oOx!VbZ0XEyUxXUgQq8%&2KHpIBZ@|`su%ye9XWd z*Q5VL>CU8|ej^UzqyI$dIY0eI9K=WeiPCd^^6vGy54ZVeFn&ep&TRg~kv~527p3R? z!e5={Z09&f<>PKbG{G z-FVnclMd>yF zJ>~P`r0r%On;GQ%K4)G*>CSTf6@S|$vo}_l!T0x=-G05GbZ62}y^nnxbw4#J@)xB$ z%l@8n{^a}lvdH_)k(m3UbZ62}-n0jIU5xxi>CU9T)pKXhg7^I%Ymq43S@th1=g%4c z>l5(|@V)m1`lJYjpW21ebAIN{vI{Z( z8Sk_&N_QsPr@we+hs5I-r8|>;o|lP(tUJ^pO3(Rempw~);Kv80JIn1aDz9IxKi~M6 zgWvhu#rh#icP9Phz5ih3|JvnfSCsBd`gvY@WOF=suwSqah|--||H^COe`rnkMd{9@ zpZ$k8h>vkBO0W6vE&6Y8@Sn4+*Fn~O&a9&J+WKGe$8(e3)A9a}bxf3Adw*B)v)}NZ z4%XlQNw2-XtN5vxI5@)he-%IN^1erTP(Pmsr8~?0Kg>^^`W()F_@MM${hb5xz7Z17 z*cW*p*}R@?pYtQ@EhL_KBuaNC{hS+l&k6C-z9_x+{9X8V8B zmhcnLIa-wNO!~R+?0Xsqtl!*sD806SR_do-)+tDPv@c4ptzQ*C`ycBK)V?Q5cP8`a zy*2wBB%ZuQ>9zf@(mwkHagcULuzyzkdT*{g%KJI*!<*NW`I9ekaK)?sfS6MRs*GwEmlXU&Dg%Lk=9lYY*r#6j9+{EE_Z{^s`sL+3u~<~%9l|BvqvD*exR zqdmxd)VT*rcP8^^ouC~^ynIl4E`R!sHBi_xw`d-cV%|>^lZ)YX@CB^O=Pn(~$2TyK^x6h(AT|Ru5;RhoEkd^3QBh-+h@cP2l4YXElSV%d1~dv1j&I}AWC z2M7O%QUAlW@CKz1G3lSyA937qzO`f*4@%GbId$-~3Uz7_r8|@Dv+Axn8>{aIubo*S zN_Qsx<<&x~j>v3iUQha|n>eUhD@u1J{mdGEAUh$mMwIRx@;7IX-@oCrhZ)0+J$As_ zyBphf_KwiQs=wBo?QZPr9jS+Lb>SQ{^MxHF%ZxRw>DV>PIJh8@Na+$8k<`X(0i~z>45r6;y diff --git a/technology/scn3me_subm/mag_lib/cell_6t.gds b/technology/scn3me_subm/mag_lib/cell_6t.gds deleted file mode 100644 index c6fdb0e8208d82752b8df1f873200eaf17b8e3c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5916 zcmbuDJ&cr96owD8v$G?tIJ@q`E?Hm=g6uE5>?-R|3>rGNFiIjZB$~KJ3SuY>p`@_T zgo1*?L<$NLEhsD~wy>ZuhLXatL*4ElH_cx7%WQR{=IcL(E_!=&%uN2det+~` z-4KbRxUly<^)J(9TeJ}DJIx0pS#jV=Y{EKz{!ZX)2o18KeSqthS zx?c3Z^ouX<_h#Z;Q*nc?XZ&Wnw!=q${2B*3L&6mqKo_zqPab@FZXk0&7YFz&;MqI zE_!>`zkf4)2XEEHhc4#*-R?g>NA4YqHOH=pF8bKA`I#qU=d2T%54u?Lk3P~EL^H<1 z{DVC`n}7di_zvC@!^iybSM3>pc-Uw36_@UWF1nua$DHvoS8?f1=wivg?{WATFMfy* zT`c*jGd}7s;zJkn{+K7Zn7^o+imqqpPab^a7nuXPIN)DS_fLC0ysS4dAn$E-(e*U{ zQ~s$NW;?H%iNsM{=z7-AzTv(7;+dM{MHdI&-+lR6cg{%sBJt2g*R%QQ$NhQFshY%5 z+^Rk6XU=@6waD4UchSFR{nUs11AY;2Rkt1o?hk$QrypY{mq;ANt=fzEx%aYuB5@QK z4%~nH@^i)wKZyB?oZsl8>)HI&p1iD|Nd3{peExW641E#r9dY_z=yt~u2W*yjv=;FZo_vI&Fx>l?mYk@9$dz%0F;P`p((O=}f$N0s8 z_g~_V^M`qJ{)mUKgf0%uKk>&ogO5EgQa8>YvE*;BN3NY$#TW}UVN6wfcK%a0!ng0S z7(Uhnf7PD)SL^!4bMo~4JM@i{Puh!q?mX%JdF`5fL2u9aVYnn{EU;e=A0CXg)X|DnSa!WzZIyTc;t5IqU#xdb8NbLp&_&m? ze#S{Z@{5cEU39(ZColJQkvNJAOZj=uSa)hAvVJ-{s`hMt-ZSalgSzRxBYJzrAI}-@ zE%J+;Mf@!y4!pmT^TTYU9evZkpLmu>+VMs(e=#uW6v;8o*xk(dq8wO>;HfC<2j4E z&98`&m*> rect -8 29 42 51 << pwell >> @@ -105,13 +105,13 @@ rect 6 2 10 48 rect 24 -2 28 48 rect 32 33 36 48 rect 32 -2 36 29 -<< m3p >> +<< bb >> rect 0 0 34 46 << labels >> rlabel metal2 0 0 0 0 1 gnd rlabel metal2 34 0 34 0 1 gnd rlabel m2contact 17 46 17 46 5 vdd -rlabel metal1 4 7 4 7 1 WL -rlabel metal2 8 43 8 43 1 BL -rlabel metal2 26 43 26 43 1 BR +rlabel metal2 8 43 8 43 1 bl +rlabel metal2 26 43 26 43 1 br +rlabel metal1 4 7 4 7 1 wl << end >> diff --git a/technology/scn3me_subm/mag_lib/dff.mag b/technology/scn3me_subm/mag_lib/dff.mag index 19825153..46d22c84 100644 --- a/technology/scn3me_subm/mag_lib/dff.mag +++ b/technology/scn3me_subm/mag_lib/dff.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1518823399 +timestamp 1536089597 << nwell >> rect 0 48 109 103 << pwell >> @@ -266,7 +266,7 @@ rect 6 30 10 50 rect 22 20 26 57 rect 70 44 74 70 rect 70 20 74 40 -<< m3p >> +<< bb >> rect 0 0 109 100 << labels >> rlabel m2contact 15 34 15 34 4 clk diff --git a/technology/scn3me_subm/mag_lib/ms_flop.mag b/technology/scn3me_subm/mag_lib/ms_flop.mag index 8b3b1d40..713d264f 100644 --- a/technology/scn3me_subm/mag_lib/ms_flop.mag +++ b/technology/scn3me_subm/mag_lib/ms_flop.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1523479368 +timestamp 1536089622 << nwell >> rect -2 0 18 200 << pwell >> @@ -280,7 +280,7 @@ rect 14 8 20 9 rect 14 4 15 8 rect 19 4 20 8 rect 14 3 20 4 -<< m3p >> +<< bb >> rect 0 0 34 200 << labels >> rlabel metal1 0 8 0 8 2 clk diff --git a/technology/scn3me_subm/mag_lib/replica_cell_6t.mag b/technology/scn3me_subm/mag_lib/replica_cell_6t.mag index 52dd6265..d0dc472f 100644 --- a/technology/scn3me_subm/mag_lib/replica_cell_6t.mag +++ b/technology/scn3me_subm/mag_lib/replica_cell_6t.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1521677136 +timestamp 1536091380 << nwell >> rect -8 29 42 51 << pwell >> @@ -106,13 +106,13 @@ rect 6 2 10 48 rect 24 -2 28 48 rect 32 33 36 48 rect 32 -2 36 29 -<< m3p >> +<< bb >> rect 0 0 34 46 << labels >> rlabel metal2 0 0 0 0 1 gnd rlabel metal2 34 0 34 0 1 gnd rlabel m2contact 17 46 17 46 5 vdd -rlabel metal1 4 7 4 7 1 WL -rlabel metal2 8 43 8 43 1 BL -rlabel metal2 26 43 26 43 1 BR +rlabel metal2 8 43 8 43 1 bl +rlabel metal2 26 43 26 43 1 br +rlabel metal1 4 7 4 7 1 wl << end >> diff --git a/technology/scn3me_subm/mag_lib/sense_amp.mag b/technology/scn3me_subm/mag_lib/sense_amp.mag index b29f7da0..e5fa4373 100644 --- a/technology/scn3me_subm/mag_lib/sense_amp.mag +++ b/technology/scn3me_subm/mag_lib/sense_amp.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1524065550 +timestamp 1536089670 << nwell >> rect 0 0 40 102 << pwell >> @@ -122,7 +122,7 @@ rect 20 44 22 48 rect 3 0 7 11 rect 10 0 14 44 rect 20 0 24 44 -<< m3p >> +<< bb >> rect 0 0 34 163 << labels >> flabel metal1 0 149 0 149 4 FreeSans 26 0 0 0 en diff --git a/technology/scn3me_subm/mag_lib/tri_gate.mag b/technology/scn3me_subm/mag_lib/tri_gate.mag index a393074b..bda635c7 100644 --- a/technology/scn3me_subm/mag_lib/tri_gate.mag +++ b/technology/scn3me_subm/mag_lib/tri_gate.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1524499924 +timestamp 1536089695 << nwell >> rect -2 45 38 73 << pwell >> @@ -86,7 +86,7 @@ rect 24 19 28 23 << metal2 >> rect 15 34 25 38 rect 15 0 19 34 -<< m3p >> +<< bb >> rect 0 0 34 73 << labels >> rlabel metal1 0 12 0 12 3 en diff --git a/technology/scn3me_subm/mag_lib/write_driver.mag b/technology/scn3me_subm/mag_lib/write_driver.mag index 80e09d11..ab2014aa 100644 --- a/technology/scn3me_subm/mag_lib/write_driver.mag +++ b/technology/scn3me_subm/mag_lib/write_driver.mag @@ -1,6 +1,6 @@ magic tech scmos -timestamp 1524499497 +timestamp 1536089714 << nwell >> rect -3 101 37 138 rect -3 0 37 51 @@ -209,7 +209,7 @@ rect 10 196 14 202 rect 20 193 24 202 rect 20 177 24 189 rect 15 0 19 6 -<< m3p >> +<< bb >> rect 0 0 34 202 << labels >> rlabel metal2 15 1 15 1 1 din diff --git a/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech b/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech index bf6ce64b..be511001 100644 --- a/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech +++ b/technology/scn3me_subm/tech/SCN3ME_SUBM.30.tech @@ -276,6 +276,11 @@ cifoutput style lambda=0.30(p) scalefactor 30 15 + # This is a custom section to add bounding boxes in OpenRAM + layer BB bb + labels bb + calma 63 0 + layer CWN nwell,rnw bloat-or pdiff,rpd,pdc/a,pfet * 180 bloat-or nsd,nsc/a * 90 @@ -1506,7 +1511,12 @@ cifinput style lambda=0.30(p) scalefactor 30 - layer nwell CWN + # This is a custom section to add bounding boxes in OpenRAM + layer bb BB + labels BB + calma 63 0 + +layer nwell CWN and-not CWNR and-not CTA labels CWN diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index d69a7d7f..4998c5b6 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -39,8 +39,8 @@ layer["via1"] = 50 layer["metal2"] = 51 layer["via2"] = 61 layer["metal3"] = 62 -layer["text"] = 83 -layer["boundary"] = 83 +layer["text"] = 63 +layer["boundary"] = 63 ################################################### ##END GDS Layer Map diff --git a/technology/scn3me_subm/tf/layers.map b/technology/scn3me_subm/tf/layers.map index d10d5f2d..b5440f23 100644 --- a/technology/scn3me_subm/tf/layers.map +++ b/technology/scn3me_subm/tf/layers.map @@ -13,4 +13,4 @@ Metal2 drawing 51 0 Via2 drawing 61 0 Metal3 drawing 62 0 Glass drawing 52 0 -text drawing 83 0 +comment drawing 63 0 From 763f1e8deea8eef0169903842df1dc0255a4ae69 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 14:03:15 -0700 Subject: [PATCH 21/31] Finish renaming replica bitcell and bitline pin names. --- compiler/modules/replica_bitcell.py | 2 +- compiler/modules/replica_bitline.py | 4 ++-- .../scn3me_subm/gds_lib/replica_cell_6t.gds | Bin 6060 -> 6060 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/modules/replica_bitcell.py b/compiler/modules/replica_bitcell.py index 5ec524f4..7bbdbe06 100644 --- a/compiler/modules/replica_bitcell.py +++ b/compiler/modules/replica_bitcell.py @@ -10,7 +10,7 @@ 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"] + pin_names = ["bl", "br", "wl", "vdd", "gnd"] (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"], layer["boundary"]) diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index b15f4515..6ca4a829 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -228,7 +228,7 @@ class replica_bitline(design.design): # 3. Route the contact of previous route to the bitcell WL # route bend of previous net to bitcell WL - wl_offset = self.rbc_inst.get_pin("WL").lc() + wl_offset = self.rbc_inst.get_pin("wl").lc() xmid_point= 0.5*(wl_offset.x+contact_offset.x) wl_mid1 = vector(xmid_point,contact_offset.y) wl_mid2 = vector(xmid_point,wl_offset.y) @@ -247,7 +247,7 @@ class replica_bitline(design.design): # Route the connection of the source route to the RBL bitline (left) # Via will go halfway down from the bitcell - bl_offset = self.rbc_inst.get_pin("BL").bc() + bl_offset = self.rbc_inst.get_pin("bl").bc() # Route down a pitch so we can use M2 routing bl_down_offset = bl_offset - vector(0, self.m2_pitch) self.add_path("metal2",[source_offset, bl_down_offset, bl_offset]) diff --git a/technology/scn3me_subm/gds_lib/replica_cell_6t.gds b/technology/scn3me_subm/gds_lib/replica_cell_6t.gds index 4336cc246d4c6955adf6e0190585cb91226ba027..12d97796c04c2045340cafe3ef594d99adef8042 100644 GIT binary patch delta 25 ccmZ3Zzeaz8EGr)aBLn|LC3^^CW15>d07_v65C8xG delta 25 ccmZ3Zzeaz8EGsVqGXvX1C3^^CW15>d07=OO00000 From d721fae5b0014de292080610180f2aa17872565e Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 14:33:14 -0700 Subject: [PATCH 22/31] Change labels in replica cell for freepdk45 too --- .../freepdk45/gds_lib/replica_cell_6t.gds | Bin 20480 -> 20480 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/technology/freepdk45/gds_lib/replica_cell_6t.gds b/technology/freepdk45/gds_lib/replica_cell_6t.gds index 9b301c08ae8f2b857d6cbdef1795cc73b82ea61a..2881a883709d5f4b04a8632ed039b1c2e7e61b9c 100644 GIT binary patch delta 961 zcmYLGPiPZa9G*9wPA6&Fnl@>gKWemC-6en8Ce|iRXWmT5aio)O+3kWXi(pX?Qe=^p zJrwE?N*WJJ>nDilK`7qDgQH#w5<;0q@F3!0PagCt9;A3FEc@FY*2Bm9e((2v-}}Ay z+(*oP#Ma}d1A-*<3TYuLghct`rOOwtFa0S9k&aF^z5U?vr(eF6j{Y8dTYd6YNOg*& z>=tApC`5&EpD@AS1h8@VIU}uVNaUF zYiSyFX$EH{QcEV@kj@eFn|pl>%YC!IVOsb$5gCNnx37%!sw_oc)>ZY^I33iK;$Z# znt__eVQA_dqAgX0s~Rv=4w2BBrq;BJR>eiFLcC(Y)GJ<7cNtvWggL`K>C8V2Elc(I z=^EQ?=?tPtvYA(2OQ-Q%o$?5h{v%#GS)7`S!LR;-r&SYI2qT1#&L~!#dE^KmX6JF6 VkaG^;x7p(UIyjs1=H{NUe*s1~O0@t0 delta 949 zcmYjNU1%It7@a#iJDuHRH`#QPY&MCpR*Tte_h;Oul%_D6t zu4`#+L^HLdHmTX#1eO14_cmXKsh`22=%6oJxFDK)(Z_o`HdbO1Pj%kvxA8D$qaAaR zj4M1aByJmZct#5eqk&;;^`G*?afut_9eCtuY!`2hZNVH(i(GOWQNlqlq2MLxXVK5vhGj86CebIXW4rjw+(tkC zJ60w*YGwmJW*X>a=%4uzPkDDZ(Loe{1vl~%EBV(jny(|8p?=1NKU0UrILi6>Bv*r% zqbK8s(>3&`CA?`LS;o;6>!*BNnX18>YGG&cCag)$on?LY2+?uA6W($5DP-_dsf@#X z3cZqp;anP*OE!!W|FL)#wbRS`n?9Zt+qhP^f_~9~S?nTO;#Zfrszq*esfnn-HiafU za)N9X1IpQMk!xB|VKo1dK=0H5)+x^Mk_^4)ga|L3yeVLvbi(0_F@c>E9P>o5AI%Fy z3mkFb|Jr%ZGfzVznq%`h=46u`&9Zow#k1Tad6+N3E*J4=Wfp&yQeoF-b7?E+HMu`H ztD}3?IauzB{jM!As7c-*{a56|u$#DFZK7LsAgdHrTex3gUSVEgUMbSOfX&k;9+$_T zb^4;5)YWAa%XMA(_^s5&xAHu$%U9t_2ZLq+Md(e5fg{l+6rno~jv5Nx2A#=zgYR_* zL0v)BoxAlj`0nC4Y+ZZ1)oxs;*<8C{%7}v_23R zc8XZu$l>;Y;GqdKO!L Date: Tue, 4 Sep 2018 16:35:40 -0700 Subject: [PATCH 23/31] Found rotate bug in transformCoordinate. Cleaned up transFlags. --- compiler/base/contact.py | 31 +++--- compiler/base/geometry.py | 80 +++++++------- compiler/base/hierarchy_layout.py | 7 +- compiler/base/pin_layout.py | 8 +- compiler/gdsMill/gdsMill/gds2reader.py | 6 +- compiler/gdsMill/gdsMill/gds2writer.py | 24 ++--- compiler/gdsMill/gdsMill/gdsPrimitives.py | 2 +- compiler/gdsMill/gdsMill/vlsiLayout.py | 44 ++++---- compiler/modules/dff_inv_array.py | 4 +- compiler/pgates/pinv.py | 2 + compiler/router/grid.py | 3 - compiler/router/router.py | 105 ++++++++----------- compiler/router/tests/10_supply_grid_test.py | 4 +- technology/freepdk45/tech/tech.py | 1 + technology/scn3me_subm/tech/tech.py | 1 + 15 files changed, 156 insertions(+), 166 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 5c300033..9ffae803 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -15,22 +15,27 @@ class contact(hierarchy_design.hierarchy_design): necessary to import layouts into Magic which requires the select to be in the same GDS hierarchy as the contact. """ + + unique_id = 1 + def __init__(self, layer_stack, dimensions=[1,1], implant_type=None, well_type=None): if implant_type or well_type: - name = "{0}_{1}_{2}_{3}x{4}_{5}{6}".format(layer_stack[0], - layer_stack[1], - layer_stack[2], - dimensions[0], - dimensions[1], - implant_type, - well_type) + name = "{0}_{1}_{2}_{3}x{4}_{5}{6}_{7}".format(layer_stack[0], + layer_stack[1], + layer_stack[2], + dimensions[0], + dimensions[1], + implant_type, + well_type, + contact.unique_id) else: - name = "{0}_{1}_{2}_{3}x{4}".format(layer_stack[0], - layer_stack[1], - layer_stack[2], - dimensions[0], - dimensions[1]) - + name = "{0}_{1}_{2}_{3}x{4}_{5}".format(layer_stack[0], + layer_stack[1], + layer_stack[2], + dimensions[0], + dimensions[1], + contact.unique_id) + contact.unique_id += 1 hierarchy_design.hierarchy_design.__init__(self, name) debug.info(4, "create contact object {0}".format(name)) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 7144a2cf..682b783b 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -34,14 +34,14 @@ class geometry: # coordinate += [(x, y)] # return coordinate - def transform_coords(self, coords, offset, mirr, angle): - """Calculate coordinates after flip, rotate, and shift""" - coordinate = [] - for item in coords: - x = item[0]*math.cos(angle) - item[1]*mirr*math.sin(angle) + offset[0] - y = item[0]*math.sin(angle) + item[1]*mirr*math.cos(angle) + offset[1] - coordinate += [[x, y]] - return coordinate + # def transform_coords(self, coords, offset, mirr, angle): + # """Calculate coordinates after flip, rotate, and shift""" + # coordinate = [] + # for item in coords: + # x = item[0]*math.cos(angle) - item[1]*mirr*math.sin(angle) + offset[0] + # y = item[0]*math.sin(angle) + item[1]*mirr*math.cos(angle) + offset[1] + # coordinate += [[x, y]] + # return coordinate def normalize(self): """ Re-find the LL and UR points after a transform """ @@ -148,40 +148,40 @@ class instance(geometry): debug.info(4, "creating instance: " + self.name) - def get_blockages(self, layer, top=False): - """ Retrieve rectangular blockages of all modules in this instance. - Apply the transform of the instance placement to give absolute blockages.""" - angle = math.radians(float(self.rotate)) - mirr = 1 - if self.mirror=="R90": - angle += math.radians(90.0) - elif self.mirror=="R180": - angle += math.radians(180.0) - elif self.mirror=="R270": - angle += math.radians(270.0) - elif self.mirror=="MX": - mirr = -1 - elif self.mirror=="MY": - mirr = -1 - angle += math.radians(180.0) - elif self.mirror=="XY": - mirr = 1 - angle += math.radians(180.0) + # def get_blockages(self, layer, top=False): + # """ Retrieve rectangular blockages of all modules in this instance. + # Apply the transform of the instance placement to give absolute blockages.""" + # angle = math.radians(float(self.rotate)) + # mirr = 1 + # if self.mirror=="R90": + # angle += math.radians(90.0) + # elif self.mirror=="R180": + # angle += math.radians(180.0) + # elif self.mirror=="R270": + # angle += math.radians(270.0) + # elif self.mirror=="MX": + # mirr = -1 + # elif self.mirror=="MY": + # mirr = -1 + # angle += math.radians(180.0) + # elif self.mirror=="XY": + # mirr = 1 + # angle += math.radians(180.0) - if self.mod.is_library_cell: - # For lib cells, block the whole thing except on metal3 - # since they shouldn't use metal3 - if layer==tech.layer["metal1"] or layer==tech.layer["metal2"]: - return [self.transform_coords(self.mod.get_boundary(), self.offset, mirr, angle)] - else: - return [] - else: + # if self.mod.is_library_cell: + # # For lib cells, block the whole thing except on metal3 + # # since they shouldn't use metal3 + # if layer==tech.layer["metal1"] or layer==tech.layer["metal2"]: + # return [self.transform_coords(self.mod.get_boundary(), self.offset, mirr, angle)] + # else: + # return [] + # else: - blockages = self.mod.get_blockages(layer) - new_blockages = [] - for b in blockages: - new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) - return new_blockages + # blockages = self.mod.get_blockages(layer) + # new_blockages = [] + # for b in blockages: + # new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) + # return new_blockages def gds_write_file(self, new_layout): """Recursively writes all the sub-modules in this instance""" diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 01a48f89..83a8420b 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -379,12 +379,10 @@ class layout(lef.lef): dimensions=size, implant_type=implant_type, well_type=well_type) - - debug.check(mirror=="R0","Use rotate to rotate vias instead of mirror.") - height = via.height width = via.width - + debug.check(mirror=="R0","Use rotate to rotate vias instead of mirror.") + if rotate==0: corrected_offset = offset + vector(-0.5*width,-0.5*height) elif rotate==90: @@ -847,6 +845,7 @@ class layout(lef.lef): """ Add a single power pin from M3 down to M1 at the given center location """ + debug.info(0,"Adding power pin {0} at {1} rotate={2}".format(name, loc, rotate)) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=loc, rotate=rotate) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index aa6bacfc..7e454df4 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -36,16 +36,14 @@ class pin_layout: def __eq__(self, other): """ Check if these are the same pins for duplicate checks """ if isinstance(other, self.__class__): - return (self.name==other.name and self.layer==other.layer and self.rect == other.rect) + return (self.layer==other.layer and self.rect == other.rect) else: return False def overlaps(self, other): """ Check if a shape overlaps with a rectangle """ - ll = self.rect[0] - ur = self.rect[1] - oll = other.rect[0] - our = other.rect[1] + (ll,ur) = self.rect + (oll,our) = other.rect # Start assuming no overlaps x_overlaps = False y_overlaps = False diff --git a/compiler/gdsMill/gdsMill/gds2reader.py b/compiler/gdsMill/gdsMill/gds2reader.py index 3d63cf53..85fc0c66 100644 --- a/compiler/gdsMill/gdsMill/gds2reader.py +++ b/compiler/gdsMill/gdsMill/gds2reader.py @@ -294,7 +294,7 @@ class Gds2reader: mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy rotateFlag = bool(transFlags&0x0002) magnifyFlag = bool(transFlags&0x0004) - thisSref.transFlags=[mirrorFlag,rotateFlag,magnifyFlag] + thisSref.transFlags=[mirrorFlag,magnifyFlag,rotateFlag] if(self.debugToTerminal==1): print("\t\t\tMirror X:"+str(mirrorFlag)) print( "\t\t\tRotate:"+str(rotateFlag)) @@ -345,7 +345,7 @@ class Gds2reader: mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy rotateFlag = bool(transFlags&0x0002) magnifyFlag = bool(transFlags&0x0004) - thisAref.transFlags=[mirrorFlag,rotateFlag,magnifyFlag] + thisAref.transFlags=[mirrorFlag,magnifyFlag,rotateFlag] if(self.debugToTerminal==1): print("\t\t\tMirror X:"+str(mirrorFlag)) print("\t\t\tRotate:"+str(rotateFlag)) @@ -408,7 +408,7 @@ class Gds2reader: mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy rotateFlag = bool(transFlags&0x0002) magnifyFlag = bool(transFlags&0x0004) - thisText.transFlags=[mirrorFlag,rotateFlag,magnifyFlag] + thisText.transFlags=[mirrorFlag,magnifyFlag,rotateFlag] if(self.debugToTerminal==1): print("\t\t\tMirror X:"+str(mirrorFlag)) print("\t\t\tRotate:"+str(rotateFlag)) diff --git a/compiler/gdsMill/gdsMill/gds2writer.py b/compiler/gdsMill/gdsMill/gds2writer.py index ce9d27e2..3ab1c0b7 100644 --- a/compiler/gdsMill/gdsMill/gds2writer.py +++ b/compiler/gdsMill/gdsMill/gds2writer.py @@ -280,15 +280,15 @@ class Gds2writer: if(thisSref.transFlags!=""): idBits=b'\x1A\x01' mirrorFlag = int(thisSref.transFlags[0])<<15 - rotateFlag = int(thisSref.transFlags[1])<<1 - magnifyFlag = int(thisSref.transFlags[2])<<3 + rotateFlag = int(thisSref.transFlags[2])<<1 + magnifyFlag = int(thisSref.transFlags[1])<<3 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) - if(thisSref.magFactor!=""): + if(thisSref.transFlags[1]): idBits=b'\x1B\x05' magFactor=self.ibmDataFromIeeeDouble(thisSref.magFactor) self.writeRecord(idBits+magFactor) - if(thisSref.rotateAngle!=""): + if(thisSref.transFlags[2]): idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisSref.rotateAngle) self.writeRecord(idBits+rotateAngle) @@ -327,15 +327,15 @@ class Gds2writer: if(thisAref.transFlags): idBits=b'\x1A\x01' mirrorFlag = int(thisAref.transFlags[0])<<15 - rotateFlag = int(thisAref.transFlags[1])<<1 - magnifyFlag = int(thisAref.transFlags[0])<<3 + rotateFlag = int(thisAref.transFlags[2])<<1 + magnifyFlag = int(thisAref.transFlags[1])<<3 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) - if(thisAref.magFactor): + if(thisAref.transFlags[1]): idBits=b'\x1B\x05' magFactor=self.ibmDataFromIeeeDouble(thisAref.magFactor) self.writeRecord(idBits+magFactor) - if(thisAref.rotateAngle): + if(thisAref.transFlags[2]): idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisAref.rotateAngle) self.writeRecord(idBits+rotateAngle) @@ -374,15 +374,15 @@ class Gds2writer: if(thisText.transFlags != ""): idBits=b'\x1A\x01' mirrorFlag = int(thisText.transFlags[0])<<15 - rotateFlag = int(thisText.transFlags[1])<<1 - magnifyFlag = int(thisText.transFlags[0])<<3 + rotateFlag = int(thisText.transFlags[2])<<1 + magnifyFlag = int(thisText.transFlags[1])<<3 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) - if(thisText.magFactor != ""): + if(thisText.transFlags[1]): idBits=b'\x1B\x05' magFactor=self.ibmDataFromIeeeDouble(thisText.magFactor) self.writeRecord(idBits+magFactor) - if(thisText.rotateAngle != ""): + if(thisText.transFlags[2]): idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisText.rotateAngle) self.writeRecord(idBits+rotateAngle) diff --git a/compiler/gdsMill/gdsMill/gdsPrimitives.py b/compiler/gdsMill/gdsMill/gdsPrimitives.py index 9b148dce..dc43fea6 100644 --- a/compiler/gdsMill/gdsMill/gdsPrimitives.py +++ b/compiler/gdsMill/gdsMill/gdsPrimitives.py @@ -141,7 +141,7 @@ class GdsText: self.plex="" self.drawingLayer="" self.purposeLayer = None - self.transFlags=[0,00] + self.transFlags=[0,0,0] self.magFactor="" self.rotateAngle="" self.pathType="" diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index b9813149..431d7d23 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -64,10 +64,11 @@ class VlsiLayout: #helper method to rotate a list of coordinates angle=math.radians(float(0)) if(rotateAngle): - angle = math.radians(float(repr(rotateAngle))) + angle = math.radians(float(rotateAngle)) coordinatesRotate = [] #this will hold the rotated values for coordinate in coordinatesToRotate: + # This is the CCW rotation matrix newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle) newY = coordinate[0]*math.sin(angle) + coordinate[1]*math.cos(angle) coordinatesRotate.extend((newX,newY)) @@ -274,8 +275,9 @@ class VlsiLayout: Method to insert one layout into another at a particular offset. """ offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1])) - if self.debug==1: - debug.info(0,"DEBUG: GdsMill vlsiLayout: addInstance: type %s, nameOfLayout "%type(layoutToAdd),nameOfLayout) + if self.debug: + debug.info(0,"DEBUG: GdsMill vlsiLayout: addInstance: type {0}, nameOfLayout {1}".format(type(layoutToAdd),nameOfLayout)) + debug.info(0,"DEBUG: name={0} offset={1} mirror={2} rotate={3}".format(layoutToAdd.rootStructureName,offsetInMicrons, mirror, rotate)) @@ -295,7 +297,7 @@ class VlsiLayout: - # If layoutToAdd is a unique object (not this), then copy heirarchy, + # If layoutToAdd is a unique object (not this), then copy hierarchy, # otherwise, if it is a text name of an internal structure, use it. if layoutToAdd != self: @@ -316,9 +318,7 @@ class VlsiLayout: if mirror or rotate: layoutToAddSref.transFlags = [0,0,0] - # This is NOT the same as the order in the GDS spec! - # It gets written out in gds2writer in the right order though. - # transFlags = (mirror around x-axis, rotation, magnification) + # transFlags = (mirror around x-axis, magnification, rotation) # If magnification or rotation is true, it is the flags are then # followed by an amount in the record if mirror=="R90": @@ -328,17 +328,16 @@ class VlsiLayout: if mirror=="R270": rotate = 270.0 if rotate: - #layoutToAddSref.transFlags = [0,1,0] + layoutToAddSref.transFlags[2] = 1 layoutToAddSref.rotateAngle = rotate if mirror == "x" or mirror == "MX": - layoutToAddSref.transFlags = [1,0,0] + layoutToAddSref.transFlags[0] = 1 if mirror == "y" or mirror == "MY": #NOTE: "MY" option will override specified rotate angle - #layoutToAddSref.transFlags = [1,1,0] - layoutToAddSref.transFlags = [1,0,0] + layoutToAddSref.transFlags[0] = 1 + layoutToAddSref.transFlags[2] = 1 layoutToAddSref.rotateAngle = 180.0 if mirror == "xy" or mirror == "XY": #NOTE: "XY" option will override specified rotate angle - #layoutToAddSref.transFlags = [0,1,0] - layoutToAddSref.transFlags = [0,0,0] + layoutToAddSref.transFlags[2] = 1 layoutToAddSref.rotateAngle = 180.0 #add the sref to the root structure @@ -405,10 +404,10 @@ class VlsiLayout: if(len(text)%2 == 1): text = text + '\x00' textToAdd.textString = text - textToAdd.transFlags = [0,0,1] + textToAdd.transFlags[1] = 1 textToAdd.magFactor = magnification if rotate: - textToAdd.transFlags = [0,1,1] + textToAdd.transFlags[2] = 1 textToAdd.rotateAngle = rotate #add the sref to the root structure self.structures[self.rootStructureName].texts.append(textToAdd) @@ -816,10 +815,10 @@ class VlsiLayout: """ (structureName,structureOrigin,structureuVector,structurevVector)=structure + #print(structureName,"u",structureuVector.transpose(),"v",structurevVector.transpose(),"o",structureOrigin.transpose()) boundaries = [] for boundary in self.structures[str(structureName)].boundaries: - # Pin enclosures only work on rectangular pins so ignore any non rectangle - # This may report not finding pins, but the user should fix this by adding a rectangle. + # FIXME: Right now, this only supports rectangular shapes! if len(boundary.coordinates)!=5: continue if layer==boundary.drawingLayer: @@ -827,10 +826,11 @@ class VlsiLayout: right_top=boundary.coordinates[2] # Rectangle is [leftx, bottomy, rightx, topy]. boundaryRect=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]] + # perform the rotation boundaryRect=self.transformRectangle(boundaryRect,structureuVector,structurevVector) + # add the offset boundaryRect=[boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(), boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item()] - boundaries.append(boundaryRect) return boundaries @@ -858,8 +858,12 @@ class VlsiLayout: """ Rotate a coordinate in space. """ - x=coordinate[0]*uVector[0].item()+coordinate[1]*uVector[1].item() - y=coordinate[1]*vVector[1].item()+coordinate[0]*vVector[0].item() + # MRG: 9/3/18 Incorrect matrixi multiplication! + # This is fixed to be: + # |u[0] v[0]| |x| |x'| + # |u[1] v[1]|x|y|=|y'| + x=coordinate[0]*uVector[0].item()+coordinate[1]*vVector[0].item() + y=coordinate[0]*uVector[1].item()+coordinate[1]*vVector[1].item() transformCoordinate=[x,y] return transformCoordinate diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index c88fabd3..c2455821 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -115,11 +115,11 @@ class dff_inv_array(design.design): def add_layout_pins(self): for row in range(self.rows): for col in range(self.columns): - # Continous vdd rail along with label. + # Adds power pin on left of row vdd_pin=self.dff_insts[row,col].get_pin("vdd") self.add_power_pin("vdd", vdd_pin.lc()) - # Continous gnd rail along with label. + # Adds gnd pin on left of row gnd_pin=self.dff_insts[row,col].get_pin("gnd") self.add_power_pin("gnd", gnd_pin.lc()) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index f0df378d..e39f95bc 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -171,6 +171,8 @@ class pinv(pgate.pgate): offset=vector(0.5*self.width,self.height), width=self.width) + + def create_ptx(self): """ Create the PMOS and NMOS netlist. diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 5f60da4e..8eb063cf 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -36,9 +36,6 @@ class grid: def add_blockage_shape(self,ll,ur,z): debug.info(3,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) - if ll[0]<42 and ll[0]>38 and ll[1]<3 and ll[1]>0: - debug.info(0,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) - block_list = [] for x in range(int(ll[0]),int(ur[0])+1): for y in range(int(ll[1]),int(ur[1])+1): diff --git a/compiler/router/router.py b/compiler/router/router.py index bb784b3c..20b8a612 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -49,6 +49,12 @@ class router: """ If we want to route something besides the top-level cell.""" self.top_name = top_name + def get_zindex(self,layer_num): + if layer_num==self.horiz_layer_number: + return 0 + else: + return 1 + def set_layers(self, layers): """Allows us to change the layers that we are routing on. First layer is always horizontal, middle is via, and last is always @@ -203,15 +209,20 @@ class router: def add_blockages(self): """ Add the blockages except the pin shapes """ + # Skip pin shapes + all_pins = [x[0] for x in list(self.pins.values())] + for blockage in self.blockages: - # Skip pin shapes - all_pins = [x[0] for x in list(self.pins.values())] for pin in all_pins: - if blockage.overlaps(pin): + # If the blockage overlaps the pin and is on the same layer, + # it must be connected, so skip it. + if blockage==pin: + debug.info(1,"Skipping blockage for pin {}".format(str(pin))) break else: + debug.info(2,"Adding blockage {}".format(str(blockage))) [ll,ur]=self.convert_blockage_to_tracks(blockage.rect) - zlayer = 0 if blockage.layer_num==self.horiz_layer_number else 1 + zlayer = self.get_zindex(blockage.layer_num) self.rg.add_blockage_shape(ll,ur,zlayer) @@ -227,37 +238,6 @@ class router: rect = [ll,ur] new_pin = pin_layout("blockage{}".format(len(self.blockages)),rect,layer_num) self.blockages.append(new_pin) - - # for boundary in self.layout.structures[sref].boundaries: - # coord_trans = self.translate_coordinates(boundary.coordinates, mirr, angle, xyShift) - # shape_coords = self.min_max_coord(coord_trans) - # shape = self.convert_shape_to_units(shape_coords) - - # # only consider the two layers that we are routing on - # if boundary.drawingLayer in [self.vert_layer_number,self.horiz_layer_number]: - # # store the blockages as pin layouts so they are easy to compare etc. - # new_pin = pin_layout("blockage",shape,boundary.drawingLayer) - # # avoid repeated blockage pins - # if new_pin not in self.blockages: - # self.blockages.append(new_pin) - - - # # recurse given the mirror, angle, etc. - # for cur_sref in self.layout.structures[sref].srefs: - # sMirr = 1 - # if cur_sref.transFlags[0] == True: - # sMirr = -1 - # sAngle = math.radians(float(0)) - # if cur_sref.rotateAngle: - # sAngle = math.radians(float(cur_sref.rotateAngle)) - # sAngle += angle - # x = cur_sref.coordinates[0] - # y = cur_sref.coordinates[1] - # newX = (x)*math.cos(angle) - mirr*(y)*math.sin(angle) + xyShift[0] - # newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1] - # sxyShift = (newX, newY) - - # self.get_blockages(cur_sref.sName, sMirr, sAngle, sxyShift) def convert_point_to_units(self,p): @@ -282,9 +262,11 @@ class router: old_ur = ur ll=ll.scale(self.track_factor) ur=ur.scale(self.track_factor) + if ll[0]<45 and ll[0]>35 and ll[1]<46 and ll[1]>39: + print(ll,ur) ll = ll.floor() ur = ur.ceil() - if ll[0]<45 and ll[0]>35 and ll[1]<10 and ll[1]>0: + if ll[0]<45 and ll[0]>35 and ll[1]<46 and ll[1]>39: debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur)) debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur)) return [ll,ur] @@ -296,9 +278,6 @@ class router: If a pin has insufficent overlap, it returns the blockage list to avoid it. """ (ll,ur) = pin.rect - #ll = snap_to_grid(ll) - #ur = snap_to_grid(ur) - #debug.info(1,"Converting [ {0} , {1} ]".format(ll,ur)) # scale the size bigger to include neaby tracks @@ -306,37 +285,40 @@ class router: ur=ur.scale(self.track_factor).ceil() # width depends on which layer it is - zindex = 0 if pin.layer_num==self.horiz_layer_number else 1 - if zindex==0: - width = self.horiz_layer_width - else: + zindex=self.get_zindex(pin.layer_num) + if zindex: width = self.vert_layer_width + else: + width = self.horiz_layer_width track_list = [] block_list = [] - # include +- 1 so when a shape is less than one grid - for x in range(ll[0]-1,ur[0]+1): - for y in range(ll[1]-1,ur[1]+1): + + track_area = self.track_width*self.track_width + for x in range(ll[0],ur[0]): + for y in range(ll[1],ur[1]): #debug.info(1,"Converting [ {0} , {1} ]".format(x,y)) - # get the rectangular pin at a track location - # if dimension of overlap is greater than min width in any dimension, - # it will be an on-grid pin - rect = self.convert_track_to_pin(vector3d(x,y,zindex)) - max_overlap=max(self.compute_overlap(pin.rect,rect)) # however, if there is not enough overlap, then if there is any overlap at all, # we need to block it to prevent routes coming in on that grid - full_rect = self.convert_full_track_to_shape(vector3d(x,y,zindex)) - full_overlap=max(self.compute_overlap(pin.rect,full_rect)) - + full_rect = self.convert_track_to_shape(vector3d(x,y,zindex)) + overlap_rect=self.compute_overlap(pin.rect,full_rect) + overlap_area = overlap_rect[0]*overlap_rect[1] #debug.info(1,"Check overlap: {0} {1} max={2}".format(shape,rect,max_overlap)) - if max_overlap >= width: + + # Assume if more than half the area, it is occupied + overlap_ratio = overlap_area/track_area + if overlap_ratio > 0.5: track_list.append(vector3d(x,y,zindex)) - elif full_overlap>0: + # otherwise, the pin may not be accessible, so block it + elif overlap_ratio > 0: block_list.append(vector3d(x,y,zindex)) else: debug.info(4,"No overlap: {0} {1} max={2}".format(pin.rect,rect,max_overlap)) - + print("H:",x,y) + if x>38 and x<42 and y>42 and y<45: + print(pin) + print(full_rect, overlap_rect, overlap_ratio) #debug.warning("Off-grid pin for {0}.".format(str(pin))) #debug.info(1,"Converted [ {0} , {1} ]".format(ll,ur)) return (track_list,block_list) @@ -381,7 +363,7 @@ class router: return [ll,ur] - def convert_full_track_to_shape(self,track): + def convert_track_to_shape(self,track): """ Convert a grid point into a rectangle shape that occupies the entire centered track. @@ -447,9 +429,8 @@ class router: grid_keys=self.rg.map.keys() partial_track=vector(0,self.track_width/6.0) for g in grid_keys: - continue # for now... - shape = self.convert_full_track_to_shape(g) - self.cell.add_rect(layer="boundary", + shape = self.convert_track_to_shape(g) + self.cell.add_rect(layer="text", offset=shape[0], width=shape[1].x-shape[0].x, height=shape[1].y-shape[0].y) @@ -481,7 +462,7 @@ class router: zoom=0.05) for blockage in self.blockages: - self.cell.add_rect(layer="boundary", + self.cell.add_rect(layer="blockage", offset=blockage.ll(), width=blockage.width(), height=blockage.height()) diff --git a/compiler/router/tests/10_supply_grid_test.py b/compiler/router/tests/10_supply_grid_test.py index 485d925c..2fa5cb7b 100755 --- a/compiler/router/tests/10_supply_grid_test.py +++ b/compiler/router/tests/10_supply_grid_test.py @@ -32,12 +32,14 @@ class no_blockages_test(openram_test): globals.setup_paths() from control_logic import control_logic cell = control_logic(16) + #from pinv import pinv + #cell = pinv() #gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),"control_logic") #cell = gds_cell(name, gds_file) self.add_inst(name=name, mod=cell, offset=[0,0]) - self.connect_inst(["csb","web","clk","s_en","w_en","clk_buf_bar","clk_buf","vdd","gnd"]) + self.connect_inst(cell.pin_map.keys()) r=router(module=cell) layer_stack =("metal3","via2","metal2") diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 50d8977c..3c28fab5 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -56,6 +56,7 @@ layer["via9"] = 28 layer["metal10"] = 29 layer["text"] = 239 layer["boundary"]= 239 +layer["blockage"]= 239 ################################################### ##END GDS Layer Map diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py index 4998c5b6..a3f51604 100755 --- a/technology/scn3me_subm/tech/tech.py +++ b/technology/scn3me_subm/tech/tech.py @@ -41,6 +41,7 @@ layer["via2"] = 61 layer["metal3"] = 62 layer["text"] = 63 layer["boundary"] = 63 +layer["blockage"] = 83 ################################################### ##END GDS Layer Map From 9d40cd4a0385d5dd796b2323b486b64e694a2aec Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 16:39:13 -0700 Subject: [PATCH 24/31] Remove verbose print statement in add_power_pin --- compiler/base/hierarchy_layout.py | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 83a8420b..28991826 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -845,7 +845,6 @@ class layout(lef.lef): """ Add a single power pin from M3 down to M1 at the given center location """ - debug.info(0,"Adding power pin {0} at {1} rotate={2}".format(name, loc, rotate)) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=loc, rotate=rotate) From 5395f21be9616633b517c0b66bce809db4fc7333 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 16:40:52 -0700 Subject: [PATCH 25/31] Remove unique id in contact that was used for debugging --- compiler/base/contact.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 9ffae803..1d4beb11 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -16,26 +16,23 @@ class contact(hierarchy_design.hierarchy_design): hierarchy as the contact. """ - unique_id = 1 - def __init__(self, layer_stack, dimensions=[1,1], implant_type=None, well_type=None): if implant_type or well_type: - name = "{0}_{1}_{2}_{3}x{4}_{5}{6}_{7}".format(layer_stack[0], - layer_stack[1], - layer_stack[2], - dimensions[0], - dimensions[1], - implant_type, - well_type, - contact.unique_id) + name = "{0}_{1}_{2}_{3}x{4}_{5}{6}".format(layer_stack[0], + layer_stack[1], + layer_stack[2], + dimensions[0], + dimensions[1], + implant_type, + well_type) + else: - name = "{0}_{1}_{2}_{3}x{4}_{5}".format(layer_stack[0], - layer_stack[1], - layer_stack[2], - dimensions[0], - dimensions[1], - contact.unique_id) - contact.unique_id += 1 + name = "{0}_{1}_{2}_{3}x{4}".format(layer_stack[0], + layer_stack[1], + layer_stack[2], + dimensions[0], + dimensions[1]) + hierarchy_design.hierarchy_design.__init__(self, name) debug.info(4, "create contact object {0}".format(name)) From 73e2bd2653516a0cb1d90bbde4a1a009703bbd9d Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 16:43:59 -0700 Subject: [PATCH 26/31] Removed solid display format for comments to allow grid/blockage visibility. --- technology/scn3me_subm/tf/display.drf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/technology/scn3me_subm/tf/display.drf b/technology/scn3me_subm/tf/display.drf index e9a22348..047879b6 100644 --- a/technology/scn3me_subm/tf/display.drf +++ b/technology/scn3me_subm/tf/display.drf @@ -625,7 +625,7 @@ drDefinePacket( ( display deviceAnt stipple0 solid yellow yellow solid ) ( display winBottomShadow solid solid winColor1 winColor1 solid ) ( display PselectNet dots4 solid brown brown outlineStipple) - ( display comment stipple0 lineStyle0 winBack winBack solid ) + ( display comment stipple0 lineStyle0 winBack winBack outline ) ( display Poly1 dots lineStyle0 red red outlineStipple) ( display Unrouted stipple0 lineStyle1 winColor5 winColor5 solid ) ( display stretch stipple0 solid yellow yellow solid ) From 8ffdcdf277f40801aa234e2086a19b7e1c96589f Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Tue, 4 Sep 2018 17:27:50 -0700 Subject: [PATCH 27/31] Fixed bit shift amount error. Removed rotate flag for Calibre. --- compiler/base/hierarchy_layout.py | 4 ++-- compiler/gdsMill/gdsMill/gds2writer.py | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 28991826..3ac5ea63 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -847,10 +847,10 @@ class layout(lef.lef): """ self.add_via_center(layers=("metal1", "via1", "metal2"), offset=loc, - rotate=rotate) + rotate=float(rotate)) via=self.add_via_center(layers=("metal2", "via2", "metal3"), offset=loc, - rotate=rotate) + rotate=float(rotate)) self.add_layout_pin_rect_center(text=name, layer="metal3", offset=loc, diff --git a/compiler/gdsMill/gdsMill/gds2writer.py b/compiler/gdsMill/gdsMill/gds2writer.py index 3ab1c0b7..1a3d75af 100644 --- a/compiler/gdsMill/gdsMill/gds2writer.py +++ b/compiler/gdsMill/gdsMill/gds2writer.py @@ -280,8 +280,11 @@ class Gds2writer: if(thisSref.transFlags!=""): idBits=b'\x1A\x01' mirrorFlag = int(thisSref.transFlags[0])<<15 - rotateFlag = int(thisSref.transFlags[2])<<1 - magnifyFlag = int(thisSref.transFlags[1])<<3 + # FIXME: For some reason, Calibre doesn't like when this flag is set + # but it will still pay attention to the rotate value! + #rotateFlag = int(thisSref.transFlags[2])<<1 + rotateFlag=0 + magnifyFlag = int(thisSref.transFlags[1])<<2 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) if(thisSref.transFlags[1]): @@ -328,7 +331,7 @@ class Gds2writer: idBits=b'\x1A\x01' mirrorFlag = int(thisAref.transFlags[0])<<15 rotateFlag = int(thisAref.transFlags[2])<<1 - magnifyFlag = int(thisAref.transFlags[1])<<3 + magnifyFlag = int(thisAref.transFlags[1])<<2 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) if(thisAref.transFlags[1]): @@ -375,7 +378,7 @@ class Gds2writer: idBits=b'\x1A\x01' mirrorFlag = int(thisText.transFlags[0])<<15 rotateFlag = int(thisText.transFlags[2])<<1 - magnifyFlag = int(thisText.transFlags[1])<<3 + magnifyFlag = int(thisText.transFlags[1])<<2 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) if(thisText.transFlags[1]): From 0f87ba742f5cfd258d86db9d9da34689f3fb6af5 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 5 Sep 2018 09:28:43 -0700 Subject: [PATCH 28/31] Add back LEF blockages. Remove "absolute" flags from GDS output --- compiler/base/geometry.py | 80 +++++++++++++------------- compiler/gdsMill/gdsMill/gds2writer.py | 40 ++++++++----- compiler/gdsMill/gdsMill/vlsiLayout.py | 10 ++-- 3 files changed, 71 insertions(+), 59 deletions(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 682b783b..7144a2cf 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -34,14 +34,14 @@ class geometry: # coordinate += [(x, y)] # return coordinate - # def transform_coords(self, coords, offset, mirr, angle): - # """Calculate coordinates after flip, rotate, and shift""" - # coordinate = [] - # for item in coords: - # x = item[0]*math.cos(angle) - item[1]*mirr*math.sin(angle) + offset[0] - # y = item[0]*math.sin(angle) + item[1]*mirr*math.cos(angle) + offset[1] - # coordinate += [[x, y]] - # return coordinate + def transform_coords(self, coords, offset, mirr, angle): + """Calculate coordinates after flip, rotate, and shift""" + coordinate = [] + for item in coords: + x = item[0]*math.cos(angle) - item[1]*mirr*math.sin(angle) + offset[0] + y = item[0]*math.sin(angle) + item[1]*mirr*math.cos(angle) + offset[1] + coordinate += [[x, y]] + return coordinate def normalize(self): """ Re-find the LL and UR points after a transform """ @@ -148,40 +148,40 @@ class instance(geometry): debug.info(4, "creating instance: " + self.name) - # def get_blockages(self, layer, top=False): - # """ Retrieve rectangular blockages of all modules in this instance. - # Apply the transform of the instance placement to give absolute blockages.""" - # angle = math.radians(float(self.rotate)) - # mirr = 1 - # if self.mirror=="R90": - # angle += math.radians(90.0) - # elif self.mirror=="R180": - # angle += math.radians(180.0) - # elif self.mirror=="R270": - # angle += math.radians(270.0) - # elif self.mirror=="MX": - # mirr = -1 - # elif self.mirror=="MY": - # mirr = -1 - # angle += math.radians(180.0) - # elif self.mirror=="XY": - # mirr = 1 - # angle += math.radians(180.0) + def get_blockages(self, layer, top=False): + """ Retrieve rectangular blockages of all modules in this instance. + Apply the transform of the instance placement to give absolute blockages.""" + angle = math.radians(float(self.rotate)) + mirr = 1 + if self.mirror=="R90": + angle += math.radians(90.0) + elif self.mirror=="R180": + angle += math.radians(180.0) + elif self.mirror=="R270": + angle += math.radians(270.0) + elif self.mirror=="MX": + mirr = -1 + elif self.mirror=="MY": + mirr = -1 + angle += math.radians(180.0) + elif self.mirror=="XY": + mirr = 1 + angle += math.radians(180.0) - # if self.mod.is_library_cell: - # # For lib cells, block the whole thing except on metal3 - # # since they shouldn't use metal3 - # if layer==tech.layer["metal1"] or layer==tech.layer["metal2"]: - # return [self.transform_coords(self.mod.get_boundary(), self.offset, mirr, angle)] - # else: - # return [] - # else: + if self.mod.is_library_cell: + # For lib cells, block the whole thing except on metal3 + # since they shouldn't use metal3 + if layer==tech.layer["metal1"] or layer==tech.layer["metal2"]: + return [self.transform_coords(self.mod.get_boundary(), self.offset, mirr, angle)] + else: + return [] + else: - # blockages = self.mod.get_blockages(layer) - # new_blockages = [] - # for b in blockages: - # new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) - # return new_blockages + blockages = self.mod.get_blockages(layer) + new_blockages = [] + for b in blockages: + new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) + return new_blockages def gds_write_file(self, new_layout): """Recursively writes all the sub-modules in this instance""" diff --git a/compiler/gdsMill/gdsMill/gds2writer.py b/compiler/gdsMill/gdsMill/gds2writer.py index 1a3d75af..402416cd 100644 --- a/compiler/gdsMill/gdsMill/gds2writer.py +++ b/compiler/gdsMill/gdsMill/gds2writer.py @@ -280,18 +280,20 @@ class Gds2writer: if(thisSref.transFlags!=""): idBits=b'\x1A\x01' mirrorFlag = int(thisSref.transFlags[0])<<15 - # FIXME: For some reason, Calibre doesn't like when this flag is set - # but it will still pay attention to the rotate value! - #rotateFlag = int(thisSref.transFlags[2])<<1 + # The rotate and magnify flags specify "absolute" rotate and magnify. + # It is unclear what that is (ignore all further rotates/mags in the + # hierarchy? But anyway, calibre doesn't support it. rotateFlag=0 - magnifyFlag = int(thisSref.transFlags[1])<<2 + magnifyFlag = 0 + #rotateFlag = int(thisSref.transFlags[2])<<1 + #magnifyFlag = int(thisSref.transFlags[1])<<2 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) - if(thisSref.transFlags[1]): + if(thisSref.magFactor!=""): idBits=b'\x1B\x05' magFactor=self.ibmDataFromIeeeDouble(thisSref.magFactor) self.writeRecord(idBits+magFactor) - if(thisSref.transFlags[2]): + if(thisSref.rotateAngle!=""): idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisSref.rotateAngle) self.writeRecord(idBits+rotateAngle) @@ -330,15 +332,20 @@ class Gds2writer: if(thisAref.transFlags): idBits=b'\x1A\x01' mirrorFlag = int(thisAref.transFlags[0])<<15 - rotateFlag = int(thisAref.transFlags[2])<<1 - magnifyFlag = int(thisAref.transFlags[1])<<2 + # The rotate and magnify flags specify "absolute" rotate and magnify. + # It is unclear what that is (ignore all further rotates/mags in the + # hierarchy? But anyway, calibre doesn't support it. + rotateFlag=0 + magnifyFlag = 0 + #rotateFlag = int(thisAref.transFlags[2])<<1 + #magnifyFlag = int(thisAref.transFlags[1])<<2 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) - if(thisAref.transFlags[1]): + if(thisAref.magFactor!=""): idBits=b'\x1B\x05' magFactor=self.ibmDataFromIeeeDouble(thisAref.magFactor) self.writeRecord(idBits+magFactor) - if(thisAref.transFlags[2]): + if(thisAref.rotateAngle!=""): idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisAref.rotateAngle) self.writeRecord(idBits+rotateAngle) @@ -377,15 +384,20 @@ class Gds2writer: if(thisText.transFlags != ""): idBits=b'\x1A\x01' mirrorFlag = int(thisText.transFlags[0])<<15 - rotateFlag = int(thisText.transFlags[2])<<1 - magnifyFlag = int(thisText.transFlags[1])<<2 + # The rotate and magnify flags specify "absolute" rotate and magnify. + # It is unclear what that is (ignore all further rotates/mags in the + # hierarchy? But anyway, calibre doesn't support it. + rotateFlag=0 + magnifyFlag = 0 + #rotateFlag = int(thisText.transFlags[2])<<1 + #magnifyFlag = int(thisText.transFlags[1])<<2 transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) self.writeRecord(idBits+transFlags) - if(thisText.transFlags[1]): + if(thisText.magFactor!=""): idBits=b'\x1B\x05' magFactor=self.ibmDataFromIeeeDouble(thisText.magFactor) self.writeRecord(idBits+magFactor) - if(thisText.transFlags[2]): + if(thisText.rotateAngle!=""): idBits=b'\x1C\x05' rotateAngle=self.ibmDataFromIeeeDouble(thisText.rotateAngle) self.writeRecord(idBits+rotateAngle) diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index 431d7d23..5e12619c 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -328,16 +328,16 @@ class VlsiLayout: if mirror=="R270": rotate = 270.0 if rotate: - layoutToAddSref.transFlags[2] = 1 + #layoutToAddSref.transFlags[2] = 1 layoutToAddSref.rotateAngle = rotate if mirror == "x" or mirror == "MX": layoutToAddSref.transFlags[0] = 1 if mirror == "y" or mirror == "MY": #NOTE: "MY" option will override specified rotate angle layoutToAddSref.transFlags[0] = 1 - layoutToAddSref.transFlags[2] = 1 + #layoutToAddSref.transFlags[2] = 1 layoutToAddSref.rotateAngle = 180.0 if mirror == "xy" or mirror == "XY": #NOTE: "XY" option will override specified rotate angle - layoutToAddSref.transFlags[2] = 1 + #layoutToAddSref.transFlags[2] = 1 layoutToAddSref.rotateAngle = 180.0 #add the sref to the root structure @@ -404,10 +404,10 @@ class VlsiLayout: if(len(text)%2 == 1): text = text + '\x00' textToAdd.textString = text - textToAdd.transFlags[1] = 1 + #textToAdd.transFlags[1] = 1 textToAdd.magFactor = magnification if rotate: - textToAdd.transFlags[2] = 1 + #textToAdd.transFlags[2] = 1 textToAdd.rotateAngle = rotate #add the sref to the root structure self.structures[self.rootStructureName].texts.append(textToAdd) From 2a27fbc98e866954da847e59b13361ca1d3de14b Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 5 Sep 2018 10:02:12 -0700 Subject: [PATCH 29/31] Fix temp directory preservation option. Make labels in freepdk45 replica bitcell lower case. --- compiler/globals.py | 18 +++--------------- compiler/router/router.py | 6 ++++-- .../freepdk45/gds_lib/replica_cell_6t.gds | Bin 20480 -> 20480 bytes 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/compiler/globals.py b/compiler/globals.py index 3bdc5f92..7a7d0098 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -118,9 +118,6 @@ def init_openram(config_file, is_unit_test=True): init_paths() - # This depends on the tech, so do it after tech is loaded - init_config() - # Reset the static duplicate name checker for unit tests. import hierarchy_design hierarchy_design.hierarchy_design.name_map=[] @@ -257,7 +254,8 @@ def cleanup_paths(): if not OPTS.purge_temp: debug.info(0,"Preserving temp directory: {}".format(OPTS.openram_temp)) return - if os.path.exists(OPTS.openram_temp): + elif os.path.exists(OPTS.openram_temp): + debug.info(1,"Purging temp directory: {}".format(OPTS.openram_temp)) # This annoyingly means you have to re-cd into the directory each debug iteration #shutil.rmtree(OPTS.openram_temp, ignore_errors=True) contents = [os.path.join(OPTS.openram_temp, i) for i in os.listdir(OPTS.openram_temp)] @@ -293,8 +291,6 @@ def setup_paths(): OPTS.openram_temp += "/" debug.info(1, "Temporary files saved in " + OPTS.openram_temp) - cleanup_paths() - def is_exe(fpath): @@ -316,6 +312,7 @@ def init_paths(): # make the directory if it doesn't exist try: + debug.info(1,"Creating temp directory: {}".format(OPTS.openram_temp)) os.makedirs(OPTS.openram_temp, 0o750) except OSError as e: if e.errno == 17: # errno.EEXIST @@ -330,16 +327,7 @@ def init_paths(): os.chmod(OPTS.output_path, 0o750) except: debug.error("Unable to make output directory.",-1) - -def init_config(): - """ Initialize the SRAM configurations. """ - # Create the SRAM configuration - from sram_config import sram_config - OPTS.sram_config = sram_config(OPTS.word_size, - OPTS.num_words, - OPTS.num_banks) - # imports correct technology directories for testing def import_tech(): diff --git a/compiler/router/router.py b/compiler/router/router.py index 20b8a612..c3ce007d 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -262,13 +262,15 @@ class router: old_ur = ur ll=ll.scale(self.track_factor) ur=ur.scale(self.track_factor) - if ll[0]<45 and ll[0]>35 and ll[1]<46 and ll[1]>39: + if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5: print(ll,ur) ll = ll.floor() ur = ur.ceil() - if ll[0]<45 and ll[0]>35 and ll[1]<46 and ll[1]>39: + if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5: debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur)) debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur)) + pin=self.convert_track_to_shape(ll) + debug.info(0,"Pin {}".format(pin)) return [ll,ur] def convert_pin_to_tracks(self, pin): diff --git a/technology/freepdk45/gds_lib/replica_cell_6t.gds b/technology/freepdk45/gds_lib/replica_cell_6t.gds index 9b301c08ae8f2b857d6cbdef1795cc73b82ea61a..1316c276cdb156fc0f599093fabe56a5310d8684 100644 GIT binary patch delta 961 zcmYLGPe>eB9G*AMj1KrKPE=0P0RjKcQx+1&b%2X5`<_+KpozH^6Q_M{w%9l1cMI=(#?k2EunFyM7k2j+2D``64s$fZ zD&%ktjjkoItWXW@po>P3d_fc8UN{Q6^)1rD>9F}!ATtT(foZ?C~^U7i7MY`jnMQTI z`byiCPcv9&=mEZTk9mDe^`tLq%=5<>!>dsZ)~Jj6Nb`SdeOQH`x`o3O$FpP%o2N*0 zisI55{F6F1Ptw1oGsnG7;{Fi*M>=<`KTHlHG*rfZnq!0Zis@d^cLN8T^yW;FsBucy$Gt*@}4eHvTGGcv|MTUA_#fOcyR2@Jn=fiH0dr zU7~u1>KWa$C+O1FbQAJ4_xwC#-e%zr?G#kcoDfCG6Dl@lOwZ2WWOUR5o^y^>z68z< zh+IWeGf>kw3{8DNw56(WRRe~~Are}1)S7eAs<^0Ch*u1ldc|w%E`zI^Fz2}^E&RvO zvQ&?stFhgd&LEm3n}6xGbQ;gqDUTrOU*m<7#o6UJ{OXVRy=vknVT|yHGmdrV0&;{8 YOBZmTkaJGp+oj_1I%_D6t zu4`#+L^HLdHmTX#1eO14_cmXKsh`22=%6oJxFDK)(Z_o`HdbO1Pj%kvxA8D$qaAaR zj4M1aByJmZct#5eqk&;;^`G*?afut_9eCtuY!`2hZNVH(i(GOWQNlqlq2MLxXVK5vhGj86CebIXW4rjw+(tkC zJ60w*YGwmJW*X>a=%4uzPkDDZ(Loe{1vl~%EBV(jny(|8p?=1NKU0UrILi6>Bv*r% zqbK8s(>3&`CA?`LS;o;6>!*BNnX18>YGG&cCag)$on?LY2+?uA6W($5DP-_dsf@#X z3cZqp;anP*OE!!W|FL)#wbRS`n?9Zt+qhP^f_~9~S?nTO;#Zfrszq*esfnn-HiafU za)N9X1IpQMk!xB|VKo1dK=0H5)+x^Mk_^4)ga|L3yeVLvbi(0_F@c>E9P>o5AI%Fy z3mkFb|Jr%ZGfzVznq%`h=46u`&9Zow#k1Tad6+N3E*J4=Wfp&yQeoF-b7?E+HMu`H ztD}3?IauzB{jM!As7c-*{a56|u$#DFZK7LsAgdHrTex3gUSVEgUMbSOfX&k;9+$_T zb^4;5)YWAa%XMA(_^s5&xAHu$%U9t_2ZLq+Md(e5fg{l+6rno~jv5Nx2A#=zgYR_* zL0v)BoxAlj`0nC4Y+ZZ1)oxs;*<8C{%7}v_23R zc8XZu$l>;Y;GqdKO!L Date: Wed, 5 Sep 2018 10:59:41 -0700 Subject: [PATCH 30/31] Change options in pbitcell test to be global again. --- compiler/tests/04_pbitcell_test.py | 60 +++++++++++++++--------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 70f51a99..0b6bd8f5 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -20,72 +20,72 @@ class pbitcell_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) from pbitcell import pbitcell import tech - OPTS.sram_config.num_rw_ports=1 - OPTS.sram_config.num_w_ports=1 - OPTS.sram_config.num_r_ports=1 + OPTS.num_rw_ports=1 + OPTS.num_w_ports=1 + OPTS.num_r_ports=1 debug.info(2, "Bitcell with 1 of each port: read/write, write, and read") tx = pbitcell() self.local_check(tx) - OPTS.sram_config.num_rw_ports=0 - OPTS.sram_config.num_w_ports=1 - OPTS.sram_config.num_r_ports=1 + OPTS.num_rw_ports=0 + OPTS.num_w_ports=1 + OPTS.num_r_ports=1 debug.info(2, "Bitcell with 0 read/write ports") tx = pbitcell() self.local_check(tx) - OPTS.sram_config.num_rw_ports=1 - OPTS.sram_config.num_w_ports=0 - OPTS.sram_config.num_r_ports=1 + OPTS.num_rw_ports=1 + OPTS.num_w_ports=0 + OPTS.num_r_ports=1 debug.info(2, "Bitcell with 0 write ports") tx = pbitcell() self.local_check(tx) - OPTS.sram_config.num_rw_ports=1 - OPTS.sram_config.num_w_ports=1 - OPTS.sram_config.num_r_ports=0 + OPTS.num_rw_ports=1 + OPTS.num_w_ports=1 + OPTS.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports") tx = pbitcell() self.local_check(tx) - OPTS.sram_config.num_rw_ports=1 - OPTS.sram_config.num_w_ports=0 - OPTS.sram_config.num_r_ports=0 + OPTS.num_rw_ports=1 + OPTS.num_w_ports=0 + OPTS.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports and 0 write ports") tx = pbitcell() self.local_check(tx) - OPTS.sram_config.num_rw_ports=2 - OPTS.sram_config.num_w_ports=2 - OPTS.sram_config.num_r_ports=2 + OPTS.num_rw_ports=2 + OPTS.num_w_ports=2 + OPTS.num_r_ports=2 debug.info(2, "Bitcell with 2 of each port: read/write, write, and read") tx = pbitcell() self.local_check(tx) - OPTS.sram_config.num_rw_ports=0 - OPTS.sram_config.num_w_ports=2 - OPTS.sram_config.num_r_ports=2 + OPTS.num_rw_ports=0 + OPTS.num_w_ports=2 + OPTS.num_r_ports=2 debug.info(2, "Bitcell with 0 read/write ports") tx = pbitcell() self.local_check(tx) - OPTS.sram_config.num_rw_ports=2 - OPTS.sram_config.num_w_ports=0 - OPTS.sram_config.num_r_ports=2 + OPTS.num_rw_ports=2 + OPTS.num_w_ports=0 + OPTS.num_r_ports=2 debug.info(2, "Bitcell with 0 write ports") tx = pbitcell() self.local_check(tx) - OPTS.sram_config.num_rw_ports=2 - OPTS.sram_config.num_w_ports=2 - OPTS.sram_config.num_r_ports=0 + OPTS.num_rw_ports=2 + OPTS.num_w_ports=2 + OPTS.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports") tx = pbitcell() self.local_check(tx) - OPTS.sram_config.num_rw_ports=2 - OPTS.sram_config.num_w_ports=0 - OPTS.sram_config.num_r_ports=0 + OPTS.num_rw_ports=2 + OPTS.num_w_ports=0 + OPTS.num_r_ports=0 debug.info(2, "Bitcell with 0 read ports and 0 write ports") tx = pbitcell() self.local_check(tx) From b1c63a6c62d1dab61b783e1d4aec902d98ca2792 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 5 Sep 2018 11:06:17 -0700 Subject: [PATCH 31/31] Add inflate blockages and remove pins from blockages. --- compiler/base/pin_layout.py | 17 ++++++++++- compiler/router/router.py | 58 ++++++++++++++++++++++--------------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 7e454df4..bb65f771 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -1,5 +1,5 @@ import debug -from tech import GDS +from tech import GDS, drc from vector import vector from tech import layer @@ -40,6 +40,21 @@ class pin_layout: else: return False + def inflate(self, spacing=None): + """ + Inflate the rectangle by the spacing (or other rule) + and return the new rectangle. + """ + if not spacing: + spacing = drc["{0}_to_{0}".format(self.layer)] + + (ll,ur) = self.rect + spacing = vector(spacing, spacing) + newll = ll - spacing + newur = ur + spacing + + return (newll, newur) + def overlaps(self, other): """ Check if a shape overlaps with a rectangle """ (ll,ur) = self.rect diff --git a/compiler/router/router.py b/compiler/router/router.py index c3ce007d..73c0681d 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -90,19 +90,19 @@ class router: - def find_pin(self,pin): + def find_pin(self,pin_name): """ Finds the pin shapes and converts to tracks. Pin can either be a label or a location,layer pair: [[x,y],layer]. """ - label_list=self.layout.getPinShapeByLabel(str(pin)) + shape_list=self.layout.getPinShapeByLabel(str(pin_name)) pin_list = [] - for label in label_list: - (name,layer,boundary)=label + for shape in shape_list: + (name,layer,boundary)=shape rect = [vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])] - # this is a list because other cells/designs may have must-connect pins - pin_list.append(pin_layout(pin, rect, layer)) + pin = pin_layout(pin_name, rect, layer) + pin_list.append(pin) debug.check(len(pin_list)>0,"Did not find any pin shapes for {0}.".format(str(pin))) @@ -208,22 +208,30 @@ class router: def add_blockages(self): - """ Add the blockages except the pin shapes """ - # Skip pin shapes - all_pins = [x[0] for x in list(self.pins.values())] - + """ Add the blockages except the pin shapes. Also remove the pin shapes from the blockages list. """ + # Join all the pin shapes into one big list + all_pins = [item for sublist in list(self.pins.values()) for item in sublist] + + # Do an n^2 check to see if any shapes are the same, otherwise add them + # FIXME: Make faster, but number of pins won't be *that* large + real_blockages = [] for blockage in self.blockages: for pin in all_pins: # If the blockage overlaps the pin and is on the same layer, # it must be connected, so skip it. if blockage==pin: - debug.info(1,"Skipping blockage for pin {}".format(str(pin))) + debug.info(1,"Removing blockage for pin {}".format(str(pin))) break else: debug.info(2,"Adding blockage {}".format(str(blockage))) - [ll,ur]=self.convert_blockage_to_tracks(blockage.rect) + # Inflate the blockage by spacing rule + [ll,ur]=self.convert_blockage_to_tracks(blockage.inflate()) zlayer = self.get_zindex(blockage.layer_num) self.rg.add_blockage_shape(ll,ur,zlayer) + real_blockages.append(blockage) + + # Remember the filtered blockages + self.blockages = real_blockages def get_blockages(self, layer_num): @@ -262,15 +270,15 @@ class router: old_ur = ur ll=ll.scale(self.track_factor) ur=ur.scale(self.track_factor) - if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5: - print(ll,ur) - ll = ll.floor() - ur = ur.ceil() - if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5: - debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur)) - debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur)) - pin=self.convert_track_to_shape(ll) - debug.info(0,"Pin {}".format(pin)) + # We can round since we are using inflated shapes + # and the track points are at the center + ll = ll.round() + ur = ur.round() + # if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5: + # debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur)) + # debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur)) + # pin=self.convert_track_to_shape(ll) + # debug.info(0,"Pin {}".format(pin)) return [ll,ur] def convert_pin_to_tracks(self, pin): @@ -464,10 +472,12 @@ class router: zoom=0.05) for blockage in self.blockages: + # Display the inflated blockage + (ll,ur) = blockage.inflate() self.cell.add_rect(layer="blockage", - offset=blockage.ll(), - width=blockage.width(), - height=blockage.height()) + offset=ll, + width=ur.x-ll.x, + height=ur.y-ll.y) # FIXME: This should be replaced with vector.snap_to_grid at some point