From f62016ad9fff6157c3a8e818a8403b95cbfec7f5 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 3 Mar 2020 12:40:08 +0000 Subject: [PATCH 001/101] revert dff_buf for no body contact --- compiler/modules/dff_buf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 6f06b778..8f581606 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -91,8 +91,8 @@ class dff_buf(design.design): def create_instances(self): self.dff_inst=self.add_inst(name="dff_buf_dff", mod=self.dff) - self.connect_inst(props.dff_buff.buf_ports) - #self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) + #self.connect_inst(props.dff_buff.buf_ports) + self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) self.inv1_inst=self.add_inst(name="dff_buf_inv1", mod=self.inv1) From f21791a904ee2ca16fb0e5db4f4a6a4757d4d874 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 23 Mar 2020 11:36:45 -0700 Subject: [PATCH 002/101] Add source drain contact options to ptx. --- compiler/pgates/pnor2.py | 3 +- compiler/pgates/ptx.py | 105 +++++++++++++-------- compiler/tests/03_ptx_1finger_nmos_test.py | 1 + 3 files changed, 68 insertions(+), 41 deletions(-) diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 3d998d25..453a62b6 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -187,8 +187,7 @@ class pnor2(pgate.pgate): def route_inputs(self): """ Route the A and B inputs """ # Use M2 spaces so we can drop vias on the pins later! - inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \ - + self.m2_space + self.m2_width + inputB_yoffset = self.nmos2_inst.uy() + 0.5 * contact.poly_contact.height self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 46726b37..ae20d586 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -27,15 +27,23 @@ class ptx(design.design): width=drc("minwidth_tx"), mults=1, tx_type="nmos", + add_source_contact=True, + add_drain_contact=True, + series_devices=False, connect_active=False, connect_poly=False, - series_devices=False, num_contacts=None): # 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 = "{0}_m{1}_w{2:.3f}".format(tx_type, mults, width) + if not add_source_contact: + name += "_ns" + if not add_drain_contact: + name += "_nd" + if series_devices: + name += "_sd" if connect_active: name += "_a" if connect_poly: @@ -52,6 +60,8 @@ class ptx(design.design): self.tx_width = width self.connect_active = connect_active self.connect_poly = connect_poly + self.add_source_contact = add_source_contact + self.add_drain_contact = add_drain_contact self.series_devices = series_devices self.num_contacts = num_contacts @@ -379,18 +389,24 @@ class ptx(design.design): # First one is always a SOURCE label = "S" pos = self.contact_offset - contact=self.add_via_center(layers=self.active_stack, - offset=pos, - size=(1, self.num_contacts), - directions=("V","V"), - implant_type=self.implant_type, - well_type=self.well_type) - self.add_layout_pin_rect_center(text=label, - layer="m1", + if self.add_source_contact: + contact=self.add_via_center(layers=self.active_stack, offset=pos, - width=contact.mod.second_layer_width, - height=contact.mod.second_layer_height) - self.source_contacts.append(contact) + size=(1, self.num_contacts), + directions=("V","V"), + implant_type=self.implant_type, + well_type=self.well_type) + self.source_contacts.append(contact) + if self.add_source_contact: + self.add_layout_pin_rect_center(text=label, + layer="m1", + offset=pos, + width=contact.mod.second_layer_width, + height=contact.mod.second_layer_height) + else: + self.add_layout_pin_rect_center(text=label, + layer="active", + offset=pos) source_positions.append(pos) # Skip these if they are going to be in series @@ -406,22 +422,26 @@ class ptx(design.design): label = "S" source_positions.append(pos) - contact=self.add_via_center(layers=self.active_stack, - offset=pos, - size=(1, self.num_contacts), - directions=("V","V"), - implant_type=self.implant_type, - well_type=self.well_type) - self.add_layout_pin_rect_center(text=label, - layer="m1", + if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact): + contact=self.add_via_center(layers=self.active_stack, offset=pos, - width=contact.mod.second_layer_width, - height=contact.mod.second_layer_height) - - if label == "S": - self.source_contacts.append(contact) + size=(1, self.num_contacts), + directions=("V", "V"), + implant_type=self.implant_type, + well_type=self.well_type) + self.add_layout_pin_rect_center(text=label, + layer="m1", + offset=pos, + width=contact.mod.second_layer_width, + height=contact.mod.second_layer_height) + if label == "S": + self.source_contacts.append(contact) + else: + self.drain_contacts.append(contact) else: - self.drain_contacts.append(contact) + self.add_layout_pin_rect_center(text=label, + layer="active", + offset=pos) pos = vector(self.active_offset.x + self.active_width - 0.5 * self.active_contact.width, self.contact_offset.y) @@ -432,21 +452,28 @@ class ptx(design.design): else: label = "S" source_positions.append(pos) - contact=self.add_via_center(layers=self.active_stack, - offset=pos, - size=(1, self.num_contacts), - directions=("V","V"), - implant_type=self.implant_type, - well_type=self.well_type) - self.add_layout_pin_rect_center(text=label, - layer="m1", + + if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact): + contact=self.add_via_center(layers=self.active_stack, offset=pos, - width=contact.mod.second_layer_width, - height=contact.mod.second_layer_height) - if label == "S": - self.source_contacts.append(contact) + size=(1, self.num_contacts), + directions=("V", "V"), + implant_type=self.implant_type, + well_type=self.well_type) + if label == "S": + self.source_contacts.append(contact) + else: + self.drain_contacts.append(contact) + if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact): + self.add_layout_pin_rect_center(text=label, + layer="m1", + offset=pos, + width=contact.mod.second_layer_width, + height=contact.mod.second_layer_height) else: - self.drain_contacts.append(contact) + self.add_layout_pin_rect_center(text=label, + layer="active", + offset=pos) if self.connect_active: self.connect_fingered_active(drain_positions, source_positions) diff --git a/compiler/tests/03_ptx_1finger_nmos_test.py b/compiler/tests/03_ptx_1finger_nmos_test.py index ae323d90..0774af4a 100755 --- a/compiler/tests/03_ptx_1finger_nmos_test.py +++ b/compiler/tests/03_ptx_1finger_nmos_test.py @@ -15,6 +15,7 @@ from globals import OPTS from sram_factory import factory import debug + class ptx_1finger_nmos_test(openram_test): def runTest(self): From 0ee6963198844a7cbb5905e3278b576f01b6564f Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 23 Mar 2020 11:46:21 -0700 Subject: [PATCH 003/101] Remove unused contact in pnand2 --- compiler/pgates/pnand2.py | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 8ecc115e..4a813269 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -66,13 +66,23 @@ class pnand2(pgate.pgate): def add_ptx(self): """ Create the PMOS and NMOS transistors. """ - self.nmos = factory.create(module_type="ptx", - width=self.nmos_width, - mults=self.tx_mults, - tx_type="nmos", - connect_poly=True, - connect_active=True) - self.add_mod(self.nmos) + self.nmos_nd = factory.create(module_type="ptx", + width=self.nmos_width, + mults=self.tx_mults, + tx_type="nmos", + add_drain_contact=False, + connect_poly=True, + connect_active=True) + self.add_mod(self.nmos_nd) + + self.nmos_ns = factory.create(module_type="ptx", + width=self.nmos_width, + mults=self.tx_mults, + tx_type="nmos", + add_source_contact=False, + connect_poly=True, + connect_active=True) + self.add_mod(self.nmos_ns) self.pmos = factory.create(module_type="ptx", width=self.pmos_width, @@ -99,9 +109,9 @@ class pnand2(pgate.pgate): # This is the extra space needed to ensure DRC rules # to the active contacts - extra_contact_space = max(-self.nmos.get_pin("D").by(), 0) + extra_contact_space = max(-self.nmos_nd.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell - self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space, + self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space, self.poly_extend_active + self.poly_space) def route_supply_rails(self): @@ -130,11 +140,11 @@ class pnand2(pgate.pgate): self.connect_inst(["Z", "B", "vdd", "vdd"]) self.nmos1_inst = self.add_inst(name="pnand2_nmos1", - mod=self.nmos) + mod=self.nmos_nd) self.connect_inst(["Z", "B", "net1", "gnd"]) self.nmos2_inst = self.add_inst(name="pnand2_nmos2", - mod=self.nmos) + mod=self.nmos_ns) self.connect_inst(["net1", "A", "gnd", "gnd"]) def place_ptx(self): @@ -160,7 +170,7 @@ class pnand2(pgate.pgate): # Output position will be in between the PMOS and NMOS self.output_pos = vector(0, - 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height)) + 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos_nd.active_height)) def add_well_contacts(self): """ @@ -169,7 +179,7 @@ class pnand2(pgate.pgate): """ self.add_nwell_contact(self.pmos, self.pmos2_pos) - self.add_pwell_contact(self.nmos, self.nmos2_pos) + self.add_pwell_contact(self.nmos_nd, self.nmos2_pos) def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ From 717cbb0fe56f4b3b66c53a771824d093aa67fd26 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 23 Mar 2020 11:52:19 -0700 Subject: [PATCH 004/101] Remove unused contact in pnand3 --- compiler/pgates/pnand3.py | 44 ++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 927ac6fd..5d6999e6 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -69,14 +69,34 @@ class pnand3(pgate.pgate): def add_ptx(self): """ Create the PMOS and NMOS transistors. """ - self.nmos = factory.create(module_type="ptx", - width=self.nmos_width, - mults=self.tx_mults, - tx_type="nmos", - connect_poly=True, - connect_active=True) - self.add_mod(self.nmos) + self.nmos_nsnd = factory.create(module_type="ptx", + width=self.nmos_width, + mults=self.tx_mults, + tx_type="nmos", + add_source_contact=False, + add_drain_contact=False, + connect_poly=True, + connect_active=True) + self.add_mod(self.nmos_nsnd) + self.nmos_ns = factory.create(module_type="ptx", + width=self.nmos_width, + mults=self.tx_mults, + tx_type="nmos", + add_source_contact=False, + connect_poly=True, + connect_active=True) + self.add_mod(self.nmos_ns) + + self.nmos_nd = factory.create(module_type="ptx", + width=self.nmos_width, + mults=self.tx_mults, + tx_type="nmos", + add_drain_contact=False, + connect_poly=True, + connect_active=True) + self.add_mod(self.nmos_nd) + self.pmos = factory.create(module_type="ptx", width=self.pmos_width, mults=self.tx_mults, @@ -130,15 +150,15 @@ class pnand3(pgate.pgate): self.connect_inst(["Z", "C", "vdd", "vdd"]) self.nmos1_inst = self.add_inst(name="pnand3_nmos1", - mod=self.nmos) + mod=self.nmos_nd) self.connect_inst(["Z", "C", "net1", "gnd"]) self.nmos2_inst = self.add_inst(name="pnand3_nmos2", - mod=self.nmos) + mod=self.nmos_nsnd) self.connect_inst(["net1", "B", "net2", "gnd"]) self.nmos3_inst = self.add_inst(name="pnand3_nmos3", - mod=self.nmos) + mod=self.nmos_ns) self.connect_inst(["net2", "A", "gnd", "gnd"]) def place_ptx(self): @@ -171,7 +191,7 @@ class pnand3(pgate.pgate): """ Add n/p well taps to the layout and connect to supplies """ self.add_nwell_contact(self.pmos, self.pmos3_pos) - self.add_pwell_contact(self.nmos, self.nmos3_pos) + self.add_pwell_contact(self.nmos_ns, self.nmos3_pos) def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ @@ -259,7 +279,7 @@ class pnand3(pgate.pgate): # In fF c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) transition_prob = 0.1094 - return transition_prob * (c_load + c_para) + return transition_prob * (c_load + c_para) def input_load(self): """Return the relative input capacitance of a single input""" From f598a359d5dd3ff976d3dd3dfccf1cb8f08c64ac Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 23 Mar 2020 11:55:17 -0700 Subject: [PATCH 005/101] Remove unused contact in pnor2 --- compiler/pgates/pnor2.py | 50 ++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 453a62b6..3efe542d 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -73,33 +73,43 @@ class pnor2(pgate.pgate): connect_active=True) self.add_mod(self.nmos) - self.pmos = factory.create(module_type="ptx", - width=self.pmos_width, - mults=self.tx_mults, - tx_type="pmos", - connect_poly=True, - connect_active=True) - self.add_mod(self.pmos) + self.pmos_nd = factory.create(module_type="ptx", + width=self.pmos_width, + mults=self.tx_mults, + tx_type="pmos", + add_drain_contact=False, + connect_poly=True, + connect_active=True) + self.add_mod(self.pmos_nd) + self.pmos_ns = factory.create(module_type="ptx", + width=self.pmos_width, + mults=self.tx_mults, + tx_type="pmos", + add_source_contact=False, + connect_poly=True, + connect_active=True) + self.add_mod(self.pmos_ns) + def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ # metal spacing to allow contacts on any layer self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width, self.m1_space + contact.m1_via.first_layer_width, - self.m2_space + contact.m2_via.first_layer_width, + self.m2_space + contact.m2_via.first_layer_width, self.m3_space + contact.m2_via.second_layer_width) # Compute the other pmos2 location, but determining # offset to overlap the source and drain pins - self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() + self.overlap_offset = self.pmos_ns.get_pin("D").ll() - self.pmos_nd.get_pin("S").ll() # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. - self.width = 2 * self.pmos.active_width \ - + self.pmos.active_contact.width \ - + 2 * self.active_space \ - + 0.5 * self.nwell_enclose_active + self.width = 2 * self.pmos_ns.active_width \ + + self.pmos_ns.active_contact.width \ + + 2 * self.active_space \ + + 0.5 * self.nwell_enclose_active self.well_width = self.width + 2 * self.nwell_enclose_active # Height is an input parameter, so it is not recomputed. @@ -107,7 +117,7 @@ class pnor2(pgate.pgate): # to the active contacts extra_contact_space = max(-self.nmos.get_pin("D").by(), 0) # This is a poly-to-poly of a flipped cell - self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space, + self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space, self.poly_extend_active, self.poly_space) @@ -130,11 +140,11 @@ class pnor2(pgate.pgate): """ self.pmos1_inst = self.add_inst(name="pnor2_pmos1", - mod=self.pmos) + mod=self.pmos_nd) self.connect_inst(["vdd", "A", "net1", "vdd"]) self.pmos2_inst = self.add_inst(name="pnor2_pmos2", - mod=self.pmos) + mod=self.pmos_ns) self.connect_inst(["net1", "B", "Z", "vdd"]) self.nmos1_inst = self.add_inst(name="pnor2_nmos1", @@ -151,15 +161,15 @@ class pnor2(pgate.pgate): to provide maximum routing in channel """ - pmos1_pos = vector(self.pmos.active_offset.x, - self.height - self.pmos.active_height \ + pmos1_pos = vector(self.pmos_ns.active_offset.x, + self.height - self.pmos_ns.active_height \ - self.top_bottom_space) self.pmos1_inst.place(pmos1_pos) self.pmos2_pos = pmos1_pos + self.overlap_offset self.pmos2_inst.place(self.pmos2_pos) - nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space) + nmos1_pos = vector(self.pmos_ns.active_offset.x, self.top_bottom_space) self.nmos1_inst.place(nmos1_pos) self.nmos2_pos = nmos1_pos + self.overlap_offset @@ -172,7 +182,7 @@ class pnor2(pgate.pgate): def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ - self.add_nwell_contact(self.pmos, self.pmos2_pos) + self.add_nwell_contact(self.pmos_ns, self.pmos2_pos) self.add_pwell_contact(self.nmos, self.nmos2_pos) def connect_rails(self): From f491876a5a595a077cfbc07ee4e75e88160dab9c Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 23 Mar 2020 13:49:08 -0700 Subject: [PATCH 006/101] Move up B input in pnor2 --- compiler/pgates/pnor2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 3efe542d..75840f26 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -197,7 +197,7 @@ class pnor2(pgate.pgate): def route_inputs(self): """ Route the A and B inputs """ # Use M2 spaces so we can drop vias on the pins later! - inputB_yoffset = self.nmos2_inst.uy() + 0.5 * contact.poly_contact.height + inputB_yoffset = self.nmos2_inst.uy() + contact.poly_contact.height self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, From e9d0db44fd6de2cdaf0590096b6f7fa2a9dd8d52 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 23 Mar 2020 16:55:38 -0700 Subject: [PATCH 007/101] Add li_stack contact to ptx and pgate if it exists. --- compiler/pgates/pgate.py | 27 +++++++++++++------ compiler/pgates/ptx.py | 57 ++++++++++++++++------------------------ 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 7693fa07..d83825ea 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -64,7 +64,7 @@ class pgate(design.design): width=source_pin.width()) def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left"): - """ + """ Route the input gate to the left side of the cell for access. Position specifies to place the contact the left, center, or right of gate. @@ -107,8 +107,12 @@ class pgate(design.design): else: debug.error("Invalid contact placement option.", -1) - v=self.add_via_center(layers=self.poly_stack, - offset=contact_offset) + if hasattr(self, "li_stack"): + self.add_via_center(layers=self.li_stack, + offset=contact_offset) + + self.add_via_center(layers=self.poly_stack, + offset=contact_offset) self.add_layout_pin_rect_center(text=name, layer="m1", @@ -182,13 +186,17 @@ class pgate(design.design): contact_offset = vector(contact_xoffset, contact_yoffset) # Offset by half a contact in x and y contact_offset += vector(0.5 * pmos.active_contact.first_layer_width, - 0.5 * pmos.active_contact.first_layer_height) + 0.5 * pmos.active_contact.first_layer_height) self.nwell_contact = self.add_via_center(layers=layer_stack, offset=contact_offset, implant_type="n", well_type="n") + if hasattr(self, "li_stack"): + self.add_via_center(layers=self.li_stack, + offset=contact_offset) + self.add_rect_center(layer="m1", - offset=contact_offset + vector(0, 0.5 * (self.height-contact_offset.y)), + offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)), width=self.nwell_contact.mod.second_layer_width, height=self.height - contact_offset.y) @@ -221,8 +229,6 @@ class pgate(design.design): layer_stack = self.active_stack - pwell_position = vector(0, -0.5 * self.m1_width) - # To the right a spacing away from the nmos right active edge contact_xoffset = nmos_pos.x + nmos.active_width \ + self.active_space @@ -240,8 +246,13 @@ class pgate(design.design): offset=contact_offset, implant_type="p", well_type="p") + + if hasattr(self, "li_stack"): + self.add_via_center(layers=self.li_stack, + offset=contact_offset) + self.add_rect_center(layer="m1", - offset=contact_offset.scale(1,0.5), + offset=contact_offset.scale(1, 0.5), width=self.pwell_contact.mod.second_layer_width, height=contact_offset.y) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index ae20d586..dee475c9 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -390,19 +390,8 @@ class ptx(design.design): label = "S" pos = self.contact_offset if self.add_source_contact: - contact=self.add_via_center(layers=self.active_stack, - offset=pos, - size=(1, self.num_contacts), - directions=("V","V"), - implant_type=self.implant_type, - well_type=self.well_type) + contact = self.add_diff_contact(label, pos) self.source_contacts.append(contact) - if self.add_source_contact: - self.add_layout_pin_rect_center(text=label, - layer="m1", - offset=pos, - width=contact.mod.second_layer_width, - height=contact.mod.second_layer_height) else: self.add_layout_pin_rect_center(text=label, layer="active", @@ -423,17 +412,7 @@ class ptx(design.design): source_positions.append(pos) if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact): - contact=self.add_via_center(layers=self.active_stack, - offset=pos, - size=(1, self.num_contacts), - directions=("V", "V"), - implant_type=self.implant_type, - well_type=self.well_type) - self.add_layout_pin_rect_center(text=label, - layer="m1", - offset=pos, - width=contact.mod.second_layer_width, - height=contact.mod.second_layer_height) + contact = self.add_diff_contact(label, pos) if label == "S": self.source_contacts.append(contact) else: @@ -454,22 +433,11 @@ class ptx(design.design): source_positions.append(pos) if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact): - contact=self.add_via_center(layers=self.active_stack, - offset=pos, - size=(1, self.num_contacts), - directions=("V", "V"), - implant_type=self.implant_type, - well_type=self.well_type) + contact = self.add_diff_contact(label, pos) if label == "S": self.source_contacts.append(contact) else: self.drain_contacts.append(contact) - if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact): - self.add_layout_pin_rect_center(text=label, - layer="m1", - offset=pos, - width=contact.mod.second_layer_width, - height=contact.mod.second_layer_height) else: self.add_layout_pin_rect_center(text=label, layer="active", @@ -478,6 +446,25 @@ class ptx(design.design): if self.connect_active: self.connect_fingered_active(drain_positions, source_positions) + def add_diff_contact(self, label, pos): + contact=self.add_via_center(layers=self.active_stack, + offset=pos, + size=(1, self.num_contacts), + directions=("V","V"), + implant_type=self.implant_type, + well_type=self.well_type) + + if hasattr(self, "li_stack"): + self.add_via_center(layers=self.li_stack, + offset=pos) + + self.add_layout_pin_rect_center(text=label, + layer="m1", + offset=pos, + width=contact.mod.second_layer_width, + height=contact.mod.second_layer_height) + return(contact) + def get_cin(self): """Returns the relative gate cin of the tx""" return self.tx_width / drc("minwidth_tx") From 1e2163c3a6319964604e7927e6f35b7307c8a532 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 24 Mar 2020 12:40:41 -0700 Subject: [PATCH 008/101] Hack for pnand3 pin spacing --- compiler/pgates/pnand3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 5d6999e6..6f4d2472 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -214,14 +214,14 @@ class pnand3(pgate.pgate): "B", position="center") - self.inputC_yoffset = self.inputB_yoffset - m1_pitch + self.inputC_yoffset = self.inputB_yoffset - 1.25 * m1_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, "C", position="center") - self.inputA_yoffset = self.inputB_yoffset + m1_pitch + self.inputA_yoffset = self.inputB_yoffset + 1.25 * m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, From 2f353187ba3a87ef465c65b82ba81174201f8c5e Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 24 Mar 2020 12:41:15 -0700 Subject: [PATCH 009/101] Skywater extraction mode for si unit scales --- compiler/pgates/ptx.py | 2 +- compiler/verify/magic.py | 36 +++++++++++++++++++----------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index dee475c9..8fbb94d8 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -99,7 +99,7 @@ class ptx(design.design): body_dir = 'POWER' dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir] self.add_pin_list(pin_list, dir_list) - + # self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # " ".join(self.pins))) # Just make a guess since these will actually diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 63aeaabe..1d3562cc 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -22,7 +22,6 @@ and include its appropriate license. import os import re -import time import shutil import debug from globals import OPTS @@ -67,26 +66,29 @@ def write_magic_script(cell_name, extract=False, final_verification=False): else: pre = "" if final_verification: - f.write(pre+"extract unique all\n".format(cell_name)) - f.write(pre+"extract\n".format(cell_name)) - #f.write(pre+"ext2spice hierarchy on\n") - #f.write(pre+"ext2spice scale off\n") + f.write(pre + "extract unique all\n".format(cell_name)) + # Hack to work around unit scales in SkyWater + if OPTS.tech_name=="s8": + f.write(pre + "extract style ngspice(si)\n") + f.write(pre + "extract\n".format(cell_name)) + # f.write(pre + "ext2spice hierarchy on\n") + # f.write(pre + "ext2spice scale off\n") # lvs exists in 8.2.79, but be backword compatible for now - #f.write(pre+"ext2spice lvs\n") - f.write(pre+"ext2spice hierarchy on\n") - f.write(pre+"ext2spice format ngspice\n") - f.write(pre+"ext2spice cthresh infinite\n") - f.write(pre+"ext2spice rthresh infinite\n") - f.write(pre+"ext2spice renumber off\n") - f.write(pre+"ext2spice scale off\n") - f.write(pre+"ext2spice blackbox on\n") - f.write(pre+"ext2spice subcircuit top auto\n") - f.write(pre+"ext2spice global off\n") + # f.write(pre + "ext2spice lvs\n") + f.write(pre + "ext2spice hierarchy on\n") + f.write(pre + "ext2spice format ngspice\n") + f.write(pre + "ext2spice cthresh infinite\n") + f.write(pre + "ext2spice rthresh infinite\n") + f.write(pre + "ext2spice renumber off\n") + f.write(pre + "ext2spice scale off\n") + f.write(pre + "ext2spice blackbox on\n") + f.write(pre + "ext2spice subcircuit top auto\n") + f.write(pre + "ext2spice global off\n") # Can choose hspice, ngspice, or spice3, # but they all seem compatible enough. - #f.write(pre+"ext2spice format ngspice\n") - f.write(pre+"ext2spice {}\n".format(cell_name)) + #f.write(pre + "ext2spice format ngspice\n") + f.write(pre + "ext2spice {}\n".format(cell_name)) f.write("quit -noprompt\n") f.write("EOF\n") From 1e3734cb267447e9e62fba22a2103865ba88e6be Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 26 Mar 2020 11:08:53 -0700 Subject: [PATCH 010/101] Hack to fix pnand3 in freepdk45 --- compiler/pgates/pnand3.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 6f4d2472..9aa54733 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -214,14 +214,16 @@ class pnand3(pgate.pgate): "B", position="center") - self.inputC_yoffset = self.inputB_yoffset - 1.25 * m1_pitch + # FIXME: constant hack + self.inputC_yoffset = self.inputB_yoffset - 1.1 * m1_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, "C", position="center") - self.inputA_yoffset = self.inputB_yoffset + 1.25 * m1_pitch + # FIXME: constant hack + self.inputA_yoffset = self.inputB_yoffset + 1.1 * m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, From d2c97d75a78c3082577203be47e3a6a78fdd06aa Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 26 Mar 2020 11:49:32 -0700 Subject: [PATCH 011/101] Add well contact and min area to power pin of precharge --- compiler/base/hierarchy_layout.py | 21 +++++++++++++-------- compiler/pgates/precharge.py | 22 +++++++++++++++------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 049d2307..043d71b5 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -9,6 +9,7 @@ import collections import geometry import gdsMill import debug +from math import sqrt from tech import drc, GDS from tech import layer as techlayer from tech import layer_stacks @@ -1193,11 +1194,9 @@ class layout(): "supply router." .format(name,inst.name,self.pwr_grid_layer)) - - def add_power_pin(self, name, loc, size=[1, 1], vertical=False, start_layer="m1"): """ - Add a single power pin from the lowest power_grid layer down to M1 at + Add a single power pin from the lowest power_grid layer down to M1 (or li) at the given center location. The starting layer is specified to determine which vias are needed. """ @@ -1211,23 +1210,29 @@ class layout(): else: direction = None - via = self.add_via_stack_center(from_layer=start_layer, to_layer=self.pwr_grid_layer, size=size, offset=loc, direction=direction) - + # Hack for min area + if OPTS.tech_name == "s8": + height = width = sqrt(drc["minarea_m3"]) + else: + width = via.width + height = via.height if start_layer == self.pwr_grid_layer: self.add_layout_pin_rect_center(text=name, layer=self.pwr_grid_layer, - offset=loc) + offset=loc, + width=width, + height=height) else: self.add_layout_pin_rect_center(text=name, layer=self.pwr_grid_layer, offset=loc, - width=via.width, - height=via.height) + width=width, + height=height) def add_power_ring(self, bbox): """ diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 56382712..ee0f96d1 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -96,17 +96,24 @@ class precharge(design.design): height=layer_width) pmos_pin = self.upper_pmos2_inst.get_pin("S") + # center of vdd rail pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos]) + + # if enable is not on M1, the supply can be if self.en_layer != "m1": self.add_via_center(layers=self.m1_stack, offset=pmos_vdd_pos) + self.add_power_pin("vdd", + self.well_contact_pos, + vertical=True) - - # Add vdd pin above the transistor - self.add_power_pin("vdd", self.well_contact_pos, vertical=True) + # Hack for li layers + if OPTS.tech_name == "s8": + self.add_via_center(layers=self.li_stack, + offset=self.well_contact_pos) def create_ptx(self): """ @@ -191,7 +198,6 @@ class precharge(design.design): if self.en_layer == "m2": self.add_via_center(layers=self.m1_stack, offset=offset) - # adds the en rail on metal1 self.add_layout_pin_segment_center(text="en_bar", @@ -205,9 +211,11 @@ class precharge(design.design): """ # adds the contact from active to metal1 - self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ - + vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \ - + self.nwell_extend_active) + offset_height = self.upper_pmos1_inst.uy() + \ + 0.5 * contact.active_contact.height + \ + self.nwell_extend_active + self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \ + vector(0, offset_height) self.add_via_center(layers=self.active_stack, offset=self.well_contact_pos, implant_type="n", From 9907daaffa8557af0edc324623d5f8358c3dc172 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 26 Mar 2020 13:05:02 -0700 Subject: [PATCH 012/101] Min area only for multiple layers --- compiler/base/hierarchy_layout.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 043d71b5..63d53d39 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1215,19 +1215,17 @@ class layout(): size=size, offset=loc, direction=direction) - # Hack for min area - if OPTS.tech_name == "s8": - height = width = sqrt(drc["minarea_m3"]) - else: - width = via.width - height = via.height if start_layer == self.pwr_grid_layer: self.add_layout_pin_rect_center(text=name, layer=self.pwr_grid_layer, - offset=loc, - width=width, - height=height) + offset=loc) else: + # Hack for min area + if OPTS.tech_name == "s8": + height = width = sqrt(drc["minarea_m3"]) + else: + width = via.width + height = via.height self.add_layout_pin_rect_center(text=name, layer=self.pwr_grid_layer, offset=loc, From b0d2946c809b9f14322fbd8560e5301d0e81763f Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Mon, 30 Mar 2020 20:00:32 -0700 Subject: [PATCH 013/101] update to sense amp and write driver modules --- compiler/modules/sense_amp_array.py | 33 ++++++++++++----------- compiler/modules/write_driver_array.py | 18 ++++++------- compiler/tests/09_sense_amp_array_test.py | 10 ++++--- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 322cc1b3..1c9c7d19 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -22,7 +22,7 @@ class sense_amp_array(design.design): def __init__(self, name, word_size, words_per_row): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) - self.add_comment("word_size {0}".format(word_size)) + self.add_comment("word_size {0}".format(word_size)) self.add_comment("words_per_row: {0}".format(words_per_row)) self.word_size = word_size @@ -56,7 +56,7 @@ class sense_amp_array(design.design): def create_layout(self): self.height = self.amp.height - + if self.bitcell.width > self.amp.width: self.width = self.bitcell.width * self.word_size * self.words_per_row else: @@ -76,16 +76,16 @@ class sense_amp_array(design.design): self.add_pin(self.en_name, "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - + def add_modules(self): self.amp = factory.create(module_type="sense_amp") - + self.add_mod(self.amp) # This is just used for measurements, # so don't add the module self.bitcell = factory.create(module_type="bitcell") - + def create_sense_amp_array(self): self.local_insts = [] for i in range(0,self.word_size): @@ -121,11 +121,11 @@ class sense_amp_array(design.design): amp_position = vector(xoffset, 0) self.local_insts[i].place(offset=amp_position,mirror=mirror) - + def add_layout_pins(self): for i in range(len(self.local_insts)): inst = self.local_insts[i] - + self.add_power_pin(name = "gnd", loc = inst.get_pin("gnd").center(), start_layer="m2", @@ -141,40 +141,41 @@ class sense_amp_array(design.design): dout_pin = inst.get_pin(inst.mod.dout_name) self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i), - layer="m2", + layer=bl_pin.layer, offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i), - layer="m2", + layer=br_pin.layer, offset=br_pin.ll(), width=br_pin.width(), height=br_pin.height()) self.add_layout_pin(text=self.data_name + "_{0}".format(i), - layer="m2", + layer=dout_pin.layer, offset=dout_pin.ll(), width=dout_pin.width(), height=dout_pin.height()) - - + + def route_rails(self): # add sclk rail across entire array + sclk = self.amp.get_pin(self.amp.en_name) sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0,1) self.add_layout_pin(text=self.en_name, - layer="m1", + layer=sclk.layer, offset=sclk_offset, width=self.width, - height=drc("minwidth_m1")) + height=drc("minwidth_" + sclk.layer)) def input_load(self): return self.amp.input_load() - + def get_en_cin(self): """Get the relative capacitance of all the sense amp enable connections in the array""" sense_amp_en_cin = self.amp.get_en_cin() return sense_amp_en_cin * self.word_size - + def get_drain_cin(self): """Get the relative capacitance of the drain of the PMOS isolation TX""" from tech import parameter diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 08a2e007..c83bd130 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -23,7 +23,7 @@ class write_driver_array(design.design): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) - self.add_comment("word_size {0}".format(word_size)) + self.add_comment("word_size {0}".format(word_size)) self.columns = columns self.word_size = word_size @@ -57,15 +57,15 @@ class write_driver_array(design.design): self.add_modules() self.add_pins() self.create_write_array() - + def create_layout(self): - + 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() self.add_layout_pins() self.add_boundary() @@ -139,26 +139,26 @@ class write_driver_array(design.design): base = vector(xoffset, 0) self.driver_insts[index].place(offset=base, mirror=mirror) - + def add_layout_pins(self): for i in range(self.word_size): inst = self.driver_insts[i] din_pin = inst.get_pin(inst.mod.din_name) self.add_layout_pin(text=self.data_name + "_{0}".format(i), - layer="m2", + layer=din_pin.layer, offset=din_pin.ll(), width=din_pin.width(), height=din_pin.height()) bl_pin = inst.get_pin(inst.mod.get_bl_names()) self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i), - layer="m2", + layer=bl_pin.layer, offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) br_pin = inst.get_pin(inst.mod.get_br_names()) self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i), - layer="m2", + layer=br_pin.layer, offset=br_pin.ll(), width=br_pin.width(), height=br_pin.height()) @@ -194,7 +194,7 @@ class write_driver_array(design.design): width=self.width) - + def get_w_en_cin(self): """Get the relative capacitance of all the enable connections in the bank""" diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index 3baf51f0..88b5a9aa 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -22,6 +22,10 @@ class sense_amp_test(openram_test): globals.init_openram(config_file) # check sense amp array for single port + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=1") + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=1) + self.local_check(a) + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2) self.local_check(a) @@ -29,7 +33,7 @@ class sense_amp_test(openram_test): debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4) self.local_check(a) - + # check sense amp array for multi-port OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 @@ -44,9 +48,9 @@ class sense_amp_test(openram_test): debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4) self.local_check(a) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() From d916322b74a6aec0472b13eb173f1bcd279ba0f6 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 31 Mar 2020 10:15:46 -0700 Subject: [PATCH 014/101] PEP8 updates --- compiler/modules/sense_amp_array.py | 31 +++++++++++----------- compiler/modules/write_driver_array.py | 36 +++++++++++--------------- 2 files changed, 30 insertions(+), 37 deletions(-) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 1c9c7d19..d8cb2869 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -13,6 +13,7 @@ import debug from globals import OPTS import logical_effort + class sense_amp_array(design.design): """ Array of sense amplifiers to read the bitlines through the column mux. @@ -69,7 +70,7 @@ class sense_amp_array(design.design): self.DRC_LVS() def add_pins(self): - for i in range(0,self.word_size): + for i in range(0, self.word_size): self.add_pin(self.data_name + "_{0}".format(i), "OUTPUT") self.add_pin(self.get_bl_name() + "_{0}".format(i), "INPUT") self.add_pin(self.get_br_name() + "_{0}".format(i), "INPUT") @@ -88,7 +89,7 @@ class sense_amp_array(design.design): def create_sense_amp_array(self): self.local_insts = [] - for i in range(0,self.word_size): + for i in range(0, self.word_size): name = "sa_d{0}".format(i) self.local_insts.append(self.add_inst(name=name, @@ -105,7 +106,7 @@ class sense_amp_array(design.design): else: amp_spacing = self.amp.width * self.words_per_row - for i in range(0,self.word_size): + for i in range(0, self.word_size): xoffset = amp_spacing * i # align the xoffset to the grid of bitcells. This way we @@ -119,20 +120,19 @@ class sense_amp_array(design.design): mirror = "" amp_position = vector(xoffset, 0) - self.local_insts[i].place(offset=amp_position,mirror=mirror) - + self.local_insts[i].place(offset=amp_position, mirror=mirror) def add_layout_pins(self): for i in range(len(self.local_insts)): inst = self.local_insts[i] - self.add_power_pin(name = "gnd", - loc = inst.get_pin("gnd").center(), + self.add_power_pin(name="gnd", + loc=inst.get_pin("gnd").center(), start_layer="m2", vertical=True) - self.add_power_pin(name = "vdd", - loc = inst.get_pin("vdd").center(), + self.add_power_pin(name="vdd", + loc=inst.get_pin("vdd").center(), start_layer="m2", vertical=True) @@ -157,16 +157,15 @@ class sense_amp_array(design.design): width=dout_pin.width(), height=dout_pin.height()) - def route_rails(self): # add sclk rail across entire array sclk = self.amp.get_pin(self.amp.en_name) - sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0,1) + sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0, 1) self.add_layout_pin(text=self.en_name, - layer=sclk.layer, - offset=sclk_offset, - width=self.width, - height=drc("minwidth_" + sclk.layer)) + layer=sclk.layer, + offset=sclk_offset, + width=self.width, + height=drc("minwidth_" + sclk.layer)) def input_load(self): return self.amp.input_load() @@ -179,6 +178,6 @@ class sense_amp_array(design.design): def get_drain_cin(self): """Get the relative capacitance of the drain of the PMOS isolation TX""" from tech import parameter - #Bitcell drain load being used to estimate PMOS drain load + # Bitcell drain load being used to estimate PMOS drain load drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) return drain_load diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index c83bd130..d1244aed 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -5,21 +5,20 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from math import log import design -from tech import drc import debug from sram_factory import factory from vector import vector from globals import OPTS + class write_driver_array(design.design): """ Array of tristate drivers to write to the bitlines through the column mux. Dynamically generated write driver array of all bitlines. """ - def __init__(self, name, columns, word_size,write_size=None): + def __init__(self, name, columns, word_size, write_size=None): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) @@ -31,7 +30,7 @@ class write_driver_array(design.design): self.words_per_row = int(columns / word_size) if self.write_size: - self.num_wmasks = int(self.word_size/self.write_size) + self.num_wmasks = int(self.word_size / self.write_size) self.create_netlist() if not OPTS.netlist_only: @@ -97,9 +96,9 @@ class write_driver_array(design.design): self.driver_insts = {} w = 0 windex=0 - for i in range(0,self.columns,self.words_per_row): + for i in range(0, self.columns, self.words_per_row): name = "write_driver{}".format(i) - index = int(i/self.words_per_row) + index = int(i / self.words_per_row) self.driver_insts[index]=self.add_inst(name=name, mod=self.driver) @@ -119,15 +118,14 @@ class write_driver_array(design.design): self.get_br_name() + "_{0}".format(index), self.en_name, "vdd", "gnd"]) - def place_write_array(self): from tech import cell_properties if self.bitcell.width > self.driver.width: self.driver_spacing = self.bitcell.width else: self.driver_spacing = self.driver.width - for i in range(0,self.columns,self.words_per_row): - index = int(i/self.words_per_row) + for i in range(0, self.columns, self.words_per_row): + index = int(i / self.words_per_row) xoffset = i * self.driver_spacing if cell_properties.bitcell.mirror.y and i % 2: @@ -139,7 +137,6 @@ class write_driver_array(design.design): base = vector(xoffset, 0) self.driver_insts[index].place(offset=base, mirror=mirror) - def add_layout_pins(self): for i in range(self.word_size): inst = self.driver_insts[i] @@ -166,16 +163,16 @@ class write_driver_array(design.design): for n in ["vdd", "gnd"]: pin_list = self.driver_insts[i].get_pins(n) for pin in pin_list: - self.add_power_pin(name = n, - loc = pin.center(), + self.add_power_pin(name=n, + loc=pin.center(), vertical=True, - start_layer = "m2") + start_layer="m2") if self.write_size: for bit in range(self.num_wmasks): - inst = self.driver_insts[bit*self.write_size] + inst = self.driver_insts[bit * self.write_size] en_pin = inst.get_pin(inst.mod.en_name) # Determine width of wmask modified en_pin with/without col mux - wmask_en_len = self.words_per_row*(self.write_size * self.driver_spacing) + wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing) if (self.words_per_row == 1): en_gap = self.driver_spacing - en_pin.width() else: @@ -184,19 +181,16 @@ class write_driver_array(design.design): self.add_layout_pin(text=self.en_name + "_{0}".format(bit), layer=en_pin.layer, offset=en_pin.ll(), - width=wmask_en_len-en_gap, + width=wmask_en_len - en_gap, height=en_pin.height()) else: inst = self.driver_insts[0] self.add_layout_pin(text=self.en_name, layer="m1", - offset=inst.get_pin(inst.mod.en_name).ll().scale(0,1), + offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1), width=self.width) - - - def get_w_en_cin(self): """Get the relative capacitance of all the enable connections in the bank""" - #The enable is connected to a nand2 for every row. + # The enable is connected to a nand2 for every row. return self.driver.get_w_en_cin() * len(self.driver_insts) From 6e2a5d7a1a29118a1a3d9620118876d5538f3112 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Wed, 1 Apr 2020 04:24:43 -0700 Subject: [PATCH 015/101] set sram output cap in characterizer to be 4x dff input cap --- compiler/characterizer/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 6d6c6ce5..d429bcd5 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -45,7 +45,7 @@ class lib: """ Determine the load/slews if they aren't specified in the config file. """ # These are the parameters to determine the table sizes #self.load_scales = np.array([0.1, 0.25, 0.5, 1, 2, 4, 8]) - self.load_scales = np.array([0.25, 1, 8]) + self.load_scales = np.array([0.25, 1, 4]) #self.load_scales = np.array([0.25, 1]) self.load = tech.spice["dff_in_cap"] self.loads = self.load_scales*self.load From bc9cbe70a7712f1b0df7b60edbc760b090e6cc38 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Apr 2020 09:42:07 -0700 Subject: [PATCH 016/101] Poly overlap doesn't convert to tx device --- compiler/pgates/precharge.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index ee0f96d1..bbde06ad 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -111,7 +111,7 @@ class precharge(design.design): vertical=True) # Hack for li layers - if OPTS.tech_name == "s8": + if hasattr(self, "li_stack"): self.add_via_center(layers=self.li_stack, offset=self.well_contact_pos) @@ -166,7 +166,7 @@ class precharge(design.design): Connects the upper and lower pmos together """ - offset = self.lower_pmos_inst.get_pin("G").ll() + offset = self.lower_pmos_inst.get_pin("G").ul() # connects the top and bottom pmos' gates together ylength = self.upper_pmos1_inst.get_pin("G").ll().y - offset.y self.add_rect(layer="poly", From da334e47aa32ea8e016ba76330053787094a99ea Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Apr 2020 11:14:50 -0700 Subject: [PATCH 017/101] Separate pbitcell tests for precharge --- compiler/tests/04_precharge_pbitcell_test.py | 52 ++++++++++++++++++++ compiler/tests/04_precharge_test.py | 22 +-------- 2 files changed, 53 insertions(+), 21 deletions(-) create mode 100755 compiler/tests/04_precharge_pbitcell_test.py diff --git a/compiler/tests/04_precharge_pbitcell_test.py b/compiler/tests/04_precharge_pbitcell_test.py new file mode 100755 index 00000000..fd2f8283 --- /dev/null +++ b/compiler/tests/04_precharge_pbitcell_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class precharge_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check precharge in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + factory.reset() + debug.info(2, "Checking precharge for pbitcell (innermost connections)") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + factory.reset() + debug.info(2, "Checking precharge for pbitcell (innermost connections)") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1") + self.local_check(tx) + + factory.reset() + debug.info(2, "Checking precharge for pbitcell (outermost connections)") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl2", bitcell_br="br2") + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index 1c12ad8b..76d433a8 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -15,6 +15,7 @@ from globals import OPTS from sram_factory import factory import debug + class precharge_test(openram_test): def runTest(self): @@ -26,27 +27,6 @@ class precharge_test(openram_test): tx = factory.create(module_type="precharge", size=1) self.local_check(tx) - # check precharge in multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 - - factory.reset() - debug.info(2, "Checking precharge for pbitcell (innermost connections)") - tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(tx) - - factory.reset() - debug.info(2, "Checking precharge for pbitcell (innermost connections)") - tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1") - self.local_check(tx) - - factory.reset() - debug.info(2, "Checking precharge for pbitcell (outermost connections)") - tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl2", bitcell_br="br2") - self.local_check(tx) - globals.end_openram() # run the test from the command line From 3074cf3b86c150189541416558997029cb2261d4 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Apr 2020 11:15:29 -0700 Subject: [PATCH 018/101] Small format cleanup --- compiler/bitcells/pbitcell.py | 70 ++++++++++++++++++----------------- compiler/pgates/ptx.py | 12 +++--- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 61bd9710..be96a129 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -7,7 +7,7 @@ # import contact import debug -from tech import drc, parameter +from tech import drc, parameter, layer from vector import vector from ptx import ptx from globals import OPTS @@ -975,42 +975,44 @@ class pbitcell(bitcell_base.bitcell_base): """ Connects wells between ptx modules and places well contacts """ - # extend pwell to encompass entire nmos region of the cell up to the - # height of the tallest nmos transistor - max_nmos_well_height = max(self.inverter_nmos.well_height, - self.readwrite_nmos.well_height, - self.write_nmos.well_height, - self.read_nmos.well_height) - well_height = max_nmos_well_height + self.port_ypos \ - - self.nwell_enclose_active - self.gnd_position.y - # FIXME fudge factor xpos - well_width = self.width + 2*self.nwell_enclose_active - offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos) - self.add_rect(layer="pwell", - offset=offset, - width=well_width, - height=well_height) - + if "pwell" in layer: + # extend pwell to encompass entire nmos region of the cell up to the + # height of the tallest nmos transistor + max_nmos_well_height = max(self.inverter_nmos.well_height, + self.readwrite_nmos.well_height, + self.write_nmos.well_height, + self.read_nmos.well_height) + well_height = max_nmos_well_height + self.port_ypos \ + - self.nwell_enclose_active - self.gnd_position.y + # FIXME fudge factor xpos + well_width = self.width + 2*self.nwell_enclose_active + offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos) + self.add_rect(layer="pwell", + offset=offset, + width=well_width, + height=well_height) + # extend nwell to encompass inverter_pmos # calculate offset of the left pmos well - inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ - - self.nwell_enclose_active - inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ - + self.inverter_gap - self.nwell_enclose_active + if "nwell" in layer: + inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ + - self.nwell_enclose_active + inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ + + self.inverter_gap - self.nwell_enclose_active + + # calculate width of the two combined nwells + # calculate height to encompass nimplant connected to vdd + well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ + + 2 * self.nwell_enclose_active + well_height = self.vdd_position.y - inverter_well_ypos \ + + self.nwell_enclose_active + drc["minwidth_tx"] - # calculate width of the two combined nwells - # calculate height to encompass nimplant connected to vdd - well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ - + 2 * self.nwell_enclose_active - well_height = self.vdd_position.y - inverter_well_ypos \ - + self.nwell_enclose_active + drc["minwidth_tx"] - - # FIXME fudge factor xpos - offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos] - self.add_rect(layer="nwell", - offset=offset, - width=well_width, - height=well_height) + # FIXME fudge factor xpos + offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos] + self.add_rect(layer="nwell", + offset=offset, + width=well_width, + height=well_height) # add well contacts # connect pimplants to gnd diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 8fbb94d8..b9d04f32 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -196,6 +196,8 @@ class ptx(design.design): # The well is not included in the height and width self.height = self.poly_height self.width = self.active_width + self.well_height = self.height + self.well_width = self.width # This is the center of the first active contact offset (centered vertically) self.contact_offset = self.active_offset + vector(0.5 * self.active_contact.width, @@ -353,10 +355,10 @@ class ptx(design.design): if not (well_name in layer or "vtg" in layer): return - center_pos = self.active_offset + vector(0.5*self.active_width, - 0.5*self.active_height) - well_ll = center_pos - vector(0.5*self.well_width, - 0.5*self.well_height) + center_pos = self.active_offset + vector(0.5 * self.active_width, + 0.5 * self.active_height) + well_ll = center_pos - vector(0.5 * self.well_width, + 0.5 * self.well_height) if well_name in layer: self.add_rect(layer=well_name, offset=well_ll, @@ -450,7 +452,7 @@ class ptx(design.design): contact=self.add_via_center(layers=self.active_stack, offset=pos, size=(1, self.num_contacts), - directions=("V","V"), + directions=("V", "V"), implant_type=self.implant_type, well_type=self.well_type) From 3e41664db6f6c955013221c1683db0b1017d2145 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Apr 2020 11:26:31 -0700 Subject: [PATCH 019/101] Split precharge array to multiport and normal cell --- .../tests/08_precharge_array_1rw1r_test.py | 50 +++++++++++++++++++ compiler/tests/08_precharge_array_test.py | 19 ------- 2 files changed, 50 insertions(+), 19 deletions(-) create mode 100755 compiler/tests/08_precharge_array_1rw1r_test.py diff --git a/compiler/tests/08_precharge_array_1rw1r_test.py b/compiler/tests/08_precharge_array_1rw1r_test.py new file mode 100755 index 00000000..8f51c2d4 --- /dev/null +++ b/compiler/tests/08_precharge_array_1rw1r_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class precharge_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check precharge array in multi-port + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + factory.reset() + debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") + pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(pc) + + # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") + # pc = precharge_array.precharge_array(name="pre3", columns=3, bitcell_bl="bl0", bitcell_br="br0") + # self.local_check(pc) + + # debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)") + # pc = precharge_array.precharge_array(name="pre4", columns=3, bitcell_bl="bl2", bitcell_br="br2") + # self.local_check(pc) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index d4f5591b..b301a909 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -25,25 +25,6 @@ class precharge_test(openram_test): debug.info(2, "Checking 3 column precharge") pc = factory.create(module_type="precharge_array", columns=3) self.local_check(pc) - - # check precharge array in multi-port - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - - factory.reset() - debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") - pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(pc) - - # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") - # pc = precharge_array.precharge_array(name="pre3", columns=3, bitcell_bl="bl0", bitcell_br="br0") - # self.local_check(pc) - - # debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)") - # pc = precharge_array.precharge_array(name="pre4", columns=3, bitcell_bl="bl2", bitcell_br="br2") - # self.local_check(pc) globals.end_openram() From 7956b63d9f9cf84f2f27d8397a64a9219fd9ebf9 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Apr 2020 11:26:45 -0700 Subject: [PATCH 020/101] Add licon option to precharge --- compiler/modules/precharge_array.py | 7 ++++--- compiler/pgates/precharge.py | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index d5b0e799..af612af4 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -73,11 +73,12 @@ class precharge_array(design.design): def add_layout_pins(self): + en_bar_pin = self.pc_cell.get_pin("en_bar") self.add_layout_pin(text="en_bar", - layer="m1", - offset=self.pc_cell.get_pin("en_bar").ll(), + layer=en_bar_pin.layer, + offset=en_bar_pin.ll(), width=self.width, - height=drc("minwidth_m1")) + height=en_bar_pin.height()) for inst in self.local_insts: self.copy_layout_pin(inst, "vdd") diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index bbde06ad..afe14a05 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -198,6 +198,9 @@ class precharge(design.design): if self.en_layer == "m2": self.add_via_center(layers=self.m1_stack, offset=offset) + if hasattr(self, "li_stack"): + self.add_via_center(layers=self.li_stack, + offset=offset) # adds the en rail on metal1 self.add_layout_pin_segment_center(text="en_bar", @@ -220,6 +223,9 @@ class precharge(design.design): offset=self.well_contact_pos, implant_type="n", well_type="n") + if hasattr(self, "li_stack"): + self.add_via_center(layers=self.li_stack, + offset=self.well_contact_pos) self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space From 3b662026d22af05aac792c923a1968ff0724f29d Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Apr 2020 11:36:04 -0700 Subject: [PATCH 021/101] pnand3 constant hack for input separation --- compiler/pgates/pnand3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 9aa54733..3eeba41e 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -215,7 +215,7 @@ class pnand3(pgate.pgate): position="center") # FIXME: constant hack - self.inputC_yoffset = self.inputB_yoffset - 1.1 * m1_pitch + self.inputC_yoffset = self.inputB_yoffset - 1.15 * m1_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, @@ -223,7 +223,7 @@ class pnand3(pgate.pgate): position="center") # FIXME: constant hack - self.inputA_yoffset = self.inputB_yoffset + 1.1 * m1_pitch + self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, From a9d3548be1b0579c39e3dee6d96a52c30780aafc Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Apr 2020 15:54:06 -0700 Subject: [PATCH 022/101] Refactor drc/lvs error output --- compiler/base/hierarchy_design.py | 28 +++++++++++------------- compiler/characterizer/lib.py | 15 +++++-------- compiler/modules/hierarchical_decoder.py | 14 ++++++++++-- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index e4c77987..09d7ea9c 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -7,15 +7,11 @@ # import hierarchy_layout import hierarchy_spice -import globals import verify import debug import os from globals import OPTS -import graph_util -total_drc_errors = 0 -total_lvs_errors = 0 class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): """ @@ -51,14 +47,12 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # Final verification option does not allow nets to be connected by label. # Unit tests will check themselves. if OPTS.is_unit_test: - return + return ("skipped", "skipped") if not OPTS.check_lvsdrc: - return + return ("skipped", "skipped") # Do not run if disabled in options. if (OPTS.inline_lvsdrc or top_level): - global total_drc_errors - global total_lvs_errors tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) self.sp_write(tempspice) @@ -68,11 +62,11 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors)) debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors)) - total_drc_errors += num_drc_errors - total_lvs_errors += num_lvs_errors os.remove(tempspice) os.remove(tempgds) + + return (num_drc_errors, num_lvs_errors) def DRC(self, final_verification=False): """Checks DRC for a module""" @@ -80,31 +74,35 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # Do not run if disabled in options. if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): - global total_drc_errors tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) self.gds_write(tempgds) num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification) - total_drc_errors += num_errors debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error)) os.remove(tempgds) + return num_errors + else: + return "skipped" + def LVS(self, final_verification=False): """Checks LVS for a module""" # Unit tests will check themselves. # Do not run if disabled in options. if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): - global total_lvs_errors tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) self.sp_write(tempspice) self.gds_write(tempgds) num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) - total_lvs_errors += num_errors debug.check(num_errors == 0,"LVS failed for {0} with {1} error(s)".format(self.name,num_errors)) os.remove(tempspice) - os.remove(tempgds) + os.remove(tempgds) + + return num_errors + else: + return "skipped" def init_graph_params(self): """Initializes parameters relevant to the graph creation""" diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 6d6c6ce5..2742dff2 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -621,17 +621,12 @@ class lib: )) # information of checks - from hierarchy_design import total_drc_errors - from hierarchy_design import total_lvs_errors - DRC = 'skipped' - LVS = 'skipped' - if OPTS.check_lvsdrc: - DRC = str(total_drc_errors) - LVS = str(total_lvs_errors) - - datasheet.write("{0},{1},".format(DRC, LVS)) + (drc_errors, lvs_errors) = self.sram.DRC_LVS(final_verification=True, top_level=True) + datasheet.write("{0},{1},".format(drc_errors, lvs_errors)) + # write area - datasheet.write(str(self.sram.width * self.sram.height)+',') + datasheet.write(str(self.sram.width * self.sram.height) + ',') + # write timing information for all ports for port in self.all_ports: #din timings diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 6e98c5e0..ebc67ee6 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -25,8 +25,7 @@ class hierarchical_decoder(design.design): self.pre2x4_inst = [] self.pre3x8_inst = [] - b = factory.create(module_type="bitcell") - self.cell_height = b.height + (self.cell_height, self.cell_multiple) = self.find_decoder_height() self.rows = rows self.num_inputs = math.ceil(math.log(self.rows, 2)) (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) @@ -35,6 +34,17 @@ class hierarchical_decoder(design.design): if not OPTS.netlist_only: self.create_layout() + def find_decoder_height(self): + b = factory.create(module_type="bitcell") + + cell_height = b.height + and3 = factory.create(module_type="pand3", + height=cell_height) + + # Try to make a nand with a given height + # Default + return (b.height, 1) + def create_netlist(self): self.add_modules() self.setup_netlist_constants() From a3683c5898582c8a0415bc06a86a18d76590986f Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 1 Apr 2020 16:39:47 -0700 Subject: [PATCH 023/101] Separate pbitcell from hierarchical decoder --- .../06_hierarchical_decoder_pbitcell_test.py | 73 +++++++++++++++++++ .../tests/06_hierarchical_decoder_test.py | 43 ----------- 2 files changed, 73 insertions(+), 43 deletions(-) create mode 100755 compiler/tests/06_hierarchical_decoder_pbitcell_test.py diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py new file mode 100755 index 00000000..1fb3a3f6 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + # check hierarchical decoder for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=16) + self.local_check(a) + + factory.reset() + debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=17) + self.local_check(a) + + factory.reset() + debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=23) + self.local_check(a) + + debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=32) + self.local_check(a) + + factory.reset() + debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=65) + self.local_check(a) + + debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=128) + self.local_check(a) + + factory.reset() + debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=341) + self.local_check(a) + + debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=512) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 9d8353f3..ab7e844f 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -63,49 +63,6 @@ class hierarchical_decoder_test(openram_test): a = factory.create(module_type="hierarchical_decoder", rows=512) self.local_check(a) - # check hierarchical decoder for multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - factory.reset() - debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=16) - self.local_check(a) - - factory.reset() - debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=17) - self.local_check(a) - - factory.reset() - debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=23) - self.local_check(a) - - debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=32) - self.local_check(a) - - factory.reset() - debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=65) - self.local_check(a) - - debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=128) - self.local_check(a) - - factory.reset() - debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=341) - self.local_check(a) - - debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=512) - self.local_check(a) - globals.end_openram() # run the test from the command line From 0d6c84036d18ceb1484def9f6760d0cef9cb2571 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 2 Apr 2020 09:47:13 -0700 Subject: [PATCH 024/101] Adjust fudge factor for pin spacing. --- compiler/pgates/pnand3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 3eeba41e..cc6fd0f8 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -223,7 +223,7 @@ class pnand3(pgate.pgate): position="center") # FIXME: constant hack - self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch + self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, From 5349323acdda050cf1b020022b0404ead7d56788 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 2 Apr 2020 09:47:39 -0700 Subject: [PATCH 025/101] PEP8 cleanup. DRC/LVS returns errors. --- compiler/base/hierarchy_design.py | 111 +++++++++++++---------- compiler/modules/hierarchical_decoder.py | 18 ++-- compiler/sram/sram_base.py | 2 +- 3 files changed, 75 insertions(+), 56 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 09d7ea9c..5119da2e 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -29,39 +29,42 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): hierarchy_layout.layout.__init__(self, name) self.init_graph_params() - def get_layout_pins(self,inst): + def get_layout_pins(self, inst): """ Return a map of pin locations of the instance offset """ # find the instance for i in self.insts: if i.name == inst.name: break else: - debug.error("Couldn't find instance {0}".format(inst_name),-1) + debug.error("Couldn't find instance {0}".format(inst_name), -1) inst_map = inst.mod.pin_map return inst_map - - def DRC_LVS(self, final_verification=False, top_level=False): + def DRC_LVS(self, final_verification=False, force_check=False): """Checks both DRC and LVS for a module""" # Final verification option does not allow nets to be connected by label. # Unit tests will check themselves. - if OPTS.is_unit_test: + if not force_check and OPTS.is_unit_test: return ("skipped", "skipped") - if not OPTS.check_lvsdrc: + if not force_check and not OPTS.check_lvsdrc: return ("skipped", "skipped") # Do not run if disabled in options. - if (OPTS.inline_lvsdrc or top_level): + if (OPTS.inline_lvsdrc or force_check): - tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name) - tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) + tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) + tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.sp_write(tempspice) self.gds_write(tempgds) - num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) - num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) - debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors)) - debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors)) + num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) + num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) + debug.check(num_drc_errors == 0, + "DRC failed for {0} with {1} error(s)".format(self.name, + num_drc_errors)) + debug.check(num_lvs_errors == 0, + "LVS failed for {0} with {1} errors(s)".format(self.name, + num_lvs_errors)) os.remove(tempspice) os.remove(tempgds) @@ -74,10 +77,12 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # Do not run if disabled in options. if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): - tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) + tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.gds_write(tempgds) - num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification) - debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error)) + num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification) + debug.check(num_errors == 0, + "DRC failed for {0} with {1} error(s)".format(self.name, + num_errors)) os.remove(tempgds) @@ -91,12 +96,14 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # Do not run if disabled in options. if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): - tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name) - tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) + tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) + tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.sp_write(tempspice) self.gds_write(tempgds) num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) - debug.check(num_errors == 0,"LVS failed for {0} with {1} error(s)".format(self.name,num_errors)) + debug.check(num_errors == 0, + "LVS failed for {0} with {1} error(s)".format(self.name, + num_errors)) os.remove(tempspice) os.remove(tempgds) @@ -106,40 +113,44 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): def init_graph_params(self): """Initializes parameters relevant to the graph creation""" - #Only initializes a set for checking instances which should not be added + # Only initializes a set for checking instances which should not be added self.graph_inst_exclude = set() - def build_graph(self, graph, inst_name, port_nets): + def build_graph(self, graph, inst_name, port_nets): """Recursively create graph from instances in module.""" - #Translate port names to external nets + # Translate port names to external nets if len(port_nets) != len(self.pins): - debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,self.pins),1) - port_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, + self.pins), + 1) + port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} debug.info(3, "Instance name={}".format(inst_name)) for subinst, conns in zip(self.insts, self.conns): if subinst in self.graph_inst_exclude: continue - subinst_name = inst_name+'.X'+subinst.name + subinst_name = inst_name + '.X' + subinst.name subinst_ports = self.translate_nets(conns, port_dict, inst_name) subinst.mod.build_graph(graph, subinst_name, subinst_ports) def build_names(self, name_dict, inst_name, port_nets): """Collects all the nets and the parent inst of that net.""" - #Translate port names to external nets + # Translate port names to external nets if len(port_nets) != len(self.pins): - debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets,self.pins),1) - port_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, + self.pins), + 1) + port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} debug.info(3, "Instance name={}".format(inst_name)) for subinst, conns in zip(self.insts, self.conns): - subinst_name = inst_name+'.X'+subinst.name + subinst_name = inst_name + '.X' + subinst.name subinst_ports = self.translate_nets(conns, port_dict, inst_name) for si_port, conn in zip(subinst_ports, conns): - #Only add for first occurrence + # Only add for first occurrence if si_port.lower() not in name_dict: - mod_info = {'mod':self, 'int_net':conn} + mod_info = {'mod': self, 'int_net': conn} name_dict[si_port.lower()] = mod_info - subinst.mod.build_names(name_dict, subinst_name, subinst_ports) + subinst.mod.build_names(name_dict, subinst_name, subinst_ports) def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None): """Given a list of nets, will compare the internal alias of a mod to determine @@ -159,17 +170,17 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): int_mod = self.name_dict[net]['mod'] if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set): aliases.append(net) - return aliases + return aliases def is_net_alias(self, known_net, net_alias, mod, exclusion_set): """Checks if the alias_net in input mod is the same as the input net for this mod (self).""" if self in exclusion_set: return False - #Check ports of this mod + # Check ports of this mod for pin in self.pins: if self.is_net_alias_name_check(known_net, pin, net_alias, mod): return True - #Check connections of all other subinsts + # Check connections of all other subinsts mod_set = set() for subinst, inst_conns in zip(self.insts, self.conns): for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins): @@ -179,7 +190,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if subinst.mod.is_net_alias(mod_pin, net_alias, mod, exclusion_set): return True mod_set.add(subinst.mod) - return False + return False def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod): """Utility function for checking single net alias.""" @@ -188,8 +199,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): parent_net.lower() == alias_net.lower() def get_mod_net(self, parent_net, child_inst, child_conns): - """Given an instance and net, returns the internal net in the mod - corresponding to input net.""" + """ + Given an instance and net, returns the internal net in the mod + corresponding to input net. + """ for conn, pin in zip(child_conns, child_inst.mod.pins): if parent_net.lower() == conn.lower(): return pin @@ -203,27 +216,27 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): converted_conns.append(port_dict[conn]) else: converted_conns.append("{}.{}".format(inst_name, conn)) - return converted_conns + return converted_conns def add_graph_edges(self, graph, port_nets): """For every input, adds an edge to every output. Only intended to be used for gates and other simple modules.""" - #The final pin names will depend on the spice hierarchy, so - #they are passed as an input. - pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + # The final pin names will depend on the spice hierarchy, so + # they are passed as an input. + pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} input_pins = self.get_inputs() output_pins = self.get_outputs() inout_pins = self.get_inouts() - for inp in input_pins+inout_pins: - for out in output_pins+inout_pins: - if inp != out: #do not add self loops - graph.add_edge(pin_dict[inp], pin_dict[out], self) + for inp in input_pins + inout_pins: + for out in output_pins + inout_pins: + if inp != out: # do not add self loops + graph.add_edge(pin_dict[inp], pin_dict[out], self) def __str__(self): """ override print function output """ pins = ",".join(self.pins) insts = [" {}".format(x) for x in self.insts] - objs = [" {}".format(x) for x in self.objs] + objs = [" {}".format(x) for x in self.objs] s = "********** design {0} **********".format(self.name) s += "\n pins ({0})={1}\n".format(len(self.pins), pins) s += "\n objs ({0})=\n{1}\n".format(len(self.objs), "\n".join(objs)) @@ -234,8 +247,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): """ override print function output """ text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n" for i in self.objs: - text+=str(i)+",\n" + text+=str(i) + ",\n" for i in self.insts: - text+=str(i)+",\n" + text+=str(i) + ",\n" return text diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index ebc67ee6..fbc3e535 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -37,13 +37,19 @@ class hierarchical_decoder(design.design): def find_decoder_height(self): b = factory.create(module_type="bitcell") - cell_height = b.height - and3 = factory.create(module_type="pand3", - height=cell_height) - - # Try to make a nand with a given height - # Default return (b.height, 1) + + cell_multiple = 1 + while cell_multiple < 3: + cell_height = cell_multiple * b.height + and3 = factory.create(module_type="pand3", + height=cell_height) + (drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) + if drc_errors + lvs_errors == 0: + return (cell_height, cell_multiple) + cell_multiple += 1 + else: + debug.error("Couldn't find a valid decoder height multiple.", -1) def create_netlist(self): self.add_modules() diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index e8a91486..38f89bc6 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -121,7 +121,7 @@ class sram_base(design, verilog, lef): start_time = datetime.datetime.now() # We only enable final verification if we have routed the design - self.DRC_LVS(final_verification=OPTS.route_supplies, top_level=True) + self.DRC_LVS(final_verification=OPTS.route_supplies, roce_check=True) if not OPTS.is_unit_test: print_time("Verification", datetime.datetime.now(), start_time) From 9106e22b58d8241178c0aefa1651e486d79075ba Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 2 Apr 2020 10:37:21 -0700 Subject: [PATCH 026/101] Fix typo and syntax error. --- compiler/sram/sram_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 38f89bc6..81f71775 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -121,7 +121,7 @@ class sram_base(design, verilog, lef): start_time = datetime.datetime.now() # We only enable final verification if we have routed the design - self.DRC_LVS(final_verification=OPTS.route_supplies, roce_check=True) + self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=True) if not OPTS.is_unit_test: print_time("Verification", datetime.datetime.now(), start_time) From 67de7efd49933c5d95c134130013abcf366b6127 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 2 Apr 2020 11:31:28 -0700 Subject: [PATCH 027/101] Fix syntax error. No DRC/LVS in netlist only mode. --- compiler/base/hierarchy_design.py | 18 +++++++++++++++--- compiler/characterizer/lib.py | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 5119da2e..e06b1d6f 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -42,8 +42,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): def DRC_LVS(self, final_verification=False, force_check=False): """Checks both DRC and LVS for a module""" - - # Final verification option does not allow nets to be connected by label. + + # No layout to check + if OPTS.netlist_only: + return ("skipped", "skipped") # Unit tests will check themselves. if not force_check and OPTS.is_unit_test: return ("skipped", "skipped") @@ -56,7 +58,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.sp_write(tempspice) self.gds_write(tempgds) - + # Final verification option does not allow nets to be connected by label. num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) debug.check(num_drc_errors == 0, @@ -70,12 +72,18 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): os.remove(tempgds) return (num_drc_errors, num_lvs_errors) + else: + return ("skipped", "skipped") def DRC(self, final_verification=False): """Checks DRC for a module""" # Unit tests will check themselves. # Do not run if disabled in options. + # No layout to check + if OPTS.netlist_only: + return "skipped" + if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.gds_write(tempgds) @@ -95,6 +103,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # Unit tests will check themselves. # Do not run if disabled in options. + # No layout to check + if OPTS.netlist_only: + return "skipped" + if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 2742dff2..8f829fae 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -621,7 +621,7 @@ class lib: )) # information of checks - (drc_errors, lvs_errors) = self.sram.DRC_LVS(final_verification=True, top_level=True) + (drc_errors, lvs_errors) = self.sram.DRC_LVS(final_verification=True, force_check=True) datasheet.write("{0},{1},".format(drc_errors, lvs_errors)) # write area From 1d5e5e3607b3f0af40de3b21d31c3dd4d439bbd5 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 2 Apr 2020 12:42:28 -0700 Subject: [PATCH 028/101] Don't run lvs/drc or route supplies in verilog test --- compiler/tests/25_verilog_sram_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index c385e455..bcaa7c9f 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -19,7 +19,9 @@ class verilog_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - + OPTS.route_supplies=False + OPTS.check_lvsdrc=False + from sram import sram from sram_config import sram_config c = sram_config(word_size=2, From f105c9ab3608a2b891d63a7c4ac89abb28fb418f Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 2 Apr 2020 12:43:19 -0700 Subject: [PATCH 029/101] Netlist only in verilog test --- compiler/tests/25_verilog_sram_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index bcaa7c9f..120974ff 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): globals.init_openram(config_file) OPTS.route_supplies=False OPTS.check_lvsdrc=False - + OPTS.netlist_only=True from sram import sram from sram_config import sram_config c = sram_config(word_size=2, From 2850b9efb5d333920700d41a1a40197c4423425c Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 2 Apr 2020 12:52:42 -0700 Subject: [PATCH 030/101] Don't force check in lib characterization. PEP8 formatting. --- compiler/base/hierarchy_design.py | 18 +++++++++++------- compiler/characterizer/lib.py | 2 +- compiler/modules/hierarchical_decoder.py | 5 +++-- compiler/sram/sram_base.py | 2 +- compiler/verify/none.py | 16 ++++++++++++---- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index e06b1d6f..82682b71 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -52,7 +52,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if not force_check and not OPTS.check_lvsdrc: return ("skipped", "skipped") # Do not run if disabled in options. - if (OPTS.inline_lvsdrc or force_check): + if (OPTS.inline_lvsdrc or force_check or final_verification): tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) @@ -61,12 +61,16 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): # Final verification option does not allow nets to be connected by label. num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) - debug.check(num_drc_errors == 0, - "DRC failed for {0} with {1} error(s)".format(self.name, - num_drc_errors)) - debug.check(num_lvs_errors == 0, - "LVS failed for {0} with {1} errors(s)".format(self.name, - num_lvs_errors)) + + # force_check is used to determine decoder height and other things, so we shouldn't fail + # if that flag is set + if OPTS.inline_lvsdrc and not force_check: + debug.check(num_drc_errors == 0, + "DRC failed for {0} with {1} error(s)".format(self.name, + num_drc_errors)) + debug.check(num_lvs_errors == 0, + "LVS failed for {0} with {1} errors(s)".format(self.name, + num_lvs_errors)) os.remove(tempspice) os.remove(tempgds) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 8f829fae..37e27c23 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -621,7 +621,7 @@ class lib: )) # information of checks - (drc_errors, lvs_errors) = self.sram.DRC_LVS(final_verification=True, force_check=True) + (drc_errors, lvs_errors) = self.sram.DRC_LVS(final_verification=True) datasheet.write("{0},{1},".format(drc_errors, lvs_errors)) # write area diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index fbc3e535..0a54a401 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -36,9 +36,10 @@ class hierarchical_decoder(design.design): def find_decoder_height(self): b = factory.create(module_type="bitcell") - + # Old behavior return (b.height, 1) - + + # Search for the smallest multiple that works cell_multiple = 1 while cell_multiple < 3: cell_height = cell_multiple * b.height diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 81f71775..563d128c 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -121,7 +121,7 @@ class sram_base(design, verilog, lef): start_time = datetime.datetime.now() # We only enable final verification if we have routed the design - self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=True) + self.DRC_LVS(final_verification=OPTS.route_supplies) if not OPTS.is_unit_test: print_time("Verification", datetime.datetime.now(), start_time) diff --git a/compiler/verify/none.py b/compiler/verify/none.py index 9d7ac938..41e5780e 100644 --- a/compiler/verify/none.py +++ b/compiler/verify/none.py @@ -16,14 +16,16 @@ drc_warned = False lvs_warned = False pex_warned = False + def run_drc(cell_name, gds_name, extract=False, final_verification=False): global drc_warned if not drc_warned: debug.warning("DRC unable to run.") drc_warned=True - # Since we warned, return a failing test. + # Since we warned, return a failing test. return 1 - + + def run_lvs(cell_name, gds_name, sp_name, final_verification=False): global lvs_warned if not lvs_warned: @@ -32,17 +34,23 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): # Since we warned, return a failing test. return 1 + def run_pex(name, gds_name, sp_name, output=None, final_verification=False): - global pex_warned + global pex_warned if not pex_warned: debug.warning("PEX unable to run.") pex_warned=True - # Since we warned, return a failing test. + # Since we warned, return a failing test. return 1 + def print_drc_stats(): pass + + def print_lvs_stats(): pass + + def print_pex_stats(): pass From 8603d3edd6ee0ce3a910de90ca2602628652f9e9 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 3 Apr 2020 11:37:06 -0700 Subject: [PATCH 031/101] PEP8 cleanup --- compiler/base/hierarchy_spice.py | 166 +++++++++++++++++-------------- 1 file changed, 89 insertions(+), 77 deletions(-) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 707e7bc8..ee8844c2 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -15,6 +15,7 @@ from wire_spice_model import * from power_data import * import logical_effort + class spice(): """ This provides a set of useful generic types for hierarchy @@ -30,19 +31,19 @@ class spice(): self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"] # Holds subckts/mods for this module - self.mods = [] + self.mods = [] # Holds the pins for this module self.pins = [] # The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND # for each instance, this is the set of nets/nodes that map to the pins for this instance - self.pin_type = {} + self.pin_type = {} # THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the # Spice format) self.conns = [] # Keep track of any comments to add the the spice try: self.commments - except: + except NameError: self.comments = [] self.sp_read() @@ -56,7 +57,7 @@ class spice(): try: self.commments - except: + except NameError: self.comments = [] self.comments.append(comment) @@ -65,7 +66,9 @@ class spice(): """ Adds a pin to the pins list. Default type is INOUT signal. """ self.pins.append(name) self.pin_type[name]=pin_type - debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(name,pin_type)) + debug.check(pin_type in self.valid_signal_types, + "Invalid signaltype for {0}: {1}".format(name, + pin_type)) def add_pin_list(self, pin_list, pin_type="INOUT"): """ Adds a pin_list to the pins list """ @@ -73,36 +76,43 @@ class spice(): # or a list that is the same length as the pin list. if type(pin_type)==str: for pin in pin_list: - debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(pin,pin_type)) - self.add_pin(pin,pin_type) + debug.check(pin_type in self.valid_signal_types, + "Invalid signaltype for {0}: {1}".format(pin, + pin_type)) + self.add_pin(pin, pin_type) elif len(pin_type)==len(pin_list): - for (pin,ptype) in zip(pin_list, pin_type): - debug.check(ptype in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(pin,ptype)) - self.add_pin(pin,ptype) + for (pin, ptype) in zip(pin_list, pin_type): + debug.check(ptype in self.valid_signal_types, + "Invalid signaltype for {0}: {1}".format(pin, + ptype)) + self.add_pin(pin, ptype) else: debug.error("Mismatch in type and pin list lengths.", -1) def add_pin_types(self, type_list): - """Add pin types for all the cell's pins. - Typically, should only be used for handmade cells.""" - #This only works if self.pins == bitcell.pin_names + """ + Add pin types for all the cell's pins. + Typically, should only be used for handmade cells. + """ + # This only works if self.pins == bitcell.pin_names if self.pin_names != self.pins: debug.error("{} spice subcircuit port names do not match pin_names\ \n SPICE names={}\ \n Module names={}\ - ".format(self.name, self.pin_names, self.pins),1) - self.pin_type = {pin:type for pin,type in zip(self.pin_names, type_list)} + ".format(self.name, self.pin_names, self.pins), 1) + self.pin_type = {pin: type for pin, type in zip(self.pin_names, type_list)} def get_pin_type(self, name): """ Returns the type of the signal pin. """ pin_type = self.pin_type[name] - debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(name,pin_type)) + debug.check(pin_type in self.valid_signal_types, + "Invalid signaltype for {0}: {1}".format(name, pin_type)) return pin_type def get_pin_dir(self, name): """ Returns the direction of the pin. (Supply/ground are INOUT). """ - if self.pin_type[name] in ["POWER","GROUND"]: + if self.pin_type[name] in ["POWER", "GROUND"]: return "INOUT" else: return self.pin_type[name] @@ -125,11 +135,10 @@ class spice(): output_list.append(pin) return output_list - def copy_pins(self, other_module, suffix=""): """ This will copy all of the pins from the other module and add an optional suffix.""" for pin in other_module.pins: - self.add_pin(pin+suffix, other_module.get_pin_type(pin)) + self.add_pin(pin + suffix, other_module.get_pin_type(pin)) def get_inouts(self): """ These use pin types to determine pin lists. These @@ -144,7 +153,6 @@ class spice(): """Adds a subckt/submodule to the subckt hierarchy""" self.mods.append(mod) - def connect_inst(self, args, check=True): """Connects the pins of the last instance added It is preferred to use the function with the check to find if @@ -169,21 +177,23 @@ class spice(): debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, len(self.insts), len(self.conns))) - debug.error("Instances: \n"+str(insts_string)) + debug.error("Instances: \n" + str(insts_string)) debug.error("-----") - debug.error("Connections: \n"+str(conns_string),1) + debug.error("Connections: \n" + str(conns_string), 1) def get_conns(self, inst): """Returns the connections of a given instance.""" for i in range(len(self.insts)): if inst is self.insts[i]: return self.conns[i] - #If not found, returns None + # If not found, returns None return None def sp_read(self): - """Reads the sp file (and parse the pins) from the library - Otherwise, initialize it to null for dynamic generation""" + """ + Reads the sp file (and parse the pins) from the library + Otherwise, initialize it to null for dynamic generation + """ if self.sp_file and os.path.isfile(self.sp_file): debug.info(3, "opening {0}".format(self.sp_file)) f = open(self.sp_file) @@ -202,13 +212,14 @@ class spice(): def check_net_in_spice(self, net_name): """Checks if a net name exists in the current. Intended to be check nets in hand-made cells.""" - #Remove spaces and lower case then add spaces. Nets are separated by spaces. - net_formatted = ' '+net_name.lstrip().rstrip().lower()+' ' + # Remove spaces and lower case then add spaces. + # Nets are separated by spaces. + net_formatted = ' ' + net_name.lstrip().rstrip().lower() + ' ' for line in self.spice: - #Lowercase the line and remove any part of the line that is a comment. + # Lowercase the line and remove any part of the line that is a comment. line = line.lower().split('*')[0] - #Skip .subckt or .ENDS lines + # Skip .subckt or .ENDS lines if line.find('.') == 0: continue if net_formatted in line: @@ -220,7 +231,7 @@ class spice(): nets_match = True for net in nets: nets_match = nets_match and self.check_net_in_spice(net) - return nets_match + return nets_match def contains(self, mod, modlist): for x in modlist: @@ -244,34 +255,31 @@ class spice(): if self.pins == []: return - # write out the first spice line (the subcircuit) sp.write("\n.SUBCKT {0} {1}\n".format(self.name, " ".join(self.pins))) for pin in self.pins: - sp.write("* {1:6}: {0} \n".format(pin,self.pin_type[pin])) + sp.write("* {1:6}: {0} \n".format(pin, self.pin_type[pin])) for line in self.comments: sp.write("* {}\n".format(line)) # every instance must have a set of connections, even if it is empty. - if len(self.insts)!=len(self.conns): + if len(self.insts) != len(self.conns): debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, len(self.insts), len(self.conns))) - debug.error("Instances: \n"+str(self.insts)) + debug.error("Instances: \n" + str(self.insts)) debug.error("-----") - debug.error("Connections: \n"+str(self.conns),1) - - + debug.error("Connections: \n" + str(self.conns), 1) for i in range(len(self.insts)): # we don't need to output connections of empty instances. # these are wires and paths if self.conns[i] == []: continue - if hasattr(self.insts[i].mod,"spice_device"): + if hasattr(self.insts[i].mod, "spice_device"): sp.write(self.insts[i].mod.spice_device.format(self.insts[i].name, " ".join(self.conns[i]))) sp.write("\n") @@ -286,7 +294,7 @@ class spice(): else: # write the subcircuit itself # Including the file path makes the unit test fail for other users. - #if os.path.isfile(self.sp_file): + # if os.path.isfile(self.sp_file): # sp.write("\n* {0}\n".format(self.sp_file)) sp.write("\n".join(self.spice)) @@ -305,7 +313,8 @@ class spice(): def analytical_delay(self, corner, slew, load=0.0): """Inform users undefined delay module while building new modules""" - # FIXME: Slew is not used in the model right now. Can be added heuristically as linear factor + # FIXME: Slew is not used in the model right now. + # Can be added heuristically as linear factor relative_cap = logical_effort.convert_farad_to_relative_c(load) stage_effort = self.get_stage_effort(relative_cap) @@ -316,7 +325,7 @@ class spice(): abs_delay = stage_effort.get_absolute_delay() corner_delay = self.apply_corners_analytically(abs_delay, corner) SLEW_APPROXIMATION = 0.1 - corner_slew = SLEW_APPROXIMATION*corner_delay + corner_slew = SLEW_APPROXIMATION * corner_delay return delay_data(corner_delay, corner_slew) def get_stage_effort(self, cout, inp_is_rise=True): @@ -326,7 +335,7 @@ class spice(): debug.warning("Class {0} name {1}" .format(self.__class__.__name__, self.name)) - return None + return None def get_cin(self): """Returns input load in Femto-Farads. All values generated using @@ -342,35 +351,35 @@ class spice(): debug.warning("Design Class {0} input capacitance function needs to be defined" .format(self.__class__.__name__)) debug.warning("Class {0} name {1}" - .format(self.__class__.__name__, - self.name)) - return 0 + .format(self.__class__.__name__, + self.name)) + return 0 - def cal_delay_with_rc(self, corner, r, c ,slew, swing = 0.5): - """ - Calculate the delay of a mosfet by + def cal_delay_with_rc(self, corner, r, c, slew, swing=0.5): + """ + Calculate the delay of a mosfet by modeling it as a resistance driving a capacitance """ - swing_factor = abs(math.log(1-swing)) # time constant based on swing - delay = swing_factor * r * c #c is in ff and delay is in fs + swing_factor = abs(math.log(1 - swing)) # time constant based on swing + delay = swing_factor * r * c # c is in ff and delay is in fs delay = self.apply_corners_analytically(delay, corner) - delay = delay * 0.001 #make the unit to ps + delay = delay * 0.001 # make the unit to ps - # Output slew should be linear to input slew which is described + # Output slew should be linear to input slew which is described # as 0.005* slew. # The slew will be also influenced by the delay. - # If no input slew(or too small to make impact) - # The mimum slew should be the time to charge RC. + # If no input slew(or too small to make impact) + # The mimum slew should be the time to charge RC. # Delay * 2 is from 0 to 100% swing. 0.6*2*delay is from 20%-80%. slew = delay * 0.6 * 2 + 0.005 * slew - return delay_data(delay = delay, slew = slew) + return delay_data(delay=delay, slew=slew) def apply_corners_analytically(self, delay, corner): """Multiply delay by corner factors""" - proc,vdd,temp = corner - #FIXME: type of delay is needed to know which process to use. - proc_mult = max(self.get_process_delay_factor(proc)) + proc, vdd, temp = corner + # FIXME: type of delay is needed to know which process to use. + proc_mult = max(self.get_process_delay_factor(proc)) volt_mult = self.get_voltage_delay_factor(vdd) temp_mult = self.get_temp_delay_factor(temp) return delay * proc_mult * volt_mult * temp_mult @@ -385,48 +394,51 @@ class spice(): elif mos_proc == 'F': proc_factors.append(0.9) elif mos_proc == 'S': - proc_factors.append(1.1) + proc_factors.append(1.1) return proc_factors def get_voltage_delay_factor(self, voltage): """Returns delay increase due to voltage. Implemented as linear factor based off nominal voltage. """ - return tech.spice["nom_supply_voltage"]/voltage + return tech.spice["nom_supply_voltage"] / voltage def get_temp_delay_factor(self, temp): """Returns delay increase due to temperature (in C). Determines effect on threshold voltage and then linear factor is estimated. """ - #Some portions of equation condensed (phi_t = k*T/q for T in Kelvin) in mV - #(k/q)/100 = .008625, The division 100 simplifies the conversion from C to K and mV to V - thermal_voltage_nom = 0.008625*tech.spice["nom_temperature"] - thermal_voltage = 0.008625*temp - vthresh = (tech.spice["nom_threshold"]+2*(thermal_voltage-thermal_voltage_nom)) - #Calculate effect on Vdd-Vth. The current vdd is not used here. A separate vdd factor is calculated. - return (tech.spice["nom_supply_voltage"] - tech.spice["nom_threshold"])/(tech.spice["nom_supply_voltage"]-vthresh) + # Some portions of equation condensed (phi_t = k*T/q for T in Kelvin) in mV + # (k/q)/100 = .008625, The division 100 simplifies the conversion from C to K and mV to V + thermal_voltage_nom = 0.008625 * tech.spice["nom_temperature"] + thermal_voltage = 0.008625 * temp + vthresh = (tech.spice["nom_threshold"] + 2 * (thermal_voltage - thermal_voltage_nom)) + # Calculate effect on Vdd-Vth. + # The current vdd is not used here. + # A separate vdd factor is calculated. + return (tech.spice["nom_supply_voltage"] - tech.spice["nom_threshold"]) / (tech.spice["nom_supply_voltage"] - vthresh) def return_delay(self, delay, slew): return delay_data(delay, slew) - def generate_rc_net(self,lump_num, wire_length, wire_width): + def generate_rc_net(self, lump_num, wire_length, wire_width): return wire_spice_model(lump_num, wire_length, wire_width) def calc_dynamic_power(self, corner, c, freq, swing=1.0): - """ + """ Calculate dynamic power using effective capacitance, frequency, and corner (PVT) """ - proc,vdd,temp = corner - net_vswing = vdd*swing - power_dyn = c*vdd*net_vswing*freq + proc, vdd, temp = corner + net_vswing = vdd * swing + power_dyn = c * vdd * net_vswing * freq - #Apply process and temperature factors. Roughly, process and Vdd affect the delay which affects the power. - #No other estimations are currently used. Increased delay->slower freq.->less power - proc_div = max(self.get_process_delay_factor(proc)) + # A pply process and temperature factors. + # Roughly, process and Vdd affect the delay which affects the power. + # No other estimations are currently used. Increased delay->slower freq.->less power + proc_div = max(self.get_process_delay_factor(proc)) temp_div = self.get_temp_delay_factor(temp) - power_dyn = power_dyn/(proc_div*temp_div) + power_dyn = power_dyn / (proc_div * temp_div) - return power_dyn + return power_dyn def return_power(self, dynamic=0.0, leakage=0.0): return power_data(dynamic, leakage) From f358de78bb714d11859d147b7e9dc3984adf4e61 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 3 Apr 2020 13:39:54 -0700 Subject: [PATCH 032/101] Add optional lvs_lib netlists for LVS usage (sp_lib is for simulation) --- compiler/base/hierarchy_design.py | 12 +++++-- compiler/base/hierarchy_spice.py | 47 +++++++++++++++++++++++---- compiler/sram/sram_base.py | 4 +-- compiler/tests/02_library_lvs_test.py | 4 ++- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 82682b71..bb57ae3e 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -24,6 +24,14 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds" self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp" + # If we have a separate lvs directory, then all the lvs files + # should be in there (all or nothing!) + lvs_dir = OPTS.openram_tech + "lvs_lib/" + if os.path.exists(lvs_dir): + self.lvs_file = lvs_dir + name + ".sp" + else: + self.lvs_file = self.sp_file + self.name = name hierarchy_spice.spice.__init__(self, name) hierarchy_layout.layout.__init__(self, name) @@ -56,7 +64,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) - self.sp_write(tempspice) + self.lvs_write(tempspice) self.gds_write(tempgds) # Final verification option does not allow nets to be connected by label. num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) @@ -114,7 +122,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) - self.sp_write(tempspice) + self.lvs_write(tempspice) self.gds_write(tempgds) num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) debug.check(num_errors == 0, diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index ee8844c2..b69888e4 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -43,7 +43,7 @@ class spice(): # Keep track of any comments to add the the spice try: self.commments - except NameError: + except AttributeError: self.comments = [] self.sp_read() @@ -57,7 +57,7 @@ class spice(): try: self.commments - except NameError: + except AttributeError: self.comments = [] self.comments.append(comment) @@ -210,6 +210,24 @@ class spice(): else: self.spice = [] + # We don't define self.lvs and will use self.spice if dynamically created + # or they are the same file + if self.lvs_file!=self.sp_file and os.path.isfile(self.lvs_file): + debug.info(3, "opening {0}".format(self.lvs_file)) + f = open(self.lvs_file) + self.lvs = f.readlines() + for i in range(len(self.lvs)): + self.lvs[i] = self.lvs[i].rstrip(" \n") + f.close() + + # pins and subckt should be the same + # find the correct subckt line in the file + subckt = re.compile("^.subckt {}".format(self.name), re.IGNORECASE) + subckt_line = list(filter(subckt.search, self.lvs))[0] + # parses line into ports and remove subckt + lvs_pins = subckt_line.split(" ")[2:] + debug.check(lvs_pins == self.pins, "LVS and spice file pin mismatch.", -1) + def check_net_in_spice(self, net_name): """Checks if a net name exists in the current. Intended to be check nets in hand-made cells.""" # Remove spaces and lower case then add spaces. @@ -239,16 +257,18 @@ class spice(): return True return False - def sp_write_file(self, sp, usedMODS): - """ Recursive spice subcircuit write; - Writes the spice subcircuit from the library or the dynamically generated one""" + def sp_write_file(self, sp, usedMODS, lvs_netlist=False): + """ + Recursive spice subcircuit write; + Writes the spice subcircuit from the library or the dynamically generated one + """ if not self.spice: # recursively write the modules for i in self.mods: if self.contains(i, usedMODS): continue usedMODS.append(i) - i.sp_write_file(sp, usedMODS) + i.sp_write_file(sp, usedMODS, lvs_netlist) if len(self.insts) == 0: return @@ -296,7 +316,10 @@ class spice(): # Including the file path makes the unit test fail for other users. # if os.path.isfile(self.sp_file): # sp.write("\n* {0}\n".format(self.sp_file)) - sp.write("\n".join(self.spice)) + if lvs_netlist: + sp.write("\n".join(self.lvs)) + else: + sp.write("\n".join(self.spice)) sp.write("\n") @@ -310,6 +333,16 @@ class spice(): del usedMODS spfile.close() + def lvs_write(self, spname): + """Writes the lvs to files""" + debug.info(3, "Writing to {0}".format(spname)) + spfile = open(spname, 'w') + spfile.write("*FIRST LINE IS A COMMENT\n") + usedMODS = list() + self.sp_write_file(spfile, usedMODS, True) + del usedMODS + spfile.close() + def analytical_delay(self, corner, slew, load=0.0): """Inform users undefined delay module while building new modules""" diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 563d128c..64e3f51b 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -574,11 +574,11 @@ class sram_base(design, verilog, lef): sp.write("* Data bits: {}\n".format(self.word_size)) sp.write("* Banks: {}\n".format(self.num_banks)) sp.write("* Column mux: {}:1\n".format(self.words_per_row)) - sp.write("**************************************************\n") + sp.write("**************************************************\n") # This causes unit test mismatch # sp.write("* Created: {0}\n".format(datetime.datetime.now())) # sp.write("* User: {0}\n".format(getpass.getuser())) - # sp.write(".global {0} {1}\n".format(spice["vdd_name"], + # sp.write(".global {0} {1}\n".format(spice["vdd_name"], # spice["gnd_name"])) usedMODS = list() self.sp_write_file(sp, usedMODS) diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index 35ac5d76..0acc8926 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -45,7 +45,9 @@ class library_lvs_test(openram_test): def setup_files(): gds_dir = OPTS.openram_tech + "/gds_lib" - sp_dir = OPTS.openram_tech + "/sp_lib" + sp_dir = OPTS.openram_tech + "/lvs_lib" + if not os.path.exists(sp_dir): + sp_dir = OPTS.openram_tech + "/sp_lib" files = os.listdir(gds_dir) nametest = re.compile("\.gds$", re.IGNORECASE) gds_files = list(filter(nametest.search, files)) From ab5dd47182b59bef7941156d46fd66c9538adb88 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 3 Apr 2020 14:06:56 -0700 Subject: [PATCH 033/101] Ptx is in microns if lvs_lib exists --- compiler/base/hierarchy_spice.py | 7 +++++-- compiler/pgates/ptx.py | 12 +++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index b69888e4..beb79ded 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -299,11 +299,14 @@ class spice(): # these are wires and paths if self.conns[i] == []: continue - if hasattr(self.insts[i].mod, "spice_device"): + if lvs_netlist and hasattr(self.insts[i].mod, "lvs_device"): + sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name, + " ".join(self.conns[i]))) + sp.write("\n") + elif hasattr(self.insts[i].mod, "spice_device"): sp.write(self.insts[i].mod.spice_device.format(self.insts[i].name, " ".join(self.conns[i]))) sp.write("\n") - else: sp.write("X{0} {1} {2}\n".format(self.insts[i].name, " ".join(self.conns[i]), diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index b9d04f32..3ed51651 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -11,6 +11,8 @@ from tech import layer, drc, spice from vector import vector from sram_factory import factory import contact +import os +from globals import OPTS class ptx(design.design): @@ -100,8 +102,6 @@ class ptx(design.design): dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir] self.add_pin_list(pin_list, dir_list) - # self.spice.append("\n.SUBCKT {0} {1}".format(self.name, - # " ".join(self.pins))) # Just make a guess since these will actually # be decided in the layout later. area_sd = 2.5 * self.poly_width * self.tx_width @@ -114,7 +114,13 @@ class ptx(design.design): area_sd) self.spice_device = main_str + area_str self.spice.append("\n* ptx " + self.spice_device) - # self.spice.append(".ENDS {0}".format(self.name)) + + if os.path.exists(OPTS.openram_tech + "lvs_lib"): + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) + def setup_layout_constants(self): """ From beef9441b72dc8ac045f806bbeb4c2711310be61 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sun, 5 Apr 2020 02:55:15 -0700 Subject: [PATCH 034/101] fix pin check debug typo --- compiler/base/hierarchy_spice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index beb79ded..7adfb4f7 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -226,7 +226,7 @@ class spice(): subckt_line = list(filter(subckt.search, self.lvs))[0] # parses line into ports and remove subckt lvs_pins = subckt_line.split(" ")[2:] - debug.check(lvs_pins == self.pins, "LVS and spice file pin mismatch.", -1) + debug.check(lvs_pins == self.pins, "LVS and spice file pin mismatch.") def check_net_in_spice(self, net_name): """Checks if a net name exists in the current. Intended to be check nets in hand-made cells.""" From b59c789dec692cee9843277f188486d621d3b35b Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sun, 5 Apr 2020 03:58:26 -0700 Subject: [PATCH 035/101] remove whitespace --- compiler/modules/dff_buf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 8f581606..e42c01c0 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -91,7 +91,7 @@ class dff_buf(design.design): def create_instances(self): self.dff_inst=self.add_inst(name="dff_buf_dff", mod=self.dff) - #self.connect_inst(props.dff_buff.buf_ports) + self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) self.inv1_inst=self.add_inst(name="dff_buf_inv1", From a12b5d9e6c2e8fa723a2b8ba93c3ebd780caa5ed Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 6 Apr 2020 13:31:31 -0700 Subject: [PATCH 036/101] Split decoder pbitcell tests --- ...hierarchical_predecode2x4_pbitcell_test.py | 41 +++++++++++++++++++ .../06_hierarchical_predecode2x4_test.py | 10 ----- ...hierarchical_predecode3x8_pbitcell_test.py | 41 +++++++++++++++++++ .../06_hierarchical_predecode3x8_test.py | 10 ----- 4 files changed, 82 insertions(+), 20 deletions(-) create mode 100755 compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py create mode 100755 compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py diff --git a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py new file mode 100755 index 00000000..8afc45da --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class hierarchical_predecode2x4_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # checking hierarchical precode 2x4 for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)") + a = factory.create(module_type="hierarchical_predecode2x4") + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index 2e55f5e9..8bb42253 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -26,16 +26,6 @@ class hierarchical_predecode2x4_test(openram_test): a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) - # checking hierarchical precode 2x4 for multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)") - a = factory.create(module_type="hierarchical_predecode2x4") - self.local_check(a) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py new file mode 100755 index 00000000..34d38ddf --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class hierarchical_predecode3x8_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # checking hierarchical precode 3x8 for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)") + a = factory.create(module_type="hierarchical_predecode3x8") + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index da7573e1..f01fae03 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -26,16 +26,6 @@ class hierarchical_predecode3x8_test(openram_test): a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) - # checking hierarchical precode 3x8 for multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)") - a = factory.create(module_type="hierarchical_predecode3x8") - self.local_check(a) - globals.end_openram() # run the test from the command line From cd8dc8e20b5431ad58bdb4cf762c8cadd17bd730 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 6 Apr 2020 14:08:38 -0700 Subject: [PATCH 037/101] Output lvs model instead of spice model --- compiler/base/hierarchy_spice.py | 4 ++-- compiler/tests/testutils.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index beb79ded..8091af63 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -226,7 +226,7 @@ class spice(): subckt_line = list(filter(subckt.search, self.lvs))[0] # parses line into ports and remove subckt lvs_pins = subckt_line.split(" ")[2:] - debug.check(lvs_pins == self.pins, "LVS and spice file pin mismatch.", -1) + debug.check(lvs_pins == self.pins, "LVS and spice file pin mismatch.") def check_net_in_spice(self, net_name): """Checks if a net name exists in the current. Intended to be check nets in hand-made cells.""" @@ -319,7 +319,7 @@ class spice(): # Including the file path makes the unit test fail for other users. # if os.path.isfile(self.sp_file): # sp.write("\n* {0}\n".format(self.sp_file)) - if lvs_netlist: + if lvs_netlist and hasattr(self, "lvs"): sp.write("\n".join(self.lvs)) else: sp.write("\n".join(self.spice)) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 95675c38..c4fd7cac 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -39,7 +39,7 @@ class openram_test(unittest.TestCase): tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name) tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name) - a.sp_write(tempspice) + a.lvs_write(tempspice) # cannot write gds in netlist_only mode if not OPTS.netlist_only: a.gds_write(tempgds) From c8c74e8b695a665a6d258ec1504a04f8e27c9f5d Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 6 Apr 2020 15:20:59 -0700 Subject: [PATCH 038/101] Fix lvs_write in sram class --- compiler/sram/sram.py | 3 +++ compiler/sram/sram_base.py | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 0efad5cc..8863c299 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -51,6 +51,9 @@ class sram(): def sp_write(self, name): self.s.sp_write(name) + def lvs_write(self, name): + self.s.lvs_write(name) + def lef_write(self, name): self.s.lef_write(name) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 64e3f51b..b9c4c909 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -561,7 +561,7 @@ class sram_base(design, verilog, lef): self.add_via_center(layers=self.m2_stack, offset=out_pos) - def sp_write(self, sp_name): + def sp_write(self, sp_name, lvs_netlist=False): # Write the entire spice of the object to the file ############################################################ # Spice circuit @@ -581,10 +581,13 @@ class sram_base(design, verilog, lef): # sp.write(".global {0} {1}\n".format(spice["vdd_name"], # spice["gnd_name"])) usedMODS = list() - self.sp_write_file(sp, usedMODS) + self.sp_write_file(sp, usedMODS, lvs_netlist=lvs_netlist) del usedMODS sp.close() - + + def lvs_write(self, sp_name): + self.sp_write(sp_name, lvs_netlist=True) + def get_wordline_stage_efforts(self, inp_is_rise=True): """Get the all the stage efforts for each stage in the path from clk_buf to a wordline""" stage_effort_list = [] From a3797094d0b4a29892097b8049cc8ed00aeae7bc Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 7 Apr 2020 10:37:49 -0700 Subject: [PATCH 039/101] Swap lvs and sp dimensions for s8 --- compiler/pgates/ptx.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 3ed51651..a555e832 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -106,20 +106,33 @@ class ptx(design.design): # be decided in the layout later. area_sd = 2.5 * self.poly_width * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width - main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly")) - area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd, - area_sd) + if OPTS.tech_name == "s8": + print("here {0}".format(self.name)) + # s8 technology is in microns + main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) + # Perimeters are in microns + # Area is in u since it is microns square + area_str = "pd={0:.2f} ps={0:.2f} as={1:.2f}u ad={1:.2f}u".format(perimeter_sd, + area_sd) + else: + main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) + area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd, + area_sd) self.spice_device = main_str + area_str self.spice.append("\n* ptx " + self.spice_device) + # LVS lib is always in SI units if os.path.exists(OPTS.openram_tech + "lvs_lib"): - self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly")) + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) def setup_layout_constants(self): From 95363856e4429e1a8ebe7d97fa14adb7da15a887 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 8 Apr 2020 02:29:57 -0700 Subject: [PATCH 040/101] Added logical effort and input load for ptx module. --- compiler/pgates/ptx.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 8fbb94d8..a305f620 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -11,6 +11,7 @@ from tech import layer, drc, spice from vector import vector from sram_factory import factory import contact +import logical_effort class ptx(design.design): @@ -445,6 +446,26 @@ class ptx(design.design): if self.connect_active: self.connect_fingered_active(drain_positions, source_positions) + + def get_stage_effort(self, cout): + """Returns an object representing the parameters for delay in tau units.""" + + # FIXME: Using the same definition as the pinv.py. + parasitic_delay = 1 + size = self.mults*self.tx_width/drc("minwidth_tx") + return logical_effort.logical_effort(self.name, + size, + self.input_load(), + cout, + parasitic_delay) + + def input_load(self): + """ + Returns the relative gate cin of the tx + """ + + # FIXME: this will be applied for the loads of the drain/source + return self.mults*self.tx_width/drc("minwidth_tx") def add_diff_contact(self, label, pos): contact=self.add_via_center(layers=self.active_stack, From 0c27942bb2ca643f0c413f36d96c8b620c32202c Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 8 Apr 2020 16:45:28 -0700 Subject: [PATCH 041/101] Dynamically try and DRC decoder for height --- compiler/modules/hierarchical_decoder.py | 25 ++++++++++++++++------- compiler/pgates/pinv.py | 26 ++++++++++++++++-------- compiler/pgates/pnand3.py | 2 +- compiler/pgates/ptx.py | 1 - 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 0a54a401..8776810a 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -11,6 +11,7 @@ import math from sram_factory import factory from vector import vector from globals import OPTS +from errors import drc_error class hierarchical_decoder(design.design): @@ -37,18 +38,28 @@ class hierarchical_decoder(design.design): def find_decoder_height(self): b = factory.create(module_type="bitcell") # Old behavior - return (b.height, 1) + # return (b.height, 1) # Search for the smallest multiple that works cell_multiple = 1 - while cell_multiple < 3: + while cell_multiple < 5: cell_height = cell_multiple * b.height - and3 = factory.create(module_type="pand3", - height=cell_height) - (drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) - if drc_errors + lvs_errors == 0: - return (cell_height, cell_multiple) + # debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height)) + try: + and3 = factory.create(module_type="pand3", + height=cell_height) + except drc_error: + # debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height)) + pass + else: + (drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) + total_errors = drc_errors + lvs_errors + if total_errors == 0: + debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple)) + return (cell_height, cell_multiple) + cell_multiple += 1 + else: debug.error("Couldn't find a valid decoder height multiple.", -1) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 3c2e41a2..3ec3e23e 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -15,6 +15,7 @@ from globals import OPTS from utils import round_to_grid import logical_effort from sram_factory import factory +from errors import drc_error class pinv(pgate.pgate): @@ -105,11 +106,14 @@ class pinv(pgate.pgate): # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, self.poly_extend_active + self.poly_space) - total_height = tx_height + min_channel + 2 * self.top_bottom_space - debug.check(self.height > total_height, - "Cell height {0} too small for simple min height {1}.".format(self.height, - total_height)) + total_height = tx_height + min_channel + 2 * self.top_bottom_space + # debug.check(self.height > total_height, + # "Cell height {0} too small for simple min height {1}.".format(self.height, + # total_height)) + if total_height > self.height: + msg = "Cell height {0} too small for simple min height {1}.".format(self.height, total_height) + raise drc_error(msg) # Determine the height left to the transistors to determine # the number of fingers @@ -141,12 +145,16 @@ class pinv(pgate.pgate): # with LVS property mismatch errors when fingers are not a grid # length and get rounded in the offset geometry. self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) - debug.check(self.nmos_width >= drc("minwidth_tx"), - "Cannot finger NMOS transistors to fit cell height.") - self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) - debug.check(self.pmos_width >= drc("minwidth_tx"), - "Cannot finger PMOS transistors to fit cell height.") + # debug.check(self.nmos_width >= drc("minwidth_tx"), + # "Cannot finger NMOS transistors to fit cell height.") + if self.nmos_width < drc("minwidth_tx"): + raise drc_error("Cannot finger NMOS transistors to fit cell height.") + self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) + #debug.check(self.pmos_width >= drc("minwidth_tx"), + # "Cannot finger PMOS transistors to fit cell height.") + if self.pmos_width < drc("minwidth_tx"): + raise drc_error("Cannot finger NMOS transistors to fit cell height.") def add_ptx(self): """ Create the PMOS and NMOS transistors. """ diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index cc6fd0f8..3eeba41e 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -223,7 +223,7 @@ class pnand3(pgate.pgate): position="center") # FIXME: constant hack - self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch + self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 357e1ce4..f07f4b29 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -108,7 +108,6 @@ class ptx(design.design): area_sd = 2.5 * self.poly_width * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width if OPTS.tech_name == "s8": - print("here {0}".format(self.name)) # s8 technology is in microns main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], self.mults, From ade3b78711ae56f56ac72afa39438a4e839303c9 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 8 Apr 2020 16:55:45 -0700 Subject: [PATCH 042/101] Add exception errors file --- compiler/base/errors.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 compiler/base/errors.py diff --git a/compiler/base/errors.py b/compiler/base/errors.py new file mode 100644 index 00000000..e2b9e5ec --- /dev/null +++ b/compiler/base/errors.py @@ -0,0 +1,15 @@ + + +class drc_error(Exception): + """Exception raised for DRC errors. + + Attributes: + expression -- input expression in which the error occurred + message -- explanation of the error + """ + + # def __init__(self, expression, message): + # self.expression = expression + # self.message = message + def __init__(self, message): + self.message = message From cddfaa0dc8d0e2820a3e806ea30ffde9af9febb3 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 8 Apr 2020 17:04:14 -0700 Subject: [PATCH 043/101] Tech dependent fudge factor --- compiler/pgates/pnand3.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 3eeba41e..eb28ba08 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -223,7 +223,10 @@ class pnand3(pgate.pgate): position="center") # FIXME: constant hack - self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch + if OPTS.tech_name == "s8": + self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch + else: + self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, From 745450fadcbd3700cadc0dcf8552e23207a5597c Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 8 Apr 2020 17:04:50 -0700 Subject: [PATCH 044/101] Syntax error --- compiler/pgates/pnand3.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index eb28ba08..e7cc02e8 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -12,6 +12,7 @@ from tech import drc, parameter, spice from vector import vector import logical_effort from sram_factory import factory +from globals import OPTS class pnand3(pgate.pgate): @@ -226,7 +227,7 @@ class pnand3(pgate.pgate): if OPTS.tech_name == "s8": self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch else: - self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch + self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, From 58fbc5351a7d1a5e95a26e2929aa9569d6bd16fc Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 8 Apr 2020 17:05:16 -0700 Subject: [PATCH 045/101] Change rows to outputs in hierarchical decoder --- compiler/modules/hierarchical_decoder.py | 26 ++++++++++--------- compiler/modules/port_address.py | 12 +++------ .../06_hierarchical_decoder_pbitcell_test.py | 16 ++++++------ .../tests/06_hierarchical_decoder_test.py | 20 +++++++------- 4 files changed, 36 insertions(+), 38 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 8776810a..36097269 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -18,7 +18,7 @@ class hierarchical_decoder(design.design): """ Dynamically generated hierarchical decoder. """ - def __init__(self, name, rows): + def __init__(self, name, num_outputs): design.design.__init__(self, name) self.AND_FORMAT = "DEC_AND_{0}" @@ -27,8 +27,10 @@ class hierarchical_decoder(design.design): self.pre3x8_inst = [] (self.cell_height, self.cell_multiple) = self.find_decoder_height() - self.rows = rows - self.num_inputs = math.ceil(math.log(self.rows, 2)) + self.num_outputs = num_outputs + # We may have more than one bitcell per decoder row + self.rows = math.ceil(num_outputs / self.cell_multiple) + self.num_inputs = math.ceil(math.log(self.num_outputs, 2)) (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) self.create_netlist() @@ -176,7 +178,7 @@ class hierarchical_decoder(design.design): self.internal_routing_width = self.m2_pitch * self.total_number_of_predecoder_outputs self.row_decoder_height = self.inv.height * self.rows - self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch + self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch # Calculates height and width of hierarchical decoder self.height = self.row_decoder_height self.width = self.input_routing_width + self.predecoder_width \ @@ -253,7 +255,7 @@ class hierarchical_decoder(design.design): for i in range(self.num_inputs): self.add_pin("addr_{0}".format(i), "INPUT") - for j in range(self.rows): + for j in range(self.num_outputs): self.add_pin("decode_{0}".format(j), "OUTPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -351,7 +353,7 @@ class hierarchical_decoder(design.design): for i in range(len(self.predec_groups[0])): for j in range(len(self.predec_groups[1])): row = len(self.predec_groups[0]) * j + i - if (row < self.rows): + if (row < self.num_outputs): name = self.AND_FORMAT.format(row) self.and_inst.append(self.add_inst(name=name, mod=self.and2)) @@ -369,7 +371,7 @@ class hierarchical_decoder(design.design): row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \ + len(self.predec_groups[0]) * j + i - if (row < self.rows): + if (row < self.num_outputs): name = self.AND_FORMAT.format(row) self.and_inst.append(self.add_inst(name=name, mod=self.and3)) @@ -405,7 +407,7 @@ class hierarchical_decoder(design.design): def place_and_array(self, and_mod): """ Add a column of AND gates for the decoder above the predecoders.""" - for row in range(self.rows): + for row in range(self.num_outputs): if ((row % 2) == 0): y_off = and_mod.height * row mirror = "R0" @@ -419,7 +421,7 @@ class hierarchical_decoder(design.design): def route_decoder(self): """ Add the pins. """ - for row in range(self.rows): + for row in range(self.num_outputs): z_pin = self.and_inst[row].get_pin("Z") self.add_layout_pin(text="decode_{0}".format(row), layer="m1", @@ -475,7 +477,7 @@ class hierarchical_decoder(design.design): for index_B in self.predec_groups[1]: for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? - if (row_index < self.rows): + if (row_index < self.num_outputs): predecode_name = "predecode_{}".format(index_A) self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A")) predecode_name = "predecode_{}".format(index_B) @@ -487,7 +489,7 @@ class hierarchical_decoder(design.design): for index_B in self.predec_groups[1]: for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? - if (row_index < self.rows): + if (row_index < self.num_outputs): predecode_name = "predecode_{}".format(index_A) self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A")) predecode_name = "predecode_{}".format(index_B) @@ -501,7 +503,7 @@ class hierarchical_decoder(design.design): # The vias will be placed in the center and right of the cells, respectively. xoffset = self.and_inst[0].rx() - for num in range(0, self.rows): + for num in range(0, self.num_outputs): for pin_name in ["vdd", "gnd"]: # The nand and inv are the same height rows... supply_pin = self.and_inst[num].get_pin(pin_name) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 44b0d5a5..6938219a 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -90,17 +90,14 @@ class port_address(design.design): # The pre/post is to access the pin from "outside" the cell to avoid DRCs decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc() driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc() - mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) - mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) + 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("m1", [decoder_out_pos, mid1, mid2, driver_in_pos]) - - - def add_modules(self): self.row_decoder = factory.create(module_type="decoder", - rows=self.num_rows) + num_outputs=self.num_rows) self.add_mod(self.row_decoder) self.wordline_driver = factory.create(module_type="wordline_driver", @@ -108,11 +105,10 @@ class port_address(design.design): cols=self.num_cols) self.add_mod(self.wordline_driver) - def create_row_decoder(self): """ Create the hierarchical row decoder """ - self.row_decoder_inst = self.add_inst(name="row_decoder", + self.row_decoder_inst = self.add_inst(name="row_decoder", mod=self.row_decoder) temp = [] diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py index 1fb3a3f6..ec8f4efe 100755 --- a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py @@ -28,39 +28,39 @@ class hierarchical_decoder_test(openram_test): factory.reset() debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=16) + a = factory.create(module_type="hierarchical_decoder", num_outputs=16) self.local_check(a) factory.reset() debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=17) + a = factory.create(module_type="hierarchical_decoder", num_outputs=17) self.local_check(a) factory.reset() debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=23) + a = factory.create(module_type="hierarchical_decoder", num_outputs=23) self.local_check(a) debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=32) + a = factory.create(module_type="hierarchical_decoder", num_outputs=32) self.local_check(a) factory.reset() debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=65) + a = factory.create(module_type="hierarchical_decoder", num_outputs=65) self.local_check(a) debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=128) + a = factory.create(module_type="hierarchical_decoder", num_outputs=128) self.local_check(a) factory.reset() debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=341) + a = factory.create(module_type="hierarchical_decoder", num_outputs=341) self.local_check(a) debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=512) + a = factory.create(module_type="hierarchical_decoder", num_outputs=512) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index ab7e844f..df979657 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -22,45 +22,45 @@ class hierarchical_decoder_test(openram_test): globals.init_openram(config_file) # Doesn't require hierarchical decoder # debug.info(1, "Testing 4 row sample for hierarchical_decoder") - # a = hierarchical_decoder.hierarchical_decoder(name="hd1, rows=4) + # a = hierarchical_decoder.hierarchical_decoder(name="hd1, num_outputs=4) # self.local_check(a) # Doesn't require hierarchical decoder # debug.info(1, "Testing 8 row sample for hierarchical_decoder") - # a = hierarchical_decoder.hierarchical_decoder(name="hd2", rows=8) + # a = hierarchical_decoder.hierarchical_decoder(name="hd2", num_outputs=8) # self.local_check(a) # check hierarchical decoder for single port debug.info(1, "Testing 16 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=16) + a = factory.create(module_type="hierarchical_decoder", num_outputs=16) self.local_check(a) debug.info(1, "Testing 17 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=17) + a = factory.create(module_type="hierarchical_decoder", num_outputs=17) self.local_check(a) debug.info(1, "Testing 23 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=23) + a = factory.create(module_type="hierarchical_decoder", num_outputs=23) self.local_check(a) debug.info(1, "Testing 32 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=32) + a = factory.create(module_type="hierarchical_decoder", num_outputs=32) self.local_check(a) debug.info(1, "Testing 65 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=65) + a = factory.create(module_type="hierarchical_decoder", num_outputs=65) self.local_check(a) debug.info(1, "Testing 128 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=128) + a = factory.create(module_type="hierarchical_decoder", num_outputs=128) self.local_check(a) debug.info(1, "Testing 341 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=341) + a = factory.create(module_type="hierarchical_decoder", num_outputs=341) self.local_check(a) debug.info(1, "Testing 512 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", rows=512) + a = factory.create(module_type="hierarchical_decoder", num_outputs=512) self.local_check(a) globals.end_openram() From 8a55c223dfa6475322c1f2940d13aed0d1232076 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 9 Apr 2020 09:48:54 -0700 Subject: [PATCH 046/101] Use single height for netlist_only mode --- compiler/modules/hierarchical_decoder.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 36097269..982d9583 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -38,9 +38,11 @@ class hierarchical_decoder(design.design): self.create_layout() def find_decoder_height(self): + b = factory.create(module_type="bitcell") # Old behavior - # return (b.height, 1) + if OPTS.netlist_only: + return (b.height, 1) # Search for the smallest multiple that works cell_multiple = 1 From 75bd2b46a548b888eeade14fdfa27895ab337e2f Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 9 Apr 2020 10:02:15 -0700 Subject: [PATCH 047/101] OpenRAM v1.1.5 --- compiler/globals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/globals.py b/compiler/globals.py index 3277ab1d..dbb1f962 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -19,7 +19,7 @@ import re import copy import importlib -VERSION = "1.1.4" +VERSION = "1.1.5" NAME = "OpenRAM v{}".format(VERSION) USAGE = "openram.py [options] \nUse -h for help.\n" From 7888e54fc4e6b652e98339d3775d40c523e27952 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 9 Apr 2020 11:38:18 -0700 Subject: [PATCH 048/101] Remove dynamic bitcell multiple detection. Check for decoder bitcell multiple in tech file or assume 1. PEP8 fixes. --- compiler/globals.py | 2 +- compiler/modules/hierarchical_decoder.py | 18 +++++++++++++----- compiler/tests/30_openram_back_end_test.py | 19 +++++++++---------- compiler/tests/30_openram_front_end_test.py | 21 ++++++++++----------- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/compiler/globals.py b/compiler/globals.py index 3277ab1d..4e6253ad 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -589,4 +589,4 @@ def report_status(): if OPTS.trim_netlist: debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).") if OPTS.nominal_corner_only: - debug.print_raw("Only characterizing nominal corner.") + debug.print_raw("Only characterizing nominal corner.") diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 982d9583..be062acf 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -12,6 +12,7 @@ from sram_factory import factory from vector import vector from globals import OPTS from errors import drc_error +from tech import cell_properties class hierarchical_decoder(design.design): @@ -26,7 +27,13 @@ class hierarchical_decoder(design.design): self.pre2x4_inst = [] self.pre3x8_inst = [] - (self.cell_height, self.cell_multiple) = self.find_decoder_height() + b = factory.create(module_type="bitcell") + try: + self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple + except AttributeError: + self.cell_multiple = 1 + self.cell_height = self.cell_multiple * b.height + self.num_outputs = num_outputs # We may have more than one bitcell per decoder row self.rows = math.ceil(num_outputs / self.cell_multiple) @@ -38,11 +45,12 @@ class hierarchical_decoder(design.design): self.create_layout() def find_decoder_height(self): - + """ + Dead code. This would dynamically determine the bitcell multiple, + but I just decided to hard code it in the tech file if it is not 1 + because a DRC tool would be required even to run in front-end mode. + """ b = factory.create(module_type="bitcell") - # Old behavior - if OPTS.netlist_only: - return (b.height, 1) # Search for the smallest multiple that works cell_multiple = 1 diff --git a/compiler/tests/30_openram_back_end_test.py b/compiler/tests/30_openram_back_end_test.py index 73d201b1..591d03ac 100755 --- a/compiler/tests/30_openram_back_end_test.py +++ b/compiler/tests/30_openram_back_end_test.py @@ -51,7 +51,7 @@ class openram_back_end_test(openram_test): debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") exe_name = "{0}/openram.py ".format(OPENRAM_HOME) else: - exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) + exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) config_name = "{0}/tests/configs/config_back_end.py".format(OPENRAM_HOME) cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, out_file, @@ -64,33 +64,32 @@ class openram_back_end_test(openram_test): # assert an error until we actually check a resul for extension in ["gds", "v", "lef", "sp"]: - filename = "{0}/{1}.{2}".format(out_path,out_file,extension) - debug.info(1,"Checking for file: " + filename) - self.assertEqual(os.path.exists(filename),True) + filename = "{0}/{1}.{2}".format(out_path, out_file, extension) + debug.info(1, "Checking for file: " + filename) + self.assertEqual(os.path.exists(filename), True) # Make sure there is any .lib file import glob files = glob.glob('{0}/*.lib'.format(out_path)) self.assertTrue(len(files)>0) - # Make sure there is any .html file + # Make sure there is any .html file if os.path.exists(out_path): datasheets = glob.glob('{0}/*html'.format(out_path)) self.assertTrue(len(datasheets)>0) # grep any errors from the output - output_log = open("{0}/output.log".format(out_path),"r") + output_log = open("{0}/output.log".format(out_path), "r") output = output_log.read() output_log.close() - self.assertEqual(len(re.findall('ERROR',output)),0) - self.assertEqual(len(re.findall('WARNING',output)),0) - + self.assertEqual(len(re.findall('ERROR', output)), 0) + self.assertEqual(len(re.findall('WARNING', output)), 0) # now clean up the directory if OPTS.purge_temp: if os.path.exists(out_path): shutil.rmtree(out_path, ignore_errors=True) - self.assertEqual(os.path.exists(out_path),False) + self.assertEqual(os.path.exists(out_path), False) globals.end_openram() diff --git a/compiler/tests/30_openram_front_end_test.py b/compiler/tests/30_openram_front_end_test.py index 3aec10f9..127df6c2 100755 --- a/compiler/tests/30_openram_front_end_test.py +++ b/compiler/tests/30_openram_front_end_test.py @@ -51,7 +51,7 @@ class openram_front_end_test(openram_test): debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") exe_name = "{0}/openram.py ".format(OPENRAM_HOME) else: - exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) + exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) config_name = "{0}/tests/configs/config_front_end.py".format(OPENRAM_HOME) cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, out_file, @@ -64,33 +64,32 @@ class openram_front_end_test(openram_test): # assert an error until we actually check a result for extension in ["v", "lef", "sp", "gds"]: - filename = "{0}/{1}.{2}".format(out_path,out_file,extension) - debug.info(1,"Checking for file: " + filename) - self.assertEqual(os.path.exists(filename),True) + filename = "{0}/{1}.{2}".format(out_path, out_file, extension) + debug.info(1, "Checking for file: " + filename) + self.assertEqual(os.path.exists(filename), True) # Make sure there is any .lib file import glob files = glob.glob('{0}/*.lib'.format(out_path)) self.assertTrue(len(files)>0) - # Make sure there is any .html file + # Make sure there is any .html file if os.path.exists(out_path): datasheets = glob.glob('{0}/*html'.format(out_path)) self.assertTrue(len(datasheets)>0) # grep any errors from the output - output_log = open("{0}/output.log".format(out_path),"r") + output_log = open("{0}/output.log".format(out_path), "r") output = output_log.read() output_log.close() - self.assertEqual(len(re.findall('ERROR',output)),0) - self.assertEqual(len(re.findall('WARNING',output)),0) + self.assertEqual(len(re.findall('ERROR', output)), 0) + self.assertEqual(len(re.findall('WARNING', output)), 0) - - # now clean up the directory + # now clean up the directory if OPTS.purge_temp: if os.path.exists(out_path): shutil.rmtree(out_path, ignore_errors=True) - self.assertEqual(os.path.exists(out_path),False) + self.assertEqual(os.path.exists(out_path), False) globals.end_openram() From a0eb9839ad55e33fdbf76d8c5fda0da528104b06 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Thu, 9 Apr 2020 19:39:21 -0700 Subject: [PATCH 049/101] revert units on sp_lib, begin discrete tx simulation --- compiler/characterizer/stimuli.py | 11 +++++++ compiler/pgates/pgate.py | 49 ++++++++++++++++++++++++++++++- compiler/pgates/pinv.py | 3 ++ compiler/pgates/precharge.py | 5 ++++ compiler/pgates/ptx.py | 4 ++- 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 58a9e3ed..fc2a106f 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -34,6 +34,10 @@ class stimuli(): self.sf = stim_file (self.process, self.voltage, self.temperature) = corner + try: + self.device_libraries = tech.spice["fet_libraries"][self.process] + except: + debug.info(2, "Not using spice library") self.device_models = tech.spice["fet_models"][self.process] self.sram_name = "Xsram" @@ -247,8 +251,15 @@ class stimuli(): def write_include(self, circuit): """Writes include statements, inputs are lists of model files""" + libraries = self.device_libraries includes = self.device_models + [circuit] self.sf.write("* {} process corner\n".format(self.process)) + for item in list(libraries): + if os.path.isfile(item[0]): + self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1])) + else: + debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0])) + for item in list(includes): if os.path.isfile(item): self.sf.write(".include \"{0}\"\n".format(item)) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index d83825ea..ba5fef75 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -8,10 +8,14 @@ import contact import design import debug -from tech import layer +import math +from bisect import bisect_left +from tech import layer, drc from vector import vector from globals import OPTS +if(OPTS.tech_name == "s8"): + from tech import nmos_bins, pmos_bins class pgate(design.design): """ @@ -282,5 +286,48 @@ class pgate(design.design): self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width self.well_width = self.width + 2 * self.nwell_enclose_active # Height is an input parameter, so it is not recomputed. + + def bin_width(self, tx_type, target_width): + + if tx_type == "nmos": + bins = nmos_bins[drc("minwidth_poly")] + elif tx_type == "pmos": + bins = pmos_bins[drc("minwidth_poly")] + else: + debug.error("invalid tx type") + + + bins = bins[0:bisect_left(bins, target_width) + 1] + if len(bins) == 1: + selected_bin = bins[0] + scaling_factor = 1 + scaled_bin = bins[0] + + else: + scaled_bins = [] + scaling_factors = [] + for width in bins[0:-1]: + m = math.ceil(target_width / width) + scaling_factors.append(m) + scaled_bins.append(m * width) + + scaled_bins.append(bins[-1]) + scaling_factors.append(1) + select = bisect_left(scaled_bins, target_width) + + selected_bin = bins[select] + scaling_factor = scaling_factors[select] + scaled_bin = scaled_bins[select] + + debug.info(2, "binning {0} tx, target: {4}, found {1} x {2} = {3}".format(tx_type, selected_bin, scaling_factor, scaled_bin, target_width)) + + return(selected_bin, scaling_factor) + + + + + + + diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 3c2e41a2..1ccad6ca 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -81,6 +81,9 @@ class pinv(pgate.pgate): self.tx_mults = 1 self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") + if OPTS.tech_name == "s8": + (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) + (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) return # Do a quick sanity check and bail if unlikely feasible height diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index afe14a05..45128421 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -8,6 +8,7 @@ import contact import design import debug +from pgate import pgate from tech import parameter from vector import vector from globals import OPTS @@ -28,6 +29,7 @@ class precharge(design.design): self.bitcell = factory.create(module_type="bitcell") self.beta = parameter["beta"] self.ptx_width = self.beta * parameter["min_tx_size"] + self.ptx_mults = 1 self.width = self.bitcell.width self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br @@ -77,8 +79,11 @@ class precharge(design.design): """ Initializes the upper and lower pmos """ + if(OPTS.tech_name == "s8"): + (self.ptx_width, self.ptx_mults) = pgate.bin_width(self, "pmos", self.ptx_width) self.pmos = factory.create(module_type="ptx", width=self.ptx_width, + mults=self.ptx_mults, tx_type="pmos") self.add_mod(self.pmos) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 357e1ce4..ffd61a48 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -107,7 +107,9 @@ class ptx(design.design): # be decided in the layout later. area_sd = 2.5 * self.poly_width * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width - if OPTS.tech_name == "s8": + + + if OPTS.tech_name == None: print("here {0}".format(self.name)) # s8 technology is in microns main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], From 2e67d44cd7e09acf733acc40fc2f599757d9089c Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 10 Apr 2020 13:29:41 -0700 Subject: [PATCH 050/101] First pass of multiple bitcells per decoder row --- compiler/modules/hierarchical_decoder.py | 245 +++++++++++------- compiler/pgates/pnand3.py | 8 +- .../tests/06_hierarchical_decoder_test.py | 9 - 3 files changed, 159 insertions(+), 103 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index be062acf..0e5b3606 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -32,11 +32,11 @@ class hierarchical_decoder(design.design): self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple except AttributeError: self.cell_multiple = 1 + # For debugging + # self.cell_multiple = 2 self.cell_height = self.cell_multiple * b.height self.num_outputs = num_outputs - # We may have more than one bitcell per decoder row - self.rows = math.ceil(num_outputs / self.cell_multiple) self.num_inputs = math.ceil(math.log(self.num_outputs, 2)) (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) @@ -51,6 +51,10 @@ class hierarchical_decoder(design.design): because a DRC tool would be required even to run in front-end mode. """ b = factory.create(module_type="bitcell") + + # Old behavior + if OPTS.netlist_only: + return (b.height, 1) # Search for the smallest multiple that works cell_multiple = 1 @@ -86,8 +90,8 @@ class hierarchical_decoder(design.design): self.setup_layout_constants() self.place_pre_decoder() self.place_row_decoder() - self.route_input_rails() - self.route_predecode_rails() + self.route_inputs() + self.route_decoder_bus() self.route_vdd_gnd() self.offset_all_coordinates() self.add_boundary() @@ -141,7 +145,7 @@ class hierarchical_decoder(design.design): def setup_netlist_constants(self): self.predec_groups = [] # This array is a 2D array. - # Distributing vertical rails to different groups. One group belongs to one pre-decoder. + # Distributing vertical bus to different groups. One group belongs to one pre-decoder. # For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will # have total 16 output lines out of these 3 pre-decoders and they will # be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ] @@ -180,39 +184,45 @@ class hierarchical_decoder(design.design): self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 + # We may have more than one bitcell per decoder row + self.num_rows = math.ceil(self.num_outputs / self.cell_multiple) + # We will place this many final decoders per row + self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows) + # Calculates height and width of row-decoder if (self.num_inputs == 4 or self.num_inputs == 5): nand_width = self.and2.width else: nand_width = self.and3.width - self.internal_routing_width = self.m2_pitch * self.total_number_of_predecoder_outputs - self.row_decoder_height = self.inv.height * self.rows + self.internal_routing_width = self.m2_pitch * (self.total_number_of_predecoder_outputs + 1) + self.row_decoder_height = self.inv.height * self.num_rows self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch # Calculates height and width of hierarchical decoder - self.height = self.row_decoder_height + self.height = max(self.predecoder_height, self.row_decoder_height) self.width = self.input_routing_width + self.predecoder_width \ - + self.internal_routing_width + nand_width + self.inv.width + + self.internal_routing_width \ + + self.decoders_per_row * nand_width + self.inv.width - def route_input_rails(self): - """ Create input rails for the predecoders """ + def route_inputs(self): + """ Create input bus for the predecoders """ # inputs should be as high as the decoders input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height # Find the left-most predecoder min_x = 0 if self.no_of_pre2x4 > 0: - min_x = min(min_x, -self.pre2_4.width) + min_x = min(min_x, self.pre2x4_inst[0].lx()) if self.no_of_pre3x8 > 0: - min_x = min(min_x, -self.pre3_8.width) + min_x = min(min_x, self.pre3x8_inst[0].lx()) input_offset=vector(min_x - self.input_routing_width, 0) input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] - self.input_rails = self.create_vertical_pin_bus(layer="m2", - pitch=self.m2_pitch, - offset=input_offset, - names=input_bus_names, - length=input_height) + self.input_bus = self.create_vertical_pin_bus(layer="m2", + pitch=self.m2_pitch, + offset=input_offset, + names=input_bus_names, + length=input_height) self.route_input_to_predecodes() @@ -222,7 +232,7 @@ class hierarchical_decoder(design.design): for i in range(2): index = pre_num * 2 + i - input_pos = self.input_rails["addr_{}".format(index)] + input_pos = self.input_bus["addr_{}".format(index)] in_name = "in_{}".format(i) decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) @@ -232,13 +242,13 @@ class hierarchical_decoder(design.design): decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) - self.route_input_rail(decoder_offset, input_offset) + self.route_input_bus(decoder_offset, input_offset) for pre_num in range(self.no_of_pre3x8): for i in range(3): index = pre_num * 3 + i + self.no_of_pre2x4 * 2 - input_pos = self.input_rails["addr_{}".format(index)] + input_pos = self.input_bus["addr_{}".format(index)] in_name = "in_{}".format(i) decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) @@ -248,9 +258,9 @@ class hierarchical_decoder(design.design): decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) - self.route_input_rail(decoder_offset, input_offset) + self.route_input_bus(decoder_offset, input_offset) - def route_input_rail(self, input_offset, output_offset): + def route_input_bus(self, input_offset, output_offset): """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ self.add_via_center(layers=self.m2_stack, @@ -334,18 +344,17 @@ class hierarchical_decoder(design.design): else: base= vector(-self.pre2_4.width, num * self.pre2_4.height) - self.pre2x4_inst[num].place(base) + self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0)) def place_pre3x8(self, num): """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ if (self.num_inputs == 3): offset = vector(-self.pre_3_8.width, 0) - mirror = "R0" else: height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height offset = vector(-self.pre3_8.width, height) - self.pre3x8_inst[num].place(offset) + self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0)) def create_row_decoder(self): """ Create the row-decoder by placing AND2/AND3 and Inverters @@ -362,14 +371,14 @@ class hierarchical_decoder(design.design): if (self.num_inputs == 4 or self.num_inputs == 5): for i in range(len(self.predec_groups[0])): for j in range(len(self.predec_groups[1])): - row = len(self.predec_groups[0]) * j + i - if (row < self.num_outputs): - name = self.AND_FORMAT.format(row) + output = len(self.predec_groups[0]) * j + i + if (output < self.num_outputs): + name = self.AND_FORMAT.format(output) self.and_inst.append(self.add_inst(name=name, mod=self.and2)) pins =["out_{0}".format(i), "out_{0}".format(j + len(self.predec_groups[0])), - "decode_{0}".format(row), + "decode_{0}".format(output), "vdd", "gnd"] self.connect_inst(pins) @@ -378,18 +387,18 @@ class hierarchical_decoder(design.design): for i in range(len(self.predec_groups[0])): for j in range(len(self.predec_groups[1])): for k in range(len(self.predec_groups[2])): - row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \ - + len(self.predec_groups[0]) * j + i + output = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \ + + len(self.predec_groups[0]) * j + i - if (row < self.num_outputs): - name = self.AND_FORMAT.format(row) + if (output < self.num_outputs): + name = self.AND_FORMAT.format(output) self.and_inst.append(self.add_inst(name=name, mod=self.and3)) pins = ["out_{0}".format(i), "out_{0}".format(j + len(self.predec_groups[0])), "out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), - "decode_{0}".format(row), + "decode_{0}".format(output), "vdd", "gnd"] self.connect_inst(pins) @@ -403,7 +412,10 @@ class hierarchical_decoder(design.design): self.route_decoder() def place_decoder_and_array(self): - """ Add a column of AND gates for final decode """ + """ + Add a column of AND gates for final decode. + This may have more than one decoder per row to match the bitcell height. + """ # Row Decoder AND GATE array for address inputs <5. if (self.num_inputs == 4 or self.num_inputs == 5): @@ -415,9 +427,13 @@ class hierarchical_decoder(design.design): self.place_and_array(and_mod=self.and3) def place_and_array(self, and_mod): - """ Add a column of AND gates for the decoder above the predecoders.""" - - for row in range(self.num_outputs): + """ + Add a column of AND gates for the decoder above the predecoders. + """ + + for inst_index in range(self.num_outputs): + row = math.floor(inst_index / self.decoders_per_row) + dec = inst_index % self.decoders_per_row if ((row % 2) == 0): y_off = and_mod.height * row mirror = "R0" @@ -425,46 +441,52 @@ class hierarchical_decoder(design.design): y_off = and_mod.height * (row + 1) mirror = "MX" - self.and_inst[row].place(offset=[self.internal_routing_width, y_off], - mirror=mirror) + x_off = self.internal_routing_width + dec * and_mod.width + self.and_inst[inst_index].place(offset=vector(x_off, y_off), + mirror=mirror) def route_decoder(self): """ Add the pins. """ - for row in range(self.num_outputs): - z_pin = self.and_inst[row].get_pin("Z") - self.add_layout_pin(text="decode_{0}".format(row), + for output in range(self.num_outputs): + z_pin = self.and_inst[output].get_pin("Z") + self.add_layout_pin(text="decode_{0}".format(output), layer="m1", offset=z_pin.ll(), width=z_pin.width(), height=z_pin.height()) - def route_predecode_rails(self): - """ Creates vertical metal 2 rails to connect predecoder and decoder stages.""" + def route_decoder_bus(self): + """ + Creates vertical metal 2 bus to connect predecoder and decoder stages. + """ # This is not needed for inputs <4 since they have no pre/decode stages. if (self.num_inputs >= 4): - input_offset = vector(0.5 * self.m2_width, 0) + # This leaves an offset for the predecoder output jogs input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)] - self.predecode_rails = self.create_vertical_pin_bus(layer="m2", - pitch=self.m2_pitch, - offset=input_offset, - names=input_bus_names, - length=self.height) + self.predecode_bus = self.create_vertical_pin_bus(layer="m2", + pitch=self.m2_pitch, + offset=vector(0, 0), + names=input_bus_names, + length=self.height) - self.route_rails_to_predecodes() - self.route_rails_to_decoder() - - def route_rails_to_predecodes(self): - """ Iterates through all of the predecodes and connects to the rails including the offsets """ + self.route_bus_to_predecodes() + self.route_bus_to_decoder() + def route_bus_to_predecodes(self): + """ + Iterates through all of the predecodes + and connects to the rails including the offsets + """ # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre2x4): for i in range(4): predecode_name = "predecode_{}".format(pre_num * 4 + i) out_name = "out_{}".format(i) pin = self.pre2x4_inst[pre_num].get_pin(out_name) - self.route_predecode_rail_m3(predecode_name, pin) + x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch + self.route_predecode_bus_inputs(predecode_name, pin, x_offset) # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre3x8): @@ -472,44 +494,68 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4) out_name = "out_{}".format(i) pin = self.pre3x8_inst[pre_num].get_pin(out_name) - self.route_predecode_rail_m3(predecode_name, pin) + x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch + self.route_predecode_bus_inputs(predecode_name, pin, x_offset) - def route_rails_to_decoder(self): - """ Use the self.predec_groups to determine the connections to the decoder AND gates. - Inputs of AND2/AND3 gates come from different groups. - For example for these groups [ [0,1,2,3] ,[4,5,6,7], - [8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to - [0,4,8] and second AND3 is connected to [0,4,9] ........... and the - 128th AND3 is connected to [3,7,15] + def route_bus_to_decoder(self): """ - row_index = 0 + Use the self.predec_groups to determine the connections to the decoder AND gates. + Inputs of AND2/AND3 gates come from different groups. + For example for these groups + [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ] + the first AND3 inputs are connected to [0,4,8], + second AND3 is connected to [0,4,9], + ... + and the 128th AND3 is connected to [3,7,15] + """ + output_index = 0 + if (self.num_inputs == 4 or self.num_inputs == 5): for index_B in self.predec_groups[1]: for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? - if (row_index < self.num_outputs): + if (output_index < self.num_outputs): + row_index = math.floor(output_index / self.decoders_per_row) + row_remainder = (output_index % self.decoders_per_row) + row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 4) * self.m1_pitch predecode_name = "predecode_{}".format(index_A) - self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A")) + self.route_predecode_bus_outputs(predecode_name, + self.and_inst[output_index].get_pin("A"), + row_offset) predecode_name = "predecode_{}".format(index_B) - self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B")) - row_index = row_index + 1 + self.route_predecode_bus_outputs(predecode_name, + self.and_inst[output_index].get_pin("B"), + row_offset + self.m1_pitch) + output_index = output_index + 1 elif (self.num_inputs > 5): for index_C in self.predec_groups[2]: for index_B in self.predec_groups[1]: for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? - if (row_index < self.num_outputs): + if (output_index < self.num_outputs): + row_index = math.floor(output_index / self.decoders_per_row) + row_remainder = (output_index % self.decoders_per_row) + row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 4) * self.m1_pitch predecode_name = "predecode_{}".format(index_A) - self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A")) + self.route_predecode_bus_outputs(predecode_name, + self.and_inst[output_index].get_pin("A"), + row_offset) predecode_name = "predecode_{}".format(index_B) - self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B")) + self.route_predecode_bus_outputs(predecode_name, + self.and_inst[output_index].get_pin("B"), + row_offset + self.m1_pitch) predecode_name = "predecode_{}".format(index_C) - self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C")) - row_index = row_index + 1 + self.route_predecode_bus_outputs(predecode_name, + self.and_inst[output_index].get_pin("C"), + row_offset + 2 * self.m1_pitch) + output_index = output_index + 1 def route_vdd_gnd(self): - """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ + """ + Add a pin for each row of vdd/gnd which are + must-connects next level up. + """ # The vias will be placed in the center and right of the cells, respectively. xoffset = self.and_inst[0].rx() @@ -526,23 +572,42 @@ class hierarchical_decoder(design.design): self.copy_layout_pin(pre, "vdd") self.copy_layout_pin(pre, "gnd") - def route_predecode_rail(self, rail_name, pin): - """ Connect the routing rail to the given metal1 pin """ - rail_pos = vector(self.predecode_rails[rail_name].x, pin.lc().y) - self.add_path("m1", [rail_pos, pin.lc()]) - self.add_via_center(layers=self.m1_stack, - offset=rail_pos) + def route_predecode_bus_outputs(self, rail_name, pin, y_offset): + """ + Connect the routing rail to the given metal1 pin + using a routing track at the given y_offset + + """ + pin_pos = pin.center() + # If we have a single decoder per row, we can route on M1 + if self.decoders_per_row == 1: + rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) + self.add_path("m1", [rail_pos, pin_pos]) + self.add_via_center(layers=self.m1_stack, + offset=rail_pos) + # If not, we must route over the decoder cells on M3 + else: + rail_pos = vector(self.predecode_bus[rail_name].x, y_offset) + mid_pos = vector(pin_pos.x, rail_pos.y) + self.add_wire(self.m2_stack[::-1], [rail_pos, mid_pos, pin_pos]) + self.add_via_center(layers=self.m2_stack, + offset=rail_pos) + self.add_via_center(layers=self.m1_stack, + offset=pin_pos) - def route_predecode_rail_m3(self, rail_name, pin): - """ Connect the routing rail to the given metal1 pin """ + def route_predecode_bus_inputs(self, rail_name, pin, x_offset): + """ + Connect the routing rail to the given metal1 pin using a jog + to the right of the cell at the given x_offset. + """ # This routes the pin up to the rail, basically, to avoid conflicts. # It would be fixed with a channel router. - mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2) - rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y) + pin_pos = pin.center() + mid_point1 = vector(x_offset, pin_pos.y) + mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2) + rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y) + self.add_wire(self.m1_stack, [pin_pos, mid_point1, mid_point2, rail_pos]) self.add_via_center(layers=self.m1_stack, - offset=pin.center()) - self.add_wire(("m3", "via2", "m2"), [rail_pos, mid_point, pin.uc()]) - self.add_via_center(layers=self.m2_stack, offset=rail_pos) def input_load(self): diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index e7cc02e8..58894ccf 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -221,7 +221,7 @@ class pnand3(pgate.pgate): self.nmos3_inst, self.inputC_yoffset, "C", - position="center") + position="left") # FIXME: constant hack if OPTS.tech_name == "s8": @@ -232,7 +232,7 @@ class pnand3(pgate.pgate): self.nmos1_inst, self.inputA_yoffset, "A", - position="center") + position="left") def route_output(self): """ Route the Z output """ @@ -255,10 +255,10 @@ class pnand3(pgate.pgate): directions=("V", "V")) # PMOS3 and NMOS3 are drain aligned - self.add_path("m2", [pmos3_pin.center(), nmos3_pin.center()]) + self.add_path("m1", [pmos3_pin.center(), nmos3_pin.center()]) # Route in the A input track (top track) mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) - self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) + self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) # This extends the output to the edge of the cell self.add_via_center(layers=self.m1_stack, diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index df979657..7b77c6d9 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -20,15 +20,6 @@ class hierarchical_decoder_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Doesn't require hierarchical decoder - # debug.info(1, "Testing 4 row sample for hierarchical_decoder") - # a = hierarchical_decoder.hierarchical_decoder(name="hd1, num_outputs=4) - # self.local_check(a) - - # Doesn't require hierarchical decoder - # debug.info(1, "Testing 8 row sample for hierarchical_decoder") - # a = hierarchical_decoder.hierarchical_decoder(name="hd2", num_outputs=8) - # self.local_check(a) # check hierarchical decoder for single port debug.info(1, "Testing 16 row sample for hierarchical_decoder") From afcb5174ac877ae66e6844c2a20ec08bb04acee8 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Sat, 11 Apr 2020 01:19:04 -0700 Subject: [PATCH 051/101] discrete dff tests working --- compiler/characterizer/stimuli.py | 14 ++++++++------ compiler/pgates/ptx.py | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index fc2a106f..b5a143cf 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -251,14 +251,16 @@ class stimuli(): def write_include(self, circuit): """Writes include statements, inputs are lists of model files""" - libraries = self.device_libraries + includes = self.device_models + [circuit] self.sf.write("* {} process corner\n".format(self.process)) - for item in list(libraries): - if os.path.isfile(item[0]): - self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1])) - else: - debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0])) + if OPTS.tech_name == "s8": + libraries = self.device_libraries + for item in list(libraries): + if os.path.isfile(item[0]): + self.sf.write(".lib \"{0}\" {1}\n".format(item[0], item[1])) + else: + debug.error("Could not find spice library: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item[0])) for item in list(includes): if os.path.isfile(item): diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index ffd61a48..d51f42be 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -109,7 +109,7 @@ class ptx(design.design): perimeter_sd = 2 * self.poly_width + 2 * self.tx_width - if OPTS.tech_name == None: + if OPTS.tech_name == "s8": print("here {0}".format(self.name)) # s8 technology is in microns main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], From 5f4ed47c57e9ec62e4f25c6426664d0c26f68543 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Mon, 13 Apr 2020 20:48:34 -0700 Subject: [PATCH 052/101] netlist only discrete simulating --- compiler/pgates/pnand2.py | 5 +++++ compiler/pgates/pnand3.py | 6 +++++- compiler/pgates/pnor2.py | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 4a813269..22a03b6d 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -9,6 +9,7 @@ import contact import pgate import debug from tech import drc, parameter, spice +from globals import OPTS from vector import vector import logical_effort from sram_factory import factory @@ -37,6 +38,10 @@ class pnand2(pgate.pgate): debug.check(size == 1, "Size 1 pnand2 is only supported now.") self.tx_mults = 1 + if OPTS.tech_name == "s8": + (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) + (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) + # Creates the netlist and layout pgate.pgate.__init__(self, name, height) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index cc6fd0f8..c93c867f 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -12,7 +12,7 @@ from tech import drc, parameter, spice from vector import vector import logical_effort from sram_factory import factory - +from globals import OPTS class pnand3(pgate.pgate): """ @@ -40,6 +40,10 @@ class pnand3(pgate.pgate): "Size 1 pnand3 is only supported now.") self.tx_mults = 1 + if OPTS.tech_name == "s8": + (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) + (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) + # Creates the netlist and layout pgate.pgate.__init__(self, name, height) diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 75840f26..47c1cc16 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -36,6 +36,10 @@ class pnor2(pgate.pgate): debug.check(size==1, "Size 1 pnor2 is only supported now.") self.tx_mults = 1 + if OPTS.tech_name == "s8": + (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) + (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) + # Creates the netlist and layout pgate.pgate.__init__(self, name, height) From 43dcf675a10ec3797191218607ec8260fb0c5a54 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 14 Apr 2020 10:52:25 -0700 Subject: [PATCH 053/101] Move pnand outputs to M1. Debug hierarchical decoder multirow. --- compiler/modules/hierarchical_decoder.py | 32 +++++++---- compiler/pgates/pnand2.py | 66 ++++++++++++++-------- compiler/pgates/pnand3.py | 72 +++++++++++++++++------- compiler/pgates/ptx.py | 37 +++++++----- 4 files changed, 139 insertions(+), 68 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 0e5b3606..a805e635 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -199,7 +199,8 @@ class hierarchical_decoder(design.design): self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch # Calculates height and width of hierarchical decoder - self.height = max(self.predecoder_height, self.row_decoder_height) + # Add extra pitch for good measure + self.height = max(self.predecoder_height, self.row_decoder_height) + self.m3_pitch self.width = self.input_routing_width + self.predecoder_width \ + self.internal_routing_width \ + self.decoders_per_row * nand_width + self.inv.width @@ -261,7 +262,10 @@ class hierarchical_decoder(design.design): self.route_input_bus(decoder_offset, input_offset) def route_input_bus(self, input_offset, output_offset): - """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ + """ + Route a vertical M2 coordinate to another + vertical M2 coordinate to the predecode inputs + """ self.add_via_center(layers=self.m2_stack, offset=input_offset) @@ -471,10 +475,10 @@ class hierarchical_decoder(design.design): names=input_bus_names, length=self.height) - self.route_bus_to_predecodes() + self.route_predecodes_to_bus() self.route_bus_to_decoder() - def route_bus_to_predecodes(self): + def route_predecodes_to_bus(self): """ Iterates through all of the predecodes and connects to the rails including the offsets @@ -517,7 +521,7 @@ class hierarchical_decoder(design.design): if (output_index < self.num_outputs): row_index = math.floor(output_index / self.decoders_per_row) row_remainder = (output_index % self.decoders_per_row) - row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 4) * self.m1_pitch + row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 1) * self.m3_pitch predecode_name = "predecode_{}".format(index_A) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("A"), @@ -525,7 +529,7 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(index_B) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("B"), - row_offset + self.m1_pitch) + row_offset + self.m3_pitch) output_index = output_index + 1 elif (self.num_inputs > 5): @@ -536,7 +540,7 @@ class hierarchical_decoder(design.design): if (output_index < self.num_outputs): row_index = math.floor(output_index / self.decoders_per_row) row_remainder = (output_index % self.decoders_per_row) - row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 4) * self.m1_pitch + row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 1) * self.m3_pitch predecode_name = "predecode_{}".format(index_A) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("A"), @@ -544,11 +548,11 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(index_B) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("B"), - row_offset + self.m1_pitch) + row_offset + self.m3_pitch) predecode_name = "predecode_{}".format(index_C) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("C"), - row_offset + 2 * self.m1_pitch) + row_offset + 2 * self.m3_pitch) output_index = output_index + 1 def route_vdd_gnd(self): @@ -557,13 +561,19 @@ class hierarchical_decoder(design.design): must-connects next level up. """ - # The vias will be placed in the center and right of the cells, respectively. - xoffset = self.and_inst[0].rx() + # The vias will be placed at the right of the cells. + xoffset = max(x.rx() for x in self.and_inst) for num in range(0, self.num_outputs): + # Only add the power pin for the 1st in each row + if num % self.decoders_per_row: + continue + for pin_name in ["vdd", "gnd"]: # The nand and inv are the same height rows... supply_pin = self.and_inst[num].get_pin(pin_name) pin_pos = vector(xoffset, supply_pin.cy()) + self.add_path("m1", + [supply_pin.lc(), vector(xoffset, supply_pin.cy())]) self.add_power_pin(name=pin_name, loc=pin_pos) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 4a813269..2f21c0bb 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -98,7 +98,7 @@ class pnand2(pgate.pgate): # metal spacing to allow contacts on any layer self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width, self.m1_space + contact.m1_via.first_layer_width, - self.m2_space + contact.m2_via.first_layer_width, + self.m2_space + contact.m2_via.first_layer_width, self.m3_space + contact.m2_via.second_layer_width) @@ -173,13 +173,15 @@ class pnand2(pgate.pgate): 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos_nd.active_height)) def add_well_contacts(self): - """ + """ Add n/p well taps to the layout and connect to supplies AFTER the wells are created """ - self.add_nwell_contact(self.pmos, self.pmos2_pos) - self.add_pwell_contact(self.nmos_nd, self.nmos2_pos) + self.add_nwell_contact(self.pmos, + self.pmos2_pos + vector(self.m1_pitch, 0)) + self.add_pwell_contact(self.nmos_nd, + self.nmos2_pos + vector(self.m1_pitch, 0)) def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ @@ -197,7 +199,7 @@ class pnand2(pgate.pgate): self.nmos2_inst, inputB_yoffset, "B", - position="right") + position="center") # This will help with the wells and the input/output placement self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \ @@ -209,6 +211,7 @@ class pnand2(pgate.pgate): def route_output(self): """ Route the Z output """ + # PMOS1 drain pmos_pin = self.pmos1_inst.get_pin("D") top_pin_offset = pmos_pin.center() @@ -217,29 +220,46 @@ class pnand2(pgate.pgate): bottom_pin_offset = nmos_pin.center() # Output pin - out_offset = vector(nmos_pin.center().x + self.m1_pitch, + c_pin = self.get_pin("B") + out_offset = vector(c_pin.cx() + self.m1_pitch, self.inputA_yoffset) - # Midpoints of the L routes go horizontal first then vertical - mid1_offset = vector(out_offset.x, top_pin_offset.y) + # This routes on M2 + # # Midpoints of the L routes go horizontal first then vertical + # mid1_offset = vector(out_offset.x, top_pin_offset.y) + # mid2_offset = vector(out_offset.x, bottom_pin_offset.y) + + # # Non-preferred active contacts + # self.add_via_center(layers=self.m1_stack, + # directions=("V", "H"), + # offset=pmos_pin.center()) + # # Non-preferred active contacts + # self.add_via_center(layers=self.m1_stack, + # directions=("V", "H"), + # offset=nmos_pin.center()) + # self.add_via_center(layers=self.m1_stack, + # offset=out_offset) + + # # PMOS1 to mid-drain to NMOS2 drain + # self.add_path("m2", + # [top_pin_offset, mid1_offset, out_offset, + # mid2_offset, bottom_pin_offset]) + + # This routes on M1 + # Midpoints of the L routes goes vertical first then horizontal + mid1_offset = vector(top_pin_offset.x, out_offset.y) + # Midpoints of the L routes goes horizontal first then vertical mid2_offset = vector(out_offset.x, bottom_pin_offset.y) - # Non-preferred active contacts - self.add_via_center(layers=self.m1_stack, - directions=("V", "H"), - offset=pmos_pin.center()) - # Non-preferred active contacts - self.add_via_center(layers=self.m1_stack, - directions=("V", "H"), - offset=nmos_pin.center()) - self.add_via_center(layers=self.m1_stack, - offset=out_offset) + self.add_path("m1", + [top_pin_offset, mid1_offset, out_offset]) + # Route in two segments to have the width rule + self.add_path("m1", + [bottom_pin_offset, mid2_offset + vector(0.5 * self.m1_width, 0)], + width=nmos_pin.height()) + self.add_path("m1", + [mid2_offset, out_offset]) - # PMOS1 to mid-drain to NMOS2 drain - self.add_path("m2", - [top_pin_offset, mid1_offset, out_offset, - mid2_offset, bottom_pin_offset]) - # This extends the output to the edge of the cell self.add_layout_pin_rect_center(text="Z", layer="m1", diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 58894ccf..83e57c0a 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -191,8 +191,10 @@ class pnand3(pgate.pgate): def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ - self.add_nwell_contact(self.pmos, self.pmos3_pos) - self.add_pwell_contact(self.nmos_ns, self.nmos3_pos) + self.add_nwell_contact(self.pmos, + self.pmos3_pos + vector(self.m1_pitch, 0)) + self.add_pwell_contact(self.nmos_ns, + self.nmos3_pos + vector(self.m1_pitch, 0)) def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ @@ -221,7 +223,7 @@ class pnand3(pgate.pgate): self.nmos3_inst, self.inputC_yoffset, "C", - position="left") + position="right") # FIXME: constant hack if OPTS.tech_name == "s8": @@ -236,6 +238,7 @@ class pnand3(pgate.pgate): def route_output(self): """ Route the Z output """ + # PMOS1 drain pmos1_pin = self.pmos1_inst.get_pin("D") # PMOS3 drain @@ -243,29 +246,56 @@ class pnand3(pgate.pgate): # NMOS3 drain nmos3_pin = self.nmos3_inst.get_pin("D") - # Go up to metal2 for ease on all output pins - self.add_via_center(layers=self.m1_stack, - offset=pmos1_pin.center(), - directions=("V", "V")) - self.add_via_center(layers=self.m1_stack, - offset=pmos3_pin.center(), - directions=("V", "V")) - self.add_via_center(layers=self.m1_stack, - offset=nmos3_pin.center(), - directions=("V", "V")) + # midpoint for routing + mid_offset = vector(nmos3_pin.cx() + self.m1_pitch, + self.inputA_yoffset) + + # Aligned with the well taps + out_offset = vector(self.nwell_contact.cx(), + self.inputA_yoffset) - # PMOS3 and NMOS3 are drain aligned - self.add_path("m1", [pmos3_pin.center(), nmos3_pin.center()]) - # Route in the A input track (top track) - mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) - self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) + # Go up to metal2 for ease on all output pins + # self.add_via_center(layers=self.m1_stack, + # offset=pmos1_pin.center(), + # directions=("V", "V")) + # self.add_via_center(layers=self.m1_stack, + # offset=pmos3_pin.center(), + # directions=("V", "V")) + # self.add_via_center(layers=self.m1_stack, + # offset=nmos3_pin.center(), + # directions=("V", "V")) + + # # Route in the A input track (top track) + # mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) + # self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) # This extends the output to the edge of the cell - self.add_via_center(layers=self.m1_stack, - offset=mid_offset) + # self.add_via_center(layers=self.m1_stack, + # offset=mid_offset) + + top_left_pin_offset = pmos1_pin.center() + top_right_pin_offset = pmos3_pin.center() + bottom_pin_offset = nmos3_pin.center() + + # PMOS1 to output + self.add_path("m1", [top_left_pin_offset, + vector(top_left_pin_offset.x, out_offset.y), + out_offset]) + # PMOS3 to output + self.add_path("m1", [top_right_pin_offset, + vector(top_right_pin_offset.x, mid_offset.y), + mid_offset]) + # NMOS3 to output + mid2_offset = vector(mid_offset.x, bottom_pin_offset.y) + self.add_path("m1", + [bottom_pin_offset, mid2_offset], + width=nmos3_pin.height()) + mid3_offset = vector(mid_offset.x, nmos3_pin.by()) + self.add_path("m1", [mid3_offset, mid_offset]) + self.add_layout_pin_rect_center(text="Z", layer="m1", - offset=mid_offset, + offset=out_offset, width=contact.m1_via.first_layer_width, height=contact.m1_via.first_layer_height) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index f07f4b29..b4b20381 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -471,13 +471,13 @@ class ptx(design.design): """Returns an object representing the parameters for delay in tau units.""" # FIXME: Using the same definition as the pinv.py. - parasitic_delay = 1 - size = self.mults*self.tx_width/drc("minwidth_tx") - return logical_effort.logical_effort(self.name, - size, - self.input_load(), - cout, - parasitic_delay) + parasitic_delay = 1 + size = self.mults * self.tx_width / drc("minwidth_tx") + return logical_effort.logical_effort(self.name, + size, + self.input_load(), + cout, + parasitic_delay) def input_load(self): """ @@ -485,7 +485,7 @@ class ptx(design.design): """ # FIXME: this will be applied for the loads of the drain/source - return self.mults*self.tx_width/drc("minwidth_tx") + return self.mults * self.tx_width / drc("minwidth_tx") def add_diff_contact(self, label, pos): contact=self.add_via_center(layers=self.active_stack, @@ -496,14 +496,25 @@ class ptx(design.design): well_type=self.well_type) if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=pos) - + contact=self.add_via_center(layers=self.li_stack, + offset=pos, + directions=("V", "V")) + + # contact_area = contact.mod.second_layer_width * contact.mod.second_layer_height + # min_area = drc("minarea_m1") + # width = contact.mod.second_layer_width + # if contact_area < min_area: + # height = min_area / width + # else: + # height = contact.mod.second_layer_height + width = contact.mod.second_layer_width + height = contact.mod.second_layer_height self.add_layout_pin_rect_center(text=label, layer="m1", offset=pos, - width=contact.mod.second_layer_width, - height=contact.mod.second_layer_height) + width=width, + height=height) + return(contact) def get_cin(self): From 32d190b8b1fe9f6e3a4fdd5174fda78e79af17dd Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 14 Apr 2020 12:15:56 -0700 Subject: [PATCH 054/101] Jog connection on M1 for bank select. --- compiler/modules/bank_select.py | 90 +++++++++++++++------------------ 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index c3307ece..b717399f 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -45,11 +45,9 @@ class bank_select(design.design): self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width self.width = max([x.rx() for x in self.inv_inst]) - self.add_boundary() self.DRC_LVS() - def add_pins(self): # Number of control lines in the bus @@ -65,19 +63,18 @@ class bank_select(design.design): if (self.port == "rw") or (self.port == "r"): self.input_control_signals.append("s_en") # These will be outputs of the gaters if this is multibank - self.control_signals = ["gated_"+str for str in self.input_control_signals] + self.control_signals = ["gated_" + str for str in self.input_control_signals] self.add_pin_list(self.input_control_signals, "INPUT") self.add_pin("bank_sel") self.add_pin_list(self.control_signals, "OUTPUT") - self.add_pin("vdd","POWER") - self.add_pin("gnd","GROUND") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def add_modules(self): """ Create modules for later instantiation """ - self.bitcell = factory.create(module_type="bitcell") - - height = self.bitcell.height + drc("poly_to_active") + self.dff = factory.create(module_type="dff") + height = self.dff.height + drc("poly_to_active") # 1x Inverter self.inv_sel = factory.create(module_type="pinv", height=height) @@ -98,17 +95,15 @@ class bank_select(design.design): def calculate_module_offsets(self): - self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") - self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") - self.xoffset_bank_sel_inv = 0 + self.xoffset_nand = self.inv4x.width + 2 * self.m2_pitch + drc("pwell_to_nwell") + self.xoffset_nor = self.inv4x.width + 2 * self.m2_pitch + drc("pwell_to_nwell") + self.xoffset_bank_sel_inv = 0 self.xoffset_inputs = 0 - self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height - def create_instances(self): - self.bank_sel_inv=self.add_inst(name="bank_sel_inv", + self.bank_sel_inv=self.add_inst(name="bank_sel_inv", mod=self.inv_sel) self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) @@ -125,36 +120,36 @@ class bank_select(design.design): # (writes occur on clk low) if input_name in ("clk_buf"): - self.logic_inst.append(self.add_inst(name=name_nor, - mod=self.nor2)) + self.logic_inst.append(self.add_inst(name=name_nor, + mod=self.nor2)) self.connect_inst([input_name, "bank_sel_bar", - gated_name+"_temp_bar", + gated_name + "_temp_bar", "vdd", "gnd"]) # They all get inverters on the output - self.inv_inst.append(self.add_inst(name=name_inv, + self.inv_inst.append(self.add_inst(name=name_inv, mod=self.inv4x_nor)) - self.connect_inst([gated_name+"_temp_bar", + self.connect_inst([gated_name + "_temp_bar", gated_name, "vdd", "gnd"]) # the rest are AND (nand2+inv) gates else: - self.logic_inst.append(self.add_inst(name=name_nand, + self.logic_inst.append(self.add_inst(name=name_nand, mod=self.nand2)) self.connect_inst([input_name, "bank_sel", - gated_name+"_temp_bar", + gated_name + "_temp_bar", "vdd", "gnd"]) # They all get inverters on the output - self.inv_inst.append(self.add_inst(name=name_inv, + self.inv_inst.append(self.add_inst(name=name_inv, mod=self.inv4x)) - self.connect_inst([gated_name+"_temp_bar", + self.connect_inst([gated_name + "_temp_bar", gated_name, "vdd", "gnd"]) @@ -177,9 +172,9 @@ class bank_select(design.design): if i == 0: y_offset = 0 else: - y_offset = self.inv4x_nor.height + self.inv4x.height * (i-1) + y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1) - if i%2: + if i % 2: y_offset += self.inv4x.height mirror = "MX" else: @@ -200,7 +195,6 @@ class bank_select(design.design): # They all get inverters on the output inv_inst.place(offset=[logic_inst.rx(), y_offset], mirror=mirror) - def route_instances(self): @@ -222,57 +216,56 @@ class bank_select(design.design): end=bank_sel_pin_end) self.add_via_center(layers=self.m2_stack, offset=bank_sel_pin_end, - directions=("H","H")) + directions=("H", "H")) # bank_sel_bar is vertical wire bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z") xoffset_bank_sel_bar = bank_sel_bar_pin.rx() self.add_label_pin(text="bank_sel_bar", - layer="m2", - offset=vector(xoffset_bank_sel_bar, 0), + layer="m2", + offset=vector(xoffset_bank_sel_bar, 0), height=self.inv4x.height) self.add_via_center(layers=self.m1_stack, offset=bank_sel_bar_pin.rc()) - for i in range(self.num_control_lines): logic_inst = self.logic_inst[i] inv_inst = self.inv_inst[i] input_name = self.input_control_signals[i] - gated_name = self.control_signals[i] + gated_name = self.control_signals[i] if input_name in ("clk_buf"): xoffset_bank_signal = xoffset_bank_sel_bar else: xoffset_bank_signal = xoffset_bank_sel # Connect the logic output to inverter input - pre = logic_inst.get_pin("Z").lc() - out_position = logic_inst.get_pin("Z").rc() + vector(0.5*self.m1_width,0) - in_position = inv_inst.get_pin("A").lc() + vector(0.5*self.m1_width,0) - post = inv_inst.get_pin("A").rc() - self.add_path("m1", [pre, out_position, in_position, post]) + out_pin = logic_inst.get_pin("Z") + out_pos = out_pin.rc() + in_pin = inv_inst.get_pin("A") + in_pos = in_pin.lc() + mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y) + mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y) + self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos]) - - # Connect the logic B input to bank_sel/bank_sel_bar - logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1_via.height,0) + # Connect the logic B input to bank_sel / bank_sel_bar + logic_pos = logic_inst.get_pin("B").lc() - vector(0.5 * contact.m1_via.height, 0) input_pos = vector(xoffset_bank_signal, logic_pos.y) - self.add_path("m2",[logic_pos, input_pos]) + self.add_path("m2", [logic_pos, input_pos]) self.add_via_center(layers=self.m1_stack, offset=logic_pos, - directions=("H","H")) + directions=("H", "H")) - # Connect the logic A input to the input pin logic_pos = logic_inst.get_pin("A").lc() - input_pos = vector(0,logic_pos.y) + input_pos = vector(0, logic_pos.y) self.add_via_center(layers=self.m1_stack, offset=logic_pos, - directions=("H","H")) + directions=("H", "H")) self.add_via_center(layers=self.m2_stack, offset=logic_pos, - directions=("H","H")) + directions=("H", "H")) self.add_layout_pin_segment_center(text=input_name, layer="m3", start=input_pos, @@ -286,7 +279,6 @@ class bank_select(design.design): width=inv_inst.rx() - out_pin.lx(), height=out_pin.height()) - # Find the x offsets for where the vias/pins should be placed a_xoffset = self.logic_inst[0].lx() b_xoffset = self.inv_inst[0].lx() @@ -294,7 +286,7 @@ class bank_select(design.design): # Route both supplies for n in ["vdd", "gnd"]: supply_pin = self.inv_inst[num].get_pin(n) - supply_offset = supply_pin.ll().scale(0,1) + supply_offset = supply_pin.ll().scale(0, 1) self.add_rect(layer="m1", offset=supply_offset, width=self.width) @@ -304,10 +296,10 @@ class bank_select(design.design): pin_pos = vector(xoffset, supply_pin.cy()) self.add_via_center(layers=self.m1_stack, offset=pin_pos, - directions=("H","H")) + directions=("H", "H")) self.add_via_center(layers=self.m2_stack, offset=pin_pos, - directions=("H","H")) + directions=("H", "H")) self.add_layout_pin_rect_center(text=n, layer="m3", offset=pin_pos) From 0941ebc3da648ecf3779cdcab42ed04e0be684b5 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 14 Apr 2020 14:08:07 -0700 Subject: [PATCH 055/101] Fix well spacing issue --- compiler/modules/bank_select.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index b717399f..3be10d3e 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -95,8 +95,8 @@ class bank_select(design.design): def calculate_module_offsets(self): - self.xoffset_nand = self.inv4x.width + 2 * self.m2_pitch + drc("pwell_to_nwell") - self.xoffset_nor = self.inv4x.width + 2 * self.m2_pitch + drc("pwell_to_nwell") + self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell") + self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell") self.xoffset_bank_sel_inv = 0 self.xoffset_inputs = 0 self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height From 6c1c72c520d096775ca784569329b9c269cf207c Mon Sep 17 00:00:00 2001 From: jcirimel Date: Wed, 15 Apr 2020 04:09:58 -0700 Subject: [PATCH 056/101] fix pgates binning off-by-one --- compiler/pgates/pgate.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index ba5fef75..de513cc3 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -300,24 +300,24 @@ class pgate(design.design): bins = bins[0:bisect_left(bins, target_width) + 1] if len(bins) == 1: selected_bin = bins[0] - scaling_factor = 1 - scaled_bin = bins[0] + scaling_factor = math.ceil(target_width / width) + scaled_bin = bins[0] * scaling_factor else: scaled_bins = [] scaling_factors = [] + scaled_bins.append(bins[-1]) + scaling_factors.append(1) for width in bins[0:-1]: m = math.ceil(target_width / width) scaling_factors.append(m) scaled_bins.append(m * width) - scaled_bins.append(bins[-1]) - scaling_factors.append(1) select = bisect_left(scaled_bins, target_width) - - selected_bin = bins[select] scaling_factor = scaling_factors[select] scaled_bin = scaled_bins[select] + select = (select + 1) % len(scaled_bins) + selected_bin = bins[select] debug.info(2, "binning {0} tx, target: {4}, found {1} x {2} = {3}".format(tx_type, selected_bin, scaling_factor, scaled_bin, target_width)) From 331a4f4606a7de3c0d1554e85ed9832e8a6eef67 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 15 Apr 2020 09:48:42 -0700 Subject: [PATCH 057/101] Fix wire width bug in short jogs. PEP8 cleanup. --- compiler/base/design.py | 2 +- compiler/base/wire.py | 56 ++++++++++++++++-------- compiler/modules/hierarchical_decoder.py | 1 - 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/compiler/base/design.py b/compiler/base/design.py index c2ef8340..73ace38e 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -74,7 +74,7 @@ class design(hierarchy_design): return pitch def setup_drc_constants(self): - """ + """ These are some DRC constants used in many places in the compiler. """ diff --git a/compiler/base/wire.py b/compiler/base/wire.py index bf1daa6a..250f2d87 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -6,18 +6,18 @@ # All rights reserved. # from tech import drc -import debug from wire_path import wire_path from sram_factory import factory + class wire(wire_path): - """ + """ Object metal wire; given the layer type - Add a wire of minimium metal width between a set of points. + Add a wire of minimium metal width between a set of points. The points should be rectilinear to control the bend points. If not, it will always go down first. The points are the center of the wire. - The layer stack is the vertical, contact/via, and horizontal layers, respectively. + The layer stack is the vertical, contact/via, and horizontal layers, respectively. """ def __init__(self, obj, layer_stack, position_list): self.obj = obj @@ -36,6 +36,7 @@ class wire(wire_path): # wires and wire_paths should not be offset to (0,0) def setup_layers(self): + (horiz_layer, via_layer, vert_layer) = self.layer_stack self.via_layer_name = via_layer @@ -47,8 +48,16 @@ class wire(wire_path): via_connect = factory.create(module_type="contact", layer_stack=self.layer_stack, dimensions=(1, 1)) + + # This is used for short connections to avoid via-to-via spacing errors + self.vert_layer_contact_width = max(via_connect.second_layer_width, + via_connect.first_layer_width) + self.horiz_layer_contact_width = max(via_connect.second_layer_height, + via_connect.first_layer_height) + self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width, drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height] + self.pitch = max(self.node_to_node) # create a 1x1 contact def create_vias(self): @@ -56,12 +65,9 @@ class wire(wire_path): self.c=factory.create(module_type="contact", layer_stack=self.layer_stack, dimensions=(1, 1)) - c_width = self.c.width - c_height = self.c.height - - from itertools import tee,islice - nwise = lambda g,n=2: zip(*(islice(g,i,None) for i,g in enumerate(tee(g,n)))) - threewise=nwise(self.position_list,3) + from itertools import tee, islice + nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n)))) + threewise = nwise(self.position_list, 3) for (a, offset, c) in list(threewise): # add a exceptions to prevent a via when we don't change directions @@ -72,18 +78,23 @@ class wire(wire_path): self.obj.add_via_center(layers=self.layer_stack, offset=offset) - def create_rectangles(self): - """ + """ Create the actual rectangles on the appropriate layers - using the position list of the corners. + using the position list of the corners. """ pl = self.position_list # position list for index in range(len(pl) - 1): + # Horizontal wire segment if pl[index][0] != pl[index + 1][0]: line_length = pl[index + 1][0] - pl[index][0] + if abs(line_length) < self.pitch: + width = self.horiz_layer_contact_width + else: + width = self.horiz_layer_width temp_offset = [pl[index][0], - pl[index][1] - 0.5*self.horiz_layer_width] + pl[index][1] - 0.5 * width] + # If we go in the negative direction, move the offset if line_length < 0: temp_offset = [temp_offset[0] + line_length, temp_offset[1]] @@ -91,10 +102,15 @@ class wire(wire_path): length=abs(line_length), offset=temp_offset, orientation="horizontal", - layer_width=self.horiz_layer_width) + layer_width=width) + # Vertical wire segment elif pl[index][1] != pl[index + 1][1]: line_length = pl[index + 1][1] - pl[index][1] - temp_offset = [pl[index][0] - 0.5 * self.vert_layer_width, + if abs(line_length) < self.pitch: + width = self.vert_layer_contact_width + else: + width = self.vert_layer_width + temp_offset = [pl[index][0] - 0.5 * width, pl[index][1]] if line_length < 0: temp_offset = [temp_offset[0], @@ -103,11 +119,13 @@ class wire(wire_path): length=abs(line_length), offset=temp_offset, orientation="vertical", - layer_width=self.vert_layer_width) + layer_width=width) def assert_node(self, A, B): - """ Check if the node movements are not big enough for the - technology sizes.""" + """ + Check if the node movements are not big enough for the + technology sizes. + """ X_diff = abs(A[0] - B[0]) Y_diff = abs(A[1] - B[1]) [minX, minY] = self.node_to_node diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index a805e635..f46849c6 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -33,7 +33,6 @@ class hierarchical_decoder(design.design): except AttributeError: self.cell_multiple = 1 # For debugging - # self.cell_multiple = 2 self.cell_height = self.cell_multiple * b.height self.num_outputs = num_outputs From 43fe1ae023bda3a81408d2ccfd73442f0f8c1294 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 15 Apr 2020 11:16:45 -0700 Subject: [PATCH 058/101] Improve pitch computation --- compiler/base/wire.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 250f2d87..2044eb3a 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -6,6 +6,7 @@ # All rights reserved. # from tech import drc +import contact from wire_path import wire_path from sram_factory import factory @@ -57,8 +58,29 @@ class wire(wire_path): self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width, drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height] - self.pitch = max(self.node_to_node) + self.pitch = self.compute_pitch(self.layer_stack) + def compute_pitch(self, layer_stack): + + """ + This is contact direction independent pitch, + i.e. we take the maximum contact dimension + """ + (layer1, via, layer2) = layer_stack + + if layer1 == "poly" or layer1 == "active": + contact1 = getattr(contact, layer1 + "_contact") + else: + contact1 = getattr(contact, layer1 + "_via") + max_contact = max(contact1.width, contact1.height) + + layer1_space = drc["{0}_to_{0}".format(layer1)] + layer2_space = drc["{0}_to_{0}".format(layer2)] + pitch = max_contact + max(layer1_space, layer2_space) + + return pitch + + # create a 1x1 contact def create_vias(self): """ Add a via and corner square at every corner of the path.""" From 1564d6e02b466a815fac7cc66b26bd615e4fb054 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 15 Apr 2020 11:24:28 -0700 Subject: [PATCH 059/101] PEP8 cleanup --- compiler/drc/design_rules.py | 5 +++-- compiler/drc/drc_lut.py | 10 +++++----- compiler/drc/drc_value.py | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/compiler/drc/design_rules.py b/compiler/drc/design_rules.py index 05168b77..b194d082 100644 --- a/compiler/drc/design_rules.py +++ b/compiler/drc/design_rules.py @@ -9,9 +9,10 @@ import debug from drc_value import * from drc_lut import * + class design_rules(dict): - """ - This is a class that implements the design rules structures. + """ + This is a class that implements the design rules structures. """ def __init__(self, name): self.tech_name = name diff --git a/compiler/drc/drc_lut.py b/compiler/drc/drc_lut.py index 0ad0fde4..8a7b49d2 100644 --- a/compiler/drc/drc_lut.py +++ b/compiler/drc/drc_lut.py @@ -7,9 +7,10 @@ # import debug + class drc_lut(): - """ - Implement a lookup table of rules. + """ + Implement a lookup table of rules. Each element is a tuple with the last value being the rule. It searches through backwards until all of the key values are met and returns the rule value. @@ -31,7 +32,6 @@ class drc_lut(): for table_key in sorted(self.table.keys(), reverse=True): if self.match(key, table_key): return self.table[table_key] - def match(self, key1, key2): """ @@ -39,8 +39,8 @@ class drc_lut(): (i.e. return false if key1 Date: Wed, 15 Apr 2020 14:29:43 -0700 Subject: [PATCH 060/101] PEP8 cleanup --- compiler/modules/control_logic.py | 353 +++++++++++++++--------------- 1 file changed, 180 insertions(+), 173 deletions(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 1612938d..b4c7402e 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -5,18 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from math import log import design -from tech import drc, parameter from tech import cell_properties as props import debug -import contact from sram_factory import factory import math from vector import vector from globals import OPTS import logical_effort + class control_logic(design.design): """ Dynamically generated Control logic for the total SRAM circuit. @@ -29,7 +27,7 @@ class control_logic(design.design): debug.info(1, "Creating {}".format(name)) self.add_comment("num_rows: {0}".format(num_rows)) self.add_comment("words_per_row: {0}".format(words_per_row)) - self.add_comment("word_size {0}".format(word_size)) + self.add_comment("word_size {0}".format(word_size)) self.sram=sram self.num_rows = num_rows @@ -37,14 +35,15 @@ class control_logic(design.design): self.word_size = word_size self.port_type = port_type - self.num_cols = word_size*words_per_row - self.num_words = num_rows*words_per_row + self.num_cols = word_size * words_per_row + self.num_words = num_rows * words_per_row self.enable_delay_chain_resizing = False self.inv_parasitic_delay = logical_effort.logical_effort.pinv # Determines how much larger the sen delay should be. Accounts for possible error in model. - self.wl_timing_tolerance = 1 + # FIXME: This should be made a parameter + self.wl_timing_tolerance = 1 self.wl_stage_efforts = None self.sen_stage_efforts = None @@ -67,17 +66,16 @@ class control_logic(design.design): """ Create layout and route between modules """ self.place_instances() self.route_all() - #self.add_lvs_correspondence_points() + # self.add_lvs_correspondence_points() self.add_boundary() self.DRC_LVS() - def add_pins(self): """ Add the pins to the control logic module. """ self.add_pin_list(self.input_list + ["clk"] + self.rbl_list, "INPUT") - self.add_pin_list(self.output_list,"OUTPUT") - self.add_pin("vdd","POWER") - self.add_pin("gnd","GROUND") + self.add_pin_list(self.output_list, "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def add_modules(self): """ Add all the required modules """ @@ -101,14 +99,13 @@ class control_logic(design.design): height=dff_height) self.add_mod(self.rbl_driver) - - # clk_buf drives a flop for every address - addr_flops = math.log(self.num_words,2) + math.log(self.words_per_row,2) + # clk_buf drives a flop for every address + addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2) # plus data flops and control flops num_flops = addr_flops + self.word_size + self.num_control_signals # each flop internally has a FO 5 approximately # plus about 5 fanouts for the control logic - clock_fanout = 5*num_flops + 5 + clock_fanout = 5 * num_flops + 5 self.clk_buf_driver = factory.create(module_type="pdriver", fanout=clock_fanout, height=dff_height) @@ -117,7 +114,7 @@ class control_logic(design.design): # We will use the maximum since this same value is used to size the wl_en # and the p_en_bar drivers - max_fanout = max(self.num_rows,self.num_cols) + max_fanout = max(self.num_rows, self.num_cols) # wl_en drives every row in the bank self.wl_en_driver = factory.create(module_type="pdriver", @@ -127,7 +124,7 @@ class control_logic(design.design): # w_en drives every write driver self.wen_and = factory.create(module_type="pand3", - size=self.word_size+8, + size=self.word_size + 8, height=dff_height) self.add_mod(self.wen_and) @@ -137,7 +134,7 @@ class control_logic(design.design): height=dff_height) self.add_mod(self.sen_and3) - # used to generate inverted signals with low fanout + # used to generate inverted signals with low fanout self.inv = factory.create(module_type="pinv", size=1, height=dff_height) @@ -151,7 +148,6 @@ class control_logic(design.design): height=dff_height) self.add_mod(self.p_en_bar_driver) - self.nand2 = factory.create(module_type="pnand2", height=dff_height) self.add_mod(self.nand2) @@ -179,14 +175,14 @@ class control_logic(design.design): # delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic, # bitcell_loads=bitcell_loads) # #Resize if necessary, condition depends on resizing method - # if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match(): + # if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match(): # #This resizes to match fall and rise delays, can make the delay chain weird sizes. # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) # self.replica_bitline = factory.create(module_type="replica_bitline", # delay_fanout_list=stage_list, # bitcell_loads=bitcell_loads) - # #This resizes based on total delay. + # #This resizes based on total delay. # # delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) # # self.replica_bitline = factory.create(module_type="replica_bitline", # # delay_fanout_list=[delay_fanout]*delay_stages, @@ -195,9 +191,10 @@ class control_logic(design.design): # self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing # self.delay_chain_resized = True - debug.check(OPTS.delay_chain_stages%2, "Must use odd number of delay chain stages for inverting delay chain.") + debug.check(OPTS.delay_chain_stages % 2, + "Must use odd number of delay chain stages for inverting delay chain.") self.delay_chain=factory.create(module_type="delay_chain", - fanout_list = OPTS.delay_chain_stages*[OPTS.delay_chain_fanout_per_stage]) + fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ]) self.add_mod(self.delay_chain) def get_heuristic_delay_chain_size(self): @@ -219,17 +216,17 @@ class control_logic(design.design): def set_sen_wl_delays(self): """Set delays for wordline and sense amp enable""" - self.wl_delay_rise,self.wl_delay_fall = self.get_delays_to_wl() - self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() - self.wl_delay = self.wl_delay_rise+self.wl_delay_fall - self.sen_delay = self.sen_delay_rise+self.sen_delay_fall + self.wl_delay_rise, self.wl_delay_fall = self.get_delays_to_wl() + self.sen_delay_rise, self.sen_delay_fall = self.get_delays_to_sen() + self.wl_delay = self.wl_delay_rise + self.wl_delay_fall + self.sen_delay = self.sen_delay_rise + self.sen_delay_fall def does_sen_rise_fall_timing_match(self): """Compare the relative rise/fall delays of the sense amp enable and wordline""" self.set_sen_wl_delays() # This is not necessarily more reliable than total delay in some cases. - if (self.wl_delay_rise*self.wl_timing_tolerance >= self.sen_delay_rise or - self.wl_delay_fall*self.wl_timing_tolerance >= self.sen_delay_fall): + if (self.wl_delay_rise * self.wl_timing_tolerance >= self.sen_delay_rise or + self.wl_delay_fall * self.wl_timing_tolerance >= self.sen_delay_fall): return False else: return True @@ -240,91 +237,107 @@ class control_logic(design.design): # The sen delay must always be bigger than than the wl # delay. This decides how much larger the sen delay must be # before a re-size is warranted. - if self.wl_delay*self.wl_timing_tolerance >= self.sen_delay: + if self.wl_delay * self.wl_timing_tolerance >= self.sen_delay: return False else: - return True + return True def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout): """Determine the size of the delay chain used for the Sense Amp Enable using path delays""" from math import ceil - previous_delay_chain_delay = (previous_fanout+1+self.inv_parasitic_delay)*previous_stages + previous_delay_chain_delay = (previous_fanout + 1 + self.inv_parasitic_delay) * previous_stages debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) - - delay_fanout = 3 # This can be anything >=2 + + # This can be anything >=2 + delay_fanout = 3 # The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each # inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value - required_delay = self.wl_delay*self.wl_timing_tolerance - (self.sen_delay-previous_delay_chain_delay) + required_delay = self.wl_delay * self.wl_timing_tolerance - (self.sen_delay - previous_delay_chain_delay) debug.check(required_delay > 0, "Cannot size delay chain to have negative delay") - delay_stages = ceil(required_delay/(delay_fanout+1+self.inv_parasitic_delay)) - if delay_stages%2 == 1: #force an even number of stages. - delay_stages+=1 + delay_per_stage = delay_fanout + 1 + self.inv_parasitic_delay + delay_stages = ceil(required_delay / delay_per_stage) + # force an even number of stages. + if delay_stages % 2 == 1: + delay_stages += 1 # Fanout can be varied as well but is a little more complicated but potentially optimal. debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay)) return (delay_stages, delay_fanout) def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout): """Determine the size of the delay chain used for the Sense Amp Enable using path delays""" - - previous_delay_chain_delay = (previous_fanout+1+self.inv_parasitic_delay)*previous_stages + + previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay + previous_delay_chain_delay = previous_delay_per_stage * previous_stages debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) fanout_rise = fanout_fall = 2 # This can be anything >=2 # The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each # inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value - required_delay_fall = self.wl_delay_fall*self.wl_timing_tolerance - (self.sen_delay_fall-previous_delay_chain_delay/2) - required_delay_rise = self.wl_delay_rise*self.wl_timing_tolerance - (self.sen_delay_rise-previous_delay_chain_delay/2) - debug.info(2,"Required delays from chain: fall={}, rise={}".format(required_delay_fall,required_delay_rise)) + required_delay_fall = self.wl_delay_fall * self.wl_timing_tolerance - \ + (self.sen_delay_fall - previous_delay_chain_delay / 2) + required_delay_rise = self.wl_delay_rise * self.wl_timing_tolerance - \ + (self.sen_delay_rise - previous_delay_chain_delay / 2) + debug.info(2, + "Required delays from chain: fall={}, rise={}".format(required_delay_fall, + required_delay_rise)) # If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic. WARNING_FANOUT_DIFF = 5 stages_close = False # The stages need to be equal (or at least a even number of stages with matching rise/fall delays) while True: - stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall) - stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,fanout_rise) - debug.info(1,"Fall stages={}, rise stages={}".format(stages_fall,stages_rise)) - if abs(stages_fall-stages_rise) == 1 and not stages_close: + stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall, + fanout_fall) + stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise, + fanout_rise) + debug.info(1, + "Fall stages={}, rise stages={}".format(stages_fall, + stages_rise)) + if abs(stages_fall - stages_rise) == 1 and not stages_close: stages_close = True safe_fanout_rise = fanout_rise safe_fanout_fall = fanout_fall - if stages_fall == stages_rise: + if stages_fall == stages_rise: break - elif abs(stages_fall-stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall-fanout_rise): + elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise): debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.") fanout_rise = safe_fanout_rise fanout_fall = safe_fanout_fall break - # There should also be a condition to make sure the fanout does not get too large. + # There should also be a condition to make sure the fanout does not get too large. # Otherwise, increase the fanout of delay with the most stages, calculate new stages elif stages_fall>stages_rise: fanout_fall+=1 else: fanout_rise+=1 - total_stages = max(stages_fall,stages_rise)*2 + total_stages = max(stages_fall, stages_rise) * 2 debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall)) # Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage. - stage_list = [fanout_fall if i%2==0 else fanout_rise for i in range(total_stages)] + stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)] return stage_list def calculate_stages_with_fixed_fanout(self, required_delay, fanout): from math import ceil # Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay - if required_delay <= 3+self.inv_parasitic_delay: #3 is the minimum delay per stage (with pinv=0). + # 3 is the minimum delay per stage (with pinv=0). + if required_delay <= 3 + self.inv_parasitic_delay: return 1 - delay_stages = ceil(required_delay/(fanout+1+self.inv_parasitic_delay)) + delay_per_stage = fanout + 1 + self.inv_parasitic_delay + delay_stages = ceil(required_delay / delay_per_stage) return delay_stages def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall): - """Produces a list of fanouts which determine the size of the delay chain. List length is the number of stages. - Assumes the first stage is falling. + """ + Produces a list of fanouts which determine the size of the delay chain. + List length is the number of stages. + Assumes the first stage is falling. """ stage_list = [] for i in range(total_stages): - if i%2 == 0: + if i % 2 == 0: stage_list.append() def setup_signal_busses(self): @@ -351,7 +364,7 @@ class control_logic(design.design): else: self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"] # leave space for the bus plus one extra space - self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch + self.internal_bus_width = (len(self.internal_bus_list) + 1) * self.m2_pitch # Outputs to the bank if self.port_type == "rw": @@ -366,15 +379,13 @@ class control_logic(design.design): self.supply_list = ["vdd", "gnd"] - def route_rails(self): """ Add the input signal inverted tracks """ height = self.control_logic_center.y - self.m2_pitch - offset = vector(self.ctrl_dff_array.width,0) + offset = vector(self.ctrl_dff_array.width, 0) self.rail_offsets = self.create_vertical_bus("m2", self.m2_pitch, offset, self.internal_bus_list, height) - - + def create_instances(self): """ Create all the instances """ self.create_dffs() @@ -388,9 +399,7 @@ class control_logic(design.design): if (self.port_type == "rw") or (self.port_type == "r"): self.create_sen_row() self.create_delay() - self.create_pen_row() - - + self.create_pen_row() def place_instances(self): """ Place all the instances """ @@ -406,13 +415,13 @@ class control_logic(design.design): row = 0 # Add the logic on the right of the bus - self.place_clk_buf_row(row) + self.place_clk_buf_row(row) row += 1 - self.place_gated_clk_bar_row(row) + self.place_gated_clk_bar_row(row) row += 1 - self.place_gated_clk_buf_row(row) + self.place_gated_clk_buf_row(row) row += 1 - self.place_wlen_row(row) + self.place_wlen_row(row) row += 1 if (self.port_type == "rw") or (self.port_type == "w"): self.place_wen_row(row) @@ -421,10 +430,10 @@ class control_logic(design.design): row += 1 self.place_pen_row(row) row += 1 - if (self.port_type == "rw") or (self.port_type == "w"): + if (self.port_type == "rw") or (self.port_type == "w"): self.place_rbl_delay_row(row) row += 1 - if (self.port_type == "rw") or (self.port_type == "r"): + if (self.port_type == "rw") or (self.port_type == "r"): self.place_sen_row(row) row += 1 self.place_delay(row) @@ -435,11 +444,11 @@ class control_logic(design.design): self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y) # Extra pitch on top and right - self.height = height + 2*self.m1_pitch + self.height = height + 2 * self.m1_pitch # Max of modules or logic rows self.width = max([inst.rx() for inst in self.row_end_inst]) if (self.port_type == "rw") or (self.port_type == "r"): - self.width = max(self.delay_inst.rx() , self.width) + self.width = max(self.delay_inst.rx(), self.width) self.width += self.m2_pitch def route_all(self): @@ -459,7 +468,6 @@ class control_logic(design.design): self.route_gated_clk_buf() self.route_supply() - def create_delay(self): """ Create the replica bitline """ self.delay_inst=self.add_inst(name="delay_chain", @@ -467,9 +475,9 @@ class control_logic(design.design): # rbl_bl_delay is asserted (1) when the bitline has been discharged self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"]) - def place_delay(self,row): + def place_delay(self, row): """ Place the replica bitline """ - y_off = row * self.and2.height + 2*self.m1_pitch + y_off = row * self.and2.height + 2 * self.m1_pitch # Add the RBL above the rows # Add to the right of the control rows and routing channel @@ -482,24 +490,22 @@ class control_logic(design.design): # Connect to the rail level with the vdd rail # Use pen since it is in every type of control logic vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by() - in_pos = vector(self.rail_offsets["rbl_bl_delay"].x,vdd_ypos) - mid1 = vector(out_pos.x,in_pos.y) - self.add_wire(self.m1_stack,[out_pos, mid1, in_pos]) + in_pos = vector(self.rail_offsets["rbl_bl_delay"].x, vdd_ypos) + mid1 = vector(out_pos.x, in_pos.y) + self.add_wire(self.m1_stack, [out_pos, mid1, in_pos]) self.add_via_center(layers=self.m1_stack, offset=in_pos) - # Input from RBL goes to the delay line for futher delay self.copy_layout_pin(self.delay_inst, "in", "rbl_bl") - def create_clk_buf_row(self): """ Create the multistage and gated clock buffer """ self.clk_buf_inst = self.add_inst(name="clkbuf", mod=self.clk_buf_driver) - self.connect_inst(["clk","clk_buf","vdd","gnd"]) + self.connect_inst(["clk", "clk_buf", "vdd", "gnd"]) - def place_clk_buf_row(self,row): + def place_clk_buf_row(self, row): x_offset = self.control_x_offset x_offset = self.place_util(self.clk_buf_inst, x_offset, row) @@ -512,17 +518,16 @@ class control_logic(design.design): self.add_layout_pin_segment_center(text="clk", layer="m2", start=clk_pos, - end=clk_pos.scale(1,0)) + end=clk_pos.scale(1, 0)) self.add_via_center(layers=self.m1_stack, offset=clk_pos) - # Connect this at the bottom of the buffer out_pos = self.clk_buf_inst.get_pin("Z").center() - mid1 = vector(out_pos.x,2*self.m2_pitch) + mid1 = vector(out_pos.x, 2 * self.m2_pitch) mid2 = vector(self.rail_offsets["clk_buf"].x, mid1.y) bus_pos = self.rail_offsets["clk_buf"] - self.add_wire(("m3","via2","m2"),[out_pos, mid1, mid2, bus_pos]) + self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos]) # The pin is on M1, so we need another via as well self.add_via_center(layers=self.m1_stack, offset=self.clk_buf_inst.get_pin("Z").center()) @@ -532,40 +537,45 @@ class control_logic(design.design): def create_gated_clk_bar_row(self): self.clk_bar_inst = self.add_inst(name="inv_clk_bar", mod=self.inv) - self.connect_inst(["clk_buf","clk_bar","vdd","gnd"]) + self.connect_inst(["clk_buf", "clk_bar", "vdd", "gnd"]) self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar", mod=self.and2) - self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"]) + self.connect_inst(["cs", "clk_bar", "gated_clk_bar", "vdd", "gnd"]) - def place_gated_clk_bar_row(self,row): + def place_gated_clk_bar_row(self, row): x_offset = self.control_x_offset x_offset = self.place_util(self.clk_bar_inst, x_offset, row) - x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row) + x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row) self.row_end_inst.append(self.gated_clk_bar_inst) def route_gated_clk_bar(self): clkbuf_map = zip(["A"], ["clk_buf"]) - self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets) + self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets) out_pos = self.clk_bar_inst.get_pin("Z").center() in_pos = self.gated_clk_bar_inst.get_pin("B").center() - mid1 = vector(in_pos.x,out_pos.y) - self.add_path("m1",[out_pos, mid1, in_pos]) + mid1 = vector(in_pos.x, out_pos.y) + self.add_path("m1", [out_pos, mid1, in_pos]) # This is the second gate over, so it needs to be on M3 clkbuf_map = zip(["A"], ["cs"]) - self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("m3", "via2", "m2")) + self.connect_vertical_bus(clkbuf_map, + self.gated_clk_bar_inst, + self.rail_offsets, + self.m2_stack[::-1]) # The pin is on M1, so we need another via as well self.add_via_center(layers=self.m1_stack, offset=self.gated_clk_bar_inst.get_pin("A").center()) - # This is the second gate over, so it needs to be on M3 clkbuf_map = zip(["Z"], ["gated_clk_bar"]) - self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("m3", "via2", "m2")) + self.connect_vertical_bus(clkbuf_map, + self.gated_clk_bar_inst, + self.rail_offsets, + self.m2_stack[::-1]) # The pin is on M1, so we need another via as well self.add_via_center(layers=self.m1_stack, offset=self.gated_clk_bar_inst.get_pin("Z").center()) @@ -573,9 +583,9 @@ class control_logic(design.design): def create_gated_clk_buf_row(self): self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf", mod=self.and2) - self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"]) + self.connect_inst(["clk_buf", "cs", "gated_clk_buf", "vdd", "gnd"]) - def place_gated_clk_buf_row(self,row): + def place_gated_clk_buf_row(self, row): x_offset = self.control_x_offset x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row) @@ -584,11 +594,13 @@ class control_logic(design.design): def route_gated_clk_buf(self): clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"]) - self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets) + self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets) - clkbuf_map = zip(["Z"], ["gated_clk_buf"]) - self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("m3", "via2", "m2")) + self.connect_vertical_bus(clkbuf_map, + self.gated_clk_buf_inst, + self.rail_offsets, + self.m2_stack[::-1]) # The pin is on M1, so we need another via as well self.add_via_center(layers=self.m1_stack, offset=self.gated_clk_buf_inst.get_pin("Z").center()) @@ -602,7 +614,7 @@ class control_logic(design.design): def place_wlen_row(self, row): x_offset = self.control_x_offset - x_offset = self.place_util(self.wl_en_inst, x_offset, row) + x_offset = self.place_util(self.wl_en_inst, x_offset, row) self.row_end_inst.append(self.wl_en_inst) @@ -623,11 +635,11 @@ class control_logic(design.design): mod=self.p_en_bar_driver) self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"]) - def place_pen_row(self,row): + def place_pen_row(self, row): x_offset = self.control_x_offset - x_offset = self.place_util(self.p_en_bar_nand_inst, x_offset, row) - x_offset = self.place_util(self.p_en_bar_driver_inst, x_offset, row) + x_offset = self.place_util(self.p_en_bar_nand_inst, x_offset, row) + x_offset = self.place_util(self.p_en_bar_driver_inst, x_offset, row) self.row_end_inst.append(self.p_en_bar_driver_inst) @@ -637,8 +649,8 @@ class control_logic(design.design): out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc() in_pos = self.p_en_bar_driver_inst.get_pin("A").lc() - mid1 = vector(out_pos.x,in_pos.y) - self.add_wire(self.m1_stack,[out_pos, mid1,in_pos]) + mid1 = vector(out_pos.x, in_pos.y) + self.add_wire(self.m1_stack, [out_pos, mid1, in_pos]) self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar") @@ -656,14 +668,12 @@ class control_logic(design.design): # hence we use rbl_bl_delay as well. self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"]) - - def place_sen_row(self,row): + def place_sen_row(self, row): x_offset = self.control_x_offset x_offset = self.place_util(self.s_en_gate_inst, x_offset, row) self.row_end_inst.append(self.s_en_gate_inst) - def route_sen(self): @@ -683,7 +693,7 @@ class control_logic(design.design): mod=self.inv) self.connect_inst(["rbl_bl_delay", "rbl_bl_delay_bar", "vdd", "gnd"]) - def place_rbl_delay_row(self,row): + def place_rbl_delay_row(self, row): x_offset = self.control_x_offset x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row) @@ -700,11 +710,9 @@ class control_logic(design.design): self.add_via_center(layers=self.m1_stack, offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center()) - rbl_map = zip(["A"], ["rbl_bl_delay"]) self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets) - def create_wen_row(self): # input: we (or cs) output: w_en @@ -720,8 +728,7 @@ class control_logic(design.design): # Only drive the writes in the second half of the clock cycle during a write operation. self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"]) - - def place_wen_row(self,row): + def place_wen_row(self, row): x_offset = self.control_x_offset x_offset = self.place_util(self.w_en_gate_inst, x_offset, row) @@ -750,22 +757,22 @@ class control_logic(design.design): self.connect_inst(inst_pins) def place_dffs(self): - self.ctrl_dff_inst.place(vector(0,0)) + self.ctrl_dff_inst.place(vector(0, 0)) def route_dffs(self): if self.port_type == "rw": dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"]) elif self.port_type == "r": - dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"]) + dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"]) else: dff_out_map = zip(["dout_bar_0"], ["cs"]) self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("m3", "via2", "m2")) # Connect the clock rail to the other clock rail in_pos = self.ctrl_dff_inst.get_pin("clk").uc() - mid_pos = in_pos + vector(0,2*self.m2_pitch) + mid_pos = in_pos + vector(0, 2 * self.m2_pitch) rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y) - self.add_wire(self.m1_stack,[in_pos, mid_pos, rail_pos]) + self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos]) self.add_via_center(layers=self.m1_stack, offset=rail_pos) @@ -773,34 +780,31 @@ class control_logic(design.design): if (self.port_type == "rw"): self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web") - def get_offset(self,row): + def get_offset(self, row): """ Compute the y-offset and mirroring """ - y_off = row*self.and2.height + y_off = row * self.and2.height if row % 2: y_off += self.and2.height mirror="MX" else: mirror="R0" - return (y_off,mirror) - + return (y_off, mirror) def connect_output(self, inst, pin_name, out_name): """ Create an output pin on the right side from the pin of a given instance. """ out_pin = inst.get_pin(pin_name) - right_pos=out_pin.center() + vector(self.width-out_pin.cx(),0) + right_pos = out_pin.center() + vector(self.width - out_pin.cx(), 0) self.add_layout_pin_segment_center(text=out_name, layer="m1", start=out_pin.center(), end=right_pos) - - def route_supply(self): """ Add vdd and gnd to the instance cells """ - max_row_x_loc = max([inst.rx() for inst in self.row_end_inst]) + max_row_x_loc = max([inst.rx() for inst in self.row_end_inst]) for inst in self.row_end_inst: pins = inst.get_pins("vdd") for pin in pins: @@ -818,16 +822,14 @@ class control_logic(design.design): self.add_power_pin("gnd", pin_loc) self.add_path("m1", [row_loc, pin_loc]) - self.copy_layout_pin(self.delay_inst,"gnd") - self.copy_layout_pin(self.delay_inst,"vdd") + self.copy_layout_pin(self.delay_inst, "gnd") + self.copy_layout_pin(self.delay_inst, "vdd") - self.copy_layout_pin(self.ctrl_dff_inst,"gnd") - self.copy_layout_pin(self.ctrl_dff_inst,"vdd") + self.copy_layout_pin(self.ctrl_dff_inst, "gnd") + self.copy_layout_pin(self.ctrl_dff_inst, "vdd") - - def add_lvs_correspondence_points(self): - """ This adds some points for easier debugging if LVS goes wrong. + """ This adds some points for easier debugging if LVS goes wrong. These should probably be turned off by default though, since extraction will show these as ports in the extracted netlist. """ @@ -851,74 +853,79 @@ class control_logic(design.design): offset=pin.ll(), height=pin.height(), width=pin.width()) - def get_delays_to_wl(self): """Get the delay (in delay units) of the clk to a wordline in the bitcell array""" debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") self.wl_stage_efforts = self.get_wordline_stage_efforts() - clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts) - total_delay = clk_to_wl_rise + clk_to_wl_fall - debug.info(1, "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, clk_to_wl_fall,total_delay)) - return clk_to_wl_rise,clk_to_wl_fall + clk_to_wl_rise, clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts) + total_delay = clk_to_wl_rise + clk_to_wl_fall + debug.info(1, + "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, + clk_to_wl_fall, + total_delay)) + return clk_to_wl_rise, clk_to_wl_fall - def get_wordline_stage_efforts(self): """Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts""" stage_effort_list = [] - #Initial direction of gated_clk_bar signal for this path + # Initial direction of gated_clk_bar signal for this path is_clk_bar_rise = True - #Calculate the load on wl_en within the module and add it to external load + # Calculate the load on wl_en within the module and add it to external load external_cout = self.sram.get_wl_en_cin() - #First stage is the clock buffer + # First stage is the clock buffer stage_effort_list += self.clk_buf_driver.get_stage_efforts(external_cout, is_clk_bar_rise) last_stage_is_rise = stage_effort_list[-1].is_rise - #Then ask the sram for the other path delays (from the bank) + # Then ask the sram for the other path delays (from the bank) stage_effort_list += self.sram.get_wordline_stage_efforts(last_stage_is_rise) return stage_effort_list def get_delays_to_sen(self): - """Get the delay (in delay units) of the clk to a sense amp enable. - This does not incorporate the delay of the replica bitline. + """ + Get the delay (in delay units) of the clk to a sense amp enable. + This does not incorporate the delay of the replica bitline. """ debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") self.sen_stage_efforts = self.get_sa_enable_stage_efforts() clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts) - total_delay = clk_to_sen_rise + clk_to_sen_fall - debug.info(1, "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, clk_to_sen_fall,total_delay)) - return clk_to_sen_rise, clk_to_sen_fall + total_delay = clk_to_sen_rise + clk_to_sen_fall + debug.info(1, + "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, + clk_to_sen_fall, + total_delay)) + return clk_to_sen_rise, clk_to_sen_fall def get_sa_enable_stage_efforts(self): """Follows the gated_clk_bar signal to the sense amp enable signal adding each stages stage effort to a list""" stage_effort_list = [] - #Initial direction of clock signal for this path + # Initial direction of clock signal for this path last_stage_rise = True - #First stage, gated_clk_bar -(and2)-> rbl_in. Only for RW ports. + # First stage, gated_clk_bar -(and2)-> rbl_in. Only for RW ports. if self.port_type == "rw": stage1_cout = self.replica_bitline.get_en_cin() stage_effort_list += self.and2.get_stage_efforts(stage1_cout, last_stage_rise) last_stage_rise = stage_effort_list[-1].is_rise - #Replica bitline stage, rbl_in -(rbl)-> pre_s_en + # Replica bitline stage, rbl_in -(rbl)-> pre_s_en stage2_cout = self.sen_and2.get_cin() stage_effort_list += self.replica_bitline.determine_sen_stage_efforts(stage2_cout, last_stage_rise) last_stage_rise = stage_effort_list[-1].is_rise - #buffer stage, pre_s_en -(buffer)-> s_en + # buffer stage, pre_s_en -(buffer)-> s_en stage3_cout = self.sram.get_sen_cin() stage_effort_list += self.s_en_driver.get_stage_efforts(stage3_cout, last_stage_rise) last_stage_rise = stage_effort_list[-1].is_rise - return stage_effort_list + return stage_effort_list def get_wl_sen_delays(self): - """Gets a list of the stages and delays in order of their path.""" + """ Gets a list of the stages and delays in order of their path. """ if self.sen_stage_efforts == None or self.wl_stage_efforts == None: debug.error("Model delays not calculated for SRAM.", 1) @@ -927,45 +934,45 @@ class control_logic(design.design): return wl_delays, sen_delays def analytical_delay(self, corner, slew, load): - """Gets the analytical delay from clk input to wl_en output""" + """ Gets the analytical delay from clk input to wl_en output """ stage_effort_list = [] - #Calculate the load on clk_buf_bar - ext_clk_buf_cout = self.sram.get_clk_bar_cin() + # Calculate the load on clk_buf_bar + # ext_clk_buf_cout = self.sram.get_clk_bar_cin() - #Operations logic starts on negative edge - last_stage_rise = False + # Operations logic starts on negative edge + last_stage_rise = False - #First stage(s), clk -(pdriver)-> clk_buf. - #clk_buf_cout = self.replica_bitline.get_en_cin() + # First stage(s), clk -(pdriver)-> clk_buf. + # clk_buf_cout = self.replica_bitline.get_en_cin() clk_buf_cout = 0 stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise) last_stage_rise = stage_effort_list[-1].is_rise - #Second stage, clk_buf -(inv)-> clk_bar + # Second stage, clk_buf -(inv)-> clk_bar clk_bar_cout = self.and2.get_cin() stage_effort_list += self.and2.get_stage_efforts(clk_bar_cout, last_stage_rise) last_stage_rise = stage_effort_list[-1].is_rise - #Third stage clk_bar -(and)-> gated_clk_bar + # Third stage clk_bar -(and)-> gated_clk_bar gated_clk_bar_cin = self.get_gated_clk_bar_cin() stage_effort_list.append(self.inv.get_stage_effort(gated_clk_bar_cin, last_stage_rise)) last_stage_rise = stage_effort_list[-1].is_rise - #Stages from gated_clk_bar -------> wordline + # Stages from gated_clk_bar -------> wordline stage_effort_list += self.get_wordline_stage_efforts() return stage_effort_list def get_clk_buf_cin(self): """ - Get the loads that are connected to the buffered clock. + Get the loads that are connected to the buffered clock. Includes all the DFFs and some logic. """ - #Control logic internal load + # Control logic internal load int_clk_buf_cap = self.inv.get_cin() + self.ctrl_dff_array.get_clk_cin() + self.and2.get_cin() - #Control logic external load (in the other parts of the SRAM) + # Control logic external load (in the other parts of the SRAM) ext_clk_buf_cap = self.sram.get_clk_bar_cin() return int_clk_buf_cap + ext_clk_buf_cap @@ -976,7 +983,7 @@ class control_logic(design.design): total_cin = 0 total_cin += self.wl_en_driver.get_cin() if self.port_type == 'rw': - total_cin +=self.and2.get_cin() + total_cin += self.and2.get_cin() return total_cin def graph_exclude_dffs(self): @@ -989,7 +996,7 @@ class control_logic(design.design): def place_util(self, inst, x_offset, row): """ Utility to place a row and compute the next offset """ - (y_offset,mirror)=self.get_offset(row) + (y_offset, mirror) = self.get_offset(row) offset = vector(x_offset, y_offset) inst.place(offset, mirror) - return x_offset+inst.width + return x_offset + inst.width From 94eb2afa36156afa193c38d1f47a206a10cf5de7 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 15 Apr 2020 15:29:55 -0700 Subject: [PATCH 061/101] Change to callable DRC rule. Use bottom coordinate for bus offsets. --- compiler/base/hierarchy_layout.py | 81 ++++++++++++++----------------- compiler/base/wire.py | 4 +- 2 files changed, 38 insertions(+), 47 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 63d53d39..e67f8d85 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -46,7 +46,6 @@ class layout(): except ImportError: self.pwr_grid_layer = "m3" - ############################################################ # GDS layout ############################################################ @@ -196,7 +195,7 @@ class layout(): self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) debug.info(3, "adding instance {}".format(self.insts[-1])) # This is commented out for runtime reasons - #debug.info(4, "instance list: " + ",".join(x.name for x in self.insts)) + # debug.info(4, "instance list: " + ",".join(x.name for x in self.insts)) return self.insts[-1] def get_inst(self, name): @@ -214,12 +213,14 @@ class layout(): width = drc["minwidth_{}".format(layer)] if not height: height = drc["minwidth_{}".format(layer)] - # negative layers indicate "unused" layers in a given technology lpp = techlayer[layer] - if lpp[0] >= 0: - self.objs.append(geometry.rectangle(lpp, offset, width, height)) - return self.objs[-1] - return None + if abs(offset[0]-5.16250)<0.01 and abs(offset[1]-8.70750)<0.01: + import pdb; pdb.set_trace() + self.objs.append(geometry.rectangle(lpp, + offset, + width, + height)) + return self.objs[-1] def add_rect_center(self, layer, offset, width=None, height=None): """ @@ -230,16 +231,13 @@ class layout(): width = drc["minwidth_{}".format(layer)] if not height: height = drc["minwidth_{}".format(layer)] - # negative layers indicate "unused" layers in a given technology lpp = techlayer[layer] corrected_offset = offset - vector(0.5 * width, 0.5 * height) - if lpp[0] >= 0: - self.objs.append(geometry.rectangle(lpp, - corrected_offset, - width, - height)) - return self.objs[-1] - return None + self.objs.append(geometry.rectangle(lpp, + corrected_offset, + width, + height)) + return self.objs[-1] def add_segment_center(self, layer, start, end): """ @@ -252,15 +250,15 @@ class layout(): elif start.x != end.x: offset = vector(0, 0.5 * minwidth_layer) return self.add_rect(layer, - start-offset, - end.x-start.x, + start - offset, + end.x - start.x, minwidth_layer) else: offset = vector(0.5 * minwidth_layer, 0) return self.add_rect(layer, - start-offset, + start - offset, minwidth_layer, - end.y-start.y) + end.y - start.y) def get_pin(self, text): """ @@ -268,14 +266,14 @@ class layout(): """ try: if len(self.pin_map[text]) > 1: - debug.error("Should use a pin iterator since more than one pin {}".format(text),-1) + debug.error("Should use a pin iterator since more than one pin {}".format(text), -1) # If we have one pin, return it and not the list. # Otherwise, should use get_pins() any_pin = next(iter(self.pin_map[text])) return any_pin except Exception: self.gds_write("missing_pin.gds") - debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text,self.name),-1) + debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.name), -1) def get_pins(self, text): """ @@ -377,7 +375,7 @@ class layout(): height = drc["minwidth_{0}".format(layer)] new_pin = pin_layout(text, - [offset, offset+vector(width, height)], + [offset, offset + vector(width, height)], layer) try: @@ -413,23 +411,18 @@ class layout(): def add_label(self, text, layer, offset=[0, 0], zoom=-1): """Adds a text label on the given layer,offset, and zoom level""" - # negative layers indicate "unused" layers in a given technology debug.info(5, "add label " + str(text) + " " + layer + " " + str(offset)) lpp = techlayer[layer] - if lpp[0] >= 0: - self.objs.append(geometry.label(text, lpp, offset, zoom)) - return self.objs[-1] - return None + self.objs.append(geometry.label(text, lpp, offset, zoom)) + return self.objs[-1] def add_path(self, layer, coordinates, width=None): """Connects a routing path on given layer,coordinates,width.""" debug.info(4, "add path " + str(layer) + " " + str(coordinates)) import wire_path # NOTE: (UNTESTED) add_path(...) is currently not used - # negative layers indicate "unused" layers in a given technology # lpp = techlayer[layer] - # if lpp[0] >= 0: - # self.objs.append(geometry.path(lpp, coordinates, width)) + # self.objs.append(geometry.path(lpp, coordinates, width)) wire_path.wire_path(obj=self, layer=layer, @@ -465,7 +458,7 @@ class layout(): from tech import preferred_directions return preferred_directions[layer] - def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None): + def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None): """ Add a three layer via structure. """ if not directions: @@ -487,7 +480,7 @@ class layout(): self.connect_inst([]) return inst - def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None): + def add_via_center(self, layers, offset, directions=None, size=[1, 1], implant_type=None, well_type=None): """ Add a three layer via structure by the center coordinate accounting for mirroring and rotation. @@ -799,9 +792,7 @@ class layout(): self.add_rect(layer=layer, offset=line_offset, height=length) - # Make this the center of the rail - line_positions[names[i]] = line_offset + vector(half_minwidth, - 0.5 * length) + line_positions[names[i]] = line_offset + vector(half_minwidth, 0) else: for i in range(len(names)): line_offset = offset + vector(0, @@ -953,14 +944,14 @@ class layout(): min_y = min([pin.center().y for pin in pins]) # if we are less than a pitch, just create a non-preferred layer jog - if max_y-min_y <= pitch: + if max_y - min_y <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] # Add the vertical trunk on the horizontal layer! self.add_path(self.horizontal_layer, [vector(trunk_offset.x, min_y - half_layer_width), - vector(trunk_offset.x,max_y + half_layer_width)]) + vector(trunk_offset.x, max_y + half_layer_width)]) # Route each pin to the trunk for pin in pins: @@ -1000,7 +991,7 @@ class layout(): # Remove the pin from all conflicts # FIXME: This is O(n^2), so maybe optimize it. - for other_pin,conflicts in g.items(): + for other_pin, conflicts in g.items(): if pin in conflicts: conflicts.remove(pin) g[other_pin]=conflicts @@ -1151,8 +1142,8 @@ class layout(): else: self.bounding_box = self.add_rect(layer=boundary_layer, offset=ll, - height=ur.y-ll.y, - width=ur.x-ll.x) + height=ur.y - ll.y, + width=ur.x - ll.x) def add_enclosure(self, insts, layer="nwell"): """ Add a layer that surrounds the given instances. Useful @@ -1171,8 +1162,8 @@ class layout(): self.add_rect(layer=layer, offset=vector(xmin, ymin), - width=xmax-xmin, - height=ymax-ymin) + width=xmax - xmin, + height=ymax - ymin) def copy_power_pins(self, inst, name): """ @@ -1192,7 +1183,7 @@ class layout(): else: debug.warning("{0} pins of {1} should be on {2} or metal1 for "\ "supply router." - .format(name,inst.name,self.pwr_grid_layer)) + .format(name, inst.name, self.pwr_grid_layer)) def add_power_pin(self, name, loc, size=[1, 1], vertical=False, start_layer="m1"): """ @@ -1244,8 +1235,8 @@ class layout(): [ll, ur] = bbox supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width - height = (ur.y-ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing - width = (ur.x-ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing + height = (ur.y - ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing + width = (ur.x - ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing # LEFT vertical rails offset = ll + vector(-2 * self.supply_rail_pitch, diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 2044eb3a..23cc1b41 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -74,8 +74,8 @@ class wire(wire_path): contact1 = getattr(contact, layer1 + "_via") max_contact = max(contact1.width, contact1.height) - layer1_space = drc["{0}_to_{0}".format(layer1)] - layer2_space = drc["{0}_to_{0}".format(layer2)] + layer1_space = drc("{0}_to_{0}".format(layer1)) + layer2_space = drc("{0}_to_{0}".format(layer2)) pitch = max_contact + max(layer1_space, layer2_space) return pitch From 9d2902de9ea6b1c5f575a434a798c30b860cd942 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 15 Apr 2020 15:55:49 -0700 Subject: [PATCH 062/101] Conditional well spacing --- compiler/modules/dff_buf.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index e42c01c0..c30f15b6 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -7,13 +7,13 @@ # import debug import design -from tech import drc,parameter +from tech import parameter from tech import cell_properties as props -from math import log from vector import vector from globals import OPTS from sram_factory import factory + class dff_buf(design.design): """ This is a simple buffered DFF. The output is buffered @@ -107,13 +107,23 @@ class dff_buf(design.design): self.dff_inst.place(vector(0,0)) # Add INV1 to the right - well_spacing = max(self.nwell_space, - self.pwell_space, - self.pwell_to_nwell) - self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active,0)) + well_spacing = 0 + try: + well_spacing = max(well_spacing, self.nwell_space) + except AttributeError: + pass + try: + well_spacing = max(well_spacing, self.pwell_space) + except AttributeError: + pass + try: + well_spacing = max(well_spacing, self.pwell_to_nwell) + except AttributeError: + pass + self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0)) # Add INV2 to the right - self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) + self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) def route_wires(self): # Route dff q to inv1 a From b347e3f7a8fdaa7289f12bbacbb982cd7e3199d4 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 15 Apr 2020 16:49:04 -0700 Subject: [PATCH 063/101] Try both layers for reversed layer stacks. --- compiler/base/wire.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 23cc1b41..ddb10f43 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -71,7 +71,10 @@ class wire(wire_path): if layer1 == "poly" or layer1 == "active": contact1 = getattr(contact, layer1 + "_contact") else: - contact1 = getattr(contact, layer1 + "_via") + try: + contact1 = getattr(contact, layer1 + "_via") + except AttributeError: + contact1 = getattr(contact, layer2 + "_via") max_contact = max(contact1.width, contact1.height) layer1_space = drc("{0}_to_{0}".format(layer1)) From d1319d633d96f68d006e719afa72542cb336bb5f Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 16 Apr 2020 11:02:54 -0700 Subject: [PATCH 064/101] Don't widen too short wires either --- compiler/base/wire.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/base/wire.py b/compiler/base/wire.py index ddb10f43..10272ede 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -83,7 +83,6 @@ class wire(wire_path): return pitch - # create a 1x1 contact def create_vias(self): """ Add a via and corner square at every corner of the path.""" @@ -113,7 +112,9 @@ class wire(wire_path): # Horizontal wire segment if pl[index][0] != pl[index + 1][0]: line_length = pl[index + 1][0] - pl[index][0] - if abs(line_length) < self.pitch: + # Make the wire wider to avoid via-to-via spacing problems + # But don't make it wider if it is shorter than one via + if abs(line_length) < self.pitch and abs(line_length) > self.horiz_layer_contact_width: width = self.horiz_layer_contact_width else: width = self.horiz_layer_width @@ -131,7 +132,9 @@ class wire(wire_path): # Vertical wire segment elif pl[index][1] != pl[index + 1][1]: line_length = pl[index + 1][1] - pl[index][1] - if abs(line_length) < self.pitch: + # Make the wire wider to avoid via-to-via spacing problems + # But don't make it wider if it is shorter than one via + if abs(line_length) < self.pitch and abs(line_length) > self.vert_layer_contact_width: width = self.vert_layer_contact_width else: width = self.vert_layer_width From 770533e7b1eab1e03bcbdbaaa1901d4cc2ae2de9 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 16 Apr 2020 11:27:26 -0700 Subject: [PATCH 065/101] Parameterize vdd and gnd pin in sense amp array. --- compiler/modules/sense_amp_array.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index d8cb2869..c2f68f9b 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -126,14 +126,16 @@ class sense_amp_array(design.design): for i in range(len(self.local_insts)): inst = self.local_insts[i] + gnd_pin = inst.get_pin("gnd") self.add_power_pin(name="gnd", - loc=inst.get_pin("gnd").center(), - start_layer="m2", + loc=gnd_pin.center(), + start_layer=gnd_pin.layer, vertical=True) - + + vdd_pin = inst.get_pin("vdd") self.add_power_pin(name="vdd", - loc=inst.get_pin("vdd").center(), - start_layer="m2", + loc=vdd_pin.center(), + start_layer=vdd_pin.layer, vertical=True) bl_pin = inst.get_pin(inst.mod.get_bl_names()) From 843e9414df9fad170a6926e42b435c9e64928cd8 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 16 Apr 2020 11:28:35 -0700 Subject: [PATCH 066/101] Parameterize vdd and gnd pin in write driver array. --- compiler/modules/write_driver_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index d1244aed..ab369dda 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -166,7 +166,7 @@ class write_driver_array(design.design): self.add_power_pin(name=n, loc=pin.center(), vertical=True, - start_layer="m2") + start_layer=pin.layer) if self.write_size: for bit in range(self.num_wmasks): inst = self.driver_insts[bit * self.write_size] From 75fce9894c13545fb4da4f681f04dc92bae4650c Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 17 Apr 2020 09:35:07 -0700 Subject: [PATCH 067/101] Allow LVS to run even if DRC fails. --- compiler/tests/testutils.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index c4fd7cac..96820e38 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -44,22 +44,30 @@ class openram_test(unittest.TestCase): if not OPTS.netlist_only: a.gds_write(tempgds) + # Run both DRC and LVS even if DRC might fail + # Magic can still extract despite DRC failing, so it might be ok in some techs + # if we ignore things like minimum metal area of pins import verify - result=verify.run_drc(a.name, tempgds, extract=True, final_verification=final_verification) - if result != 0: + drc_result=verify.run_drc(a.name, tempgds, extract=True, final_verification=final_verification) + lvs_result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification) + + if lvs_result != 0: + #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) + #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + self.fail("LVS mismatch: {}".format(a.name)) + if lvs_result == 0 and drc_result != 0: + #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) + #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + debug.warning("DRC failed but LVS passed: {}".format(a.name)) + elif drc_result != 0: #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("DRC failed: {}".format(a.name)) - result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification) - if result != 0: - #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) - #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) - #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) - self.fail("LVS mismatch: {}".format(a.name)) - # For debug... #import pdb; pdb.set_trace() if OPTS.purge_temp: From f1925420cffd2336cc06ac843face6f5e6b28bc1 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 17 Apr 2020 10:30:26 -0700 Subject: [PATCH 068/101] Only allow DRC fail with LVS pass if using Magic. --- compiler/tests/testutils.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 96820e38..eaac38e2 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -44,19 +44,18 @@ class openram_test(unittest.TestCase): if not OPTS.netlist_only: a.gds_write(tempgds) + import verify # Run both DRC and LVS even if DRC might fail # Magic can still extract despite DRC failing, so it might be ok in some techs # if we ignore things like minimum metal area of pins - import verify drc_result=verify.run_drc(a.name, tempgds, extract=True, final_verification=final_verification) - lvs_result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification) + + # Always run LVS if we are using magic + if "magic" in OPTS.drc_exe: + lvs_result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification) - if lvs_result != 0: - #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) - #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) - #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) - self.fail("LVS mismatch: {}".format(a.name)) - if lvs_result == 0 and drc_result != 0: + # Only allow DRC to fail and LVS to pass if we are using magic + if "magic" in OPTS.drc_exe and lvs_result == 0 and drc_result != 0: #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) @@ -66,6 +65,12 @@ class openram_test(unittest.TestCase): #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("DRC failed: {}".format(a.name)) + + if lvs_result != 0: + #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) + #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + self.fail("LVS mismatch: {}".format(a.name)) # For debug... From fbc6dfdaac3688f3a18283646bd3f1e961a45dcd Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Fri, 17 Apr 2020 12:26:18 -0700 Subject: [PATCH 069/101] split pbitcell tests --- ...4_single_level_column_mux_pbitcell_test.py | 49 +++++++++++++++++ .../tests/04_single_level_column_mux_test.py | 20 +------ ...hierarchical_predecode2x4_pbitcell_test.py | 6 +- .../06_hierarchical_predecode2x4_test.py | 2 +- ...hierarchical_predecode3x8_pbitcell_test.py | 2 +- .../06_hierarchical_predecode3x8_test.py | 2 +- ...le_level_column_mux_array_pbitcell_test.py | 55 +++++++++++++++++++ .../07_single_level_column_mux_array_test.py | 27 +-------- .../tests/08_wordline_driver_pbitcell_test.py | 44 +++++++++++++++ compiler/tests/08_wordline_driver_test.py | 13 +---- compiler/tests/09_sense_amp_array_test.py | 15 ----- .../tests/09_sense_amp_array_test_pbitcell.py | 46 ++++++++++++++++ .../10_write_driver_array_pbitcell_test.py | 46 ++++++++++++++++ compiler/tests/10_write_driver_array_test.py | 15 ----- ..._write_driver_array_wmask_pbitcell_test.py | 49 +++++++++++++++++ .../tests/10_write_driver_array_wmask_test.py | 15 ----- .../10_write_mask_and_array_pbitcell_test.py | 49 +++++++++++++++++ .../tests/10_write_mask_and_array_test.py | 15 ----- .../tests/19_bank_select_pbitcell_test.py | 48 ++++++++++++++++ compiler/tests/19_bank_select_test.py | 19 +------ 20 files changed, 398 insertions(+), 139 deletions(-) create mode 100644 compiler/tests/04_single_level_column_mux_pbitcell_test.py create mode 100644 compiler/tests/07_single_level_column_mux_array_pbitcell_test.py create mode 100644 compiler/tests/08_wordline_driver_pbitcell_test.py create mode 100644 compiler/tests/09_sense_amp_array_test_pbitcell.py create mode 100644 compiler/tests/10_write_driver_array_pbitcell_test.py create mode 100644 compiler/tests/10_write_driver_array_wmask_pbitcell_test.py create mode 100644 compiler/tests/10_write_mask_and_array_pbitcell_test.py create mode 100644 compiler/tests/19_bank_select_pbitcell_test.py diff --git a/compiler/tests/04_single_level_column_mux_pbitcell_test.py b/compiler/tests/04_single_level_column_mux_pbitcell_test.py new file mode 100644 index 00000000..85f909e6 --- /dev/null +++ b/compiler/tests/04_single_level_column_mux_pbitcell_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING 04_driver_test") + +class single_level_column_mux_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check single level column mux in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + factory.reset() + debug.info(2, "Checking column mux for pbitcell (innermost connections)") + tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + factory.reset() + debug.info(2, "Checking column mux for pbitcell (outermost connections)") + tx = factory.create(module_type="single_level_column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2") + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_single_level_column_mux_test.py b/compiler/tests/04_single_level_column_mux_test.py index de5870fc..2b437987 100755 --- a/compiler/tests/04_single_level_column_mux_test.py +++ b/compiler/tests/04_single_level_column_mux_test.py @@ -22,30 +22,14 @@ class single_level_column_mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - + # check single level column mux in single port debug.info(2, "Checking column mux") tx = factory.create(module_type="single_level_column_mux", tx_size=8) self.local_check(tx) - - # check single level column mux in multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 - - factory.reset() - debug.info(2, "Checking column mux for pbitcell (innermost connections)") - tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(tx) - - factory.reset() - debug.info(2, "Checking column mux for pbitcell (outermost connections)") - tx = factory.create(module_type="single_level_column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2") - self.local_check(tx) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py index 8afc45da..bc4a153f 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py @@ -26,13 +26,13 @@ class hierarchical_predecode2x4_test(openram_test): OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - + debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)") a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index 8bb42253..ebb06330 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -27,7 +27,7 @@ class hierarchical_predecode2x4_test(openram_test): self.local_check(a) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py index 34d38ddf..ffe68102 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py @@ -26,7 +26,7 @@ class hierarchical_predecode3x8_test(openram_test): OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - + debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index f01fae03..63acc416 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -25,7 +25,7 @@ class hierarchical_predecode3x8_test(openram_test): debug.info(1, "Testing sample for hierarchy_predecode3x8") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py new file mode 100644 index 00000000..d929b855 --- /dev/null +++ b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class single_level_column_mux_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + import single_level_column_mux_array + + # check single level column mux array in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + factory.reset() + debug.info(1, "Testing sample for 2-way column_mux_array in multi-port") + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) + + debug.info(1, "Testing sample for 4-way column_mux_array in multi-port") + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) + + debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)") + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) + + debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)") + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2") + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index ff6c51eb..c5d48689 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -20,7 +20,7 @@ class single_level_column_mux_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) import single_level_column_mux_array - + # check single level column mux array in single port debug.info(1, "Testing sample for 2-way column_mux_array") a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8) @@ -33,32 +33,9 @@ class single_level_column_mux_test(openram_test): debug.info(1, "Testing sample for 8-way column_mux_array") a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4) self.local_check(a) - - # check single level column mux array in multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 - - factory.reset() - debug.info(1, "Testing sample for 2-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(a) - - debug.info(1, "Testing sample for 4-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(a) - - debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(a) - - debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2") - self.local_check(a) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": diff --git a/compiler/tests/08_wordline_driver_pbitcell_test.py b/compiler/tests/08_wordline_driver_pbitcell_test.py new file mode 100644 index 00000000..42de5c26 --- /dev/null +++ b/compiler/tests/08_wordline_driver_pbitcell_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING 04_driver_test") + +class wordline_driver_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check wordline driver for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(2, "Checking driver (multi-port case)") + tx = factory.create(module_type="wordline_driver", rows=8, cols=64) + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/08_wordline_driver_test.py index f64503f8..8a18a59d 100755 --- a/compiler/tests/08_wordline_driver_test.py +++ b/compiler/tests/08_wordline_driver_test.py @@ -28,19 +28,8 @@ class wordline_driver_test(openram_test): tx = factory.create(module_type="wordline_driver", rows=8, cols=32) self.local_check(tx) - # check wordline driver for multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - factory.reset() - debug.info(2, "Checking driver (multi-port case)") - tx = factory.create(module_type="wordline_driver", rows=8, cols=64) - self.local_check(tx) - globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index 88b5a9aa..459113f2 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -34,21 +34,6 @@ class sense_amp_test(openram_test): a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4) self.local_check(a) - # check sense amp array for multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - factory.reset() - debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)") - a = factory.create(module_type="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 (multi-port case)") - a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4) - self.local_check(a) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/09_sense_amp_array_test_pbitcell.py b/compiler/tests/09_sense_amp_array_test_pbitcell.py new file mode 100644 index 00000000..7c2fbd2d --- /dev/null +++ b/compiler/tests/09_sense_amp_array_test_pbitcell.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class sense_amp_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + #check sense amp array for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2 (multi-port case)") + a = factory.create(module_type="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 (multi-port case)") + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/10_write_driver_array_pbitcell_test.py b/compiler/tests/10_write_driver_array_pbitcell_test.py new file mode 100644 index 00000000..87849001 --- /dev/null +++ b/compiler/tests/10_write_driver_array_pbitcell_test.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class write_driver_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check write driver array for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)") + a = factory.create(module_type="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 (multi-port case)") + a = factory.create(module_type="write_driver_array", columns=16, word_size=8) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py index 16e280ed..8db26a5a 100755 --- a/compiler/tests/10_write_driver_array_test.py +++ b/compiler/tests/10_write_driver_array_test.py @@ -29,22 +29,7 @@ class write_driver_test(openram_test): debug.info(2, "Testing write_driver_array for columns=16, word_size=8") a = factory.create(module_type="write_driver_array", columns=16, word_size=8) self.local_check(a) - - # check write driver array for multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - factory.reset() - debug.info(2, "Testing write_driver_array for columns=8, word_size=8 (multi-port case)") - a = factory.create(module_type="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 (multi-port case)") - a = factory.create(module_type="write_driver_array", columns=16, word_size=8) - self.local_check(a) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py new file mode 100644 index 00000000..046b4d22 --- /dev/null +++ b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class write_driver_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check write driver array for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4 (multi-port case)") + a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4) + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=16, word_size=8, write_size=4 (multi-port case)") + a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/10_write_driver_array_wmask_test.py b/compiler/tests/10_write_driver_array_wmask_test.py index 743d331a..15e32b19 100755 --- a/compiler/tests/10_write_driver_array_wmask_test.py +++ b/compiler/tests/10_write_driver_array_wmask_test.py @@ -36,21 +36,6 @@ class write_driver_test(openram_test): a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4) self.local_check(a) - # check write driver array for multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - factory.reset() - debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4 (multi-port case)") - a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4) - self.local_check(a) - - debug.info(2, "Testing write_driver_array for columns=16, word_size=8, write_size=4 (multi-port case)") - a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4) - self.local_check(a) - globals.end_openram() diff --git a/compiler/tests/10_write_mask_and_array_pbitcell_test.py b/compiler/tests/10_write_mask_and_array_pbitcell_test.py new file mode 100644 index 00000000..5068c119 --- /dev/null +++ b/compiler/tests/10_write_mask_and_array_pbitcell_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class write_mask_and_array_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check write driver array for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4 (multi-port case)") + a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4) + self.local_check(a) + + debug.info(2, "Testing write_mask_and_array for columns=16, word_size=8, write_size=2 (multi-port case)") + a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/10_write_mask_and_array_test.py b/compiler/tests/10_write_mask_and_array_test.py index 303ce2f4..42d80fa4 100755 --- a/compiler/tests/10_write_mask_and_array_test.py +++ b/compiler/tests/10_write_mask_and_array_test.py @@ -36,21 +36,6 @@ class write_mask_and_array_test(openram_test): a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2) self.local_check(a) - # check write driver array for multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - factory.reset() - debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4 (multi-port case)") - a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4) - self.local_check(a) - - debug.info(2, "Testing write_mask_and_array for columns=16, word_size=8, write_size=2 (multi-port case)") - a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2) - self.local_check(a) - globals.end_openram() diff --git a/compiler/tests/19_bank_select_pbitcell_test.py b/compiler/tests/19_bank_select_pbitcell_test.py new file mode 100644 index 00000000..6bf5929e --- /dev/null +++ b/compiler/tests/19_bank_select_pbitcell_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class bank_select_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.bitcell = "pbitcell" + debug.info(1, "No column mux, rw control logic") + a = factory.create(module_type="bank_select", port="rw") + self.local_check(a) + + OPTS.num_rw_ports = 0 + OPTS.num_w_ports = 1 + OPTS.num_r_ports = 1 + + debug.info(1, "No column mux, w control logic") + a = factory.create(module_type="bank_select", port="w") + self.local_check(a) + + debug.info(1, "No column mux, r control logic") + a = factory.create(module_type="bank_select", port="r") + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_bank_select_test.py b/compiler/tests/19_bank_select_test.py index adb2523e..afec4c3c 100755 --- a/compiler/tests/19_bank_select_test.py +++ b/compiler/tests/19_bank_select_test.py @@ -24,26 +24,9 @@ class bank_select_test(openram_test): debug.info(1, "No column mux, rw control logic") a = factory.create(module_type="bank_select", port="rw") self.local_check(a) - - OPTS.bitcell = "pbitcell" - debug.info(1, "No column mux, rw control logic") - a = factory.create(module_type="bank_select", port="rw") - self.local_check(a) - - OPTS.num_rw_ports = 0 - OPTS.num_w_ports = 1 - OPTS.num_r_ports = 1 - debug.info(1, "No column mux, w control logic") - a = factory.create(module_type="bank_select", port="w") - self.local_check(a) - - debug.info(1, "No column mux, r control logic") - a = factory.create(module_type="bank_select", port="r") - self.local_check(a) - globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() From 7920b0cef99f0ece902a14c47fe5c37e9fa0f263 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Fri, 17 Apr 2020 12:36:48 -0700 Subject: [PATCH 070/101] m3 min area rounding fix --- compiler/base/hierarchy_layout.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index e67f8d85..14136c9c 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -17,6 +17,7 @@ import os from globals import OPTS from vector import vector from pin_layout import pin_layout +from utils import round_to_grid class layout(): @@ -325,7 +326,7 @@ class layout(): file_name = "non_rectilinear.gds" self.gds_write(file_name) debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1) - + minwidth_layer = drc["minwidth_{}".format(layer)] # one of these will be zero @@ -1213,7 +1214,8 @@ class layout(): else: # Hack for min area if OPTS.tech_name == "s8": - height = width = sqrt(drc["minarea_m3"]) + width = round_to_grid(sqrt(drc["minarea_m3"])) + height = round_to_grid(drc["minarea_m3"]/width) else: width = via.width height = via.height From 7e36cd482855bfa5408ea0fb794be107adb6ada8 Mon Sep 17 00:00:00 2001 From: David Ratchkov Date: Fri, 17 Apr 2020 13:45:57 -0700 Subject: [PATCH 071/101] - Write voltage_map and pg_pin - Remove 'when' condition on leakage power - Remove 'clk*' from 'when' condition on internal_power on the same 'clk*' pin --- compiler/characterizer/lib.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 1e68fc2e..b0668ddc 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -181,17 +181,20 @@ class lib: self.lib.write(" dont_touch : true;\n") self.lib.write(" area : {};\n\n".format(self.sram.width * self.sram.height)) - #Build string of all control signals. + self.write_pg_pin() + + #Build string of all control signals. control_str = 'csb0' #assume at least 1 port for i in range(1, self.total_port_num): control_str += ' & csb{0}'.format(i) # Leakage is included in dynamic when macro is enabled self.lib.write(" leakage_power () {\n") - self.lib.write(" when : \"{0}\";\n".format(control_str)) + # 'when' condition unnecessary when cs pin does not turn power to devices + # self.lib.write(" when : \"{0}\";\n".format(control_str)) self.lib.write(" value : {};\n".format(self.char_sram_results["leakage_power"])) self.lib.write(" }\n") - self.lib.write(" cell_leakage_power : {};\n".format(0)) + self.lib.write(" cell_leakage_power : {};\n".format(self.char_sram_results["leakage_power"])) def write_units(self): @@ -240,6 +243,9 @@ class lib: self.lib.write(" default_max_fanout : 4.0 ;\n") self.lib.write(" default_connection_class : universal ;\n\n") + self.lib.write(" voltage_map ( vdd, {} );\n".format(tech.spice["nom_supply_voltage"])) + self.lib.write(" voltage_map ( gnd, 0 );\n\n") + def create_list(self,values): """ Helper function to create quoted, line wrapped list """ list_values = ", ".join(str(v) for v in values) @@ -518,7 +524,7 @@ class lib: web_name = " & !web{0}".format(port) avg_write_power = np.mean(self.char_port_results[port]["write1_power"] + self.char_port_results[port]["write0_power"]) self.lib.write(" internal_power(){\n") - self.lib.write(" when : \"!csb{0} & clk{0}{1}\"; \n".format(port, web_name)) + self.lib.write(" when : \"!csb{0}{1}\"; \n".format(port, web_name)) self.lib.write(" rise_power(scalar){\n") self.lib.write(" values(\"{0}\");\n".format(avg_write_power/2.0)) self.lib.write(" }\n") @@ -532,7 +538,7 @@ class lib: web_name = " & web{0}".format(port) avg_read_power = np.mean(self.char_port_results[port]["read1_power"] + self.char_port_results[port]["read0_power"]) self.lib.write(" internal_power(){\n") - self.lib.write(" when : \"!csb{0} & !clk{0}{1}\"; \n".format(port, web_name)) + self.lib.write(" when : \"!csb{0}{1}\"; \n".format(port, web_name)) self.lib.write(" rise_power(scalar){\n") self.lib.write(" values(\"{0}\");\n".format(avg_read_power/2.0)) self.lib.write(" }\n") @@ -552,6 +558,16 @@ class lib: self.lib.write(" }\n") self.lib.write(" }\n") + def write_pg_pin(self): + self.lib.write(" pg_pin(vdd) {\n") + self.lib.write(" voltage_name : VDD;\n") + self.lib.write(" pg_type : primary_power;\n") + self.lib.write(" }\n\n") + self.lib.write(" pg_pin(gnd) {\n") + self.lib.write(" voltage_name : GND;\n") + self.lib.write(" pg_type : primary_ground;\n") + self.lib.write(" }\n\n") + def compute_delay(self): """Compute SRAM delays for current corner""" self.d = delay(self.sram, self.sp_file, self.corner) From cbb67ad483fd6873d562e1d6f6a90711cf03c8a1 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 17 Apr 2020 13:57:52 -0700 Subject: [PATCH 072/101] Update to run LVS when no DRC errors too. --- compiler/tests/testutils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index eaac38e2..99d534d0 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -51,9 +51,9 @@ class openram_test(unittest.TestCase): drc_result=verify.run_drc(a.name, tempgds, extract=True, final_verification=final_verification) # Always run LVS if we are using magic - if "magic" in OPTS.drc_exe: + if "magic" in OPTS.drc_exe or drc_result == 0: lvs_result=verify.run_lvs(a.name, tempgds, tempspice, final_verification=final_verification) - + # Only allow DRC to fail and LVS to pass if we are using magic if "magic" in OPTS.drc_exe and lvs_result == 0 and drc_result != 0: #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) From a158ad1e81144c21c6b1e0b680cc38f58fdad01d Mon Sep 17 00:00:00 2001 From: jcirimel Date: Fri, 17 Apr 2020 14:24:52 -0700 Subject: [PATCH 073/101] add missing import --- compiler/pgates/pnor2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 47c1cc16..b1899b4c 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -8,6 +8,7 @@ import contact import pgate import debug +from globals import OPTS from tech import drc, parameter, spice from vector import vector from sram_factory import factory From 1f816e2823b56c0c3f406d4f7424e7fb6311b236 Mon Sep 17 00:00:00 2001 From: David Ratchkov Date: Fri, 17 Apr 2020 14:55:17 -0700 Subject: [PATCH 074/101] - Characterize actual disabled power (read mode only) - Report rise/fall power individually --- .gitignore | 2 ++ compiler/characterizer/delay.py | 15 +++++++++++++-- compiler/characterizer/lib.py | 22 +++++++++++++--------- compiler/characterizer/simulation.py | 16 ++++++++++++++++ 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 948e7d19..3d6e4f92 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ *.toc *.synctex.gz **/model_data +outputs +technology/freepdk45/ncsu_basekit diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index a9f9542c..2823a415 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -59,7 +59,7 @@ class delay(simulation): """ Create measurement names. The names themselves currently define the type of measurement """ self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] - self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"] + self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power", "disabled_read0_power", "disabled_read1_power"] # self.voltage_when_names = ["volt_bl", "volt_br"] # self.bitline_delay_names = ["delay_bl", "delay_br"] @@ -108,6 +108,11 @@ class delay(simulation): self.read_lib_meas.append(power_measure("read0_power", "FALL", measure_scale=1e3)) self.read_lib_meas[-1].meta_str = sram_op.READ_ZERO + self.read_lib_meas.append(power_measure("disabled_read1_power", "RISE", measure_scale=1e3)) + self.read_lib_meas[-1].meta_str = "disabled_read1" + self.read_lib_meas.append(power_measure("disabled_read0_power", "FALL", measure_scale=1e3)) + self.read_lib_meas[-1].meta_str = "disabled_read0" + # This will later add a half-period to the spice time delay. Only for reading 0. for obj in self.read_lib_meas: if obj.meta_str is sram_op.READ_ZERO: @@ -665,7 +670,7 @@ class delay(simulation): if not success: feasible_period = 2 * feasible_period continue - + # Positions of measurements currently hardcoded. First 2 are delays, next 2 are slews feasible_delays = [results[port][mname] for mname in self.delay_meas_names if "delay" in mname] feasible_slews = [results[port][mname] for mname in self.delay_meas_names if "slew" in mname] @@ -1208,6 +1213,9 @@ class delay(simulation): read_port) self.measure_cycles[read_port][sram_op.READ_ZERO] = len(self.cycle_times)-1 + self.add_nop(self.probe_address, data_zeros, read_port) + self.measure_cycles[write_port]["disabled_read0"] = len(self.cycle_times) - 1 + self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)") self.add_write("W data 1 address {} to write value".format(self.probe_address), @@ -1223,6 +1231,9 @@ class delay(simulation): wmask_ones, write_port) + self.add_nop(self.probe_address, data_zeros, read_port) + self.measure_cycles[write_port]["disabled_read1"] = len(self.cycle_times) - 1 + # This also ensures we will have a L->H transition on the next read self.add_read("R data 0 address {} to clear dout caps".format(inverse_address), inverse_address, diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index b0668ddc..9f3c12d3 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -522,39 +522,43 @@ class lib: if port in self.write_ports: if port in self.read_ports: web_name = " & !web{0}".format(port) - avg_write_power = np.mean(self.char_port_results[port]["write1_power"] + self.char_port_results[port]["write0_power"]) + write1_power = np.mean(self.char_port_results[port]["write1_power"]) + write0_power = np.mean(self.char_port_results[port]["write0_power"]) self.lib.write(" internal_power(){\n") self.lib.write(" when : \"!csb{0}{1}\"; \n".format(port, web_name)) self.lib.write(" rise_power(scalar){\n") - self.lib.write(" values(\"{0}\");\n".format(avg_write_power/2.0)) + self.lib.write(" values(\"{0:.6e}\");\n".format(write1_power)) self.lib.write(" }\n") self.lib.write(" fall_power(scalar){\n") - self.lib.write(" values(\"{0}\");\n".format(avg_write_power/2.0)) + self.lib.write(" values(\"{0:.6e}\");\n".format(write0_power)) self.lib.write(" }\n") self.lib.write(" }\n") if port in self.read_ports: if port in self.write_ports: web_name = " & web{0}".format(port) - avg_read_power = np.mean(self.char_port_results[port]["read1_power"] + self.char_port_results[port]["read0_power"]) + read1_power = np.mean(self.char_port_results[port]["read1_power"]) + read0_power = np.mean(self.char_port_results[port]["read0_power"]) self.lib.write(" internal_power(){\n") self.lib.write(" when : \"!csb{0}{1}\"; \n".format(port, web_name)) self.lib.write(" rise_power(scalar){\n") - self.lib.write(" values(\"{0}\");\n".format(avg_read_power/2.0)) + self.lib.write(" values(\"{0:.6e}\");\n".format(read1_power)) self.lib.write(" }\n") self.lib.write(" fall_power(scalar){\n") - self.lib.write(" values(\"{0}\");\n".format(avg_read_power/2.0)) + self.lib.write(" values(\"{0:.6e}\");\n".format(read0_power)) self.lib.write(" }\n") self.lib.write(" }\n") - # Have 0 internal power when disabled, this will be represented as leakage power. + # Disabled power. + disabled_read1_power = np.mean(self.char_port_results[port]["disabled_read1_power"]) + disabled_read0_power = np.mean(self.char_port_results[port]["disabled_read0_power"]) self.lib.write(" internal_power(){\n") self.lib.write(" when : \"csb{0}\"; \n".format(port)) self.lib.write(" rise_power(scalar){\n") - self.lib.write(" values(\"0\");\n") + self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read1_power)) self.lib.write(" }\n") self.lib.write(" fall_power(scalar){\n") - self.lib.write(" values(\"0\");\n") + self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read0_power)) self.lib.write(" }\n") self.lib.write(" }\n") diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index adbe5f5f..0cfc860f 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -210,6 +210,22 @@ class simulation(): if unselected_port != port: self.add_noop_one_port(unselected_port) + def add_nop(self, address, din_data, port): + """ Add the control values for a cycle with clock only. Does not increment the period. """ + debug.check(port in self.read_ports or port in self.write_ports, + "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports)) + debug.info(2, 'Clock only on port {}'.format(port)) + self.fn_cycle_comments.append('Clock only on port {}'.format(port)) + self.append_cycle_comment(port, 'Clock only on port {}'.format(port)) + + self.cycle_times.append(self.t_current) + self.t_current += self.period + self.add_control_one_port(port, "noop") + # If the port is also a readwrite then add data. + if port in self.write_ports: + self.add_data(din_data, port) + self.add_address(address, port) + def add_noop_all_ports(self, comment): """ Add the control values for a noop to all ports. """ debug.info(2, comment) From 486819ae0d1d11281e1f5544bc0a670c56632627 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Fri, 17 Apr 2020 15:27:36 -0700 Subject: [PATCH 075/101] fix width bin typo --- compiler/pgates/pgate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index de513cc3..d3084146 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -300,7 +300,7 @@ class pgate(design.design): bins = bins[0:bisect_left(bins, target_width) + 1] if len(bins) == 1: selected_bin = bins[0] - scaling_factor = math.ceil(target_width / width) + scaling_factor = math.ceil(target_width / selected_bin) scaled_bin = bins[0] * scaling_factor else: From 123cc371beabe50a21bdf4382ce332198cd20255 Mon Sep 17 00:00:00 2001 From: David Ratchkov Date: Fri, 17 Apr 2020 16:09:58 -0700 Subject: [PATCH 076/101] - Fix disabled power char --- compiler/characterizer/delay.py | 24 ++++++++++++++---- compiler/characterizer/lib.py | 37 +++++++++++++++++++--------- compiler/characterizer/simulation.py | 33 +++++++++++++------------ 3 files changed, 61 insertions(+), 33 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 2823a415..62367d72 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -59,7 +59,8 @@ class delay(simulation): """ Create measurement names. The names themselves currently define the type of measurement """ self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] - self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power", "disabled_read0_power", "disabled_read1_power"] + self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power", + "disabled_read0_power", "disabled_read1_power", "disabled_write0_power", "disabled_write1_power"] # self.voltage_when_names = ["volt_bl", "volt_br"] # self.bitline_delay_names = ["delay_bl", "delay_br"] @@ -161,6 +162,11 @@ class delay(simulation): self.write_lib_meas.append(power_measure("write0_power", "FALL", measure_scale=1e3)) self.write_lib_meas[-1].meta_str = sram_op.WRITE_ZERO + self.write_lib_meas.append(power_measure("disabled_write1_power", "RISE", measure_scale=1e3)) + self.write_lib_meas[-1].meta_str = "disabled_write1" + self.write_lib_meas.append(power_measure("disabled_write0_power", "FALL", measure_scale=1e3)) + self.write_lib_meas[-1].meta_str = "disabled_write0" + write_measures = [] write_measures.append(self.write_lib_meas) write_measures.append(self.create_write_bit_measures()) @@ -1203,6 +1209,9 @@ class delay(simulation): write_port) self.measure_cycles[write_port][sram_op.WRITE_ZERO] = len(self.cycle_times)-1 + self.add_noop_clock_one_port(write_port) + self.measure_cycles[write_port]["disabled_write0"] = len(self.cycle_times)-1 + # This also ensures we will have a H->L transition on the next read self.add_read("R data 1 address {} to set dout caps".format(inverse_address), inverse_address, @@ -1213,8 +1222,9 @@ class delay(simulation): read_port) self.measure_cycles[read_port][sram_op.READ_ZERO] = len(self.cycle_times)-1 - self.add_nop(self.probe_address, data_zeros, read_port) - self.measure_cycles[write_port]["disabled_read0"] = len(self.cycle_times) - 1 + self.add_noop_clock_one_port(read_port) + self.measure_cycles[read_port]["disabled_read0"] = len(self.cycle_times) - 1 + self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)") @@ -1225,14 +1235,18 @@ class delay(simulation): write_port) self.measure_cycles[write_port][sram_op.WRITE_ONE] = len(self.cycle_times)-1 + self.add_noop_clock_one_port(write_port) + self.measure_cycles[write_port]["disabled_write1"] = len(self.cycle_times)-1 + self.add_write("W data 0 address {} to clear din caps".format(inverse_address), inverse_address, data_zeros, wmask_ones, write_port) - self.add_nop(self.probe_address, data_zeros, read_port) - self.measure_cycles[write_port]["disabled_read1"] = len(self.cycle_times) - 1 + self.add_noop_clock_one_port(read_port) + self.measure_cycles[read_port]["disabled_read1"] = len(self.cycle_times) - 1 + # This also ensures we will have a L->H transition on the next read self.add_read("R data 0 address {} to clear dout caps".format(inverse_address), diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 9f3c12d3..57e21763 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -534,6 +534,19 @@ class lib: self.lib.write(" }\n") self.lib.write(" }\n") + # Disabled power. + disabled_read1_power = np.mean(self.char_port_results[port]["disabled_read1_power"]) + disabled_read0_power = np.mean(self.char_port_results[port]["disabled_raed0_power"]) + self.lib.write(" internal_power(){\n") + self.lib.write(" when : \"csb{0}{1}\"; \n".format(port, web_name)) + self.lib.write(" rise_power(scalar){\n") + self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read1_power)) + self.lib.write(" }\n") + self.lib.write(" fall_power(scalar){\n") + self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read0_power)) + self.lib.write(" }\n") + self.lib.write(" }\n") + if port in self.read_ports: if port in self.write_ports: web_name = " & web{0}".format(port) @@ -549,18 +562,18 @@ class lib: self.lib.write(" }\n") self.lib.write(" }\n") - # Disabled power. - disabled_read1_power = np.mean(self.char_port_results[port]["disabled_read1_power"]) - disabled_read0_power = np.mean(self.char_port_results[port]["disabled_read0_power"]) - self.lib.write(" internal_power(){\n") - self.lib.write(" when : \"csb{0}\"; \n".format(port)) - self.lib.write(" rise_power(scalar){\n") - self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read1_power)) - self.lib.write(" }\n") - self.lib.write(" fall_power(scalar){\n") - self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read0_power)) - self.lib.write(" }\n") - self.lib.write(" }\n") + # Disabled power. + disabled_write1_power = np.mean(self.char_port_results[port]["disabled_write1_power"]) + disabled_write0_power = np.mean(self.char_port_results[port]["disabled_write0_power"]) + self.lib.write(" internal_power(){\n") + self.lib.write(" when : \"csb{0}{1}\"; \n".format(port, web_name)) + self.lib.write(" rise_power(scalar){\n") + self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_write1_power)) + self.lib.write(" }\n") + self.lib.write(" fall_power(scalar){\n") + self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_write0_power)) + self.lib.write(" }\n") + self.lib.write(" }\n") def write_pg_pin(self): self.lib.write(" pg_pin(vdd) {\n") diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 0cfc860f..0d5cfb25 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -210,22 +210,6 @@ class simulation(): if unselected_port != port: self.add_noop_one_port(unselected_port) - def add_nop(self, address, din_data, port): - """ Add the control values for a cycle with clock only. Does not increment the period. """ - debug.check(port in self.read_ports or port in self.write_ports, - "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports)) - debug.info(2, 'Clock only on port {}'.format(port)) - self.fn_cycle_comments.append('Clock only on port {}'.format(port)) - self.append_cycle_comment(port, 'Clock only on port {}'.format(port)) - - self.cycle_times.append(self.t_current) - self.t_current += self.period - self.add_control_one_port(port, "noop") - # If the port is also a readwrite then add data. - if port in self.write_ports: - self.add_data(din_data, port) - self.add_address(address, port) - def add_noop_all_ports(self, comment): """ Add the control values for a noop to all ports. """ debug.info(2, comment) @@ -295,6 +279,23 @@ class simulation(): except: self.add_wmask("0"*self.num_wmasks, port) + def add_noop_clock_one_port(self, port): + """ Add the control values for a noop to a single port. Increments the period. """ + debug.info(2, 'Clock only on port {}'.format(port)) + self.fn_cycle_comments.append('Clock only on port {}'.format(port)) + self.append_cycle_comment(port, 'Clock only on port {}'.format(port)) + + self.cycle_times.append(self.t_current) + self.t_current += self.period + + self.add_noop_one_port(port) + + #Add noops to all other ports. + for unselected_port in self.all_ports: + if unselected_port != port: + self.add_noop_one_port(unselected_port) + + def append_cycle_comment(self, port, comment): """Add comment to list to be printed in stimulus file""" #Clean up time before appending. Make spacing dynamic as well. From 5aea45ed690593e4e9b965f84639e12ea29731e9 Mon Sep 17 00:00:00 2001 From: David Ratchkov Date: Fri, 17 Apr 2020 16:23:06 -0700 Subject: [PATCH 077/101] - Fix switched disabled powers --- compiler/characterizer/lib.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 57e21763..276ea767 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -535,15 +535,15 @@ class lib: self.lib.write(" }\n") # Disabled power. - disabled_read1_power = np.mean(self.char_port_results[port]["disabled_read1_power"]) - disabled_read0_power = np.mean(self.char_port_results[port]["disabled_raed0_power"]) + disabled_write1_power = np.mean(self.char_port_results[port]["disabled_write1_power"]) + disabled_write0_power = np.mean(self.char_port_results[port]["disabled_write0_power"]) self.lib.write(" internal_power(){\n") self.lib.write(" when : \"csb{0}{1}\"; \n".format(port, web_name)) self.lib.write(" rise_power(scalar){\n") - self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read1_power)) + self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_write1_power)) self.lib.write(" }\n") self.lib.write(" fall_power(scalar){\n") - self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read0_power)) + self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_write0_power)) self.lib.write(" }\n") self.lib.write(" }\n") @@ -563,18 +563,18 @@ class lib: self.lib.write(" }\n") # Disabled power. - disabled_write1_power = np.mean(self.char_port_results[port]["disabled_write1_power"]) - disabled_write0_power = np.mean(self.char_port_results[port]["disabled_write0_power"]) + disabled_read1_power = np.mean(self.char_port_results[port]["disabled_read1_power"]) + disabled_read0_power = np.mean(self.char_port_results[port]["disabled_read0_power"]) self.lib.write(" internal_power(){\n") self.lib.write(" when : \"csb{0}{1}\"; \n".format(port, web_name)) self.lib.write(" rise_power(scalar){\n") - self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_write1_power)) + self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read1_power)) self.lib.write(" }\n") self.lib.write(" fall_power(scalar){\n") - self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_write0_power)) + self.lib.write(" values(\"{0:.6e}\");\n".format(disabled_read0_power)) self.lib.write(" }\n") self.lib.write(" }\n") - + def write_pg_pin(self): self.lib.write(" pg_pin(vdd) {\n") self.lib.write(" voltage_name : VDD;\n") From 1f094b03bc3f2d962ebd06138cfbade446eac82c Mon Sep 17 00:00:00 2001 From: jcirimel Date: Sat, 18 Apr 2020 05:26:39 -0700 Subject: [PATCH 078/101] use more optimal discrete pinv sizing --- compiler/pgates/pgate.py | 29 +++++++++++---- compiler/pgates/pinv.py | 77 ++++++++++++++++++++++++++++------------ 2 files changed, 78 insertions(+), 28 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index d3084146..a49be6a0 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -15,7 +15,7 @@ from vector import vector from globals import OPTS if(OPTS.tech_name == "s8"): - from tech import nmos_bins, pmos_bins + from tech import nmos_bins, pmos_bins, accuracy_requirement class pgate(design.design): """ @@ -323,11 +323,28 @@ class pgate(design.design): return(selected_bin, scaling_factor) + def permute_widths(self, tx_type, target_width): - - - + if tx_type == "nmos": + bins = nmos_bins[drc("minwidth_poly")] + elif tx_type == "pmos": + bins = pmos_bins[drc("minwidth_poly")] + else: + debug.error("invalid tx type") - - + bins = bins[0:bisect_left(bins, target_width) + 1] + + if len(bins) == 1: + selected_bins = (bins[0], math.ceil(target_width / bins[0])) + else: + scaled_bins = [] + scaled_bins.append((bins[-1], 1)) + for width in bins[:-1]: + m = math.ceil(target_width / width) + scaled_bins.append((m * width, m)) + + return(scaled_bins) + + def bin_accuracy(self, ideal_width, width): + return abs(1-(ideal_width - width)/ideal_width) \ No newline at end of file diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 5e44ecff..07f724e0 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -8,15 +8,19 @@ import contact import pgate import debug +import operator from tech import drc, parameter, spice from vector import vector from math import ceil from globals import OPTS from utils import round_to_grid +from bisect import bisect_left import logical_effort from sram_factory import factory from errors import drc_error +if(OPTS.tech_name == "s8"): + from tech import nmos_bins, pmos_bins, accuracy_requirement class pinv(pgate.pgate): """ @@ -134,30 +138,59 @@ class pinv(pgate.pgate): # Determine the number of mults for each to fit width # into available space - self.nmos_width = self.nmos_size * drc("minwidth_tx") - self.pmos_width = self.pmos_size * drc("minwidth_tx") - nmos_required_mults = max(int(ceil(self.nmos_width / nmos_height_available)), 1) - pmos_required_mults = max(int(ceil(self.pmos_width / pmos_height_available)), 1) - # The mults must be the same for easy connection of poly - self.tx_mults = max(nmos_required_mults, pmos_required_mults) + if OPTS.tech_name != "s8": + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") + nmos_required_mults = max(int(ceil(self.nmos_width / nmos_height_available)), 1) + pmos_required_mults = max(int(ceil(self.pmos_width / pmos_height_available)), 1) + # The mults must be the same for easy connection of poly + self.tx_mults = max(nmos_required_mults, pmos_required_mults) - # Recompute each mult width and check it isn't too small - # This could happen if the height is narrow and the size is small - # User should pick a bigger size to fix it... - # We also need to round the width to the grid or we will end up - # with LVS property mismatch errors when fingers are not a grid - # length and get rounded in the offset geometry. - self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) - # debug.check(self.nmos_width >= drc("minwidth_tx"), - # "Cannot finger NMOS transistors to fit cell height.") - if self.nmos_width < drc("minwidth_tx"): - raise drc_error("Cannot finger NMOS transistors to fit cell height.") + # Recompute each mult width and check it isn't too small + # This could happen if the height is narrow and the size is small + # User should pick a bigger size to fix it... + # We also need to round the width to the grid or we will end up + # with LVS property mismatch errors when fingers are not a grid + # length and get rounded in the offset geometry. + self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) + # debug.check(self.nmos_width >= drc("minwidth_tx"), + # "Cannot finger NMOS transistors to fit cell height.") + if self.nmos_width < drc("minwidth_tx"): + raise drc_error("Cannot finger NMOS transistors to fit cell height.") - self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) - #debug.check(self.pmos_width >= drc("minwidth_tx"), - # "Cannot finger PMOS transistors to fit cell height.") - if self.pmos_width < drc("minwidth_tx"): - raise drc_error("Cannot finger NMOS transistors to fit cell height.") + self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) + #debug.check(self.pmos_width >= drc("minwidth_tx"), + # "Cannot finger PMOS transistors to fit cell height.") + if self.pmos_width < drc("minwidth_tx"): + raise drc_error("Cannot finger NMOS transistors to fit cell height.") + else: + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") + nmos_bins = self.permute_widths("nmos", self.nmos_width) + pmos_bins = self.permute_widths("pmos", self.pmos_width) + + valid_pmos = [] + for bin in pmos_bins: + if self.bin_accuracy(self.pmos_width, bin[0]) > accuracy_requirement: + valid_pmos.append(bin) + valid_pmos.sort(key = operator.itemgetter(1)) + + valid_nmos = [] + for bin in nmos_bins: + if self.bin_accuracy(self.nmos_width, bin[0]) > accuracy_requirement: + valid_nmos.append(bin) + valid_nmos.sort(key = operator.itemgetter(1)) + + for bin in valid_pmos: + if bin[0]/bin[1] < pmos_height_available: + self.pmos_width = valid_nmos[0][0] + self.tx_mults = valid_pmos[0][1] + break + + for bin in valid_nmos: + if bin[0]/bin[1] < nmos_height_available: + self.nmos_width = valid_nmos[0][0] + break def add_ptx(self): """ Create the PMOS and NMOS transistors. """ From 85bc801689a5afbdb5f79d59a6c6cda605df3159 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Sat, 18 Apr 2020 05:34:55 -0700 Subject: [PATCH 079/101] fix pinv drc bug --- compiler/pgates/pinv.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 07f724e0..d3062cba 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -183,14 +183,17 @@ class pinv(pgate.pgate): for bin in valid_pmos: if bin[0]/bin[1] < pmos_height_available: - self.pmos_width = valid_nmos[0][0] - self.tx_mults = valid_pmos[0][1] + self.pmos_width = bin[0]/bin[1] + pmos_mults = valid_pmos[0][1] break for bin in valid_nmos: if bin[0]/bin[1] < nmos_height_available: - self.nmos_width = valid_nmos[0][0] + self.nmos_width = bin[0]/bin[1] + nmos_mults = valid_pmos[0][1] break + + self.tx_mults = max(pmos_mults, nmos_mults) def add_ptx(self): """ Create the PMOS and NMOS transistors. """ From add9ec7b2836677ba46e296d48f6bf4fe2837608 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Sat, 18 Apr 2020 05:42:23 -0700 Subject: [PATCH 080/101] remove excess newlines --- compiler/pgates/pgate.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index a49be6a0..f3fc9abc 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -296,7 +296,6 @@ class pgate(design.design): else: debug.error("invalid tx type") - bins = bins[0:bisect_left(bins, target_width) + 1] if len(bins) == 1: selected_bin = bins[0] @@ -330,11 +329,8 @@ class pgate(design.design): elif tx_type == "pmos": bins = pmos_bins[drc("minwidth_poly")] else: - debug.error("invalid tx type") - - + debug.error("invalid tx type") bins = bins[0:bisect_left(bins, target_width) + 1] - if len(bins) == 1: selected_bins = (bins[0], math.ceil(target_width / bins[0])) else: From f590ecf83c2f1f318982a3152771e51cc9edef71 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Sat, 18 Apr 2020 05:51:21 -0700 Subject: [PATCH 081/101] fix minimum pinv sizing --- compiler/pgates/pgate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index f3fc9abc..a942b35f 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -332,7 +332,7 @@ class pgate(design.design): debug.error("invalid tx type") bins = bins[0:bisect_left(bins, target_width) + 1] if len(bins) == 1: - selected_bins = (bins[0], math.ceil(target_width / bins[0])) + scaled_bins = [(bins[0], math.ceil(target_width / bins[0]))] else: scaled_bins = [] scaled_bins.append((bins[-1], 1)) From 7f65176908ec460b3229c897a3cfd93bb7100e64 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 20 Apr 2020 14:23:40 -0700 Subject: [PATCH 082/101] Configured bitline directions into prot_data --- compiler/base/hierarchy_layout.py | 36 +++++++++++++++++++++---------- compiler/modules/bitcell_array.py | 24 ++++++++++----------- compiler/modules/port_data.py | 7 +++++- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index e67f8d85..3cf3e727 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -974,6 +974,7 @@ class layout(): def create_channel_route(self, netlist, offset, layer_stack, + layer_dirs=None, vertical=False): """ The net list is a list of the nets. Each net is a list of pins @@ -1012,25 +1013,38 @@ class layout(): def vcg_pin_overlap(pin1, pin2, vertical, pitch): """ Check for vertical or horizontal overlap of the two pins """ + # FIXME: If the pins are not in a row, this may break. # However, a top pin shouldn't overlap another top pin, # for example, so the # extra comparison *shouldn't* matter. # Pin 1 must be in the "BOTTOM" set - x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x) Date: Mon, 20 Apr 2020 14:26:44 -0700 Subject: [PATCH 083/101] Split port data test into single and multi-port. --- compiler/tests/18_port_data_1rw_1r_test.py | 78 ++++++++++++++++++++++ compiler/tests/18_port_data_test.py | 46 ------------- 2 files changed, 78 insertions(+), 46 deletions(-) create mode 100755 compiler/tests/18_port_data_1rw_1r_test.py diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py new file mode 100755 index 00000000..3e1dbded --- /dev/null +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class port_data_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + + OPTS.bitcell = "bitcell_1w_1r" + OPTS.num_rw_ports = 0 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + c = sram_config(word_size=4, + num_words=16) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_test.py b/compiler/tests/18_port_data_test.py index 71681056..57e19846 100755 --- a/compiler/tests/18_port_data_test.py +++ b/compiler/tests/18_port_data_test.py @@ -55,52 +55,6 @@ class port_data_test(openram_test): a = factory.create("port_data", sram_config=c, port=0) self.local_check(a) - OPTS.bitcell = "bitcell_1w_1r" - OPTS.num_rw_ports = 0 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 - - c.num_words=16 - c.words_per_row=1 - factory.reset() - c.recompute_sizes() - debug.info(1, "No column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - a = factory.create("port_data", sram_config=c, port=1) - self.local_check(a) - - c.num_words=32 - c.words_per_row=2 - factory.reset() - c.recompute_sizes() - debug.info(1, "Two way column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - a = factory.create("port_data", sram_config=c, port=1) - self.local_check(a) - - c.num_words=64 - c.words_per_row=4 - factory.reset() - c.recompute_sizes() - debug.info(1, "Four way column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - a = factory.create("port_data", sram_config=c, port=1) - self.local_check(a) - - c.word_size=2 - c.num_words=128 - c.words_per_row=8 - factory.reset() - c.recompute_sizes() - debug.info(1, "Eight way column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - a = factory.create("port_data", sram_config=c, port=1) - self.local_check(a) - globals.end_openram() # run the test from the command line From 7995451cbb669cd346fca33cb8f4a9889e5434ce Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 20 Apr 2020 14:45:18 -0700 Subject: [PATCH 084/101] PEP8 formatting --- .../modules/single_level_column_mux_array.py | 63 ++++++++----------- 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 324b7415..d1c08e18 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -5,17 +5,14 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from math import log import design -import contact -from tech import drc import debug -import math from vector import vector from sram_factory import factory from globals import OPTS import logical_effort + class single_level_column_mux_array(design.design): """ Dynamically generated column mux array. @@ -56,7 +53,7 @@ class single_level_column_mux_array(design.design): self.add_routing() # Find the highest shapes to determine height before adding well highest = self.find_highest_coords() - self.height = highest.y + self.height = highest.y self.add_layout_pins() self.add_enclosure(self.mux_inst, "pwell") @@ -74,22 +71,18 @@ class single_level_column_mux_array(design.design): self.add_pin("br_out_{}".format(i)) self.add_pin("gnd") - def add_modules(self): self.mux = factory.create(module_type="single_level_column_mux", bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) self.add_mod(self.mux) - def setup_layout_constants(self): - self.column_addr_size = num_of_inputs = int(self.words_per_row / 2) + self.column_addr_size = int(self.words_per_row / 2) self.width = self.columns * self.mux.width # one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br # one extra route pitch is to space from the sense amp - self.route_height = (self.words_per_row + 3)*self.m1_pitch - - + self.route_height = (self.words_per_row + 3) * self.m1_pitch def create_array(self): self.mux_inst = [] @@ -101,8 +94,8 @@ class single_level_column_mux_array(design.design): self.connect_inst(["bl_{}".format(col_num), "br_{}".format(col_num), - "bl_out_{}".format(int(col_num/self.words_per_row)), - "br_out_{}".format(int(col_num/self.words_per_row)), + "bl_out_{}".format(int(col_num / self.words_per_row)), + "br_out_{}".format(int(col_num / self.words_per_row)), "sel_{}".format(col_num % self.words_per_row), "gnd"]) @@ -117,11 +110,9 @@ class single_level_column_mux_array(design.design): else: mirror = "" - name = "XMUX{0}".format(col_num) offset = vector(xoffset, self.route_height) self.mux_inst[col_num].place(offset=offset, mirror=mirror) - def add_layout_pins(self): """ Add the pins after we determine the height. """ # For every column, add a pass gate @@ -131,18 +122,17 @@ class single_level_column_mux_array(design.design): self.add_layout_pin(text="bl_{}".format(col_num), layer="m2", offset=offset, - height=self.height-offset.y) + height=self.height - offset.y) offset = mux_inst.get_pin("br").ll() self.add_layout_pin(text="br_{}".format(col_num), layer="m2", offset=offset, - height=self.height-offset.y) + height=self.height - offset.y) for inst in self.mux_inst: self.copy_layout_pin(inst, "gnd") - def add_routing(self): self.add_horizontal_input_rail() self.add_vertical_poly_rail() @@ -151,7 +141,7 @@ class single_level_column_mux_array(design.design): def add_horizontal_input_rail(self): """ Create address input rails on M1 below the mux transistors """ for j in range(self.words_per_row): - offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch) + offset = vector(0, self.route_height + (j - self.words_per_row) * self.m1_pitch) self.add_layout_pin(text="sel_{}".format(j), layer="m1", offset=offset, @@ -167,9 +157,10 @@ class single_level_column_mux_array(design.design): # Add the column x offset to find the right select bit gate_offset = self.mux_inst[col].get_pin("sel").bc() # height to connect the gate to the correct horizontal row - sel_height = self.get_pin("sel_{}".format(sel_index)).by() + # sel_height = self.get_pin("sel_{}".format(sel_index)).by() # use the y offset from the sel pin and the x offset from the gate - offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy()) + offset = vector(gate_offset.x, + self.get_pin("sel_{}".format(sel_index)).cy()) # Add the poly contact with a shift to account for the rotation self.add_via_center(layers=("m1", "contact", "poly"), offset=offset) @@ -182,11 +173,11 @@ class single_level_column_mux_array(design.design): bl_offset = self.mux_inst[j].get_pin("bl_out").bc() br_offset = self.mux_inst[j].get_pin("br_out").bc() - bl_out_offset = bl_offset - vector(0,(self.words_per_row+1)*self.m1_pitch) - br_out_offset = br_offset - vector(0,(self.words_per_row+2)*self.m1_pitch) + bl_out_offset = bl_offset - vector(0, (self.words_per_row + 1) * self.m1_pitch) + br_out_offset = br_offset - vector(0, (self.words_per_row + 2) * self.m1_pitch) - bl_out_offset_end = bl_out_offset + vector(0,self.route_height) - br_out_offset_end = br_out_offset + vector(0,self.route_height) + bl_out_offset_end = bl_out_offset + vector(0, self.route_height) + br_out_offset_end = br_out_offset + vector(0, self.route_height) if cell_properties.bitcell.mirror.y and j % 2: tmp_bl_out_end = br_out_offset_end @@ -208,21 +199,20 @@ class single_level_column_mux_array(design.design): else: dist = 0 - self.add_path("m1", [bl_out_offset, bl_out_offset+vector(width+dist,0)]) - self.add_path("m1", [br_out_offset, br_out_offset+vector(width-dist,0)]) + self.add_path("m1", [bl_out_offset, bl_out_offset + vector(width + dist, 0)]) + self.add_path("m1", [br_out_offset, br_out_offset + vector(width - dist, 0)]) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux - self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)), + self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)), layer="m2", start=bl_out_offset, end=tmp_bl_out_end) - self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)), + self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)), layer="m2", start=br_out_offset, end=tmp_br_out_end) - - # This via is on the right of the wire + # This via is on the right of the wire self.add_via_center(layers=self.m1_stack, offset=bl_out_offset) @@ -231,20 +221,19 @@ class single_level_column_mux_array(design.design): offset=br_out_offset) else: - - self.add_path("m2", [ bl_out_offset, tmp_bl_out_end]) - self.add_path("m2", [ br_out_offset, tmp_br_out_end]) + self.add_path("m2", [bl_out_offset, tmp_bl_out_end]) + self.add_path("m2", [br_out_offset, tmp_br_out_end]) # This via is on the right of the wire self.add_via_center(layers=self.m1_stack, offset=bl_out_offset) - # This via is on the left of the wire + # This via is on the left of the wire self.add_via_center(layers=self.m1_stack, offset=br_out_offset) def get_drain_cin(self): """Get the relative capacitance of the drain of the NMOS pass TX""" from tech import parameter - #Bitcell drain load being used to estimate mux NMOS drain load + # Bitcell drain load being used to estimate mux NMOS drain load drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - return drain_load + return drain_load From 8c177f9947e6c1f98977cad5b8a6ab87ddfa0ea7 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 20 Apr 2020 15:03:32 -0700 Subject: [PATCH 085/101] Split col mux test --- ...4_single_level_column_mux_pbitcell_test.py | 49 +++++++++++++++++++ .../tests/04_single_level_column_mux_test.py | 16 ------ compiler/tests/18_port_data_1rw_1r_test.py | 2 +- 3 files changed, 50 insertions(+), 17 deletions(-) create mode 100755 compiler/tests/04_single_level_column_mux_pbitcell_test.py diff --git a/compiler/tests/04_single_level_column_mux_pbitcell_test.py b/compiler/tests/04_single_level_column_mux_pbitcell_test.py new file mode 100755 index 00000000..6fe55736 --- /dev/null +++ b/compiler/tests/04_single_level_column_mux_pbitcell_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +#@unittest.skip("SKIPPING 04_driver_test") + +class single_level_column_mux_pbitcell_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check single level column mux in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + factory.reset() + debug.info(2, "Checking column mux for pbitcell (innermost connections)") + tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + factory.reset() + debug.info(2, "Checking column mux for pbitcell (outermost connections)") + tx = factory.create(module_type="single_level_column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2") + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_single_level_column_mux_test.py b/compiler/tests/04_single_level_column_mux_test.py index de5870fc..9f1d03f9 100755 --- a/compiler/tests/04_single_level_column_mux_test.py +++ b/compiler/tests/04_single_level_column_mux_test.py @@ -28,22 +28,6 @@ class single_level_column_mux_test(openram_test): tx = factory.create(module_type="single_level_column_mux", tx_size=8) self.local_check(tx) - # check single level column mux in multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 - - factory.reset() - debug.info(2, "Checking column mux for pbitcell (innermost connections)") - tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(tx) - - factory.reset() - debug.info(2, "Checking column mux for pbitcell (outermost connections)") - tx = factory.create(module_type="single_level_column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2") - self.local_check(tx) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index 3e1dbded..6201de6a 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -13,7 +13,7 @@ from globals import OPTS from sram_factory import factory import debug -class port_data_test(openram_test): +class port_data_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) From dfbf6fe45c4115ec79b96a9880433c51ac1ca770 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 20 Apr 2020 15:33:53 -0700 Subject: [PATCH 086/101] Default is to use preferred layer directions --- compiler/base/hierarchy_layout.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 3cf3e727..b970666d 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1127,13 +1127,13 @@ class layout(): self.horizontal_pitch) offset += vector(0, self.horizontal_pitch) - def create_vertical_channel_route(self, netlist, offset, layer_stack, layer_dirs): + def create_vertical_channel_route(self, netlist, offset, layer_stack, layer_dirs=None): """ Wrapper to create a vertical channel route """ self.create_channel_route(netlist, offset, layer_stack, layer_dirs, vertical=True) - def create_horizontal_channel_route(self, netlist, offset, layer_stack, layer_dirs): + def create_horizontal_channel_route(self, netlist, offset, layer_stack, layer_dirs=None): """ Wrapper to create a horizontal channel route """ From f6135f34711e755ab80c6a3891506a3fc8e380f0 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 20 Apr 2020 16:38:30 -0700 Subject: [PATCH 087/101] PEP8 formatting --- compiler/modules/dff_buf_array.py | 96 +++++++++++++++---------------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 1735a7a1..f95bf576 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -7,13 +7,12 @@ # import debug import design -from tech import drc from tech import cell_properties as props -from math import log from vector import vector from globals import OPTS from sram_factory import factory + class dff_buf_array(design.design): """ This is a simple row (or multiple rows) of flops. @@ -54,13 +53,13 @@ class dff_buf_array(design.design): self.DRC_LVS() def add_pins(self): - for row in range(self.rows): + for row in range(self.rows): for col in range(self.columns): - self.add_pin(self.get_din_name(row,col), "INPUT") - for row in range(self.rows): + self.add_pin(self.get_din_name(row, col), "INPUT") + for row in range(self.rows): for col in range(self.columns): - self.add_pin(self.get_dout_name(row,col), "OUTPUT") - self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT") + self.add_pin(self.get_dout_name(row, col), "OUTPUT") + self.add_pin(self.get_dout_bar_name(row, col), "OUTPUT") self.add_pin("clk", "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -75,17 +74,16 @@ class dff_buf_array(design.design): inv2_size=self.inv2_size) self.add_mod(self.dff) - def create_dff_array(self): self.dff_insts={} - for row in range(self.rows): + for row in range(self.rows): for col in range(self.columns): - name = "dff_r{0}_c{1}".format(row,col) - self.dff_insts[row,col]=self.add_inst(name=name, - mod=self.dff) - inst_ports = [self.get_din_name(row,col), - self.get_dout_name(row,col), - self.get_dout_bar_name(row,col), + name = "dff_r{0}_c{1}".format(row, col) + self.dff_insts[row, col]=self.add_inst(name=name, + mod=self.dff) + inst_ports = [self.get_din_name(row, col), + self.get_dout_name(row, col), + self.get_dout_bar_name(row, col), "clk", "vdd", "gnd"] @@ -102,17 +100,17 @@ class dff_buf_array(design.design): dff_pitch = self.dff.width + well_spacing + self.well_extend_active - for row in range(self.rows): + for row in range(self.rows): for col in range(self.columns): - name = "Xdff_r{0}_c{1}".format(row,col) + name = "Xdff_r{0}_c{1}".format(row, col) if (row % 2 == 0): - base = vector(col*dff_pitch,row*self.dff.height) + base = vector(col * dff_pitch, row * self.dff.height) mirror = "R0" else: - base = vector(col*dff_pitch,(row+1)*self.dff.height) + base = vector(col * dff_pitch, (row + 1) * self.dff.height) mirror = "MX" - self.dff_insts[row,col].place(offset=base, - mirror=mirror) + self.dff_insts[row, col].place(offset=base, + mirror=mirror) def get_din_name(self, row, col): if self.columns == 1: @@ -120,7 +118,7 @@ class dff_buf_array(design.design): elif self.rows == 1: din_name = "din_{0}".format(col) else: - din_name = "din_{0}_{1}".format(row,col) + din_name = "din_{0}_{1}".format(row, col) return din_name @@ -130,7 +128,7 @@ class dff_buf_array(design.design): elif self.rows == 1: dout_name = "dout_{0}".format(col) else: - dout_name = "dout_{0}_{1}".format(row,col) + dout_name = "dout_{0}_{1}".format(row, col) return dout_name @@ -140,75 +138,73 @@ class dff_buf_array(design.design): elif self.rows == 1: dout_bar_name = "dout_bar_{0}".format(col) else: - dout_bar_name = "dout_bar_{0}_{1}".format(row,col) + dout_bar_name = "dout_bar_{0}_{1}".format(row, col) return dout_bar_name def add_layout_pins(self): for row in range(self.rows): - for col in range(self.columns): + for col in range(self.columns): # Continous vdd rail along with label. - vdd_pin=self.dff_insts[row,col].get_pin("vdd") + vdd_pin=self.dff_insts[row, col].get_pin("vdd") self.add_power_pin("vdd", vdd_pin.lc()) # Continous gnd rail along with label. - gnd_pin=self.dff_insts[row,col].get_pin("gnd") + gnd_pin=self.dff_insts[row, col].get_pin("gnd") self.add_power_pin("gnd", gnd_pin.lc()) - - for row in range(self.rows): - for col in range(self.columns): - din_pin = self.dff_insts[row,col].get_pin("D") - debug.check(din_pin.layer=="m2","DFF D pin not on metal2") - self.add_layout_pin(text=self.get_din_name(row,col), + for row in range(self.rows): + for col in range(self.columns): + din_pin = self.dff_insts[row, col].get_pin("D") + debug.check(din_pin.layer=="m2", "DFF D pin not on metal2") + self.add_layout_pin(text=self.get_din_name(row, col), layer=din_pin.layer, offset=din_pin.ll(), width=din_pin.width(), height=din_pin.height()) - dout_pin = self.dff_insts[row,col].get_pin("Q") - debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2") - self.add_layout_pin(text=self.get_dout_name(row,col), + dout_pin = self.dff_insts[row, col].get_pin("Q") + debug.check(dout_pin.layer=="m2", "DFF Q pin not on metal2") + self.add_layout_pin(text=self.get_dout_name(row, col), layer=dout_pin.layer, offset=dout_pin.ll(), width=dout_pin.width(), height=dout_pin.height()) - dout_bar_pin = self.dff_insts[row,col].get_pin("Qb") - debug.check(dout_bar_pin.layer=="m2","DFF Qb pin not on metal2") - self.add_layout_pin(text=self.get_dout_bar_name(row,col), + dout_bar_pin = self.dff_insts[row, col].get_pin("Qb") + debug.check(dout_bar_pin.layer=="m2", "DFF Qb pin not on metal2") + self.add_layout_pin(text=self.get_dout_bar_name(row, col), layer=dout_bar_pin.layer, offset=dout_bar_pin.ll(), width=dout_bar_pin.width(), height=dout_bar_pin.height()) - # Create vertical spines to a single horizontal rail - clk_pin = self.dff_insts[0,0].get_pin("clk") - clk_ypos = 2*self.m3_pitch+self.m3_width - debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2") + clk_pin = self.dff_insts[0, 0].get_pin("clk") + clk_ypos = 2 * self.m3_pitch + self.m3_width + debug.check(clk_pin.layer=="m2", "DFF clk pin not on metal2") if self.columns==1: self.add_layout_pin(text="clk", layer="m2", - offset=clk_pin.ll().scale(1,0), + offset=clk_pin.ll().scale(1, 0), width=self.m2_width, height=self.height) else: self.add_layout_pin_segment_center(text="clk", - layer="m3", - start=vector(0,clk_ypos), - end=vector(self.width,clk_ypos)) + layer="m3", + start=vector(0, clk_ypos), + end=vector(self.width, clk_ypos)) for col in range(self.columns): - clk_pin = self.dff_insts[0,col].get_pin("clk") + clk_pin = self.dff_insts[0, col].get_pin("clk") # Make a vertical strip for each column self.add_rect(layer="m2", - offset=clk_pin.ll().scale(1,0), + offset=clk_pin.ll().scale(1, 0), width=self.m2_width, height=self.height) # Drop a via to the M3 pin self.add_via_center(layers=self.m2_stack, - offset=vector(clk_pin.cx(),clk_ypos)) + offset=vector(clk_pin.cx(), clk_ypos)) def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff array""" From 63bea67fb5700f5a7110ceab4faae542e07d00e5 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Mon, 20 Apr 2020 20:22:46 -0700 Subject: [PATCH 088/101] col_mux.py changes --- compiler/pgates/single_level_column_mux.py | 86 ++++++++++++---------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 90645326..9d21211e 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -7,7 +7,7 @@ # import pgate import debug -from tech import drc +from tech import drc, layer from vector import vector from sram_factory import factory import logical_effort @@ -22,13 +22,13 @@ class single_level_column_mux(pgate.pgate): for optimal speed """ def __init__(self, name, tx_size=8, bitcell_bl="bl", bitcell_br="br"): - + debug.info(2, "creating single column mux cell: {0}".format(name)) self.tx_size = int(tx_size) self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br - + pgate.pgate.__init__(self, name) def get_bl_names(self): @@ -41,9 +41,9 @@ class single_level_column_mux(pgate.pgate): self.add_modules() self.add_pins() self.add_ptx() - + def create_layout(self): - + self.pin_height = 2 * self.m2_width self.width = self.bitcell.width self.height = self.nmos_upper.uy() + self.pin_height @@ -59,39 +59,42 @@ class single_level_column_mux(pgate.pgate): self.ptx_width = self.tx_size * drc("minwidth_tx") self.nmos = factory.create(module_type="ptx", width=self.ptx_width) self.add_mod(self.nmos) - + def add_pins(self): self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"]) def add_bitline_pins(self): """ Add the top and bottom pins to this cell """ - bl_pos = vector(self.bitcell.get_pin(self.bitcell_bl).lx(), 0) - br_pos = vector(self.bitcell.get_pin(self.bitcell_br).lx(), 0) + bl_pin=self.bitcell.get_pin(self.bitcell_bl) + br_pin=self.bitcell.get_pin(self.bitcell_br) + + bl_pos = vector(bl_pin.lx(), 0) + br_pos = vector(br_pin.lx(), 0) # bl and br self.add_layout_pin(text="bl", - layer="m2", + layer=bl_pin.layer, offset=bl_pos + vector(0, self.height - self.pin_height), height=self.pin_height) self.add_layout_pin(text="br", - layer="m2", + layer=br_pin.layer, offset=br_pos + vector(0, self.height - self.pin_height), height=self.pin_height) - + # bl_out and br_out self.add_layout_pin(text="bl_out", - layer="m2", + layer=bl_pin.layer, offset=bl_pos, height=self.pin_height) self.add_layout_pin(text="br_out", - layer="m2", + layer=br_pin.layer, offset=br_pos, height=self.pin_height) def add_ptx(self): """ Create the two pass gate NMOS transistors to switch the bitlines""" - + # Space it in the center nmos_lower_position = self.nmos.active_offset.scale(0,1) \ + vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0) @@ -102,7 +105,7 @@ class single_level_column_mux(pgate.pgate): # This aligns it directly above the other tx with gates abutting nmos_upper_position = nmos_lower_position \ - + vector(0, self.nmos.active_height + self.poly_space) + + vector(0, self.nmos.active_height + self.active_space) self.nmos_upper = self.add_inst(name="mux_tx2", mod=self.nmos, offset=nmos_upper_position) @@ -110,12 +113,11 @@ class single_level_column_mux(pgate.pgate): def connect_poly(self): """ Connect the poly gate of the two pass transistors """ - - height = self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by() + self.add_layout_pin(text="sel", layer="poly", - offset=self.nmos_lower.get_pin("G").ll(), - height=height) + offset=self.nmos_lower.get_pin("G").ul() - vector(0,self.poly_extend_active), + height=self.active_space) def connect_bitlines(self): """ Connect the bitlines to the mux transistors """ @@ -131,36 +133,42 @@ class single_level_column_mux(pgate.pgate): nmos_upper_s_pin = self.nmos_upper.get_pin("S") nmos_upper_d_pin = self.nmos_upper.get_pin("D") + # If li exists, use li and m1 for the mux, otherwise use m1 and m2 + if "li" in layer: + col_mux_stack = self.li_stack + else: + col_mux_stack = self.m1_stack + # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D - self.add_via_center(layers=self.m1_stack, + self.add_via_center(layers=col_mux_stack, offset=bl_pin.bc(), directions=("V", "V")) - self.add_via_center(layers=self.m1_stack, + self.add_via_center(layers=col_mux_stack, offset=br_out_pin.uc(), directions=("V", "V")) - self.add_via_center(layers=self.m1_stack, + self.add_via_center(layers=col_mux_stack, offset=nmos_upper_s_pin.center(), directions=("V", "V")) - self.add_via_center(layers=self.m1_stack, + self.add_via_center(layers=col_mux_stack, offset=nmos_lower_d_pin.center(), directions=("V", "V")) - + # bl -> nmos_upper/D on metal1 # bl_out -> nmos_upper/S on metal2 - self.add_path("m1", + self.add_path(col_mux_stack[0], [bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()), nmos_upper_d_pin.center()]) # halfway up, move over mid1 = bl_out_pin.uc().scale(1, 0.4) \ + nmos_upper_s_pin.bc().scale(0, 0.4) mid2 = bl_out_pin.uc().scale(0, 0.4) \ - + nmos_upper_s_pin.bc().scale(1, 0.4) - self.add_path("m2", - [bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()]) - + + nmos_upper_s_pin.bc().scale(1, 0.4) + self.add_path(col_mux_stack[2], + [bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.center()]) + # br -> nmos_lower/D on metal2 # br_out -> nmos_lower/S on metal1 - self.add_path("m1", + self.add_path(col_mux_stack[0], [br_out_pin.uc(), vector(nmos_lower_s_pin.cx(), br_out_pin.uy()), nmos_lower_s_pin.center()]) @@ -169,9 +177,9 @@ class single_level_column_mux(pgate.pgate): + nmos_lower_d_pin.uc().scale(0,0.5) mid2 = br_pin.bc().scale(0,0.5) \ + nmos_lower_d_pin.uc().scale(1,0.5) - self.add_path("m2", - [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()]) - + self.add_path(col_mux_stack[2], + [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()]) + def add_wells(self): """ Add a well and implant over the whole cell. Also, add the @@ -192,11 +200,12 @@ class single_level_column_mux(pgate.pgate): start_layer="m1") # Add well enclosure over all the tx and contact - self.add_rect(layer="pwell", - offset=vector(0, 0), - width=self.bitcell.width, - height=self.height) - + if "pwell" in layer: + self.add_rect(layer="pwell", + offset=vector(0, 0), + width=self.bitcell.width, + height=self.height) + def get_stage_effort(self, corner, slew, load): """ Returns relative delay that the column mux. @@ -211,4 +220,3 @@ class single_level_column_mux(pgate.pgate): load, parasitic_delay, False) - From 829f3e03fa08da37cfdfb22d01018237bbe753f5 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Mon, 20 Apr 2020 22:08:29 -0700 Subject: [PATCH 089/101] col_mux.py update with correct contacts --- compiler/pgates/single_level_column_mux.py | 29 +++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 9d21211e..e84469db 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -57,7 +57,10 @@ class single_level_column_mux(pgate.pgate): # Adds nmos_lower,nmos_upper to the module self.ptx_width = self.tx_size * drc("minwidth_tx") - self.nmos = factory.create(module_type="ptx", width=self.ptx_width) + self.nmos = factory.create(module_type="ptx", + width=self.ptx_width, + add_source_contact=False, + add_drain_contact=False) self.add_mod(self.nmos) def add_pins(self): @@ -153,6 +156,30 @@ class single_level_column_mux(pgate.pgate): offset=nmos_lower_d_pin.center(), directions=("V", "V")) + # Add diffusion contacts + # These were previously omitted with the options: add_source_contact=False, add_drain_contact=False + # They are added now and not previously due to a s8 tech special case in which the contacts intersected the mux intraconnect + self.add_via_center(layers=self.active_stack, + offset=nmos_upper_d_pin.center(), + directions=("V", "V"), + implant_type="n", + well_type="nwell") + self.add_via_center(layers=self.active_stack, + offset=nmos_lower_s_pin.center(), + directions=("V", "V"), + implant_type="n", + well_type="nwell") + self.add_via_center(layers=self.active_stack, + offset=nmos_upper_s_pin.center(), + directions=("V", "V"), + implant_type="n", + well_type="nwell") + self.add_via_center(layers=self.active_stack, + offset=nmos_lower_d_pin.center(), + directions=("V", "V"), + implant_type="n", + well_type="nwell") + # bl -> nmos_upper/D on metal1 # bl_out -> nmos_upper/S on metal2 self.add_path(col_mux_stack[0], From ab91d0ab1d86afb4d9f73b22c613cbe6cc7c0853 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 21 Apr 2020 15:20:30 -0700 Subject: [PATCH 090/101] Add purpose to string output --- compiler/base/geometry.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 5d7f8d76..cfcfa73a 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -296,11 +296,11 @@ class path(geometry): def __str__(self): """ override print function output """ - return "path: layer=" + self.layerNumber + " w=" + self.width + return "path: layer=" + self.layerNumber + " purpose=" + str(self.layerPurpose) + " w=" + self.width def __repr__(self): """ override print function output """ - return "( path: layer=" + self.layerNumber + " w=" + self.width + " coords=" + str(self.coordinates) + " )" + return "( path: layer=" + self.layerNumber + " purpose=" + str(self.layerPurpose) + " w=" + self.width + " coords=" + str(self.coordinates) + " )" class label(geometry): @@ -340,11 +340,11 @@ class label(geometry): def __str__(self): """ override print function output """ - return "label: " + self.text + " layer=" + str(self.layerNumber) + return "label: " + self.text + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose) def __repr__(self): """ override print function output """ - return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " )" + return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose) + " )" class rectangle(geometry): @@ -391,4 +391,4 @@ class rectangle(geometry): def __repr__(self): """ override print function output """ - return "( rect: @" + str(self.offset) + " WxH=" + str(self.width) + "x" + str(self.height) + " layer=" + str(self.layerNumber) + " )" + return "( rect: @" + str(self.offset) + " WxH=" + str(self.width) + "x" + str(self.height) + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose) + " )" From 5f76514cf09844936e3929106cc089c3317e1f9c Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 21 Apr 2020 15:20:51 -0700 Subject: [PATCH 091/101] Remove end of line whitespace --- technology/scn4m_subm/tech/tech.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 7c360d38..d8384d7f 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -89,7 +89,7 @@ power_grid = m3_stack ################################################### # create the GDS layer map -layer={} +layer={} layer["pwell"] = (41, 0) layer["nwell"] = (42, 0) layer["active"] = (43, 0) @@ -98,9 +98,9 @@ layer["nimplant"] = (45, 0) layer["poly"] = (46, 0) layer["poly_contact"] = (47, 0) layer["active_contact"] = (48, 0) -layer["m1"] = (49, 0) +layer["m1"] = (49, 0) layer["via1"] = (50, 0) -layer["m2"] = (51, 0) +layer["m2"] = (51, 0) layer["via2"] = (61, 0) layer["m3"] = (62, 0) layer["via3"] = (30, 0) From cd66ddb37c9af43e56a679aadb3ff0b1805d92d1 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 21 Apr 2020 15:21:29 -0700 Subject: [PATCH 092/101] Add supply rails to dff array. PEP8 cleanup. --- compiler/modules/dff_buf.py | 33 +++++++++++++++--------------- compiler/modules/dff_buf_array.py | 34 +++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index c30f15b6..500767d8 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -35,7 +35,7 @@ class dff_buf(design.design): # This causes a DRC in the pinv which assumes min width rails. This ensures the output # contact does not violate spacing to the rail in the NMOS. debug.check(inv1_size>=2, "Inverter must be greater than two for rail spacing DRC rules.") - debug.check(inv2_size>=2, "Inverter must be greater than two for rail spacing DRC rules.") + debug.check(inv2_size>=2, "Inverter must be greater than two for rail spacing DRC rules.") self.inv1_size=inv1_size self.inv2_size=inv2_size @@ -52,14 +52,13 @@ class dff_buf(design.design): def create_layout(self): self.place_instances() self.width = self.inv2_inst.rx() + self.height = self.dff.height - - self.route_wires() self.add_layout_pins() self.add_boundary() self.DRC_LVS() - + def add_modules(self): self.dff = factory.create(module_type="dff") self.add_mod(self.dff) @@ -73,8 +72,6 @@ class dff_buf(design.design): size=self.inv2_size, height=self.dff.height) self.add_mod(self.inv2) - - def add_pins(self): self.add_pin("D", "INPUT") @@ -96,17 +93,19 @@ class dff_buf(design.design): self.inv1_inst=self.add_inst(name="dff_buf_inv1", mod=self.inv1) - self.connect_inst(["qint", "Qb", "vdd", "gnd"]) + self.connect_inst(["qint", "Qb", "vdd", "gnd"]) self.inv2_inst=self.add_inst(name="dff_buf_inv2", mod=self.inv2) - self.connect_inst(["Qb", "Q", "vdd", "gnd"]) + self.connect_inst(["Qb", "Q", "vdd", "gnd"]) def place_instances(self): # Add the DFF - self.dff_inst.place(vector(0,0)) + self.dff_inst.place(vector(0, 0)) # Add INV1 to the right + # The INV needs well spacing because the DFF is likely from a library + # with different well construction rules well_spacing = 0 try: well_spacing = max(well_spacing, self.nwell_space) @@ -129,7 +128,7 @@ class dff_buf(design.design): # Route dff q to inv1 a q_pin = self.dff_inst.get_pin("Q") a1_pin = self.inv1_inst.get_pin("A") - mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx()) + mid_x_offset = 0.5 * (a1_pin.cx() + q_pin.cx()) mid1 = vector(mid_x_offset, q_pin.cy()) mid2 = vector(mid_x_offset, a1_pin.cy()) self.add_path("m3", [q_pin.center(), mid1, mid2, a1_pin.center()]) @@ -143,7 +142,7 @@ class dff_buf(design.design): # Route inv1 z to inv2 a z1_pin = self.inv1_inst.get_pin("Z") a2_pin = self.inv2_inst.get_pin("A") - mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx()) + mid_x_offset = 0.5 * (z1_pin.cx() + a2_pin.cx()) self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy()) mid2 = vector(mid_x_offset, a2_pin.cy()) self.add_path("m1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()]) @@ -181,8 +180,8 @@ class dff_buf(design.design): height=din_pin.height()) dout_pin = self.inv2_inst.get_pin("Z") - mid_pos = dout_pin.center() + vector(self.m1_pitch,0) - q_pos = mid_pos - vector(0,self.m2_pitch) + mid_pos = dout_pin.center() + vector(self.m1_pitch, 0) + q_pos = mid_pos - vector(0, self.m2_pitch) self.add_layout_pin_rect_center(text="Q", layer="m2", offset=q_pos) @@ -190,7 +189,7 @@ class dff_buf(design.design): self.add_via_center(layers=self.m1_stack, offset=q_pos) - qb_pos = self.mid_qb_pos + vector(0,self.m2_pitch) + qb_pos = self.mid_qb_pos + vector(0, self.m2_pitch) self.add_layout_pin_rect_center(text="Qb", layer="m2", offset=qb_pos) @@ -200,7 +199,7 @@ class dff_buf(design.design): def get_clk_cin(self): """Return the total capacitance (in relative units) that the clock is loaded by in the dff""" - #This is a handmade cell so the value must be entered in the tech.py file or estimated. - #Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width. - #FIXME: Dff changed in a past commit. The parameter need to be updated. + # This is a handmade cell so the value must be entered in the tech.py file or estimated. + # Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width. + # FIXME: Dff changed in a past commit. The parameter need to be updated. return parameter["dff_clk_cin"] diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index f95bf576..b608cdeb 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -48,6 +48,7 @@ class dff_buf_array(design.design): self.width = self.columns * self.dff.width self.height = self.rows * self.dff.height self.place_dff_array() + self.route_supplies() self.add_layout_pins() self.add_boundary() self.DRC_LVS() @@ -94,15 +95,25 @@ class dff_buf_array(design.design): def place_dff_array(self): - well_spacing = max(self.nwell_space, - self.pwell_space, - self.pwell_to_nwell) + well_spacing = 0 + try: + well_spacing = max(self.nwell_space, well_spacing) + except AttributeError: + pass + try: + well_spacing = max(self.pwell_space, well_spacing) + except AttributeError: + pass + try: + well_spacing = max(self.pwell_to_nwell, well_spacing) + except AttributeError: + pass dff_pitch = self.dff.width + well_spacing + self.well_extend_active for row in range(self.rows): for col in range(self.columns): - name = "Xdff_r{0}_c{1}".format(row, col) + # name = "Xdff_r{0}_c{1}".format(row, col) if (row % 2 == 0): base = vector(col * dff_pitch, row * self.dff.height) mirror = "R0" @@ -141,8 +152,17 @@ class dff_buf_array(design.design): dout_bar_name = "dout_bar_{0}_{1}".format(row, col) return dout_bar_name - - def add_layout_pins(self): + + def route_supplies(self): + for row in range(self.rows): + vdd0_pin=self.dff_insts[row, 0].get_pin("vdd") + vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd") + self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height()) + + gnd0_pin=self.dff_insts[row, 0].get_pin("gnd") + gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd") + self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height()) + for row in range(self.rows): for col in range(self.columns): # Continous vdd rail along with label. @@ -152,6 +172,8 @@ class dff_buf_array(design.design): # Continous gnd rail along with label. gnd_pin=self.dff_insts[row, col].get_pin("gnd") self.add_power_pin("gnd", gnd_pin.lc()) + + def add_layout_pins(self): for row in range(self.rows): for col in range(self.columns): From fc85dfe29fd27b02b19a3c6d37631297abf6bf5b Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 21 Apr 2020 15:21:57 -0700 Subject: [PATCH 093/101] Add boundary to all pgates --- compiler/pgates/pand2.py | 1 + compiler/pgates/pand3.py | 1 + compiler/pgates/pbuf.py | 1 + compiler/pgates/pdriver.py | 1 + compiler/pgates/pinv.py | 1 + compiler/pgates/pinvbuf.py | 1 + compiler/pgates/pnand2.py | 3 ++- compiler/pgates/pnand3.py | 1 + compiler/pgates/pnor2.py | 1 + compiler/pgates/precharge.py | 3 ++- compiler/pgates/ptristate_inv.py | 1 + compiler/pgates/pwrite_driver.py | 1 + 12 files changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 23fd5e5b..995296b6 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -45,6 +45,7 @@ class pand2(pgate.pgate): self.place_insts() self.add_wires() self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_pins(self): diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index dd1d87f7..f8cc2ac3 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -44,6 +44,7 @@ class pand3(pgate.pgate): self.place_insts() self.add_wires() self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_pins(self): diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index 3171324b..6f9719eb 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -37,6 +37,7 @@ class pbuf(pgate.pgate): self.place_insts() self.add_wires() self.add_layout_pins() + self.add_boundary() def add_pins(self): self.add_pin("A", "INPUT") diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 5aa5393e..4bf654a4 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -76,6 +76,7 @@ class pdriver(pgate.pgate): self.width = self.inv_inst_list[-1].rx() self.height = self.inv_inst_list[0].height + self.add_boundary() def add_pins(self): self.add_pin("A", "INPUT") diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 3ec3e23e..d5a26a7a 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -64,6 +64,7 @@ class pinv(pgate.pgate): "A", position="farleft") self.route_outputs() + self.add_boundary() def add_pins(self): """ Adds pins for spice netlist """ diff --git a/compiler/pgates/pinvbuf.py b/compiler/pgates/pinvbuf.py index b8ecb3bf..fe376bc4 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/pgates/pinvbuf.py @@ -47,6 +47,7 @@ class pinvbuf(pgate.pgate): self.place_modules() self.route_wires() self.add_layout_pins() + self.add_boundary() self.offset_all_coordinates() diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 2f21c0bb..8398abbc 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -57,7 +57,8 @@ class pnand2(pgate.pgate): self.extend_wells() self.route_inputs() self.route_output() - + self.add_boundary() + def add_pins(self): """ Adds pins for spice netlist """ pin_list = ["A", "B", "Z", "vdd", "gnd"] diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 83e57c0a..fd563145 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -67,6 +67,7 @@ class pnand3(pgate.pgate): self.extend_wells() self.route_inputs() self.route_output() + self.add_boundary() def add_ptx(self): """ Create the PMOS and NMOS transistors. """ diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 75840f26..9dbc0c52 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -56,6 +56,7 @@ class pnor2(pgate.pgate): self.extend_wells() self.route_inputs() self.route_output() + self.add_boundary() def add_pins(self): """ Adds pins for spice netlist """ diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index afe14a05..7adec605 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -68,7 +68,8 @@ class precharge(design.design): self.route_vdd_rail() self.route_bitlines() self.connect_to_bitlines() - + self.add_boundary() + def add_pins(self): self.add_pin_list(["bl", "br", "en_bar", "vdd"], ["OUTPUT", "OUTPUT", "INPUT", "POWER"]) diff --git a/compiler/pgates/ptristate_inv.py b/compiler/pgates/ptristate_inv.py index 9586e72b..22f6b164 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/pgates/ptristate_inv.py @@ -56,6 +56,7 @@ class ptristate_inv(pgate.pgate): self.connect_rails() self.route_inputs() self.route_outputs() + self.add_boundary() def add_pins(self): """ Adds pins for spice netlist """ diff --git a/compiler/pgates/pwrite_driver.py b/compiler/pgates/pwrite_driver.py index 16830e62..da5eacdc 100644 --- a/compiler/pgates/pwrite_driver.py +++ b/compiler/pgates/pwrite_driver.py @@ -52,6 +52,7 @@ class pwrite_driver(design.design): self.place_modules() self.route_wires() self.route_supplies() + self.add_boundary() def add_pins(self): self.add_pin("din", "INPUT") From 0f6998a1c5dca33abb6b0604b84709688d0ae6bf Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 21 Apr 2020 15:36:38 -0700 Subject: [PATCH 094/101] PEP8 cleanup --- compiler/modules/delay_chain.py | 72 +++++++++++++++------------------ 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index a07576d4..4e421d0a 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -7,12 +7,11 @@ # import debug import design -from tech import drc -from contact import contact from vector import vector from globals import OPTS from sram_factory import factory + class delay_chain(design.design): """ Generate a delay chain with the given number of stages and fanout. @@ -28,7 +27,7 @@ class delay_chain(design.design): # Two fanouts are needed so that we can route the vdd/gnd connections for f in fanout_list: - debug.check(f>=2,"Must have >=2 fanouts for each stage.") + debug.check(f>=2, "Must have >=2 fanouts for each stage.") # number of inverters including any fanout loads. self.fanout_list = fanout_list @@ -36,7 +35,6 @@ class delay_chain(design.design): self.create_netlist() if not OPTS.netlist_only: self.create_layout() - def create_netlist(self): self.add_modules() @@ -45,9 +43,9 @@ class delay_chain(design.design): def create_layout(self): # Each stage is a a row - self.height = len(self.fanout_list)*self.inv.height + self.height = len(self.fanout_list) * self.inv.height # The width is determined by the largest fanout plus the driver - self.width = (max(self.fanout_list)+1) * self.inv.width + self.width = (max(self.fanout_list) + 1) * self.inv.width self.place_inverters() self.route_inverters() @@ -71,7 +69,7 @@ class delay_chain(design.design): self.driver_inst_list = [] self.rightest_load_inst = {} self.load_inst_map = {} - for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list): + for stage_num, fanout_size in zip(range(len(self.fanout_list)), self.fanout_list): # Add the inverter cur_driver=self.add_inst(name="dinv{}".format(stage_num), mod=self.inv) @@ -79,23 +77,23 @@ class delay_chain(design.design): self.driver_inst_list.append(cur_driver) # Hook up the driver - if stage_num+1==len(self.fanout_list): + if stage_num + 1 == len(self.fanout_list): stageout_name = "out" else: - stageout_name = "dout_{}".format(stage_num+1) + stageout_name = "dout_{}".format(stage_num + 1) if stage_num == 0: stagein_name = "in" else: - stagein_name = "dout_{}".format(stage_num) + stagein_name = "dout_{}".format(stage_num) self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"]) # Now add the dummy loads to the right self.load_inst_map[cur_driver]=[] for i in range(fanout_size): - cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num,i), + cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num, i), mod=self.inv) # Fanout stage is always driven by driver and output is disconnected - disconnect_name = "n_{0}_{1}".format(stage_num,i) + disconnect_name = "n_{0}_{1}".format(stage_num, i) self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"]) # Keep track of all the loads to connect their inputs as a load @@ -106,13 +104,13 @@ class delay_chain(design.design): def place_inverters(self): """ Place the inverters and connect them based on the stage list """ - for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list): + for stage_num, fanout_size in zip(range(len(self.fanout_list)), self.fanout_list): if stage_num % 2: inv_mirror = "MX" - inv_offset = vector(0, (stage_num+1)* self.inv.height) + inv_offset = vector(0, (stage_num + 1) * self.inv.height) else: inv_mirror = "R0" - inv_offset = vector(0, stage_num * self.inv.height) + inv_offset = vector(0, stage_num * self.inv.height) # Add the inverter cur_driver=self.driver_inst_list[stage_num] @@ -122,10 +120,9 @@ class delay_chain(design.design): # Now add the dummy loads to the right load_list = self.load_inst_map[cur_driver] for i in range(fanout_size): - inv_offset += vector(self.inv.width,0) + inv_offset += vector(self.inv.width, 0) load_list[i].place(offset=inv_offset, mirror=inv_mirror) - def add_route(self, pin1, pin2): """ This guarantees that we route from the top to bottom row correctly. """ @@ -134,9 +131,9 @@ class delay_chain(design.design): if pin1_pos.y == pin2_pos.y: self.add_path("m2", [pin1_pos, pin2_pos]) else: - mid_point = vector(pin2_pos.x, 0.5*(pin1_pos.y+pin2_pos.y)) + mid_point = vector(pin2_pos.x, 0.5 * (pin1_pos.y + pin2_pos.y)) # Written this way to guarantee it goes right first if we are switching rows - self.add_path("m2", [pin1_pos, vector(pin1_pos.x,mid_point.y), mid_point, vector(mid_point.x,pin2_pos.y), pin2_pos]) + self.add_path("m2", [pin1_pos, vector(pin1_pos.x, mid_point.y), mid_point, vector(mid_point.x, pin2_pos.y), pin2_pos]) def route_inverters(self): """ Add metal routing for each of the fanout stages """ @@ -145,7 +142,7 @@ class delay_chain(design.design): inv = self.driver_inst_list[i] for load in self.load_inst_map[inv]: # Drop a via on each A pin - a_pin = load.get_pin("A") + a_pin = load.get_pin("A") self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) self.add_via_center(layers=self.m2_stack, @@ -161,19 +158,17 @@ class delay_chain(design.design): offset=z_pin.center()) self.add_via_center(layers=self.m2_stack, offset=z_pin.center()) - self.add_path("m3",[z_pin.center(), a_max.center()]) + self.add_path("m3", [z_pin.center(), a_max.center()]) - # Route Z to the A of the next stage - if i+1 < len(self.driver_inst_list): + if i + 1 < len(self.driver_inst_list): z_pin = inv.get_pin("Z") - next_inv = self.driver_inst_list[i+1] + next_inv = self.driver_inst_list[i + 1] next_a_pin = next_inv.get_pin("A") - y_mid = (z_pin.cy() + next_a_pin.cy())/2 + y_mid = (z_pin.cy() + next_a_pin.cy()) / 2 mid1_point = vector(z_pin.cx(), y_mid) mid2_point = vector(next_a_pin.cx(), y_mid) - self.add_path("m2",[z_pin.center(), mid1_point, mid2_point, next_a_pin.center()]) - + self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()]) def add_layout_pins(self): """ Add vdd and gnd rails and the input/output. Connect the gnd rails internally on @@ -184,7 +179,7 @@ class delay_chain(design.design): # The routing to connect the loads is over the first and last cells # We have an even number of drivers and must only do every other # supply rail - for i in range(0,len(self.driver_inst_list),2): + for i in range(0, len(self.driver_inst_list), 2): inv = self.driver_inst_list[i] for load in self.load_inst_map[inv]: if load==self.rightest_load_inst[inv]: @@ -202,43 +197,42 @@ class delay_chain(design.design): pin = load.get_pin(pin_name) self.add_power_pin(pin_name, pin.rc()) - # input is A pin of first inverter a_pin = self.driver_inst_list[0].get_pin("A") self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) self.add_layout_pin(text="in", layer="m2", - offset=a_pin.ll().scale(1,0), + offset=a_pin.ll().scale(1, 0), height=a_pin.cy()) - # output is A pin of last load inverter last_driver_inst = self.driver_inst_list[-1] a_pin = self.rightest_load_inst[last_driver_inst].get_pin("A") self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) - mid_point = vector(a_pin.cx()+3*self.m2_width,a_pin.cy()) - self.add_path("m2",[a_pin.center(), mid_point, mid_point.scale(1,0)]) + mid_point = vector(a_pin.cx() + 3 * self.m2_width, a_pin.cy()) + self.add_path("m2", [a_pin.center(), mid_point, mid_point.scale(1, 0)]) self.add_layout_pin_segment_center(text="out", layer="m2", start=mid_point, - end=mid_point.scale(1,0)) + end=mid_point.scale(1, 0)) def get_cin(self): """Get the enable input ralative capacitance""" - #Only 1 input to the delay chain which is connected to an inverter. + # Only 1 input to the delay chain which is connected to an inverter. dc_cin = self.inv.get_cin() - return dc_cin + return dc_cin def determine_delayed_en_stage_efforts(self, ext_delayed_en_cout, inp_is_rise=True): """Get the stage efforts from the en to s_en. Does not compute the delay for the bitline load.""" stage_effort_list = [] - #Add a stage to the list for every stage in delay chain. Stages only differ in fanout except the last which has an external cout. + # Add a stage to the list for every stage in delay chain. + # Stages only differ in fanout except the last which has an external cout. last_stage_is_rise = inp_is_rise for stage_fanout in self.fanout_list: - stage_cout = self.inv.get_cin()*(stage_fanout+1) - if len(stage_effort_list) == len(self.fanout_list)-1: #last stage + stage_cout = self.inv.get_cin() * (stage_fanout + 1) + if len(stage_effort_list) == len(self.fanout_list) - 1: stage_cout+=ext_delayed_en_cout stage = self.inv.get_stage_effort(stage_cout, last_stage_is_rise) stage_effort_list.append(stage) From 3d4a40b338f2cc196cb48f780e15b776db5b3e4c Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Tue, 21 Apr 2020 15:38:19 -0700 Subject: [PATCH 095/101] freepdk45 col_mux fix --- compiler/pgates/single_level_column_mux.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index e84469db..288894aa 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -108,7 +108,7 @@ class single_level_column_mux(pgate.pgate): # This aligns it directly above the other tx with gates abutting nmos_upper_position = nmos_lower_position \ - + vector(0, self.nmos.active_height + self.active_space) + + vector(0, self.nmos.active_height + max(self.active_space,self.poly_space)) self.nmos_upper = self.add_inst(name="mux_tx2", mod=self.nmos, offset=nmos_upper_position) @@ -117,10 +117,14 @@ class single_level_column_mux(pgate.pgate): def connect_poly(self): """ Connect the poly gate of the two pass transistors """ + # offset is the top of the lower nmos' diffusion + # height is the distance between the nmos' diffusions, which depends on max(self.active_space,self.poly_space) + offset = self.nmos_lower.get_pin("G").ul() - vector(0,self.poly_extend_active) + height = self.nmos_upper.get_pin("G").by() + self.poly_extend_active - offset.y self.add_layout_pin(text="sel", layer="poly", - offset=self.nmos_lower.get_pin("G").ul() - vector(0,self.poly_extend_active), - height=self.active_space) + offset=offset, + height=height) def connect_bitlines(self): """ Connect the bitlines to the mux transistors """ From f1c1adc9bd0aeac481d8cf011ad018971b469c6e Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 21 Apr 2020 16:12:54 -0700 Subject: [PATCH 096/101] Simplify supply contacts in delay chain. --- compiler/modules/delay_chain.py | 42 ++++++++++++--------------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index 4e421d0a..b34974e5 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -49,6 +49,7 @@ class delay_chain(design.design): self.place_inverters() self.route_inverters() + self.route_supplies() self.add_layout_pins() self.add_boundary() self.DRC_LVS() @@ -67,7 +68,6 @@ class delay_chain(design.design): def create_inverters(self): """ Create the inverters and connect them based on the stage list """ self.driver_inst_list = [] - self.rightest_load_inst = {} self.load_inst_map = {} for stage_num, fanout_size in zip(range(len(self.fanout_list)), self.fanout_list): # Add the inverter @@ -98,9 +98,6 @@ class delay_chain(design.design): # Keep track of all the loads to connect their inputs as a load self.load_inst_map[cur_driver].append(cur_load) - else: - # Keep track of the last one so we can add the the wire later - self.rightest_load_inst[cur_driver]=cur_load def place_inverters(self): """ Place the inverters and connect them based on the stage list """ @@ -151,7 +148,7 @@ class delay_chain(design.design): # Route an M3 horizontal wire to the furthest z_pin = inv.get_pin("Z") a_pin = inv.get_pin("A") - a_max = self.rightest_load_inst[inv].get_pin("A") + a_max = self.load_inst_map[inv][-1].get_pin("A") self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) self.add_via_center(layers=self.m1_stack, @@ -169,33 +166,24 @@ class delay_chain(design.design): mid1_point = vector(z_pin.cx(), y_mid) mid2_point = vector(next_a_pin.cx(), y_mid) self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()]) - - def add_layout_pins(self): - """ Add vdd and gnd rails and the input/output. Connect the gnd rails internally on - the top end with no input/output to obstruct. """ + def route_supplies(self): # Add power and ground to all the cells except: # the fanout driver, the right-most load # The routing to connect the loads is over the first and last cells # We have an even number of drivers and must only do every other # supply rail - for i in range(0, len(self.driver_inst_list), 2): - inv = self.driver_inst_list[i] - for load in self.load_inst_map[inv]: - if load==self.rightest_load_inst[inv]: - continue - for pin_name in ["vdd", "gnd"]: - pin = load.get_pin(pin_name) - self.add_power_pin(pin_name, pin.rc()) - else: - # We have an even number of rows, so need to get the last gnd rail - inv = self.driver_inst_list[-1] - for load in self.load_inst_map[inv]: - if load==self.rightest_load_inst[inv]: - continue - pin_name = "gnd" - pin = load.get_pin(pin_name) - self.add_power_pin(pin_name, pin.rc()) + + for inst in self.driver_inst_list: + load_list = self.load_inst_map[inst] + for pin_name in ["vdd", "gnd"]: + pin = load_list[0].get_pin(pin_name) + self.add_power_pin(pin_name, pin.rc() - vector(self.m1_pitch, 0)) + + pin = load_list[-1].get_pin(pin_name) + self.add_power_pin(pin_name, pin.rc() - vector(0.5 * self.m1_pitch, 0)) + + def add_layout_pins(self): # input is A pin of first inverter a_pin = self.driver_inst_list[0].get_pin("A") @@ -208,7 +196,7 @@ class delay_chain(design.design): # output is A pin of last load inverter last_driver_inst = self.driver_inst_list[-1] - a_pin = self.rightest_load_inst[last_driver_inst].get_pin("A") + a_pin = self.load_inst_map[last_driver_inst][-1].get_pin("A") self.add_via_center(layers=self.m1_stack, offset=a_pin.center()) mid_point = vector(a_pin.cx() + 3 * self.m2_width, a_pin.cy()) From 60ba2c1aa55ebf664deeaec919530d2ab6917e64 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Tue, 21 Apr 2020 17:20:29 -0700 Subject: [PATCH 097/101] updated pbitcell test names --- compiler/tests/04_precharge_pbitcell_test.py | 8 ++++---- .../tests/04_single_level_column_mux_pbitcell_test.py | 2 +- compiler/tests/05_replica_pbitcell_array_test.py | 6 +++--- compiler/tests/06_hierarchical_decoder_pbitcell_test.py | 4 ++-- .../tests/06_hierarchical_predecode2x4_pbitcell_test.py | 2 +- .../tests/06_hierarchical_predecode3x8_pbitcell_test.py | 2 +- .../07_single_level_column_mux_array_pbitcell_test.py | 2 +- compiler/tests/08_wordline_driver_pbitcell_test.py | 2 +- compiler/tests/09_sense_amp_array_test_pbitcell.py | 2 +- compiler/tests/10_write_driver_array_pbitcell_test.py | 2 +- .../tests/10_write_driver_array_wmask_pbitcell_test.py | 2 +- compiler/tests/10_write_mask_and_array_pbitcell_test.py | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/tests/04_precharge_pbitcell_test.py b/compiler/tests/04_precharge_pbitcell_test.py index fd2f8283..3eb7628d 100755 --- a/compiler/tests/04_precharge_pbitcell_test.py +++ b/compiler/tests/04_precharge_pbitcell_test.py @@ -15,12 +15,12 @@ from globals import OPTS from sram_factory import factory import debug -class precharge_test(openram_test): +class precharge_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - + # check precharge in multi-port OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 @@ -36,14 +36,14 @@ class precharge_test(openram_test): debug.info(2, "Checking precharge for pbitcell (innermost connections)") tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1") self.local_check(tx) - + factory.reset() debug.info(2, "Checking precharge for pbitcell (outermost connections)") tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl2", bitcell_br="br2") self.local_check(tx) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/04_single_level_column_mux_pbitcell_test.py b/compiler/tests/04_single_level_column_mux_pbitcell_test.py index 85f909e6..18ab631f 100644 --- a/compiler/tests/04_single_level_column_mux_pbitcell_test.py +++ b/compiler/tests/04_single_level_column_mux_pbitcell_test.py @@ -17,7 +17,7 @@ import debug #@unittest.skip("SKIPPING 04_driver_test") -class single_level_column_mux_test(openram_test): +class single_level_column_mux_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/05_replica_pbitcell_array_test.py b/compiler/tests/05_replica_pbitcell_array_test.py index 34bdbee7..6a87f408 100755 --- a/compiler/tests/05_replica_pbitcell_array_test.py +++ b/compiler/tests/05_replica_pbitcell_array_test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # See LICENSE for licensing information. # -# Copyright (c) 2016-2019 Regents of the University of California +# Copyright (c) 2016-2019 Regents of the University of California # All rights reserved. # import unittest @@ -13,7 +13,7 @@ from globals import OPTS from sram_factory import factory import debug -class replica_bitcell_array_test(openram_test): +class replica_pbitcell_array_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -41,7 +41,7 @@ class replica_bitcell_array_test(openram_test): debug.info(2, "Testing 4x4 array for pbitcell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0]) self.local_check(a) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py index ec8f4efe..094e8b2e 100755 --- a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class hierarchical_decoder_test(openram_test): +class hierarchical_decoder_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -64,7 +64,7 @@ class hierarchical_decoder_test(openram_test): self.local_check(a) globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py index bc4a153f..b5532891 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class hierarchical_predecode2x4_test(openram_test): +class hierarchical_predecode2x4_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py index ffe68102..1d5ab41b 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class hierarchical_predecode3x8_test(openram_test): +class hierarchical_predecode3x8_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py index d929b855..84e62a96 100644 --- a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py +++ b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py @@ -14,7 +14,7 @@ from globals import OPTS from sram_factory import factory import debug -class single_level_column_mux_test(openram_test): +class single_level_column_mux_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/08_wordline_driver_pbitcell_test.py b/compiler/tests/08_wordline_driver_pbitcell_test.py index 42de5c26..3dd5933d 100644 --- a/compiler/tests/08_wordline_driver_pbitcell_test.py +++ b/compiler/tests/08_wordline_driver_pbitcell_test.py @@ -17,7 +17,7 @@ import debug #@unittest.skip("SKIPPING 04_driver_test") -class wordline_driver_test(openram_test): +class wordline_driver_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/09_sense_amp_array_test_pbitcell.py b/compiler/tests/09_sense_amp_array_test_pbitcell.py index 7c2fbd2d..a0fe256f 100644 --- a/compiler/tests/09_sense_amp_array_test_pbitcell.py +++ b/compiler/tests/09_sense_amp_array_test_pbitcell.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class sense_amp_test(openram_test): +class sense_amp_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/10_write_driver_array_pbitcell_test.py b/compiler/tests/10_write_driver_array_pbitcell_test.py index 87849001..397b3762 100644 --- a/compiler/tests/10_write_driver_array_pbitcell_test.py +++ b/compiler/tests/10_write_driver_array_pbitcell_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class write_driver_test(openram_test): +class write_driver_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py index 046b4d22..b6593eb0 100644 --- a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py +++ b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py @@ -17,7 +17,7 @@ from sram_factory import factory import debug -class write_driver_test(openram_test): +class write_driver_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) diff --git a/compiler/tests/10_write_mask_and_array_pbitcell_test.py b/compiler/tests/10_write_mask_and_array_pbitcell_test.py index 5068c119..448ee8b1 100644 --- a/compiler/tests/10_write_mask_and_array_pbitcell_test.py +++ b/compiler/tests/10_write_mask_and_array_pbitcell_test.py @@ -17,7 +17,7 @@ from sram_factory import factory import debug -class write_mask_and_array_test(openram_test): +class write_mask_and_array_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) From c2419af2e2a61e4caa821ca04abcb90007a0ca0e Mon Sep 17 00:00:00 2001 From: David Ratchkov Date: Mon, 20 Apr 2020 13:36:47 -0700 Subject: [PATCH 098/101] Fix voltage_map names (these do not need to match pg_pin names) --- compiler/characterizer/lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 276ea767..e6befc2b 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -243,8 +243,8 @@ class lib: self.lib.write(" default_max_fanout : 4.0 ;\n") self.lib.write(" default_connection_class : universal ;\n\n") - self.lib.write(" voltage_map ( vdd, {} );\n".format(tech.spice["nom_supply_voltage"])) - self.lib.write(" voltage_map ( gnd, 0 );\n\n") + self.lib.write(" voltage_map ( VDD, {} );\n".format(tech.spice["nom_supply_voltage"])) + self.lib.write(" voltage_map ( GND, 0 );\n\n") def create_list(self,values): """ Helper function to create quoted, line wrapped list """ From 14f440df7360f3b799b3ba1448fac9ccb580097e Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 22 Apr 2020 10:40:04 -0700 Subject: [PATCH 099/101] Update golden results with new lib syntax --- compiler/tests/21_hspice_delay_test.py | 47 +++-- compiler/tests/21_ngspice_delay_test.py | 44 ++-- ..._16_1_freepdk45_FF_1p0V_25C_analytical.lib | 83 +++++--- ..._16_1_freepdk45_SS_1p0V_25C_analytical.lib | 83 +++++--- .../sram_2_16_1_freepdk45_TT_1p0V_25C.lib | 167 ++++++++------- ..._16_1_freepdk45_TT_1p0V_25C_analytical.lib | 83 +++++--- ...16_1_scn4m_subm_FF_5p0V_25C_analytical.lib | 95 +++++---- ...16_1_scn4m_subm_SS_5p0V_25C_analytical.lib | 95 +++++---- .../sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib | 191 ++++++++++-------- ...16_1_scn4m_subm_TT_5p0V_25C_analytical.lib | 95 +++++---- 10 files changed, 583 insertions(+), 400 deletions(-) diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index 832a6308..a602f9d2 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -60,29 +60,36 @@ class timing_sram_test(openram_test): data, port_data = d.analyze(probe_address, probe_data, slews, loads) #Combine info about port into all data data.update(port_data[0]) - + if OPTS.tech_name == "freepdk45": - golden_data = {'delay_hl': [0.2383338], - 'delay_lh': [0.2383338], - 'leakage_power': 0.0014532999999999998, - 'min_period': 0.898, - 'read0_power': [0.30059800000000003], - 'read1_power': [0.30061810000000005], - 'slew_hl': [0.25358420000000004], - 'slew_lh': [0.25358420000000004], - 'write0_power': [0.34616749999999996], - 'write1_power': [0.2792924]} + golden_data = {'min_period': 0.898, + 'write1_power': [0.2659137999999999], + 'disabled_write0_power': [0.1782495], + 'disabled_read0_power': [0.14490679999999997], + 'write0_power': [0.3330119], + 'disabled_write1_power': [0.1865223], + 'leakage_power': 0.0014532, + 'disabled_read1_power': [0.1627516], + 'slew_lh': [0.25367799999999996], + 'slew_hl': [0.25367799999999996], + 'delay_lh': [0.23820930000000004], + 'delay_hl': [0.23820930000000004], + 'read1_power': [0.3005756], + 'read0_power': [0.3005888]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl': [1.7448], - 'delay_lh': [1.7448], - 'leakage_power': 0.0006356744000000001, + golden_data = {'leakage_power': 0.0006356576000000001, + 'write1_power': [11.292700000000002], + 'read0_power': [12.98], + 'disabled_write1_power': [8.3707], + 'write0_power': [14.4447], 'delay_hl': [1.7445000000000002], + 'disabled_read0_power': [6.4325], + 'slew_hl': [1.7437], + 'disabled_write0_power': [8.1307], + 'slew_lh': [1.7437], + 'read1_power': [12.9869], + 'disabled_read1_power': [7.706], 'min_period': 6.25, - 'read0_power': [12.9846], - 'read1_power': [12.9722], - 'slew_hl': [1.7433], - 'slew_lh': [1.7433], - 'write0_power': [14.8772], - 'write1_power': [11.7217]} + 'delay_lh': [1.7445000000000002]} else: self.assertTrue(False) # other techs fail # Check if no too many or too few results diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index fb72a57d..c944b3ce 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -55,27 +55,35 @@ class timing_sram_test(openram_test): data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_hl': [0.2264205], - 'delay_lh': [0.2264205], - 'leakage_power': 0.0021017429999999997, - 'min_period': 0.859, - 'read0_power': [0.3339161], - 'read1_power': [0.31329440000000003], - 'slew_hl': [0.2590786], - 'slew_lh': [0.2590786], - 'write0_power': [0.36360849999999995], - 'write1_power': [0.3486931]} + golden_data = {'slew_lh': [0.2592187], + 'slew_hl': [0.2592187], + 'delay_lh': [0.2465583], + 'disabled_write0_power': [0.1924678], + 'disabled_read0_power': [0.152483], + 'write0_power': [0.3409064], + 'disabled_read1_power': [0.1737818], + 'read0_power': [0.3096708], + 'read1_power': [0.3107916], + 'delay_hl': [0.2465583], + 'write1_power': [0.26915849999999997], + 'leakage_power': 0.002044307, + 'min_period': 0.898, + 'disabled_write1_power': [0.201411]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl': [1.85985], - 'delay_lh': [1.85985], + golden_data = {'read1_power': [12.11658], + 'write1_power': [10.52653], + 'read0_power': [11.956710000000001], + 'disabled_write0_power': [7.673665], + 'disabled_write1_power': [7.981922000000001], + 'slew_lh': [1.868836], + 'slew_hl': [1.868836], + 'delay_hl': [1.8598510000000001], + 'delay_lh': [1.8598510000000001], 'leakage_power': 0.008613619, + 'disabled_read0_power': [5.904712], 'min_period': 6.875, - 'read0_power': [12.656310000000001], - 'read1_power': [12.11682], - 'slew_hl': [1.868942], - 'slew_lh': [1.868942], - 'write0_power': [13.978110000000001], - 'write1_power': [11.437930000000001]} + 'disabled_read1_power': [7.132159], + 'write0_power': [13.406400000000001]} else: self.assertTrue(False) # other techs fail diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_FF_1p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_FF_1p0V_25C_analytical.lib index a1c5a04b..b3ef0e0a 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_FF_1p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_FF_1p0V_25C_analytical.lib @@ -35,11 +35,14 @@ library (sram_2_16_1_freepdk45_FF_1p0V_25C_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; + voltage_map ( VDD, 1.0 ); + voltage_map ( GND, 0 ); + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; index_1("0.00125, 0.005, 0.04"); - index_2("0.052275, 0.2091, 1.6728"); + index_2("5.2275e-05, 0.0002091, 0.0008364"); } lu_table_template(CONSTRAINT_TABLE){ @@ -78,17 +81,26 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 1124.88; + area : 0; + + pg_pin(vdd) { + voltage_name : VDD; + pg_type : primary_power; + } + + pg_pin(gnd) { + voltage_name : GND; + pg_type : primary_ground; + } leakage_power () { - when : "csb0"; - value : 0.000167; + value : 0.000198; } - cell_leakage_power : 0; + cell_leakage_power : 0.000198; bus(din0){ bus_type : data; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; memory_write(){ address : addr0; clocked_on : clk0; @@ -127,8 +139,8 @@ cell (sram_2_16_1_freepdk45){ bus(dout0){ bus_type : data; direction : output; - max_capacitance : 1.6728; - min_capacitance : 0.052275; + max_capacitance : 0.0008364000000000001; + min_capacitance : 5.2275000000000003e-05; memory_read(){ address : addr0; } @@ -138,14 +150,14 @@ cell (sram_2_16_1_freepdk45){ related_pin : "clk0"; timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.088, 0.088, 0.088",\ - "0.088, 0.088, 0.088",\ - "0.088, 0.088, 0.088"); + values("0.193, 0.193, 0.194",\ + "0.193, 0.193, 0.194",\ + "0.193, 0.193, 0.194"); } cell_fall(CELL_TABLE) { - values("0.088, 0.088, 0.088",\ - "0.088, 0.088, 0.088",\ - "0.088, 0.088, 0.088"); + values("0.193, 0.193, 0.194",\ + "0.193, 0.193, 0.194",\ + "0.193, 0.193, 0.194"); } rise_transition(CELL_TABLE) { values("0.001, 0.001, 0.001",\ @@ -164,7 +176,7 @@ cell (sram_2_16_1_freepdk45){ bus(addr0){ bus_type : addr; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; max_transition : 0.04; pin(addr0[3:0]){ timing(){ @@ -200,7 +212,7 @@ cell (sram_2_16_1_freepdk45){ pin(csb0){ direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -233,7 +245,7 @@ cell (sram_2_16_1_freepdk45){ pin(web0){ direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -267,52 +279,61 @@ cell (sram_2_16_1_freepdk45){ pin(clk0){ clock : true; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; internal_power(){ - when : "!csb0 & clk0 & !web0"; + when : "!csb0 & !web0"; rise_power(scalar){ - values("0.033101244168888884"); + values("9.240667e-02"); } fall_power(scalar){ - values("0.033101244168888884"); + values("9.240667e-02"); } } internal_power(){ - when : "!csb0 & !clk0 & web0"; + when : "csb0 & !web0"; rise_power(scalar){ - values("0.033101244168888884"); + values("9.240667e-02"); } fall_power(scalar){ - values("0.033101244168888884"); + values("9.240667e-02"); } } internal_power(){ - when : "csb0"; + when : "!csb0 & web0"; rise_power(scalar){ - values("0"); + values("9.240667e-02"); } fall_power(scalar){ - values("0"); + values("9.240667e-02"); + } + } + internal_power(){ + when : "csb0 & web0"; + rise_power(scalar){ + values("9.240667e-02"); + } + fall_power(scalar){ + values("9.240667e-02"); } } timing(){ timing_type :"min_pulse_width"; related_pin : clk0; rise_constraint(scalar) { - values("0.009"); + values("0.0195"); } fall_constraint(scalar) { - values("0.009"); + values("0.0195"); } } timing(){ timing_type :"minimum_period"; related_pin : clk0; rise_constraint(scalar) { - values("0.018"); + values("0.039"); } fall_constraint(scalar) { - values("0.018"); + values("0.039"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_SS_1p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_SS_1p0V_25C_analytical.lib index 865daada..34be4fe4 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_SS_1p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_SS_1p0V_25C_analytical.lib @@ -35,11 +35,14 @@ library (sram_2_16_1_freepdk45_SS_1p0V_25C_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; + voltage_map ( VDD, 1.0 ); + voltage_map ( GND, 0 ); + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; index_1("0.00125, 0.005, 0.04"); - index_2("0.052275, 0.2091, 1.6728"); + index_2("5.2275e-05, 0.0002091, 0.0008364"); } lu_table_template(CONSTRAINT_TABLE){ @@ -78,17 +81,26 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 1124.88; + area : 0; + + pg_pin(vdd) { + voltage_name : VDD; + pg_type : primary_power; + } + + pg_pin(gnd) { + voltage_name : GND; + pg_type : primary_ground; + } leakage_power () { - when : "csb0"; - value : 0.000167; + value : 0.000198; } - cell_leakage_power : 0; + cell_leakage_power : 0.000198; bus(din0){ bus_type : data; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; memory_write(){ address : addr0; clocked_on : clk0; @@ -127,8 +139,8 @@ cell (sram_2_16_1_freepdk45){ bus(dout0){ bus_type : data; direction : output; - max_capacitance : 1.6728; - min_capacitance : 0.052275; + max_capacitance : 0.0008364000000000001; + min_capacitance : 5.2275000000000003e-05; memory_read(){ address : addr0; } @@ -138,14 +150,14 @@ cell (sram_2_16_1_freepdk45){ related_pin : "clk0"; timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.107, 0.107, 0.107",\ - "0.107, 0.107, 0.107",\ - "0.107, 0.107, 0.107"); + values("0.236, 0.236, 0.237",\ + "0.236, 0.236, 0.237",\ + "0.236, 0.236, 0.237"); } cell_fall(CELL_TABLE) { - values("0.107, 0.107, 0.107",\ - "0.107, 0.107, 0.107",\ - "0.107, 0.107, 0.107"); + values("0.236, 0.236, 0.237",\ + "0.236, 0.236, 0.237",\ + "0.236, 0.236, 0.237"); } rise_transition(CELL_TABLE) { values("0.001, 0.001, 0.001",\ @@ -164,7 +176,7 @@ cell (sram_2_16_1_freepdk45){ bus(addr0){ bus_type : addr; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; max_transition : 0.04; pin(addr0[3:0]){ timing(){ @@ -200,7 +212,7 @@ cell (sram_2_16_1_freepdk45){ pin(csb0){ direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -233,7 +245,7 @@ cell (sram_2_16_1_freepdk45){ pin(web0){ direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -267,52 +279,61 @@ cell (sram_2_16_1_freepdk45){ pin(clk0){ clock : true; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; internal_power(){ - when : "!csb0 & clk0 & !web0"; + when : "!csb0 & !web0"; rise_power(scalar){ - values("0.033101244168888884"); + values("7.560546e-02"); } fall_power(scalar){ - values("0.033101244168888884"); + values("7.560546e-02"); } } internal_power(){ - when : "!csb0 & !clk0 & web0"; + when : "csb0 & !web0"; rise_power(scalar){ - values("0.033101244168888884"); + values("7.560546e-02"); } fall_power(scalar){ - values("0.033101244168888884"); + values("7.560546e-02"); } } internal_power(){ - when : "csb0"; + when : "!csb0 & web0"; rise_power(scalar){ - values("0"); + values("7.560546e-02"); } fall_power(scalar){ - values("0"); + values("7.560546e-02"); + } + } + internal_power(){ + when : "csb0 & web0"; + rise_power(scalar){ + values("7.560546e-02"); + } + fall_power(scalar){ + values("7.560546e-02"); } } timing(){ timing_type :"min_pulse_width"; related_pin : clk0; rise_constraint(scalar) { - values("0.0105"); + values("0.0235"); } fall_constraint(scalar) { - values("0.0105"); + values("0.0235"); } } timing(){ timing_type :"minimum_period"; related_pin : clk0; rise_constraint(scalar) { - values("0.021"); + values("0.047"); } fall_constraint(scalar) { - values("0.021"); + values("0.047"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib index 72d01a0f..cca9c1ed 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C.lib @@ -35,11 +35,14 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; + voltage_map ( VDD, 1.0 ); + voltage_map ( GND, 0 ); + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; index_1("0.00125, 0.005, 0.04"); - index_2("0.052275, 0.2091, 1.6728"); + index_2("5.2275e-05, 0.0002091, 0.0008364"); } lu_table_template(CONSTRAINT_TABLE){ @@ -78,17 +81,26 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 977.4951374999999; + area : 0; + + pg_pin(vdd) { + voltage_name : VDD; + pg_type : primary_power; + } + + pg_pin(gnd) { + voltage_name : GND; + pg_type : primary_ground; + } leakage_power () { - when : "csb0"; - value : 0.0011164579999999999; + value : 0.00163; } - cell_leakage_power : 0; + cell_leakage_power : 0.00163; bus(din0){ bus_type : data; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; memory_write(){ address : addr0; clocked_on : clk0; @@ -98,9 +110,9 @@ cell (sram_2_16_1_freepdk45){ timing_type : setup_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.033, 0.039",\ - "0.033, 0.033, 0.039",\ - "0.033, 0.033, 0.039"); + values("0.033, 0.033, 0.033",\ + "0.033, 0.033, 0.033",\ + "0.033, 0.033, 0.033"); } fall_constraint(CONSTRAINT_TABLE) { values("0.027, 0.027, 0.033",\ @@ -112,14 +124,14 @@ cell (sram_2_16_1_freepdk45){ timing_type : hold_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("-0.01, -0.016, -0.022",\ - "-0.01, -0.016, -0.022",\ - "-0.01, -0.016, -0.022"); + values("-0.01, -0.01, 0.021",\ + "-0.01, -0.01, 0.021",\ + "-0.01, -0.01, 0.021"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.016, -0.016, -0.016",\ - "-0.016, -0.016, -0.016",\ - "-0.016, -0.016, -0.016"); + values("-0.016, -0.01, -0.016",\ + "-0.016, -0.01, -0.016",\ + "-0.016, -0.01, -0.016"); } } } @@ -127,8 +139,8 @@ cell (sram_2_16_1_freepdk45){ bus(dout0){ bus_type : data; direction : output; - max_capacitance : 1.6728; - min_capacitance : 0.052275; + max_capacitance : 0.0008364000000000001; + min_capacitance : 5.2275000000000003e-05; memory_read(){ address : addr0; } @@ -138,24 +150,24 @@ cell (sram_2_16_1_freepdk45){ related_pin : "clk0"; timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.235, 0.235, 0.239",\ - "0.235, 0.236, 0.24",\ - "0.241, 0.242, 0.246"); + values("0.226, 0.227, 0.232",\ + "0.227, 0.228, 0.233",\ + "0.232, 0.234, 0.238"); } cell_fall(CELL_TABLE) { - values("2.583, 2.585, 2.612",\ - "2.584, 2.585, 2.613",\ - "2.59, 2.592, 2.62"); + values("0.226, 0.227, 0.232",\ + "0.227, 0.228, 0.233",\ + "0.232, 0.234, 0.238"); } rise_transition(CELL_TABLE) { - values("0.022, 0.022, 0.03",\ - "0.022, 0.023, 0.03",\ - "0.022, 0.022, 0.03"); + values("0.256, 0.256, 0.257",\ + "0.256, 0.256, 0.257",\ + "0.256, 0.256, 0.257"); } fall_transition(CELL_TABLE) { - values("0.078, 0.079, 0.083",\ - "0.078, 0.079, 0.083",\ - "0.079, 0.079, 0.083"); + values("0.256, 0.256, 0.257",\ + "0.256, 0.256, 0.257",\ + "0.256, 0.256, 0.257"); } } } @@ -164,16 +176,16 @@ cell (sram_2_16_1_freepdk45){ bus(addr0){ bus_type : addr; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; max_transition : 0.04; pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.033, 0.039",\ - "0.033, 0.033, 0.039",\ - "0.033, 0.033, 0.039"); + values("0.033, 0.033, 0.033",\ + "0.033, 0.033, 0.033",\ + "0.033, 0.033, 0.033"); } fall_constraint(CONSTRAINT_TABLE) { values("0.027, 0.027, 0.033",\ @@ -185,14 +197,14 @@ cell (sram_2_16_1_freepdk45){ timing_type : hold_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("-0.01, -0.016, -0.022",\ - "-0.01, -0.016, -0.022",\ - "-0.01, -0.016, -0.022"); + values("-0.01, -0.01, 0.021",\ + "-0.01, -0.01, 0.021",\ + "-0.01, -0.01, 0.021"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.016, -0.016, -0.016",\ - "-0.016, -0.016, -0.016",\ - "-0.016, -0.016, -0.016"); + values("-0.016, -0.01, -0.016",\ + "-0.016, -0.01, -0.016",\ + "-0.016, -0.01, -0.016"); } } } @@ -200,14 +212,14 @@ cell (sram_2_16_1_freepdk45){ pin(csb0){ direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; timing(){ timing_type : setup_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.033, 0.039",\ - "0.033, 0.033, 0.039",\ - "0.033, 0.033, 0.039"); + values("0.033, 0.033, 0.033",\ + "0.033, 0.033, 0.033",\ + "0.033, 0.033, 0.033"); } fall_constraint(CONSTRAINT_TABLE) { values("0.027, 0.027, 0.033",\ @@ -219,28 +231,28 @@ cell (sram_2_16_1_freepdk45){ timing_type : hold_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("-0.01, -0.016, -0.022",\ - "-0.01, -0.016, -0.022",\ - "-0.01, -0.016, -0.022"); + values("-0.01, -0.01, 0.021",\ + "-0.01, -0.01, 0.021",\ + "-0.01, -0.01, 0.021"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.016, -0.016, -0.016",\ - "-0.016, -0.016, -0.016",\ - "-0.016, -0.016, -0.016"); + values("-0.016, -0.01, -0.016",\ + "-0.016, -0.01, -0.016",\ + "-0.016, -0.01, -0.016"); } } } pin(web0){ direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; timing(){ timing_type : setup_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.033, 0.033, 0.039",\ - "0.033, 0.033, 0.039",\ - "0.033, 0.033, 0.039"); + values("0.033, 0.033, 0.033",\ + "0.033, 0.033, 0.033",\ + "0.033, 0.033, 0.033"); } fall_constraint(CONSTRAINT_TABLE) { values("0.027, 0.027, 0.033",\ @@ -252,14 +264,14 @@ cell (sram_2_16_1_freepdk45){ timing_type : hold_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("-0.01, -0.016, -0.022",\ - "-0.01, -0.016, -0.022",\ - "-0.01, -0.016, -0.022"); + values("-0.01, -0.01, 0.021",\ + "-0.01, -0.01, 0.021",\ + "-0.01, -0.01, 0.021"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.016, -0.016, -0.016",\ - "-0.016, -0.016, -0.016",\ - "-0.016, -0.016, -0.016"); + values("-0.016, -0.01, -0.016",\ + "-0.016, -0.01, -0.016",\ + "-0.016, -0.01, -0.016"); } } } @@ -267,52 +279,61 @@ cell (sram_2_16_1_freepdk45){ pin(clk0){ clock : true; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; internal_power(){ - when : "!csb0 & clk0 & !web0"; + when : "!csb0 & !web0"; rise_power(scalar){ - values("0.03599689694444445"); + values("3.069977e-01"); } fall_power(scalar){ - values("0.03599689694444445"); + values("3.686680e-01"); } } internal_power(){ - when : "!csb0 & !clk0 & web0"; + when : "csb0 & !web0"; rise_power(scalar){ - values("0.029906643888888886"); + values("2.055845e-01"); } fall_power(scalar){ - values("0.029906643888888886"); + values("1.933561e-01"); } } internal_power(){ - when : "csb0"; + when : "!csb0 & web0"; rise_power(scalar){ - values("0"); + values("3.315565e-01"); } fall_power(scalar){ - values("0"); + values("3.314553e-01"); + } + } + internal_power(){ + when : "csb0 & web0"; + rise_power(scalar){ + values("1.777355e-01"); + } + fall_power(scalar){ + values("1.615044e-01"); } } timing(){ timing_type :"min_pulse_width"; related_pin : clk0; rise_constraint(scalar) { - values("2.422"); + values("0.449"); } fall_constraint(scalar) { - values("2.422"); + values("0.449"); } } timing(){ timing_type :"minimum_period"; related_pin : clk0; rise_constraint(scalar) { - values("4.844"); + values("0.898"); } fall_constraint(scalar) { - values("4.844"); + values("0.898"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib index 33587063..26028892 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_freepdk45_TT_1p0V_25C_analytical.lib @@ -35,11 +35,14 @@ library (sram_2_16_1_freepdk45_TT_1p0V_25C_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; + voltage_map ( VDD, 1.0 ); + voltage_map ( GND, 0 ); + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; index_1("0.00125, 0.005, 0.04"); - index_2("0.052275, 0.2091, 1.6728"); + index_2("5.2275e-05, 0.0002091, 0.0008364"); } lu_table_template(CONSTRAINT_TABLE){ @@ -78,17 +81,26 @@ cell (sram_2_16_1_freepdk45){ dont_use : true; map_only : true; dont_touch : true; - area : 977.4951374999999; + area : 0; + + pg_pin(vdd) { + voltage_name : VDD; + pg_type : primary_power; + } + + pg_pin(gnd) { + voltage_name : GND; + pg_type : primary_ground; + } leakage_power () { - when : "csb0"; - value : 0.000179; + value : 0.000198; } - cell_leakage_power : 0; + cell_leakage_power : 0.000198; bus(din0){ bus_type : data; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; memory_write(){ address : addr0; clocked_on : clk0; @@ -127,8 +139,8 @@ cell (sram_2_16_1_freepdk45){ bus(dout0){ bus_type : data; direction : output; - max_capacitance : 1.6728; - min_capacitance : 0.052275; + max_capacitance : 0.0008364000000000001; + min_capacitance : 5.2275000000000003e-05; memory_read(){ address : addr0; } @@ -138,14 +150,14 @@ cell (sram_2_16_1_freepdk45){ related_pin : "clk0"; timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.098, 0.098, 0.098",\ - "0.098, 0.098, 0.098",\ - "0.098, 0.098, 0.098"); + values("0.215, 0.215, 0.216",\ + "0.215, 0.215, 0.216",\ + "0.215, 0.215, 0.216"); } cell_fall(CELL_TABLE) { - values("0.098, 0.098, 0.098",\ - "0.098, 0.098, 0.098",\ - "0.098, 0.098, 0.098"); + values("0.215, 0.215, 0.216",\ + "0.215, 0.215, 0.216",\ + "0.215, 0.215, 0.216"); } rise_transition(CELL_TABLE) { values("0.001, 0.001, 0.001",\ @@ -164,7 +176,7 @@ cell (sram_2_16_1_freepdk45){ bus(addr0){ bus_type : addr; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; max_transition : 0.04; pin(addr0[3:0]){ timing(){ @@ -200,7 +212,7 @@ cell (sram_2_16_1_freepdk45){ pin(csb0){ direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -233,7 +245,7 @@ cell (sram_2_16_1_freepdk45){ pin(web0){ direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -267,52 +279,61 @@ cell (sram_2_16_1_freepdk45){ pin(clk0){ clock : true; direction : input; - capacitance : 0.2091; + capacitance : 0.00020910000000000001; internal_power(){ - when : "!csb0 & clk0 & !web0"; + when : "!csb0 & !web0"; rise_power(scalar){ - values("0.0747594982142222"); + values("8.316600e-02"); } fall_power(scalar){ - values("0.0747594982142222"); + values("8.316600e-02"); } } internal_power(){ - when : "!csb0 & !clk0 & web0"; + when : "csb0 & !web0"; rise_power(scalar){ - values("0.0747594982142222"); + values("8.316600e-02"); } fall_power(scalar){ - values("0.0747594982142222"); + values("8.316600e-02"); } } internal_power(){ - when : "csb0"; + when : "!csb0 & web0"; rise_power(scalar){ - values("0"); + values("8.316600e-02"); } fall_power(scalar){ - values("0"); + values("8.316600e-02"); + } + } + internal_power(){ + when : "csb0 & web0"; + rise_power(scalar){ + values("8.316600e-02"); + } + fall_power(scalar){ + values("8.316600e-02"); } } timing(){ timing_type :"min_pulse_width"; related_pin : clk0; rise_constraint(scalar) { - values("0.0"); + values("0.0215"); } fall_constraint(scalar) { - values("0.0"); + values("0.0215"); } } timing(){ timing_type :"minimum_period"; related_pin : clk0; rise_constraint(scalar) { - values("0"); + values("0.043"); } fall_constraint(scalar) { - values("0"); + values("0.043"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_FF_5p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_FF_5p0V_25C_analytical.lib index c370f993..6912aec7 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_FF_5p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_FF_5p0V_25C_analytical.lib @@ -35,11 +35,14 @@ library (sram_2_16_1_scn4m_subm_FF_5p0V_25C_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; + voltage_map ( VDD, 5.0 ); + voltage_map ( GND, 0 ); + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; index_1("0.0125, 0.05, 0.4"); - index_2("2.45605, 9.8242, 78.5936"); + index_2("0.00245605, 0.0098242, 0.0392968"); } lu_table_template(CONSTRAINT_TABLE){ @@ -78,17 +81,26 @@ cell (sram_2_16_1_scn4m_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 73068.14000000001; + area : 0; + + pg_pin(vdd) { + voltage_name : VDD; + pg_type : primary_power; + } + + pg_pin(gnd) { + voltage_name : GND; + pg_type : primary_ground; + } leakage_power () { - when : "csb0"; - value : 0.000167; + value : 0.000198; } - cell_leakage_power : 0; + cell_leakage_power : 0.000198; bus(din0){ bus_type : data; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; memory_write(){ address : addr0; clocked_on : clk0; @@ -127,8 +139,8 @@ cell (sram_2_16_1_scn4m_subm){ bus(dout0){ bus_type : data; direction : output; - max_capacitance : 78.5936; - min_capacitance : 2.45605; + max_capacitance : 0.0392968; + min_capacitance : 0.00245605; memory_read(){ address : addr0; } @@ -138,24 +150,24 @@ cell (sram_2_16_1_scn4m_subm){ related_pin : "clk0"; timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.241, 0.241, 0.241",\ - "0.241, 0.241, 0.241",\ - "0.241, 0.241, 0.241"); + values("1.183, 1.199, 1.264",\ + "1.183, 1.199, 1.264",\ + "1.183, 1.199, 1.264"); } cell_fall(CELL_TABLE) { - values("0.241, 0.241, 0.241",\ - "0.241, 0.241, 0.241",\ - "0.241, 0.241, 0.241"); + values("1.183, 1.199, 1.264",\ + "1.183, 1.199, 1.264",\ + "1.183, 1.199, 1.264"); } rise_transition(CELL_TABLE) { - values("0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004"); + values("0.006, 0.007, 0.014",\ + "0.006, 0.007, 0.014",\ + "0.006, 0.007, 0.014"); } fall_transition(CELL_TABLE) { - values("0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004"); + values("0.006, 0.007, 0.014",\ + "0.006, 0.007, 0.014",\ + "0.006, 0.007, 0.014"); } } } @@ -164,7 +176,7 @@ cell (sram_2_16_1_scn4m_subm){ bus(addr0){ bus_type : addr; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; max_transition : 0.4; pin(addr0[3:0]){ timing(){ @@ -200,7 +212,7 @@ cell (sram_2_16_1_scn4m_subm){ pin(csb0){ direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -233,7 +245,7 @@ cell (sram_2_16_1_scn4m_subm){ pin(web0){ direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -267,52 +279,61 @@ cell (sram_2_16_1_scn4m_subm){ pin(clk0){ clock : true; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; internal_power(){ - when : "!csb0 & clk0 & !web0"; + when : "!csb0 & !web0"; rise_power(scalar){ - values("4.99880645"); + values("7.797263e+00"); } fall_power(scalar){ - values("4.99880645"); + values("7.797263e+00"); } } internal_power(){ - when : "!csb0 & !clk0 & web0"; + when : "csb0 & !web0"; rise_power(scalar){ - values("4.99880645"); + values("7.797263e+00"); } fall_power(scalar){ - values("4.99880645"); + values("7.797263e+00"); } } internal_power(){ - when : "csb0"; + when : "!csb0 & web0"; rise_power(scalar){ - values("0"); + values("7.797263e+00"); } fall_power(scalar){ - values("0"); + values("7.797263e+00"); + } + } + internal_power(){ + when : "csb0 & web0"; + rise_power(scalar){ + values("7.797263e+00"); + } + fall_power(scalar){ + values("7.797263e+00"); } } timing(){ timing_type :"min_pulse_width"; related_pin : clk0; rise_constraint(scalar) { - values("0.024"); + values("0.1265"); } fall_constraint(scalar) { - values("0.024"); + values("0.1265"); } } timing(){ timing_type :"minimum_period"; related_pin : clk0; rise_constraint(scalar) { - values("0.048"); + values("0.253"); } fall_constraint(scalar) { - values("0.048"); + values("0.253"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_SS_5p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_SS_5p0V_25C_analytical.lib index f52de676..a7605cb3 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_SS_5p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_SS_5p0V_25C_analytical.lib @@ -35,11 +35,14 @@ library (sram_2_16_1_scn4m_subm_SS_5p0V_25C_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; + voltage_map ( VDD, 5.0 ); + voltage_map ( GND, 0 ); + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; index_1("0.0125, 0.05, 0.4"); - index_2("2.45605, 9.8242, 78.5936"); + index_2("0.00245605, 0.0098242, 0.0392968"); } lu_table_template(CONSTRAINT_TABLE){ @@ -78,17 +81,26 @@ cell (sram_2_16_1_scn4m_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 73068.14000000001; + area : 0; + + pg_pin(vdd) { + voltage_name : VDD; + pg_type : primary_power; + } + + pg_pin(gnd) { + voltage_name : GND; + pg_type : primary_ground; + } leakage_power () { - when : "csb0"; - value : 0.000167; + value : 0.000198; } - cell_leakage_power : 0; + cell_leakage_power : 0.000198; bus(din0){ bus_type : data; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; memory_write(){ address : addr0; clocked_on : clk0; @@ -127,8 +139,8 @@ cell (sram_2_16_1_scn4m_subm){ bus(dout0){ bus_type : data; direction : output; - max_capacitance : 78.5936; - min_capacitance : 2.45605; + max_capacitance : 0.0392968; + min_capacitance : 0.00245605; memory_read(){ address : addr0; } @@ -138,24 +150,24 @@ cell (sram_2_16_1_scn4m_subm){ related_pin : "clk0"; timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.294, 0.294, 0.294",\ - "0.294, 0.294, 0.294",\ - "0.294, 0.294, 0.294"); + values("1.446, 1.466, 1.545",\ + "1.446, 1.466, 1.545",\ + "1.446, 1.466, 1.545"); } cell_fall(CELL_TABLE) { - values("0.294, 0.294, 0.294",\ - "0.294, 0.294, 0.294",\ - "0.294, 0.294, 0.294"); + values("1.446, 1.466, 1.545",\ + "1.446, 1.466, 1.545",\ + "1.446, 1.466, 1.545"); } rise_transition(CELL_TABLE) { - values("0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004"); + values("0.007, 0.009, 0.017",\ + "0.007, 0.009, 0.017",\ + "0.007, 0.009, 0.017"); } fall_transition(CELL_TABLE) { - values("0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004"); + values("0.007, 0.009, 0.017",\ + "0.007, 0.009, 0.017",\ + "0.007, 0.009, 0.017"); } } } @@ -164,7 +176,7 @@ cell (sram_2_16_1_scn4m_subm){ bus(addr0){ bus_type : addr; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; max_transition : 0.4; pin(addr0[3:0]){ timing(){ @@ -200,7 +212,7 @@ cell (sram_2_16_1_scn4m_subm){ pin(csb0){ direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -233,7 +245,7 @@ cell (sram_2_16_1_scn4m_subm){ pin(web0){ direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -267,52 +279,61 @@ cell (sram_2_16_1_scn4m_subm){ pin(clk0){ clock : true; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; internal_power(){ - when : "!csb0 & clk0 & !web0"; + when : "!csb0 & !web0"; rise_power(scalar){ - values("4.99880645"); + values("6.379579e+00"); } fall_power(scalar){ - values("4.99880645"); + values("6.379579e+00"); } } internal_power(){ - when : "!csb0 & !clk0 & web0"; + when : "csb0 & !web0"; rise_power(scalar){ - values("4.99880645"); + values("6.379579e+00"); } fall_power(scalar){ - values("4.99880645"); + values("6.379579e+00"); } } internal_power(){ - when : "csb0"; + when : "!csb0 & web0"; rise_power(scalar){ - values("0"); + values("6.379579e+00"); } fall_power(scalar){ - values("0"); + values("6.379579e+00"); + } + } + internal_power(){ + when : "csb0 & web0"; + rise_power(scalar){ + values("6.379579e+00"); + } + fall_power(scalar){ + values("6.379579e+00"); } } timing(){ timing_type :"min_pulse_width"; related_pin : clk0; rise_constraint(scalar) { - values("0.0295"); + values("0.1545"); } fall_constraint(scalar) { - values("0.0295"); + values("0.1545"); } } timing(){ timing_type :"minimum_period"; related_pin : clk0; rise_constraint(scalar) { - values("0.059"); + values("0.309"); } fall_constraint(scalar) { - values("0.059"); + values("0.309"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib index 7447a1e2..8bec74c3 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C.lib @@ -35,11 +35,14 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; + voltage_map ( VDD, 5.0 ); + voltage_map ( GND, 0 ); + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; index_1("0.0125, 0.05, 0.4"); - index_2("2.45605, 9.8242, 78.5936"); + index_2("0.00245605, 0.0098242, 0.0392968"); } lu_table_template(CONSTRAINT_TABLE){ @@ -78,17 +81,26 @@ cell (sram_2_16_1_scn4m_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 60774.3; + area : 0; + + pg_pin(vdd) { + voltage_name : VDD; + pg_type : primary_power; + } + + pg_pin(gnd) { + voltage_name : GND; + pg_type : primary_ground; + } leakage_power () { - when : "csb0"; - value : 0.0009813788999999999; + value : 0.000198; } - cell_leakage_power : 0; + cell_leakage_power : 0.000198; bus(din0){ bus_type : data; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; memory_write(){ address : addr0; clocked_on : clk0; @@ -98,28 +110,28 @@ cell (sram_2_16_1_scn4m_subm){ timing_type : setup_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.167, 0.167, 0.228",\ - "0.167, 0.167, 0.228",\ - "0.167, 0.167, 0.228"); + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.131, 0.125, 0.137",\ - "0.131, 0.125, 0.137",\ - "0.131, 0.125, 0.137"); + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.089",\ - "-0.089, -0.089, -0.089",\ - "-0.089, -0.089, -0.089"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } @@ -127,8 +139,8 @@ cell (sram_2_16_1_scn4m_subm){ bus(dout0){ bus_type : data; direction : output; - max_capacitance : 78.5936; - min_capacitance : 2.45605; + max_capacitance : 0.0392968; + min_capacitance : 0.00245605; memory_read(){ address : addr0; } @@ -138,24 +150,24 @@ cell (sram_2_16_1_scn4m_subm){ related_pin : "clk0"; timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("1.556, 1.576, 1.751",\ - "1.559, 1.579, 1.754",\ - "1.624, 1.643, 1.819"); + values("1.314, 1.332, 1.404",\ + "1.314, 1.332, 1.404",\ + "1.314, 1.332, 1.404"); } cell_fall(CELL_TABLE) { - values("3.445, 3.504, 3.926",\ - "3.448, 3.507, 3.93",\ - "3.49, 3.549, 3.972"); + values("1.314, 1.332, 1.404",\ + "1.314, 1.332, 1.404",\ + "1.314, 1.332, 1.404"); } rise_transition(CELL_TABLE) { - values("0.13, 0.169, 0.574",\ - "0.13, 0.169, 0.574",\ - "0.13, 0.169, 0.574"); + values("0.006, 0.008, 0.015",\ + "0.006, 0.008, 0.015",\ + "0.006, 0.008, 0.015"); } fall_transition(CELL_TABLE) { - values("0.467, 0.49, 0.959",\ - "0.467, 0.49, 0.959",\ - "0.47, 0.493, 0.96"); + values("0.006, 0.008, 0.015",\ + "0.006, 0.008, 0.015",\ + "0.006, 0.008, 0.015"); } } } @@ -164,35 +176,35 @@ cell (sram_2_16_1_scn4m_subm){ bus(addr0){ bus_type : addr; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; max_transition : 0.4; pin(addr0[3:0]){ timing(){ timing_type : setup_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.167, 0.167, 0.228",\ - "0.167, 0.167, 0.228",\ - "0.167, 0.167, 0.228"); + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.131, 0.125, 0.137",\ - "0.131, 0.125, 0.137",\ - "0.131, 0.125, 0.137"); + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.089",\ - "-0.089, -0.089, -0.089",\ - "-0.089, -0.089, -0.089"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } @@ -200,66 +212,66 @@ cell (sram_2_16_1_scn4m_subm){ pin(csb0){ direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; timing(){ timing_type : setup_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.167, 0.167, 0.228",\ - "0.167, 0.167, 0.228",\ - "0.167, 0.167, 0.228"); + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.131, 0.125, 0.137",\ - "0.131, 0.125, 0.137",\ - "0.131, 0.125, 0.137"); + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.089",\ - "-0.089, -0.089, -0.089",\ - "-0.089, -0.089, -0.089"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } pin(web0){ direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; timing(){ timing_type : setup_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("0.167, 0.167, 0.228",\ - "0.167, 0.167, 0.228",\ - "0.167, 0.167, 0.228"); + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } fall_constraint(CONSTRAINT_TABLE) { - values("0.131, 0.125, 0.137",\ - "0.131, 0.125, 0.137",\ - "0.131, 0.125, 0.137"); + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); } } timing(){ timing_type : hold_rising; related_pin : "clk0"; rise_constraint(CONSTRAINT_TABLE) { - values("-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114",\ - "-0.065, -0.071, -0.114"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } fall_constraint(CONSTRAINT_TABLE) { - values("-0.089, -0.089, -0.089",\ - "-0.089, -0.089, -0.089",\ - "-0.089, -0.089, -0.089"); + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); } } } @@ -267,52 +279,61 @@ cell (sram_2_16_1_scn4m_subm){ pin(clk0){ clock : true; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; internal_power(){ - when : "!csb0 & clk0 & !web0"; + when : "!csb0 & !web0"; rise_power(scalar){ - values("9.972790277777777"); + values("7.017537e+00"); } fall_power(scalar){ - values("9.972790277777777"); + values("7.017537e+00"); } } internal_power(){ - when : "!csb0 & !clk0 & web0"; + when : "csb0 & !web0"; rise_power(scalar){ - values("8.899322499999998"); + values("7.017537e+00"); } fall_power(scalar){ - values("8.899322499999998"); + values("7.017537e+00"); } } internal_power(){ - when : "csb0"; + when : "!csb0 & web0"; rise_power(scalar){ - values("0"); + values("7.017537e+00"); } fall_power(scalar){ - values("0"); + values("7.017537e+00"); + } + } + internal_power(){ + when : "csb0 & web0"; + rise_power(scalar){ + values("7.017537e+00"); + } + fall_power(scalar){ + values("7.017537e+00"); } } timing(){ timing_type :"min_pulse_width"; related_pin : clk0; rise_constraint(scalar) { - values("2.344"); + values("0.1405"); } fall_constraint(scalar) { - values("2.344"); + values("0.1405"); } } timing(){ timing_type :"minimum_period"; related_pin : clk0; rise_constraint(scalar) { - values("4.688"); + values("0.281"); } fall_constraint(scalar) { - values("4.688"); + values("0.281"); } } } diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib index 4248b986..8bec74c3 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm_TT_5p0V_25C_analytical.lib @@ -35,11 +35,14 @@ library (sram_2_16_1_scn4m_subm_TT_5p0V_25C_lib){ default_max_fanout : 4.0 ; default_connection_class : universal ; + voltage_map ( VDD, 5.0 ); + voltage_map ( GND, 0 ); + lu_table_template(CELL_TABLE){ variable_1 : input_net_transition; variable_2 : total_output_net_capacitance; index_1("0.0125, 0.05, 0.4"); - index_2("2.45605, 9.8242, 78.5936"); + index_2("0.00245605, 0.0098242, 0.0392968"); } lu_table_template(CONSTRAINT_TABLE){ @@ -78,17 +81,26 @@ cell (sram_2_16_1_scn4m_subm){ dont_use : true; map_only : true; dont_touch : true; - area : 60774.3; + area : 0; + + pg_pin(vdd) { + voltage_name : VDD; + pg_type : primary_power; + } + + pg_pin(gnd) { + voltage_name : GND; + pg_type : primary_ground; + } leakage_power () { - when : "csb0"; - value : 0.000179; + value : 0.000198; } - cell_leakage_power : 0; + cell_leakage_power : 0.000198; bus(din0){ bus_type : data; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; memory_write(){ address : addr0; clocked_on : clk0; @@ -127,8 +139,8 @@ cell (sram_2_16_1_scn4m_subm){ bus(dout0){ bus_type : data; direction : output; - max_capacitance : 78.5936; - min_capacitance : 2.45605; + max_capacitance : 0.0392968; + min_capacitance : 0.00245605; memory_read(){ address : addr0; } @@ -138,24 +150,24 @@ cell (sram_2_16_1_scn4m_subm){ related_pin : "clk0"; timing_type : falling_edge; cell_rise(CELL_TABLE) { - values("0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268"); + values("1.314, 1.332, 1.404",\ + "1.314, 1.332, 1.404",\ + "1.314, 1.332, 1.404"); } cell_fall(CELL_TABLE) { - values("0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268",\ - "0.268, 0.268, 0.268"); + values("1.314, 1.332, 1.404",\ + "1.314, 1.332, 1.404",\ + "1.314, 1.332, 1.404"); } rise_transition(CELL_TABLE) { - values("0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004"); + values("0.006, 0.008, 0.015",\ + "0.006, 0.008, 0.015",\ + "0.006, 0.008, 0.015"); } fall_transition(CELL_TABLE) { - values("0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004",\ - "0.004, 0.004, 0.004"); + values("0.006, 0.008, 0.015",\ + "0.006, 0.008, 0.015",\ + "0.006, 0.008, 0.015"); } } } @@ -164,7 +176,7 @@ cell (sram_2_16_1_scn4m_subm){ bus(addr0){ bus_type : addr; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; max_transition : 0.4; pin(addr0[3:0]){ timing(){ @@ -200,7 +212,7 @@ cell (sram_2_16_1_scn4m_subm){ pin(csb0){ direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -233,7 +245,7 @@ cell (sram_2_16_1_scn4m_subm){ pin(web0){ direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; timing(){ timing_type : setup_rising; related_pin : "clk0"; @@ -267,52 +279,61 @@ cell (sram_2_16_1_scn4m_subm){ pin(clk0){ clock : true; direction : input; - capacitance : 9.8242; + capacitance : 0.0098242; internal_power(){ - when : "!csb0 & clk0 & !web0"; + when : "!csb0 & !web0"; rise_power(scalar){ - values("11.3049604371"); + values("7.017537e+00"); } fall_power(scalar){ - values("11.3049604371"); + values("7.017537e+00"); } } internal_power(){ - when : "!csb0 & !clk0 & web0"; + when : "csb0 & !web0"; rise_power(scalar){ - values("11.3049604371"); + values("7.017537e+00"); } fall_power(scalar){ - values("11.3049604371"); + values("7.017537e+00"); } } internal_power(){ - when : "csb0"; + when : "!csb0 & web0"; rise_power(scalar){ - values("0"); + values("7.017537e+00"); } fall_power(scalar){ - values("0"); + values("7.017537e+00"); + } + } + internal_power(){ + when : "csb0 & web0"; + rise_power(scalar){ + values("7.017537e+00"); + } + fall_power(scalar){ + values("7.017537e+00"); } } timing(){ timing_type :"min_pulse_width"; related_pin : clk0; rise_constraint(scalar) { - values("0.0"); + values("0.1405"); } fall_constraint(scalar) { - values("0.0"); + values("0.1405"); } } timing(){ timing_type :"minimum_period"; related_pin : clk0; rise_constraint(scalar) { - values("0"); + values("0.281"); } fall_constraint(scalar) { - values("0"); + values("0.281"); } } } From 32576fb62c9ac7a69518f7fe1714eeb1de42d912 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 22 Apr 2020 13:27:50 -0700 Subject: [PATCH 100/101] Convert wordline driver to pand2 rather than pnand2+pdriver --- compiler/modules/control_logic.py | 2 +- compiler/modules/hierarchical_decoder.py | 1 - compiler/modules/wordline_driver.py | 119 ++++++++++------------- compiler/pgates/pand2.py | 3 +- 4 files changed, 54 insertions(+), 71 deletions(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index b4c7402e..5dd8182e 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -90,7 +90,7 @@ class control_logic(design.design): self.add_mod(self.ctrl_dff_array) self.and2 = factory.create(module_type="pand2", - size=4, + size=12, height=dff_height) self.add_mod(self.and2) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index f46849c6..69822149 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -32,7 +32,6 @@ class hierarchical_decoder(design.design): self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple except AttributeError: self.cell_multiple = 1 - # For debugging self.cell_height = self.cell_multiple * b.height self.num_outputs = num_outputs diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index d84457ba..76a31074 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -7,10 +7,12 @@ # import debug import design +import math import contact from vector import vector from sram_factory import factory from globals import OPTS +from tech import cell_properties class wordline_driver(design.design): @@ -26,6 +28,13 @@ class wordline_driver(design.design): self.rows = rows self.cols = cols + + b = factory.create(module_type="bitcell") + try: + self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple + except AttributeError: + self.cell_multiple = 1 + self.cell_height = self.cell_multiple * b.height self.create_netlist() if not OPTS.netlist_only: @@ -37,6 +46,7 @@ class wordline_driver(design.design): self.create_drivers() def create_layout(self): + self.setup_layout_constants() self.place_drivers() self.route_layout() self.route_vdd_gnd() @@ -56,17 +66,10 @@ class wordline_driver(design.design): self.add_pin("gnd", "GROUND") def add_modules(self): - b = factory.create(module_type="bitcell") - - self.inv = factory.create(module_type="pdriver", - fanout=self.cols, - neg_polarity=True, - height=b.height) - self.add_mod(self.inv) - - self.nand2 = factory.create(module_type="pnand2", - height=b.height) - self.add_mod(self.nand2) + self.and2 = factory.create(module_type="pand2", + height=self.cell_height, + size=self.cols) + self.add_mod(self.and2) def route_vdd_gnd(self): """ @@ -75,68 +78,64 @@ class wordline_driver(design.design): """ # Find the x offsets for where the vias/pins should be placed - a_xoffset = self.nand_inst[0].lx() + xoffset_list = [self.and_inst[0].lx()] for num in range(self.rows): # this will result in duplicate polygons for rails, but who cares - # use the inverter offset even though it will be the nand's too + # use the inverter offset even though it will be the and's too (gate_offset, y_dir) = self.get_gate_offset(0, - self.inv.height, + self.and2.height, num) - # Route both supplies for name in ["vdd", "gnd"]: - supply_pin = self.inv2_inst[num].get_pin(name) + supply_pin = self.and_inst[num].get_pin(name) # Add pins in two locations - for xoffset in [a_xoffset]: + for xoffset in xoffset_list: pin_pos = vector(xoffset, supply_pin.cy()) self.add_power_pin(name, pin_pos) def create_drivers(self): - self.nand_inst = [] - self.inv2_inst = [] + self.and_inst = [] for row in range(self.rows): - name_nand = "wl_driver_nand{}".format(row) - name_inv2 = "wl_driver_inv{}".format(row) + name_and = "wl_driver_and{}".format(row) - # add nand 2 - self.nand_inst.append(self.add_inst(name=name_nand, - mod=self.nand2)) + # add and2 + self.and_inst.append(self.add_inst(name=name_and, + mod=self.and2)) self.connect_inst(["en", "in_{0}".format(row), - "wl_bar_{0}".format(row), - "vdd", "gnd"]) - # add inv2 - self.inv2_inst.append(self.add_inst(name=name_inv2, - mod=self.inv)) - self.connect_inst(["wl_bar_{0}".format(row), "wl_{0}".format(row), "vdd", "gnd"]) + + def setup_layout_constants(self): + # We may have more than one bitcell per decoder row + self.num_rows = math.ceil(self.rows / self.cell_multiple) + # We will place this many final decoders per row + self.decoders_per_row = math.ceil(self.rows / self.num_rows) def place_drivers(self): - nand2_xoffset = 2 * self.m1_width + 5 * self.m1_space - inv2_xoffset = nand2_xoffset + self.nand2.width + and2_xoffset = 2 * self.m1_width + 5 * self.m1_space - self.width = inv2_xoffset + self.inv.width - self.height = self.inv.height * self.rows + self.width = and2_xoffset + self.and2.width + self.height = self.and2.height * self.num_rows for row in range(self.rows): + #row = math.floor(inst_index / self.decoders_per_row) + #dec = inst_index % self.decoders_per_row + if (row % 2): - y_offset = self.inv.height * (row + 1) + y_offset = self.and2.height * (row + 1) inst_mirror = "MX" else: - y_offset = self.inv.height * row + y_offset = self.and2.height * row inst_mirror = "R0" - nand2_offset = [nand2_xoffset, y_offset] - inv2_offset = [inv2_xoffset, y_offset] + # x_off = self.internal_routing_width + dec * and_mod.width + and2_offset = [and2_xoffset, y_offset] - # add nand 2 - self.nand_inst[row].place(offset=nand2_offset, - mirror=inst_mirror) - # add inv2 - self.inv2_inst[row].place(offset=inv2_offset, + # add and2 + self.and_inst[row].place(offset=and2_offset, mirror=inst_mirror) def route_layout(self): @@ -151,11 +150,10 @@ class wordline_driver(design.design): height=self.height) for row in range(self.rows): - nand_inst = self.nand_inst[row] - inv2_inst = self.inv2_inst[row] + and_inst = self.and_inst[row] # en connection - a_pin = nand_inst.get_pin("A") + a_pin = and_inst.get_pin("A") a_pos = a_pin.lc() clk_offset = vector(en_pin.bc().x, a_pos.y) self.add_segment_center(layer="m1", @@ -164,18 +162,10 @@ class wordline_driver(design.design): self.add_via_center(layers=self.m1_stack, offset=clk_offset) - # Nand2 out to 2nd inv - zr_pos = nand_inst.get_pin("Z").rc() - al_pos = inv2_inst.get_pin("A").lc() - # ensure the bend is in the middle - mid1_pos = vector(0.5 * (zr_pos.x + al_pos.x), zr_pos.y) - mid2_pos = vector(0.5 * (zr_pos.x + al_pos.x), al_pos.y) - self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos]) - - # connect the decoder input pin to nand2 B - b_pin = nand_inst.get_pin("B") + # connect the decoder input pin to and2 B + b_pin = and_inst.get_pin("B") b_pos = b_pin.lc() - # needs to move down since B nand input is + # needs to move down since B and input is # nearly aligned with A inv input up_or_down = self.m2_space if row % 2 else -self.m2_space input_offset = vector(0, b_pos.y + up_or_down) @@ -192,7 +182,7 @@ class wordline_driver(design.design): offset=mid_via_offset, directions=("V", "V")) - # now connect to the nand2 B + # now connect to the and2 B self.add_path("m2", [mid_via_offset, b_pos]) contact_offset = b_pos - vector(0.5 * contact.m1_via.height, 0) self.add_via_center(layers=self.m1_stack, @@ -200,7 +190,7 @@ class wordline_driver(design.design): directions=("H", "H")) # output each WL on the right - wl_offset = inv2_inst.get_pin("Z").rc() + wl_offset = and_inst.get_pin("Z").rc() self.add_layout_pin_segment_center(text="wl_{0}".format(row), layer="m1", start=wl_offset, @@ -213,13 +203,8 @@ class wordline_driver(design.design): """ stage_effort_list = [] - stage1_cout = self.inv.get_cin() - stage1 = self.nand2.get_stage_effort(stage1_cout, inp_is_rise) + stage1 = self.and2.get_stage_effort(external_cout, inp_is_rise) stage_effort_list.append(stage1) - last_stage_is_rise = stage1.is_rise - - stage2 = self.inv.get_stage_efforts(external_cout, last_stage_is_rise) - stage_effort_list.extend(stage2) return stage_effort_list @@ -228,6 +213,6 @@ class wordline_driver(design.design): Get the relative capacitance of all the enable connections in the bank """ - # The enable is connected to a nand2 for every row. - total_cin = self.nand2.get_cin() * self.rows + # The enable is connected to a and2 for every row. + total_cin = self.and2.get_cin() * self.rows return total_cin diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 995296b6..4c044f1c 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -30,13 +30,12 @@ class pand2(pgate.pgate): self.create_insts() def create_modules(self): - # Shield the cap, but have at least a stage effort of 4 self.nand = factory.create(module_type="pnand2", height=self.height) self.add_mod(self.nand) self.inv = factory.create(module_type="pdriver", neg_polarity=True, - fanout=3*self.size, + fanout=self.size, height=self.height) self.add_mod(self.inv) From fed1c0bbe10c43425bae1dd46c16775c1714e1e3 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 22 Apr 2020 16:22:34 -0700 Subject: [PATCH 101/101] s8 col mux array --- .../modules/single_level_column_mux_array.py | 115 +++++++++--------- compiler/pgates/single_level_column_mux.py | 45 ++++--- 2 files changed, 85 insertions(+), 75 deletions(-) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 324b7415..581e20b0 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -5,17 +5,15 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from math import log import design -import contact -from tech import drc import debug -import math +from tech import layer from vector import vector from sram_factory import factory from globals import OPTS import logical_effort + class single_level_column_mux_array(design.design): """ Dynamically generated column mux array. @@ -26,13 +24,20 @@ class single_level_column_mux_array(design.design): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br)) - + self.columns = columns self.word_size = word_size self.words_per_row = int(self.columns / self.word_size) self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br - + + if "li" in layer: + self.col_mux_stack = self.li_stack + self.col_mux_stack_pitch = self.li_pitch + else: + self.col_mux_stack = self.m1_stack + self.col_mux_stack_pitch = self.m1_pitch + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -49,20 +54,20 @@ class single_level_column_mux_array(design.design): self.add_modules() self.add_pins() self.create_array() - + def create_layout(self): self.setup_layout_constants() self.place_array() self.add_routing() # Find the highest shapes to determine height before adding well highest = self.find_highest_coords() - self.height = highest.y + self.height = highest.y self.add_layout_pins() - self.add_enclosure(self.mux_inst, "pwell") - + if "pwell" in layer: + self.add_enclosure(self.mux_inst, "pwell") self.add_boundary() self.DRC_LVS() - + def add_pins(self): for i in range(self.columns): self.add_pin("bl_{}".format(i)) @@ -74,23 +79,19 @@ class single_level_column_mux_array(design.design): self.add_pin("br_out_{}".format(i)) self.add_pin("gnd") - def add_modules(self): self.mux = factory.create(module_type="single_level_column_mux", bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) self.add_mod(self.mux) - def setup_layout_constants(self): - self.column_addr_size = num_of_inputs = int(self.words_per_row / 2) + self.column_addr_size = int(self.words_per_row / 2) self.width = self.columns * self.mux.width # one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br # one extra route pitch is to space from the sense amp - self.route_height = (self.words_per_row + 3)*self.m1_pitch - + self.route_height = (self.words_per_row + 3) * self.col_mux_stack_pitch - def create_array(self): self.mux_inst = [] # For every column, add a pass gate @@ -98,11 +99,11 @@ class single_level_column_mux_array(design.design): name = "XMUX{0}".format(col_num) self.mux_inst.append(self.add_inst(name=name, mod=self.mux)) - + self.connect_inst(["bl_{}".format(col_num), "br_{}".format(col_num), - "bl_out_{}".format(int(col_num/self.words_per_row)), - "br_out_{}".format(int(col_num/self.words_per_row)), + "bl_out_{}".format(int(col_num / self.words_per_row)), + "br_out_{}".format(int(col_num / self.words_per_row)), "sel_{}".format(col_num % self.words_per_row), "gnd"]) @@ -117,32 +118,31 @@ class single_level_column_mux_array(design.design): else: mirror = "" - name = "XMUX{0}".format(col_num) offset = vector(xoffset, self.route_height) self.mux_inst[col_num].place(offset=offset, mirror=mirror) - def add_layout_pins(self): """ Add the pins after we determine the height. """ # For every column, add a pass gate for col_num in range(self.columns): mux_inst = self.mux_inst[col_num] - offset = mux_inst.get_pin("bl").ll() + bl_pin = mux_inst.get_pin("bl") + offset = bl_pin.ll() self.add_layout_pin(text="bl_{}".format(col_num), - layer="m2", + layer=bl_pin.layer, offset=offset, - height=self.height-offset.y) + height=self.height - offset.y) - offset = mux_inst.get_pin("br").ll() + br_pin = mux_inst.get_pin("br") + offset = br_pin.ll() self.add_layout_pin(text="br_{}".format(col_num), - layer="m2", + layer=br_pin.layer, offset=offset, - height=self.height-offset.y) + height=self.height - offset.y) for inst in self.mux_inst: self.copy_layout_pin(inst, "gnd") - def add_routing(self): self.add_horizontal_input_rail() self.add_vertical_poly_rail() @@ -151,15 +151,15 @@ class single_level_column_mux_array(design.design): def add_horizontal_input_rail(self): """ Create address input rails on M1 below the mux transistors """ for j in range(self.words_per_row): - offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch) + offset = vector(0, self.route_height + (j - self.words_per_row) * self.col_mux_stack_pitch) #edit self.add_layout_pin(text="sel_{}".format(j), - layer="m1", + layer=self.col_mux_stack[0], offset=offset, width=self.mux.width * self.columns) def add_vertical_poly_rail(self): """ Connect the poly to the address rails """ - + # Offset to the first transistor gate in the pass gate for col in range(self.columns): # which select bit should this column connect to depends on the position in the word @@ -167,11 +167,12 @@ class single_level_column_mux_array(design.design): # Add the column x offset to find the right select bit gate_offset = self.mux_inst[col].get_pin("sel").bc() # height to connect the gate to the correct horizontal row - sel_height = self.get_pin("sel_{}".format(sel_index)).by() + # sel_height = self.get_pin("sel_{}".format(sel_index)).by() # use the y offset from the sel pin and the x offset from the gate - offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy()) + offset = vector(gate_offset.x, + self.get_pin("sel_{}".format(sel_index)).cy()) # Add the poly contact with a shift to account for the rotation - self.add_via_center(layers=("m1", "contact", "poly"), + self.add_via_center(layers=self.poly_stack, offset=offset) self.add_path("poly", [offset, gate_offset]) @@ -182,11 +183,11 @@ class single_level_column_mux_array(design.design): bl_offset = self.mux_inst[j].get_pin("bl_out").bc() br_offset = self.mux_inst[j].get_pin("br_out").bc() - bl_out_offset = bl_offset - vector(0,(self.words_per_row+1)*self.m1_pitch) - br_out_offset = br_offset - vector(0,(self.words_per_row+2)*self.m1_pitch) + bl_out_offset = bl_offset - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch) + br_out_offset = br_offset - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch) - bl_out_offset_end = bl_out_offset + vector(0,self.route_height) - br_out_offset_end = br_out_offset + vector(0,self.route_height) + bl_out_offset_end = bl_out_offset + vector(0, self.route_height) + br_out_offset_end = br_out_offset + vector(0, self.route_height) if cell_properties.bitcell.mirror.y and j % 2: tmp_bl_out_end = br_out_offset_end @@ -208,43 +209,41 @@ class single_level_column_mux_array(design.design): else: dist = 0 - self.add_path("m1", [bl_out_offset, bl_out_offset+vector(width+dist,0)]) - self.add_path("m1", [br_out_offset, br_out_offset+vector(width-dist,0)]) + self.add_path(self.col_mux_stack[0], [bl_out_offset, bl_out_offset + vector(width + dist, 0)]) + self.add_path(self.col_mux_stack[0], [br_out_offset, br_out_offset + vector(width - dist, 0)]) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux - self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)), - layer="m2", + self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)), + layer=self.col_mux_stack[2], start=bl_out_offset, end=tmp_bl_out_end) - self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)), - layer="m2", + self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)), + layer=self.col_mux_stack[2], start=br_out_offset, end=tmp_br_out_end) - - # This via is on the right of the wire - self.add_via_center(layers=self.m1_stack, + # This via is on the right of the wire + self.add_via_center(layers=self.col_mux_stack, offset=bl_out_offset) # This via is on the left of the wire - self.add_via_center(layers=self.m1_stack, + self.add_via_center(layers=self.col_mux_stack, offset=br_out_offset) else: - - self.add_path("m2", [ bl_out_offset, tmp_bl_out_end]) - self.add_path("m2", [ br_out_offset, tmp_br_out_end]) - + self.add_path(self.col_mux_stack[2], [bl_out_offset, bl_offset]) + self.add_path(self.col_mux_stack[2], [br_out_offset, br_offset]) + # This via is on the right of the wire - self.add_via_center(layers=self.m1_stack, + self.add_via_center(layers=self.col_mux_stack, offset=bl_out_offset) - # This via is on the left of the wire - self.add_via_center(layers=self.m1_stack, + # This via is on the left of the wire + self.add_via_center(layers=self.col_mux_stack, offset=br_out_offset) def get_drain_cin(self): """Get the relative capacitance of the drain of the NMOS pass TX""" from tech import parameter - #Bitcell drain load being used to estimate mux NMOS drain load + # Bitcell drain load being used to estimate mux NMOS drain load drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) - return drain_load + return drain_load diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 288894aa..9e38287f 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -121,13 +121,25 @@ class single_level_column_mux(pgate.pgate): # height is the distance between the nmos' diffusions, which depends on max(self.active_space,self.poly_space) offset = self.nmos_lower.get_pin("G").ul() - vector(0,self.poly_extend_active) height = self.nmos_upper.get_pin("G").by() + self.poly_extend_active - offset.y - self.add_layout_pin(text="sel", - layer="poly", + self.add_rect(layer="poly", offset=offset, height=height) + # Add the sel pin to the bottom of the mux + self.add_layout_pin(text="sel", + layer="poly", + offset=self.nmos_lower.get_pin("G").ll(), + height=self.poly_extend_active) + def connect_bitlines(self): """ Connect the bitlines to the mux transistors """ + + # If li exists, use li and m1 for the mux, otherwise use m1 and m2 + if "li" in layer: + self.col_mux_stack = self.li_stack + else: + self.col_mux_stack = self.m1_stack + # These are on metal2 bl_pin = self.get_pin("bl") br_pin = self.get_pin("br") @@ -140,29 +152,24 @@ class single_level_column_mux(pgate.pgate): nmos_upper_s_pin = self.nmos_upper.get_pin("S") nmos_upper_d_pin = self.nmos_upper.get_pin("D") - # If li exists, use li and m1 for the mux, otherwise use m1 and m2 - if "li" in layer: - col_mux_stack = self.li_stack - else: - col_mux_stack = self.m1_stack - # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D - self.add_via_center(layers=col_mux_stack, + self.add_via_center(layers=self.col_mux_stack, offset=bl_pin.bc(), directions=("V", "V")) - self.add_via_center(layers=col_mux_stack, + self.add_via_center(layers=self.col_mux_stack, offset=br_out_pin.uc(), directions=("V", "V")) - self.add_via_center(layers=col_mux_stack, + self.add_via_center(layers=self.col_mux_stack, offset=nmos_upper_s_pin.center(), directions=("V", "V")) - self.add_via_center(layers=col_mux_stack, + self.add_via_center(layers=self.col_mux_stack, offset=nmos_lower_d_pin.center(), directions=("V", "V")) # Add diffusion contacts # These were previously omitted with the options: add_source_contact=False, add_drain_contact=False - # They are added now and not previously due to a s8 tech special case in which the contacts intersected the mux intraconnect + # They are added now and not previously so that they do not include m1 (which is usually included by default) + # This is only a concern when the local interconnect (li) layer is being used self.add_via_center(layers=self.active_stack, offset=nmos_upper_d_pin.center(), directions=("V", "V"), @@ -186,7 +193,7 @@ class single_level_column_mux(pgate.pgate): # bl -> nmos_upper/D on metal1 # bl_out -> nmos_upper/S on metal2 - self.add_path(col_mux_stack[0], + self.add_path(self.col_mux_stack[0], [bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()), nmos_upper_d_pin.center()]) # halfway up, move over @@ -194,12 +201,12 @@ class single_level_column_mux(pgate.pgate): + nmos_upper_s_pin.bc().scale(0, 0.4) mid2 = bl_out_pin.uc().scale(0, 0.4) \ + nmos_upper_s_pin.bc().scale(1, 0.4) - self.add_path(col_mux_stack[2], + self.add_path(self.col_mux_stack[2], [bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.center()]) # br -> nmos_lower/D on metal2 # br_out -> nmos_lower/S on metal1 - self.add_path(col_mux_stack[0], + self.add_path(self.col_mux_stack[0], [br_out_pin.uc(), vector(nmos_lower_s_pin.cx(), br_out_pin.uy()), nmos_lower_s_pin.center()]) @@ -208,7 +215,7 @@ class single_level_column_mux(pgate.pgate): + nmos_lower_d_pin.uc().scale(0,0.5) mid2 = br_pin.bc().scale(0,0.5) \ + nmos_lower_d_pin.uc().scale(1,0.5) - self.add_path(col_mux_stack[2], + self.add_path(self.col_mux_stack[2], [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()]) def add_wells(self): @@ -225,6 +232,10 @@ class single_level_column_mux(pgate.pgate): implant_type="p", well_type="p") + # If there is a li layer, include it in the power stack + self.add_via_center(layers=self.col_mux_stack, + offset=active_pos) + # Add the M1->..->power_grid_layer stack self.add_power_pin(name = "gnd", loc = active_pos,