From 35110a4453dd7b9f03959c326f3a6141c229b12e Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 25 Feb 2020 00:34:28 +0000 Subject: [PATCH 01/22] Improve debug of non-manhattan error --- compiler/base/hierarchy_layout.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 451d1356..2e0bf755 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -322,9 +322,10 @@ class layout(): """ Creates a path like pin with center-line convention """ - - debug.check(start.x == end.x or start.y == end.y, - "Cannot have a non-manhatten layout pin.") + if start.x != end.x and start.y != end.y: + 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)] From 6bcffb8efb680f1321098580c52e59bf3fb0e2a8 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 25 Feb 2020 00:34:59 +0000 Subject: [PATCH 02/22] Change default cell height and fix contact width error --- compiler/pgates/pgate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 6d385d09..8b9a1c66 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -26,8 +26,8 @@ class pgate(design.design): if height: self.height = height elif not height: - # By default, we make it 8 M1 pitch tall - self.height = 8*self.m1_pitch + # By default, we make it 10 M1 pitch tall + self.height = 10*self.m1_pitch self.create_netlist() if not OPTS.netlist_only: @@ -103,7 +103,7 @@ class pgate(design.design): - vector(0.5 * contact_width - 0.5 * self.poly_width, 0) elif position == "right": contact_offset = left_gate_offset \ - + vector(0.5 * contact.width + 0.5 * self.poly_width, 0) + + vector(0.5 * contact_width + 0.5 * self.poly_width, 0) else: debug.error("Invalid contact placement option.", -1) From d565c9ac727917fc32efb999c13ffb671f620386 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 25 Feb 2020 00:35:32 +0000 Subject: [PATCH 03/22] Generalize input y offsets --- compiler/pgates/pnand2.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index dd04ccb9..7c40c7ab 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -190,16 +190,16 @@ class pnand2(pgate.pgate): def route_inputs(self): """ Route the A and B inputs """ - inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \ - + self.m2_space + 0.5 * 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, "B", - position="center") + position="right") # This will help with the wells and the input/output placement - self.inputA_yoffset = inputB_yoffset + self.input_spacing + self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \ + - contact.poly_contact.height self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, From 585a708e0c6972a3799ffc3b3919884ee40546ab Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 25 Feb 2020 00:36:02 +0000 Subject: [PATCH 04/22] Generalize y offsets in pnand3 --- compiler/pgates/pnand3.py | 46 ++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 9ec9ff60..087cffd5 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -87,13 +87,14 @@ class pnand3(pgate.pgate): """ Pre-compute some handy layout parameters. """ # Compute the overlap of the source and drain pins - self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() - + overlap_xoffset = self.pmos.get_pin("D").ll().x - self.pmos.get_pin("S").ll().x + self.ptx_offset = vector(overlap_xoffset, 0) + # Two PMOS devices and a well contact. Separation between each. # Enclosure space on the sides. self.width = 3 * self.pmos.active_width + self.pmos.active_contact.width \ + 2 * self.active_space + 0.5 * self.nwell_enclose_active \ - - self.overlap_offset.x + - self.ptx_offset.x self.well_width = self.width + 2 * self.nwell_enclose_active # Height is an input parameter, so it is not recomputed. @@ -157,10 +158,10 @@ class pnand3(pgate.pgate): - self.top_bottom_space) self.pmos1_inst.place(pmos1_pos) - pmos2_pos = pmos1_pos + self.overlap_offset + pmos2_pos = pmos1_pos + self.ptx_offset self.pmos2_inst.place(pmos2_pos) - self.pmos3_pos = pmos2_pos + self.overlap_offset + self.pmos3_pos = pmos2_pos + self.ptx_offset self.pmos3_inst.place(self.pmos3_pos) @@ -168,10 +169,10 @@ class pnand3(pgate.pgate): self.top_bottom_space) self.nmos1_inst.place(nmos1_pos) - nmos2_pos = nmos1_pos + self.overlap_offset + nmos2_pos = nmos1_pos + self.ptx_offset self.nmos2_inst.place(nmos2_pos) - self.nmos3_pos = nmos2_pos + self.overlap_offset + self.nmos3_pos = nmos2_pos + self.ptx_offset self.nmos3_inst.place(self.nmos3_pos) # This will help with the wells and the input/output placement @@ -194,34 +195,29 @@ class pnand3(pgate.pgate): def route_inputs(self): """ Route the A and B inputs """ - # wire space or wire and one contact space - metal_spacing = max(self.m1_space + self.m1_width, - self.m2_space + self.m2_width, - self.m1_space + 0.5 *contact.poly_contact.width + 0.5 * self.m1_width) - - active_spacing = max(self.m1_space, - 0.5 * contact.poly_contact.first_layer_width + self.poly_to_active) - inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing + self.inputC_yoffset = self.nmos3_inst.uy() + 0.5 * contact.poly_contact.height self.route_input_gate(self.pmos3_inst, self.nmos3_inst, - inputC_yoffset, + self.inputC_yoffset, "C", position="center") - inputB_yoffset = inputC_yoffset + metal_spacing - self.route_input_gate(self.pmos2_inst, - self.nmos2_inst, - inputB_yoffset, - "B", - position="center") - - self.inputA_yoffset = inputB_yoffset + metal_spacing + self.inputA_yoffset = self.pmos1_inst.by() - self.poly_extend_active \ + - contact.poly_contact.height self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A", position="center") + + self.inputB_yoffset = 0.5*(self.inputA_yoffset + self.inputC_yoffset) + self.route_input_gate(self.pmos2_inst, + self.nmos2_inst, + self.inputB_yoffset, + "B", + position="center") + def route_output(self): """ Route the Z output """ # PMOS1 drain @@ -243,7 +239,7 @@ class pnand3(pgate.pgate): directions=("V", "V")) # PMOS3 and NMOS3 are drain aligned - self.add_path("m2", [pmos3_pin.center(), nmos3_pin.uc()]) + self.add_path("m2", [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()]) From 254e584e35d51f5327effa5241ff4132d86cd9fa Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 25 Feb 2020 00:36:22 +0000 Subject: [PATCH 05/22] Cleanup and simplify ptx for multiple technologies --- compiler/pgates/ptx.py | 153 ++++++++++++++++++++++------------------- 1 file changed, 84 insertions(+), 69 deletions(-) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index f331dc28..8dd0da33 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -10,6 +10,7 @@ import debug from tech import layer, drc, spice from vector import vector from sram_factory import factory +import contact class ptx(design.design): @@ -28,6 +29,7 @@ class ptx(design.design): tx_type="nmos", 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 @@ -50,6 +52,7 @@ class ptx(design.design): self.tx_width = width self.connect_active = connect_active self.connect_poly = connect_poly + self.series_devices = series_devices self.num_contacts = num_contacts # Since it has variable height, it is not a pgate. @@ -127,27 +130,25 @@ class ptx(design.design): directions=("V", "V"), dimensions=(1, self.num_contacts)) - # The contacted poly pitch - self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width, - self.poly_space) - - # The contacted poly pitch - self.contact_spacing = 2 * self.contact_to_gate + \ - self.contact_width + self.poly_width - - # This is measured because of asymmetric enclosure rules - active_enclose_contact = 0.5*(self.active_contact.width - self.contact_width) + # This is the extra poly spacing due to the poly contact to poly contact pitch + # of contacted gates + extra_poly_contact_width = contact.poly_contact.width - self.poly_width - # This is the distance from the side of - # poly gate to the contacted end of active - # (i.e. the "outside" contacted diffusion sizes) - self.end_to_poly = self.active_contact.width - active_enclose_contact + \ - self.contact_to_gate + # This is the spacing between S/D contacts + # This is the spacing between the poly gates + self.min_poly_pitch = self.poly_space + self.poly_width + self.contacted_poly_pitch = self.poly_space + contact.poly_contact.width + self.contact_pitch = 2 * self.contact_to_gate + self.poly_width + self.contact_width + self.poly_pitch = max(self.min_poly_pitch, + self.contacted_poly_pitch, + self.contact_pitch) + + self.end_to_contact = 0.5 * self.active_contact.width # Active width is determined by enclosure on both ends and contacted pitch, # at least one poly and n-1 poly pitches - self.active_width = 2 * self.end_to_poly + self.poly_width + \ - (self.mults - 1) * self.poly_pitch + self.active_width = 2 * self.end_to_contact + self.active_contact.width \ + + 2 * self.contact_to_gate + self.poly_width + (self.mults - 1) * self.poly_pitch # Active height is just the transistor width self.active_height = self.tx_width @@ -287,12 +288,11 @@ class ptx(design.design): Add the poly gates(s) and (optionally) connect them. """ # poly is one contacted spacing from the end and down an extension - poly_offset = self.active_offset \ - + vector(self.poly_width, self.poly_height).scale(0.5, 0.5) \ - + vector(self.end_to_poly, -self.poly_extend_active) - + poly_offset = self.contact_offset \ + + vector(0.5 * self.active_contact.width + 0.5 * self.poly_width + self.contact_to_gate, 0) + # poly_positions are the bottom center of the poly gates - poly_positions = [] + self.poly_positions = [] # It is important that these are from left to right, # so that the pins are in the right @@ -309,11 +309,11 @@ class ptx(design.design): offset=poly_offset, height=self.poly_height, width=self.poly_width) - poly_positions.append(poly_offset) + self.poly_positions.append(poly_offset) poly_offset = poly_offset + vector(self.poly_pitch,0) if self.connect_poly: - self.connect_fingered_poly(poly_positions) + self.connect_fingered_poly(self.poly_positions) def add_active(self): """ @@ -362,59 +362,74 @@ class ptx(design.design): """ return 1 - def get_contact_positions(self): - """ - Create a list of the centers of drain and source contact positions. - """ - # The first one will always be a source - source_positions = [self.contact_offset] - drain_positions = [] - # It is important that these are from left to right, - # so that the pins are in the right - # order for the accessors. - for i in range(self.mults): - if i%2: - # It's a source... so offset from previous drain. - source_positions.append(drain_positions[-1] + vector(self.contact_spacing, 0)) - else: - # It's a drain... so offset from previous source. - drain_positions.append(source_positions[-1] + vector(self.contact_spacing, 0)) - - return [source_positions,drain_positions] - def add_active_contacts(self): """ Add the active contacts to the transistor. """ + drain_positions = [] + source_positions = [] - [source_positions,drain_positions] = self.get_contact_positions() - - for pos in source_positions: - contact=self.add_via_center(layers=self.active_stack, + # 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", 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="S", - layer="m1", - offset=pos, - width=contact.mod.second_layer_width, - height=contact.mod.second_layer_height) + width=contact.mod.second_layer_width, + height=contact.mod.second_layer_height) + source_positions.append(pos) - - for pos in drain_positions: - 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="D", - layer="m1", + # Skip these if they are going to be in series + if not self.series_devices: + for (poly1, poly2) in zip(self.poly_positions, self.poly_positions[1:]): + pos = vector(0.5 * (poly1.x + poly2.x), + self.contact_offset.y) + # Alternate source and drains + if label == "S": + label = "D" + drain_positions.append(pos) + else: + label = "S" + source_positions.append(pos) + + contact=self.add_via_center(layers=self.active_stack, offset=pos, - width=contact.mod.second_layer_width, - height=contact.mod.second_layer_height) + 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) + + pos = vector(self.active_offset.x + self.active_width - 0.5 * self.active_contact.width, + self.contact_offset.y) + # Last one is the opposite of previous + if label == "S": + label = "D" + drain_positions.append(pos) + 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", + offset=pos, + width=contact.mod.second_layer_width, + height=contact.mod.second_layer_height) if self.connect_active: self.connect_fingered_active(drain_positions, source_positions) From 266d68c3955046d811bb84c5a0cd1e3bd787c250 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 25 Feb 2020 17:09:07 +0000 Subject: [PATCH 06/22] Generalize pgate width based on nwell/pwell contacts --- compiler/pgates/pgate.py | 9 +++++++++ compiler/pgates/pinv.py | 17 +---------------- compiler/pgates/pnand2.py | 14 +++----------- compiler/pgates/pnand3.py | 17 +++++------------ compiler/pgates/pnor2.py | 5 +++-- 5 files changed, 21 insertions(+), 41 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 8b9a1c66..59ec2448 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -264,3 +264,12 @@ class pgate(design.design): # offset=implant_offset, # width=implant_width, # height=implant_height) + + def determine_width(self): + """ Determine the width based on the well contacts (assumed to be on the right side) """ + # Width is determined by well contact and spacing + self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + self.well_width = self.width + 2 * self.nwell_enclose_active + # Height is an input parameter, so it is not recomputed. + + diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index c0f1f955..3c2e41a2 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -51,9 +51,9 @@ class pinv(pgate.pgate): def create_layout(self): """ Calls all functions related to the generation of the layout """ - self.setup_layout_constants() self.place_ptx() self.add_well_contacts() + self.determine_width() self.extend_wells() self.route_supply_rails() self.connect_rails() @@ -147,21 +147,6 @@ class pinv(pgate.pgate): debug.check(self.pmos_width >= drc("minwidth_tx"), "Cannot finger PMOS transistors to fit cell height.") - def setup_layout_constants(self): - """ - Compute the width and height - """ - - # the width is determined the multi-finger PMOS device width plus - # the well contact width, spacing between them - # space is for power supply contact to nwell m1 spacing - self.width = self.pmos.active_offset.x + self.pmos.active_width \ - + self.active_space + contact.nwell_contact.width \ - + 0.5 * self.nwell_enclose_active \ - + self.m1_space - # This includes full enclosures on each end - self.well_width = self.width + 2*self.nwell_enclose_active - # Height is an input parameter, so it is not recomputed. def add_ptx(self): """ Create the PMOS and NMOS transistors. """ diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 7c40c7ab..05eabbe0 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -49,10 +49,11 @@ class pnand2(pgate.pgate): """ Calls all functions related to the generation of the layout """ self.setup_layout_constants() - self.route_supply_rails() self.place_ptx() - self.connect_rails() self.add_well_contacts() + self.determine_width() + self.route_supply_rails() + self.connect_rails() self.extend_wells() self.route_inputs() self.route_output() @@ -96,15 +97,6 @@ class pnand2(pgate.pgate): # source and drain pins self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.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 + contact.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. - # 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) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 087cffd5..0a46b633 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -57,13 +57,15 @@ class pnand3(pgate.pgate): """ Calls all functions related to the generation of the layout """ self.setup_layout_constants() - self.route_supply_rails() self.place_ptx() - self.connect_rails() self.add_well_contacts() + self.determine_width() + self.route_supply_rails() + self.connect_rails() self.extend_wells() self.route_inputs() self.route_output() + def add_ptx(self): """ Create the PMOS and NMOS transistors. """ @@ -90,14 +92,6 @@ class pnand3(pgate.pgate): overlap_xoffset = self.pmos.get_pin("D").ll().x - self.pmos.get_pin("S").ll().x self.ptx_offset = vector(overlap_xoffset, 0) - # Two PMOS devices and a well contact. Separation between each. - # Enclosure space on the sides. - self.width = 3 * self.pmos.active_width + self.pmos.active_contact.width \ - + 2 * self.active_space + 0.5 * self.nwell_enclose_active \ - - self.ptx_offset.x - self.well_width = self.width + 2 * self.nwell_enclose_active - # Height is an input parameter, so it is not recomputed. - # This is the extra space needed to ensure DRC rules # to the active contacts nmos = factory.create(module_type="ptx", tx_type="nmos") @@ -164,7 +158,6 @@ class pnand3(pgate.pgate): self.pmos3_pos = pmos2_pos + self.ptx_offset self.pmos3_inst.place(self.pmos3_pos) - nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space) self.nmos1_inst.place(nmos1_pos) @@ -189,7 +182,7 @@ class pnand3(pgate.pgate): self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd") - self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd") + self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd") self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd") diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index d6605d68..3d998d25 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -48,10 +48,11 @@ class pnor2(pgate.pgate): """ Calls all functions related to the generation of the layout """ self.setup_layout_constants() - self.route_supply_rails() self.place_ptx() - self.connect_rails() self.add_well_contacts() + self.determine_width() + self.route_supply_rails() + self.connect_rails() self.extend_wells() self.route_inputs() self.route_output() From 073bd47b316e3f9ff103071d76a465ff3e024f0d Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 28 Feb 2020 18:23:36 +0000 Subject: [PATCH 07/22] Add source/drain/gate to structure --- compiler/pgates/ptx.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index 8dd0da33..46726b37 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -293,7 +293,8 @@ class ptx(design.design): # poly_positions are the bottom center of the poly gates self.poly_positions = [] - + self.poly_gates = [] + # It is important that these are from left to right, # so that the pins are in the right # order for the accessors @@ -304,13 +305,15 @@ class ptx(design.design): offset=poly_offset, height=self.poly_height, width=self.poly_width) - self.add_layout_pin_rect_center(text="G", - layer="poly", - offset=poly_offset, - height=self.poly_height, - width=self.poly_width) + gate = self.add_layout_pin_rect_center(text="G", + layer="poly", + offset=poly_offset, + height=self.poly_height, + width=self.poly_width) self.poly_positions.append(poly_offset) - poly_offset = poly_offset + vector(self.poly_pitch,0) + self.poly_gates.append(gate) + + poly_offset = poly_offset + vector(self.poly_pitch, 0) if self.connect_poly: self.connect_fingered_poly(self.poly_positions) @@ -369,6 +372,10 @@ class ptx(design.design): drain_positions = [] source_positions = [] + # Keep a list of the source/drain contacts + self.source_contacts = [] + self.drain_contacts = [] + # First one is always a SOURCE label = "S" pos = self.contact_offset @@ -383,6 +390,7 @@ class ptx(design.design): offset=pos, width=contact.mod.second_layer_width, height=contact.mod.second_layer_height) + self.source_contacts.append(contact) source_positions.append(pos) # Skip these if they are going to be in series @@ -409,6 +417,11 @@ class ptx(design.design): 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) pos = vector(self.active_offset.x + self.active_width - 0.5 * self.active_contact.width, self.contact_offset.y) @@ -430,6 +443,10 @@ class ptx(design.design): 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) if self.connect_active: self.connect_fingered_active(drain_positions, source_positions) From 0b739793885e6f411e40fdd4c364e211ccdd1f30 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 28 Feb 2020 18:23:49 +0000 Subject: [PATCH 08/22] Space inputs by M1 pitch --- compiler/pgates/pnand3.py | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 0a46b633..a9fd19b7 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -36,7 +36,8 @@ class pnand3(pgate.pgate): self.pmos_width = self.pmos_size * drc("minwidth_tx") # FIXME: Allow these to be sized - debug.check(size == 1,"Size 1 pnand3 is only supported now.") + debug.check(size == 1, + "Size 1 pnand3 is only supported now.") self.tx_mults = 1 # Creates the netlist and layout @@ -66,7 +67,6 @@ class pnand3(pgate.pgate): self.route_inputs() self.route_output() - def add_ptx(self): """ Create the PMOS and NMOS transistors. """ self.nmos = factory.create(module_type="ptx", @@ -148,8 +148,7 @@ class pnand3(pgate.pgate): """ pmos1_pos = vector(self.pmos.active_offset.x, - self.height - self.pmos.active_height \ - - self.top_bottom_space) + self.height - self.pmos.active_height - self.top_bottom_space) self.pmos1_inst.place(pmos1_pos) pmos2_pos = pmos1_pos + self.ptx_offset @@ -168,9 +167,6 @@ class pnand3(pgate.pgate): self.nmos3_pos = nmos2_pos + self.ptx_offset self.nmos3_inst.place(self.nmos3_pos) - # This will help with the wells and the input/output placement - self.output_pos = vector(0, 0.5*self.height) - def add_well_contacts(self): """ Add n/p well taps to the layout and connect to supplies """ @@ -187,29 +183,30 @@ class pnand3(pgate.pgate): self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd") def route_inputs(self): - """ Route the A and B inputs """ - self.inputC_yoffset = self.nmos3_inst.uy() + 0.5 * contact.poly_contact.height + """ Route the A and B and C inputs """ + + # Put B right on the well line + self.inputB_yoffset = self.nwell_y_offset + self.route_input_gate(self.pmos2_inst, + self.nmos2_inst, + self.inputB_yoffset, + "B", + position="center") + + self.inputC_yoffset = self.inputB_yoffset - self.m1_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, "C", position="center") - self.inputA_yoffset = self.pmos1_inst.by() - self.poly_extend_active \ - - contact.poly_contact.height + self.inputA_yoffset = self.inputB_yoffset + self.m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A", position="center") - - self.inputB_yoffset = 0.5*(self.inputA_yoffset + self.inputC_yoffset) - self.route_input_gate(self.pmos2_inst, - self.nmos2_inst, - self.inputB_yoffset, - "B", - position="center") def route_output(self): """ Route the Z output """ From e1b97f58e1cdd084395467c9796a3cbfee725631 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 28 Feb 2020 18:24:09 +0000 Subject: [PATCH 09/22] Add instance center location --- compiler/base/geometry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index cfda3553..5d7f8d76 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -136,7 +136,7 @@ class geometry: def cy(self): """ Return the center y """ return 0.5 * (self.boundary[0].y + self.boundary[1].y) - + class instance(geometry): """ From bb2305d56a313bd5a5210b551bca0f54f63168a9 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 28 Feb 2020 18:24:39 +0000 Subject: [PATCH 10/22] PEP8 format fixes --- compiler/modules/hierarchical_decoder.py | 158 +++++++++-------------- compiler/pgates/pgate.py | 10 +- 2 files changed, 69 insertions(+), 99 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 242ef476..7d625b75 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -77,27 +77,27 @@ class hierarchical_decoder(design.design): height=self.cell_height) self.add_mod(self.pre3_8) - def determine_predecodes(self,num_inputs): + def determine_predecodes(self, num_inputs): """ Determines the number of 2:4 pre-decoder and 3:8 pre-decoder needed based on the number of inputs """ if (num_inputs == 2): - return (1,0) + return (1, 0) elif (num_inputs == 3): - return(0,1) + return(0, 1) elif (num_inputs == 4): - return(2,0) + return(2, 0) elif (num_inputs == 5): - return(1,1) + return(1, 1) elif (num_inputs == 6): - return(3,0) + return(3, 0) elif (num_inputs == 7): - return(2,1) + return(2, 1) elif (num_inputs == 8): - return(1,2) + return(1, 2) elif (num_inputs == 9): - return(0,3) + return(0, 3) else: - debug.error("Invalid number of inputs for hierarchical decoder",-1) + debug.error("Invalid number of inputs for hierarchical decoder", -1) def setup_netlist_constants(self): self.predec_groups = [] # This array is a 2D array. @@ -122,35 +122,35 @@ class hierarchical_decoder(design.design): index = index + 1 self.predec_groups.append(lines) - def setup_layout_constants(self): """ Calculate the overall dimensions of the hierarchical decoder """ # If we have 4 or fewer rows, the predecoder is the decoder itself if self.num_inputs>=4: - self.total_number_of_predecoder_outputs = 4*self.no_of_pre2x4 + 8*self.no_of_pre3x8 + self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8 else: - self.total_number_of_predecoder_outputs = 0 - debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),-1) + self.total_number_of_predecoder_outputs = 0 + debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs), + -1) # Calculates height and width of pre-decoder, if self.no_of_pre3x8 > 0: - self.predecoder_width = self.pre3_8.width + self.predecoder_width = self.pre3_8.width else: self.predecoder_width = self.pre2_4.width - self.predecoder_height = self.pre2_4.height*self.no_of_pre2x4 + self.pre3_8.height*self.no_of_pre3x8 + self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 - # Calculates height and width of row-decoder + # Calculates height and width of row-decoder if (self.num_inputs == 4 or self.num_inputs == 5): nand_width = self.nand2.width else: - nand_width = self.nand3.width - self.internal_routing_width = self.m2_pitch*self.total_number_of_predecoder_outputs + nand_width = self.nand3.width + 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 - # Calculates height and width of hierarchical decoder + 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 \ + self.internal_routing_width + nand_width + self.inv.width @@ -158,7 +158,7 @@ class hierarchical_decoder(design.design): def route_input_rails(self): """ Create input rails 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 + 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 @@ -166,7 +166,7 @@ class hierarchical_decoder(design.design): min_x = min(min_x, -self.pre2_4.width) if self.no_of_pre3x8 > 0: min_x = min(min_x, -self.pre3_8.width) - input_offset=vector(min_x - self.input_routing_width,0) + 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", @@ -177,7 +177,6 @@ class hierarchical_decoder(design.design): self.route_input_to_predecodes() - def route_input_to_predecodes(self): """ Route the vertical input rail to the predecoders """ for pre_num in range(self.no_of_pre2x4): @@ -191,11 +190,10 @@ class hierarchical_decoder(design.design): # To prevent conflicts, we will offset each input connect so # that it aligns with the vdd/gnd rails - decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height) - input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1) + 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) - for pre_num in range(self.no_of_pre3x8): for i in range(3): @@ -208,11 +206,10 @@ class hierarchical_decoder(design.design): # To prevent conflicts, we will offset each input connect so # that it aligns with the vdd/gnd rails - decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height) - input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1) + 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_rail(decoder_offset, input_offset) def route_input_rail(self, input_offset, output_offset): """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ @@ -222,7 +219,6 @@ class hierarchical_decoder(design.design): self.add_via_center(layers=self.m2_stack, offset=output_offset) self.add_path(("m3"), [input_offset, output_offset]) - def add_pins(self): """ Add the module pins """ @@ -235,7 +231,6 @@ class hierarchical_decoder(design.design): self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - def create_pre_decoder(self): """ Creates pre-decoder and places labels input address [A] """ @@ -245,7 +240,7 @@ class hierarchical_decoder(design.design): for i in range(self.no_of_pre3x8): self.create_pre3x8(i) - def create_pre2x4(self,num): + def create_pre2x4(self, num): """ Add a 2x4 predecoder to the left of the origin """ if (self.num_inputs == 2): @@ -265,8 +260,7 @@ class hierarchical_decoder(design.design): mod=self.pre2_4)) self.connect_inst(pins) - - def create_pre3x8(self,num): + def create_pre3x8(self, num): """ Add 3x8 predecoder to the left of the origin and above any 2x4 decoders """ # If we had 2x4 predecodes, those are used as the lower # decode output bits @@ -280,11 +274,10 @@ class hierarchical_decoder(design.design): pins.append("out_{0}".format(output_index + out_index_offset)) pins.extend(["vdd", "gnd"]) - self.pre3x8_inst.append(self.add_inst(name="pre3x8_{0}".format(num), + self.pre3x8_inst.append(self.add_inst(name="pre3x8_{0}".format(num), mod=self.pre3_8)) self.connect_inst(pins) - def place_pre_decoder(self): """ Creates pre-decoder and places labels input address [A] """ @@ -294,23 +287,23 @@ class hierarchical_decoder(design.design): for i in range(self.no_of_pre3x8): self.place_pre3x8(i) - def place_pre2x4(self,num): + def place_pre2x4(self, num): """ Place 2x4 predecoder to the left of the origin """ if (self.num_inputs == 2): - base = vector(-self.pre2_4.width,0) + base = vector(-self.pre2_4.width, 0) else: base= vector(-self.pre2_4.width, num * self.pre2_4.height) self.pre2x4_inst[num].place(base) - def place_pre3x8(self,num): + 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" + 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 + 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) @@ -331,7 +324,7 @@ 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 + row = len(self.predec_groups[0]) * j + i if (row < self.rows): name = self.NAND_FORMAT.format(row) self.nand_inst.append(self.add_inst(name=name, @@ -342,14 +335,13 @@ class hierarchical_decoder(design.design): "vdd", "gnd"] self.connect_inst(pins) - # Row Decoder NAND GATE array for address inputs >5. elif (self.num_inputs > 5): 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 + row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \ + + len(self.predec_groups[0]) * j + i if (row < self.rows): name = self.NAND_FORMAT.format(row) @@ -363,9 +355,8 @@ class hierarchical_decoder(design.design): "vdd", "gnd"] self.connect_inst(pins) - def create_decoder_inv_array(self): - """ + """ Add a column of INV gates for the decoder. """ @@ -378,15 +369,12 @@ class hierarchical_decoder(design.design): "decode_{0}".format(row), "vdd", "gnd"]) - def place_decoder_inv_array(self): """ Place the column of INV gates for the decoder above the predecoders and to the right of the NAND decoders. """ - z_pin = self.inv.get_pin("Z") - if (self.num_inputs == 4 or self.num_inputs == 5): x_off = self.internal_routing_width + self.nand2.width else: @@ -396,27 +384,24 @@ class hierarchical_decoder(design.design): if (row % 2 == 0): inv_row_height = self.inv.height * row mirror = "R0" - y_dir = 1 else: inv_row_height = self.inv.height * (row + 1) mirror = "MX" - y_dir = -1 y_off = inv_row_height - offset = vector(x_off,y_off) + offset = vector(x_off, y_off) self.inv_inst[row].place(offset=offset, mirror=mirror) def place_row_decoder(self): - """ + """ Place the row-decoder by placing NAND2/NAND3 and Inverters - and add the primary decoder output pins. + and add the primary decoder output pins. """ if (self.num_inputs >= 4): self.place_decoder_nand_array() self.place_decoder_inv_array() self.route_decoder() - def place_decoder_nand_array(self): """ Add a column of NAND gates for final decode """ @@ -433,22 +418,16 @@ class hierarchical_decoder(design.design): """ Add a column of NAND gates for the decoder above the predecoders.""" for row in range(self.rows): - name = self.NAND_FORMAT.format(row) if ((row % 2) == 0): - y_off = nand_mod.height*row - y_dir = 1 + y_off = nand_mod.height * row mirror = "R0" else: - y_off = nand_mod.height*(row + 1) - y_dir = -1 + y_off = nand_mod.height * (row + 1) mirror = "MX" self.nand_inst[row].place(offset=[self.internal_routing_width, y_off], mirror=mirror) - - - def route_decoder(self): """ Route the nand to inverter in the decoder and add the pins. """ @@ -457,9 +436,9 @@ class hierarchical_decoder(design.design): # route nand output to output inv input zr_pos = self.nand_inst[row].get_pin("Z").rc() al_pos = self.inv_inst[row].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) + # 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]) z_pin = self.inv_inst[row].get_pin("Z") @@ -469,21 +448,18 @@ class hierarchical_decoder(design.design): width=z_pin.width(), height=z_pin.height()) - - def route_predecode_rails(self): """ Creates vertical metal 2 rails 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) + input_offset = vector(0.5 * self.m2_width, 0) input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)] self.predecode_rails = self.create_vertical_pin_bus(layer="m2", pitch=self.m2_pitch, offset=input_offset, names=input_bus_names, length=self.height) - self.route_rails_to_predecodes() self.route_rails_to_decoder() @@ -497,8 +473,7 @@ class hierarchical_decoder(design.design): 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) - + self.route_predecode_rail_m3(predecode_name, pin) # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre3x8): @@ -506,10 +481,8 @@ 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) + self.route_predecode_rail_m3(predecode_name, pin) - - def route_rails_to_decoder(self): """ Use the self.predec_groups to determine the connections to the decoder NAND gates. Inputs of NAND2/NAND3 gates come from different groups. @@ -536,11 +509,11 @@ class hierarchical_decoder(design.design): for index_A in self.predec_groups[0]: # FIXME: convert to connect_bus? if (row_index < self.rows): - predecode_name = "predecode_{}".format(index_A) + predecode_name = "predecode_{}".format(index_A) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) - predecode_name = "predecode_{}".format(index_B) + predecode_name = "predecode_{}".format(index_B) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) - predecode_name = "predecode_{}".format(index_C) + predecode_name = "predecode_{}".format(index_C) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C")) row_index = row_index + 1 @@ -548,8 +521,8 @@ class hierarchical_decoder(design.design): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ # The vias will be placed in the center and right of the cells, respectively. - xoffset = self.nand_inst[0].rx() - for num in range(0,self.rows): + xoffset = self.nand_inst[0].cx() + for num in range(0, self.rows): for pin_name in ["vdd", "gnd"]: # The nand and inv are the same height rows... supply_pin = self.nand_inst[num].get_pin(pin_name) @@ -558,39 +531,36 @@ class hierarchical_decoder(design.design): loc=pin_pos) # Make a redundant rail too - for num in range(0,self.rows,2): + for num in range(0, self.rows, 2): for pin_name in ["vdd", "gnd"]: start = self.nand_inst[num].get_pin(pin_name).lc() end = self.inv_inst[num].get_pin(pin_name).rc() - mid = (start+end).scale(0.5,0.5) + mid = (start + end).scale(0.5, 0.5) self.add_rect_center(layer="m1", offset=mid, - width=end.x-start.x) - + width=end.x - start.x) # Copy the pins from the predecoders for pre in self.pre2x4_inst + self.pre3x8_inst: 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) + 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_rail_m3(self, rail_name, pin): """ Connect the routing rail to the given metal1 pin """ # 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) + mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2) + rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y) self.add_via_center(layers=self.m1_stack, offset=pin.center()) - self.add_wire(("m3","via2","m2"), [rail_pos, mid_point, pin.uc()]) + self.add_wire(("m3", "via2", "m2"), [rail_pos, mid_point, pin.uc()]) self.add_via_center(layers=self.m2_stack, offset=rail_pos) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 59ec2448..a6a11a28 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -128,16 +128,16 @@ class pgate(design.design): """ Extend the n/p wells to cover whole cell """ # This should match the cells in the cell library - nwell_y_offset = 0.48 * self.height - full_height = self.height + 0.5*self.m1_width + self.nwell_y_offset = 0.48 * self.height + full_height = self.height + 0.5* self.m1_width # FIXME: float rounding problem if "nwell" in layer: # Add a rail width to extend the well to the top of the rail nwell_max_offset = max(self.find_highest_layer_coords("nwell").y, full_height) - nwell_position = vector(0, nwell_y_offset) - vector(self.well_extend_active, 0) - nwell_height = nwell_max_offset - nwell_y_offset + nwell_position = vector(0, self.nwell_y_offset) - vector(self.well_extend_active, 0) + nwell_height = nwell_max_offset - self.nwell_y_offset self.add_rect(layer="nwell", offset=nwell_position, width=self.well_width, @@ -153,7 +153,7 @@ class pgate(design.design): pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y, -0.5 * self.m1_width) pwell_position = vector(-self.well_extend_active, pwell_min_offset) - pwell_height = nwell_y_offset - pwell_position.y + pwell_height = self.nwell_y_offset - pwell_position.y self.add_rect(layer="pwell", offset=pwell_position, width=self.well_width, From 7ba9e09e12bc81b361d182d9795b10aeaea02d3a Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 4 Mar 2020 22:23:05 +0000 Subject: [PATCH 11/22] Incomplete precharge layer decoupling --- compiler/base/hierarchy_layout.py | 13 ++-- compiler/modules/hierarchical_decoder.py | 2 +- compiler/modules/wordline_driver.py | 5 +- compiler/pgates/pgate.py | 4 +- compiler/pgates/precharge.py | 94 +++++++++++++----------- 5 files changed, 63 insertions(+), 55 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 2e0bf755..6b1ca3fd 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -517,12 +517,12 @@ class layout(): self.connect_inst([]) return inst - def add_via_stack(self, offset, direction, from_layer, to_layer, - size=[1,1]): + def add_via_stack(self, offset, from_layer, to_layer, + direction=None, + size=[1, 1]): """ Punch a stack of vias from a start layer to a target layer. """ - return self.__add_via_stack_internal(offset=offset, direction=direction, from_layer=from_layer, @@ -531,8 +531,9 @@ class layout(): last_via=None, size=size) - def add_via_stack_center(self, offset, direction, from_layer, to_layer, - size=[1,1]): + def add_via_stack_center(self, offset, from_layer, to_layer, + direction=None, + size=[1, 1]): """ Punch a stack of vias from a start layer to a target layer by the center coordinate accounting for mirroring and rotation. @@ -545,7 +546,6 @@ class layout(): last_via=None, size=size) - def __add_via_stack_internal(self, offset, direction, from_layer, to_layer, via_func, last_via, size): """ @@ -581,7 +581,6 @@ class layout(): last_via=via, size=size) - def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): """Adds a ptx module to the design.""" import ptx diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 7d625b75..f9c21619 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -521,7 +521,7 @@ class hierarchical_decoder(design.design): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ # The vias will be placed in the center and right of the cells, respectively. - xoffset = self.nand_inst[0].cx() + xoffset = self.nand_inst[0].rx() for num in range(0, self.rows): for pin_name in ["vdd", "gnd"]: # The nand and inv are the same height rows... diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 27671fd2..35a2b8bf 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -75,8 +75,7 @@ class wordline_driver(design.design): """ # Find the x offsets for where the vias/pins should be placed - a_xoffset = self.nand_inst[0].rx() - b_xoffset = self.inv2_inst[0].lx() + a_xoffset = self.nand_inst[0].lx() for num in range(self.rows): # this will result in duplicate polygons for rails, but who cares @@ -90,7 +89,7 @@ class wordline_driver(design.design): supply_pin = self.inv2_inst[num].get_pin(name) # Add pins in two locations - for xoffset in [a_xoffset, b_xoffset]: + for xoffset in [a_xoffset]: pin_pos = vector(xoffset, supply_pin.cy()) self.add_power_pin(name, pin_pos) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index a6a11a28..7693fa07 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -267,8 +267,8 @@ class pgate(design.design): def determine_width(self): """ Determine the width based on the well contacts (assumed to be on the right side) """ - # Width is determined by well contact and spacing - self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + # Width is determined by well contact and spacing and allowing a supply via between each cell + 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. diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index acb2062f..4a494763 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -12,7 +12,7 @@ from tech import parameter from vector import vector from globals import OPTS from sram_factory import factory - +from tech import drc class precharge(design.design): """ @@ -30,7 +30,10 @@ class precharge(design.design): self.width = self.bitcell.width self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br - + self.bitcell_bl_pin =self.bitcell.get_pin(self.bitcell_bl) + self.bitcell_br_pin =self.bitcell.get_pin(self.bitcell_br) + print(self.bitcell_bl_pin) + print(self.bitcell_br_pin) # Creates the netlist and layout # Since it has variable height, it is not a pgate. self.create_netlist() @@ -50,6 +53,7 @@ class precharge(design.design): self.create_ptx() def create_layout(self): + self.place_ptx() self.connect_poly() self.route_en() @@ -120,17 +124,18 @@ class precharge(design.design): contact_xdiff = self.pmos.get_pin("S").lx() # adds the lower pmos to layout - bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx() + bl_xoffset = self.bitcell_bl_pin.lx() self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, self.nwell_enclose_active), self.pmos.active_offset.y) self.lower_pmos_inst.place(self.lower_pmos_position) # adds the upper pmos(s) to layout - ydiff = self.pmos.height + 2 * self.m1_space + contact.poly_contact.width + ydiff = self.pmos.height + 4 * self.m2_space self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff) self.upper_pmos1_inst.place(self.upper_pmos1_pos) + # Second pmos to the right of the first upper_pmos2_pos = self.upper_pmos1_pos + overlap_offset self.upper_pmos2_inst.place(upper_pmos2_pos) @@ -161,16 +166,25 @@ class precharge(design.design): """ Adds the en input rail, en contact/vias, and connects to the pmos """ - + + if self.bitcell_bl_pin.layer == "m1": + layer = "m2" + else: + layer = "m1" + # adds the en contact to connect the gates to the en rail on metal1 offset = self.lower_pmos_inst.get_pin("G").ul() \ - + vector(0, 0.5 * self.poly_space) + + vector(0, self.m2_pitch) self.add_via_center(layers=self.poly_stack, offset=offset) + if 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", - layer="m1", + layer=layer, start=offset.scale(0, 1), end=offset.scale(0, 1) + vector(self.width, 0)) @@ -201,20 +215,21 @@ class precharge(design.design): """ Adds both bit-line and bit-line-bar to the module """ - + layer_width = drc("minwidth_" + self.bitcell_br_pin.layer) + layer_space = drc("{0}_to_{0}".format(self.bitcell_br_pin.layer)) + layer = self.bitcell_bl_pin.layer + # adds the BL on metal 2 - offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(), 0) \ - - vector(0.5 * self.m2_width, 0) + offset = vector(layer_space - 0.5 * layer_width, 0) self.bl_pin = self.add_layout_pin(text="bl", - layer="m2", + layer=layer, offset=offset, height=self.height) # adds the BR on metal 2 - offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(), 0) \ - - vector(0.5 * self.m2_width, 0) + offset = vector(self.width - layer_space - 0.5 * layer_width, 0) self.br_pin = self.add_layout_pin(text="br", - layer="m2", + layer=layer, offset=offset, height=self.height) @@ -225,11 +240,12 @@ class precharge(design.design): self.add_bitline_contacts() self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"), self.get_pin("bl")) + self.connect_pmos_m2(self.lower_pmos_inst.get_pin("D"), + self.get_pin("br")) + self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"), self.get_pin("bl")) - self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"), - self.get_pin("br")) - self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"), + self.connect_pmos_m2(self.upper_pmos2_inst.get_pin("D"), self.get_pin("br")) def add_bitline_contacts(self): @@ -237,34 +253,28 @@ class precharge(design.design): Adds contacts/via from metal1 to metal2 for bit-lines """ - upper_pin = self.upper_pmos1_inst.get_pin("S") - lower_pin = self.lower_pmos_inst.get_pin("S") - # BL goes up to M2 at the transistor - self.bl_contact =self.add_via_center(layers=self.m1_stack, - offset=upper_pin.center(), + # BL + lower_pin = self.lower_pmos_inst.get_pin("S") + self.lower_via = self.add_via_center(layers=self.m1_stack, + offset=lower_pin.center(), directions=("V", "V")) - self.add_via_center(layers=self.m1_stack, - offset=lower_pin.center(), - directions=("V", "V")) - # BR routes over on M1 first - self.add_via_center(layers=self.m1_stack, - offset=vector(self.br_pin.cx(), upper_pin.cy()), - directions=("V", "V")) - self.add_via_center(layers=self.m1_stack, - offset=vector(self.br_pin.cx(), lower_pin.cy()), - directions=("V", "V")) + lower_pin = self.lower_pmos_inst.get_pin("D") + self.lower_via = self.add_via_center(layers=self.m1_stack, + offset=lower_pin.center(), + directions=("V", "V")) + + # BR + upper_pin = self.upper_pmos1_inst.get_pin("S") + self.upper_via2 = self.add_via_center(layers=self.m1_stack, + offset=upper_pin.center(), + directions=("V", "V")) - def connect_pmos_m1(self, pmos_pin, bit_pin): - """ - Connect a pmos pin to bitline pin - """ - - left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) - right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) - - self.add_path("m1", [left_pos, right_pos] ) + upper_pin = self.upper_pmos2_inst.get_pin("D") + self.upper_via2 = self.add_via_center(layers=self.m1_stack, + offset=upper_pin.center(), + directions=("V", "V")) def connect_pmos_m2(self, pmos_pin, bit_pin): """ @@ -274,7 +284,7 @@ class precharge(design.design): left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) - self.add_path("m2", [left_pos, right_pos], self.bl_contact.height) + self.add_path("m2", [left_pos, right_pos]) def get_en_cin(self): """Get the relative capacitance of the enable in the precharge cell""" From 287a31f59800a1607fc000936de87b27fa121c58 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 4 Mar 2020 17:39:11 -0800 Subject: [PATCH 12/22] Precharge updates. Enable different layers for bitlines. Jog bitlines to fit precharge transistors for close proximity bitlines. PEP8 cleanup. --- compiler/base/hierarchy_layout.py | 2 +- compiler/modules/precharge_array.py | 29 ++----- compiler/pgates/precharge.py | 129 +++++++++++++++++----------- 3 files changed, 88 insertions(+), 72 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 6b1ca3fd..35aa73b1 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -316,7 +316,7 @@ class layout(): You can optionally rename the pin to a new name. """ for pin_name in self.pin_map.keys(): - self.copy_layout_pin(instance, pin_name, prefix+pin_name) + self.copy_layout_pin(instance, pin_name, prefix + pin_name) def add_layout_pin_segment_center(self, text, layer, start, end): """ diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index efc9c4a3..d5b0e799 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -12,6 +12,7 @@ from vector import vector from sram_factory import factory from globals import OPTS + class precharge_array(design.design): """ Dynamically generated precharge array of all bitlines. Cols is number @@ -68,11 +69,8 @@ class precharge_array(design.design): size=self.size, bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) - - self.add_mod(self.pc_cell) - def add_layout_pins(self): self.add_layout_pin(text="en_bar", @@ -86,20 +84,9 @@ class precharge_array(design.design): for i in range(len(self.local_insts)): inst = self.local_insts[i] - bl_pin = inst.get_pin("bl") - self.add_layout_pin(text="bl_{0}".format(i), - layer="m2", - offset=bl_pin.ll(), - width=drc("minwidth_m2"), - height=bl_pin.height()) - br_pin = inst.get_pin("br") - self.add_layout_pin(text="br_{0}".format(i), - layer="m2", - offset=br_pin.ll(), - width=drc("minwidth_m2"), - height=bl_pin.height()) + self.copy_layout_pin(inst, "bl", "bl_{0}".format(i)) + self.copy_layout_pin(inst, "br", "br_{0}".format(i)) - def create_insts(self): """Creates a precharge array by horizontally tiling the precharge cell""" self.local_insts = [] @@ -112,7 +99,6 @@ class precharge_array(design.design): self.local_insts.append(inst) self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"]) - def place_insts(self): """ Places precharge array by horizontally tiling the precharge cell""" from tech import cell_properties @@ -130,8 +116,11 @@ class precharge_array(design.design): xoffset = xoffset + self.pc_cell.width def get_en_cin(self): - """Get the relative capacitance of all the clk connections in the precharge array""" - #Assume single port + """ + Get the relative capacitance of all the clk connections + in the precharge array + """ + # Assume single port precharge_en_cin = self.pc_cell.get_en_cin() - return precharge_en_cin*self.columns + return precharge_en_cin * self.columns diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 4a494763..3f0560f5 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -14,6 +14,7 @@ from globals import OPTS from sram_factory import factory from tech import drc + class precharge(design.design): """ Creates a single precharge cell @@ -32,8 +33,14 @@ class precharge(design.design): self.bitcell_br = bitcell_br self.bitcell_bl_pin =self.bitcell.get_pin(self.bitcell_bl) self.bitcell_br_pin =self.bitcell.get_pin(self.bitcell_br) - print(self.bitcell_bl_pin) - print(self.bitcell_br_pin) + + if self.bitcell_bl_pin.layer == "m1": + self.bitline_layer = "m1" + self.en_layer = "m2" + else: + self.bitline_layer = "m2" + self.en_layer = "m1" + # Creates the netlist and layout # Since it has variable height, it is not a pgate. self.create_netlist() @@ -82,18 +89,24 @@ class precharge(design.design): # Adds the rail across the width of the cell vdd_position = vector(0.5 * self.width, self.height) - self.add_rect_center(layer="m1", + layer_width = drc("minwidth_" + self.en_layer) + self.add_rect_center(layer=self.en_layer, offset=vdd_position, width=self.width, - height=self.m1_width) + 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 self.en_layer != "m1": + self.add_via_center(layers=self.m1_stack, + offset=pmos_vdd_pos) + + # Add vdd pin above the transistor - self.add_power_pin("vdd", pmos_pin.center(), vertical=True) + self.add_power_pin("vdd", self.well_contact_pos, vertical=True) def create_ptx(self): """ @@ -117,6 +130,8 @@ class precharge(design.design): Place both the upper_pmos and lower_pmos to the module """ + # reserve some offset to jog the bitlines + self.initial_yoffset = self.pmos.active_offset.y + self.m2_pitch # Compute the other pmos2 location, # but determining offset to overlap the source and drain pins overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() @@ -127,11 +142,11 @@ class precharge(design.design): bl_xoffset = self.bitcell_bl_pin.lx() self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, self.nwell_enclose_active), - self.pmos.active_offset.y) + self.initial_yoffset) self.lower_pmos_inst.place(self.lower_pmos_position) - # adds the upper pmos(s) to layout - ydiff = self.pmos.height + 4 * self.m2_space + # adds the upper pmos(s) to layout with 2 M2 tracks + ydiff = self.pmos.height + self.m2_pitch self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff) self.upper_pmos1_inst.place(self.upper_pmos1_pos) @@ -167,24 +182,20 @@ class precharge(design.design): Adds the en input rail, en contact/vias, and connects to the pmos """ - if self.bitcell_bl_pin.layer == "m1": - layer = "m2" - else: - layer = "m1" - - # adds the en contact to connect the gates to the en rail on metal1 + # adds the en contact to connect the gates to the en rail + # midway in the 4 M2 tracks offset = self.lower_pmos_inst.get_pin("G").ul() \ - + vector(0, self.m2_pitch) + + vector(0, 0.5 * self.m2_pitch) self.add_via_center(layers=self.poly_stack, offset=offset) - if layer == "m2": + 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", - layer=layer, + layer=self.en_layer, start=offset.scale(0, 1), end=offset.scale(0, 1) + vector(self.width, 0)) @@ -194,16 +205,15 @@ class precharge(design.design): """ # adds the contact from active to metal1 - well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ - + vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \ - + self.nwell_extend_active) + 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) self.add_via_center(layers=self.active_stack, - offset=well_contact_pos, + offset=self.well_contact_pos, implant_type="n", well_type="n") - # leave an extra pitch for the height - self.height = well_contact_pos.y + contact.active_contact.height + self.m1_pitch + self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space # nwell should span the whole design since it is pmos only self.add_rect(layer="nwell", @@ -215,44 +225,59 @@ class precharge(design.design): """ Adds both bit-line and bit-line-bar to the module """ - layer_width = drc("minwidth_" + self.bitcell_br_pin.layer) - layer_space = drc("{0}_to_{0}".format(self.bitcell_br_pin.layer)) - layer = self.bitcell_bl_pin.layer + layer_width = drc("minwidth_" + self.bitline_layer) + layer_space = drc("{0}_to_{0}".format(self.bitline_layer)) - # adds the BL on metal 2 - offset = vector(layer_space - 0.5 * layer_width, 0) - self.bl_pin = self.add_layout_pin(text="bl", - layer=layer, - offset=offset, - height=self.height) - - # adds the BR on metal 2 - offset = vector(self.width - layer_space - 0.5 * layer_width, 0) - self.br_pin = self.add_layout_pin(text="br", - layer=layer, - offset=offset, - height=self.height) + yoffset = self.initial_yoffset - 0.5 * self.m2_pitch + # adds the BL + self.bl_xoffset = layer_space + 0.5 * layer_width + top_pos = vector(self.bl_xoffset, self.height) + mid1_pos = vector(self.bl_xoffset, yoffset) + mid2_pos = vector(self.bitcell_bl_pin.cx(), yoffset) + pin_pos = vector(self.bitcell_bl_pin.cx(), 0) + self.add_path(self.bitline_layer, + [top_pos, mid1_pos, mid2_pos, pin_pos]) + self.bl_pin = self.add_layout_pin_segment_center(text="bl", + layer=self.bitline_layer, + start=pin_pos, + end=mid2_pos) + + # adds the BR + self.br_xoffset = self.width - layer_space - 0.5 * layer_width + top_pos = vector(self.br_xoffset, self.height) + mid1_pos = vector(self.br_xoffset, yoffset) + mid2_pos = vector(self.bitcell_br_pin.cx(), yoffset) + pin_pos = vector(self.bitcell_br_pin.cx(), 0) + self.add_path(self.bitline_layer, + [top_pos, mid1_pos, mid2_pos, pin_pos]) + self.br_pin = self.add_layout_pin_segment_center(text="br", + layer=self.bitline_layer, + start=pin_pos, + end=mid2_pos) def connect_to_bitlines(self): """ Connect the bitlines to the devices """ self.add_bitline_contacts() - self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"), - self.get_pin("bl")) - self.connect_pmos_m2(self.lower_pmos_inst.get_pin("D"), - self.get_pin("br")) + self.connect_pmos(self.lower_pmos_inst.get_pin("S"), + self.bl_xoffset) + self.connect_pmos(self.lower_pmos_inst.get_pin("D"), + self.br_xoffset) - self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"), - self.get_pin("bl")) - self.connect_pmos_m2(self.upper_pmos2_inst.get_pin("D"), - self.get_pin("br")) + self.connect_pmos(self.upper_pmos1_inst.get_pin("S"), + self.bl_xoffset) + self.connect_pmos(self.upper_pmos2_inst.get_pin("D"), + self.br_xoffset) def add_bitline_contacts(self): """ Adds contacts/via from metal1 to metal2 for bit-lines """ + # No contacts needed if M1 + if self.bitline_layer == "m1": + return # BL lower_pin = self.lower_pmos_inst.get_pin("S") @@ -276,15 +301,17 @@ class precharge(design.design): offset=upper_pin.center(), directions=("V", "V")) - def connect_pmos_m2(self, pmos_pin, bit_pin): + def connect_pmos(self, pmos_pin, bit_xoffset): """ Connect a pmos pin to bitline pin """ - left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) - right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy()) + left_pos = vector(min(pmos_pin.cx(), bit_xoffset), pmos_pin.cy()) + right_pos = vector(max(pmos_pin.cx(), bit_xoffset), pmos_pin.cy()) - self.add_path("m2", [left_pos, right_pos]) + self.add_path(self.bitline_layer, + [left_pos, right_pos], + width=pmos_pin.height()) def get_en_cin(self): """Get the relative capacitance of the enable in the precharge cell""" From 7adeef6c9e46e3f43259ed8594c2283422a0b167 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 5 Mar 2020 10:21:18 -0800 Subject: [PATCH 13/22] PEP8 Formatting --- compiler/modules/port_data.py | 146 ++++++++++++++-------------------- 1 file changed, 60 insertions(+), 86 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 7c37d670..fbf5223a 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -1,18 +1,17 @@ # 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 sys -from tech import drc, parameter +from tech import drc import debug import design from sram_factory import factory from collections import namedtuple from vector import vector - from globals import OPTS + class port_data(design.design): """ Create the data port (column mux, sense amps, write driver, etc.) for the given port number. @@ -24,18 +23,21 @@ class port_data(design.design): sram_config.set_local_config(self) self.port = port if self.write_size is not None: - self.num_wmasks = int(self.word_size/self.write_size) + self.num_wmasks = int(self.word_size / self.write_size) else: self.num_wmasks = 0 if name == "": name = "port_data_{0}".format(self.port) design.design.__init__(self, name) - debug.info(2, "create data port of size {0} with {1} words per row".format(self.word_size,self.words_per_row)) + debug.info(2, + "create data port of size {0} with {1} words per row".format(self.word_size, + self.words_per_row)) self.create_netlist() if not OPTS.netlist_only: - debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.") + debug.check(len(self.all_ports)<=2, + "Bank layout cannot handle more than two ports.") self.create_layout() self.add_boundary() @@ -93,8 +95,6 @@ class port_data(design.design): else: self.column_mux_array_inst = None - - def create_layout(self): self.compute_instance_offsets() self.place_instances() @@ -104,33 +104,32 @@ class port_data(design.design): def add_pins(self): """ Adding pins for port address module""" - self.add_pin("rbl_bl","INOUT") - self.add_pin("rbl_br","INOUT") + self.add_pin("rbl_bl", "INOUT") + self.add_pin("rbl_br", "INOUT") for bit in range(self.num_cols): bl_name = self.get_bl_name(self.port) br_name = self.get_br_name(self.port) - self.add_pin("{0}_{1}".format(bl_name, bit),"INOUT") - self.add_pin("{0}_{1}".format(br_name, bit),"INOUT") + self.add_pin("{0}_{1}".format(bl_name, bit), "INOUT") + self.add_pin("{0}_{1}".format(br_name, bit), "INOUT") if self.port in self.read_ports: for bit in range(self.word_size): - self.add_pin("dout_{}".format(bit),"OUTPUT") + self.add_pin("dout_{}".format(bit), "OUTPUT") if self.port in self.write_ports: for bit in range(self.word_size): - self.add_pin("din_{}".format(bit),"INPUT") + self.add_pin("din_{}".format(bit), "INPUT") # Will be empty if no col addr lines sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] for pin_name in sel_names: - self.add_pin(pin_name,"INPUT") + self.add_pin(pin_name, "INPUT") if self.port in self.read_ports: self.add_pin("s_en", "INPUT") self.add_pin("p_en_bar", "INPUT") if self.port in self.write_ports: self.add_pin("w_en", "INPUT") for bit in range(self.num_wmasks): - self.add_pin("bank_wmask_{}".format(bit),"INPUT") - self.add_pin("vdd","POWER") - self.add_pin("gnd","GROUND") - + self.add_pin("bank_wmask_{}".format(bit), "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def route_layout(self): """ Create routing among the modules """ @@ -172,8 +171,8 @@ class port_data(design.design): """ Propagate all vdd/gnd pins up to this level for all modules """ for inst in self.insts: - self.copy_power_pins(inst,"vdd") - self.copy_power_pins(inst,"gnd") + self.copy_power_pins(inst, "vdd") + self.copy_power_pins(inst, "gnd") def add_modules(self): @@ -193,7 +192,6 @@ class port_data(design.design): else: self.sense_amp_array = None - if self.col_addr_size > 0: self.column_mux_array = factory.create(module_type="column_mux_array", columns=self.num_cols, @@ -204,7 +202,6 @@ class port_data(design.design): else: self.column_mux_array = None - if self.port in self.write_ports: self.write_driver_array = factory.create(module_type="write_driver_array", columns=self.num_cols, @@ -234,29 +231,27 @@ class port_data(design.design): else: self.num_col_addr_lines = 0 - # A space for wells or jogging m2 between modules - self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"), - 3*self.m2_pitch) + self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"), + 3 * self.m2_pitch) - # create arrays of bitline and bitline_bar names for read, write, or all ports + # create arrays of bitline and bitline_bar names for read, + # write, or all ports self.bitcell = factory.create(module_type="bitcell") self.bl_names = self.bitcell.get_all_bl_names() self.br_names = self.bitcell.get_all_br_names() self.wl_names = self.bitcell.get_all_wl_names() # used for bl/br names self.precharge = factory.create(module_type="precharge", - bitcell_bl = self.bl_names[0], - bitcell_br = self.br_names[0]) + bitcell_bl=self.bl_names[0], + bitcell_br=self.br_names[0]) # We create a dummy here to get bl/br names to add those pins to this # module, which happens before we create the real precharge_array self.precharge_array = factory.create(module_type="precharge_array", - columns=self.num_cols + 1, - bitcell_bl=self.bl_names[self.port], - bitcell_br=self.br_names[self.port]) - - + columns=self.num_cols + 1, + bitcell_bl=self.bl_names[self.port], + bitcell_br=self.br_names[self.port]) def create_precharge_array(self): """ Creating Precharge """ @@ -285,13 +280,11 @@ class port_data(design.design): temp.extend(["p_en_bar", "vdd"]) self.connect_inst(temp) - def place_precharge_array(self, offset): """ Placing Precharge """ self.precharge_array_inst.place(offset=offset, mirror="MX") - def create_column_mux_array(self): """ Creating Column Mux when words_per_row > 1 . """ @@ -314,7 +307,6 @@ class port_data(design.design): temp.append("gnd") self.connect_inst(temp) - def place_column_mux_array(self, offset): """ Placing Column Mux when words_per_row > 1 . """ if self.col_addr_size == 0: @@ -322,7 +314,6 @@ class port_data(design.design): self.column_mux_array_inst.place(offset=offset, mirror="MX") - def create_sense_amp_array(self): """ Creating Sense amp """ self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port), @@ -340,16 +331,13 @@ class port_data(design.design): temp.append("{0}_out_{1}".format(bl_name, bit)) temp.append("{0}_out_{1}".format(br_name, bit)) - temp.extend(["s_en", "vdd", "gnd"]) self.connect_inst(temp) - def place_sense_amp_array(self, offset): """ Placing Sense amp """ self.sense_amp_array_inst.place(offset=offset, mirror="MX") - def create_write_driver_array(self): """ Creating Write Driver """ self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port), @@ -378,12 +366,10 @@ class port_data(design.design): self.connect_inst(temp) - def place_write_driver_array(self, offset): """ Placing Write Driver """ self.write_driver_array_inst.place(offset=offset, mirror="MX") - def create_write_mask_and_array(self): """ Creating Write Mask AND Array """ self.write_mask_and_array_inst = self.add_inst(name="write_mask_and_array{}".format(self.port), @@ -398,12 +384,10 @@ class port_data(design.design): temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def place_write_mask_and_array(self, offset): """ Placing Write Mask AND array """ self.write_mask_and_array_inst.place(offset=offset, mirror="MX") - def compute_instance_offsets(self): """ Compute the empty instance offsets for port0 and port1 (if needed) @@ -458,19 +442,17 @@ class port_data(design.design): if self.column_mux_offset: self.place_column_mux_array(self.column_mux_offset) - def route_sense_amp_out(self, port): """ Add pins for the sense amp output """ for bit in range(self.word_size): data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(bit)) self.add_layout_pin_rect_center(text="dout_{0}".format(bit), - layer=data_pin.layer, + layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), width=data_pin.width()) - def route_write_driver_in(self, port): """ Connecting write driver """ @@ -479,7 +461,6 @@ class port_data(design.design): din_name = "din_{}".format(row) self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) - def route_write_mask_and_array_in(self, port): """ Add pins for the write mask and array input """ @@ -488,7 +469,6 @@ class port_data(design.design): bank_wmask_name = "bank_wmask_{}".format(bit) self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name) - def route_write_mask_and_array_to_write_driver(self,port): """ Routing of wdriver_sel_{} between write mask AND array and write driver array. Adds layout pin for write mask AND array output and via for write driver enable """ @@ -511,15 +491,15 @@ class port_data(design.design): while (wmask_out_pin.lx() + self.m2_pitch > inst2.get_pin("data_{0}".format(loc)).rx()): loc += 1 length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch - debug.check(loc<=self.num_wmasks,"Couldn't route the write mask select.") + debug.check(loc<=self.num_wmasks, + "Couldn't route the write mask select.") else: # Stride by the write size rather than finding the next pin to the right loc += self.write_size - length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch - + length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch beg_pos = wmask_out_pin.center() - middle_pos = vector(length,wmask_out_pin.cy()) + middle_pos = vector(length, wmask_out_pin.cy()) end_pos = vector(length, wdriver_en_pin.cy()) # Add via for the write driver array's enable input @@ -529,7 +509,6 @@ class port_data(design.design): # Route between write mask AND array and write driver array self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos]) - def route_column_mux_to_precharge_array(self, port): """ Routing of BL and BR between col mux and precharge array """ @@ -547,7 +526,6 @@ class port_data(design.design): num_bits=self.num_cols, inst2_start_bit=insn2_start_bit) - def route_sense_amp_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ inst2 = self.sense_amp_array_inst @@ -573,12 +551,9 @@ class port_data(design.design): num_bits=self.word_size, inst1_start_bit=start_bit) - def route_write_driver_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ inst2 = self.write_driver_array_inst - inst2_bl_name = inst2.mod.get_bl_name() - inst2_br_name = inst2.mod.get_br_name() if self.col_addr_size>0: # Write driver is connected to the col mux @@ -599,7 +574,6 @@ class port_data(design.design): inst1_bls_template=inst1_bls_templ, inst1_start_bit=start_bit) - def route_write_driver_to_sense_amp(self, port): """ Routing of BL and BR between write driver and sense amp """ @@ -612,7 +586,6 @@ class port_data(design.design): inst2=inst2, num_bits=self.word_size) - def route_bitline_pins(self): """ Add the bitline pins for the given port """ @@ -630,12 +603,15 @@ class port_data(design.design): for bit in range(self.num_cols): if self.precharge_array_inst: - self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit+bit_offset), "bl_{}".format(bit)) - self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit+bit_offset), "br_{}".format(bit)) + self.copy_layout_pin(self.precharge_array_inst, + "bl_{}".format(bit + bit_offset), + "bl_{}".format(bit)) + self.copy_layout_pin(self.precharge_array_inst, + "br_{}".format(bit + bit_offset), + "br_{}".format(bit)) else: debug.error("Didn't find precharge array.") - def route_control_pins(self): """ Add the control pins: s_en, p_en_bar, w_en """ if self.precharge_array_inst: @@ -656,8 +632,6 @@ class port_data(design.design): if self.write_mask_and_array_inst: self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en") - - def _group_bitline_instances(self, inst1, inst2, num_bits, inst1_bls_template, inst1_start_bit, @@ -694,17 +668,16 @@ class port_data(design.design): Extracts bl/br pins from an InstanceGroup based on the bit modifier. """ full_bl_name = inst_group.bls_template.format( - **{'inst' : inst_group.bl_name, - 'bit' : inst_group.start_bit + bit} - ) + **{'inst': inst_group.bl_name, + 'bit': inst_group.start_bit + bit} + ) full_br_name = inst_group.bls_template.format( - **{'inst' : inst_group.br_name, - 'bit' : inst_group.start_bit + bit} - ) + **{'inst': inst_group.br_name, + 'bit': inst_group.start_bit + bit} + ) return (inst_group.inst.get_pin(full_bl_name), inst_group.inst.get_pin(full_br_name)) - def channel_route_bitlines(self, inst1, inst2, num_bits, inst1_bls_template="{inst}_{bit}", inst1_start_bit=0, @@ -721,14 +694,13 @@ class port_data(design.design): # Channel route each mux separately since we don't minimize the number # of tracks in teh channel router yet. If we did, we could route all the bits at once! - offset = bot_inst_group.inst.ul() + vector(0,self.m1_pitch) + offset = bot_inst_group.inst.ul() + vector(0, self.m1_pitch) for bit in range(num_bits): bottom_names = self._get_bitline_pins(bot_inst_group, bit) top_names = self._get_bitline_pins(top_inst_group, bit) route_map = list(zip(bottom_names, top_names)) self.create_horizontal_channel_route(route_map, offset, self.m1_stack) - def connect_bitlines(self, inst1, inst2, num_bits, inst1_bls_template="{inst}_{bit}", @@ -742,11 +714,9 @@ class port_data(design.design): """ bot_inst_group, top_inst_group = self._group_bitline_instances( - inst1, inst2, num_bits, - inst1_bls_template, inst1_start_bit, - inst2_bls_template, inst2_start_bit) - bottom_inst = bot_inst_group.inst - top_inst = top_inst_group.inst + inst1, inst2, num_bits, + inst1_bls_template, inst1_start_bit, + inst2_bls_template, inst2_start_bit) for col in range(num_bits): bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col) @@ -754,11 +724,15 @@ class port_data(design.design): bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc() top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc() - yoffset = 0.5*(top_bl.y+bot_bl.y) - self.add_path("m2",[bot_bl, vector(bot_bl.x,yoffset), - vector(top_bl.x,yoffset), top_bl]) - self.add_path("m2",[bot_br, vector(bot_br.x,yoffset), - vector(top_br.x,yoffset), top_br]) + yoffset = 0.5 * (top_bl.y + bot_bl.y) + self.add_path("m2", [bot_bl, + vector(bot_bl.x, yoffset), + vector(top_bl.x, yoffset), + top_bl]) + self.add_path("m2", [bot_br, + vector(bot_br.x, yoffset), + vector(top_br.x, yoffset), + top_br]) def graph_exclude_precharge(self): """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" From 9c1f0657dda328a0c3339aad1ffb1b63aff2f668 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 5 Mar 2020 11:58:36 -0800 Subject: [PATCH 14/22] PEP8 Formatting --- compiler/modules/bank.py | 333 ++++++++++++++++++--------------------- 1 file changed, 154 insertions(+), 179 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index f5f3feb6..60c84a1e 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -17,9 +17,9 @@ from globals import OPTS class bank(design.design): """ Dynamically generated a single bank including bitcell array, - hierarchical_decoder, precharge, (optional column_mux and column decoder), + hierarchical_decoder, precharge, (optional column_mux and column decoder), write driver and sense amplifiers. - This can create up to two ports in any combination: rw, w, r. + This can create up to two ports in any combination: rw, w, r. """ def __init__(self, sram_config, name=""): @@ -27,15 +27,15 @@ class bank(design.design): self.sram_config = sram_config sram_config.set_local_config(self) if self.write_size: - self.num_wmasks = int(self.word_size/self.write_size) + self.num_wmasks = int(self.word_size / self.write_size) else: self.num_wmasks = 0 if name == "": name = "bank_{0}_{1}".format(self.word_size, self.num_words) design.design.__init__(self, name) - debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words)) - + debug.info(2, "create sram of size {0} with {1} words".format(self.word_size, + self.num_words)) # The local control signals are gated when we have bank select logic, # so this prefix will be added to all of the input signals to create @@ -47,18 +47,17 @@ class bank(design.design): self.create_netlist() if not OPTS.netlist_only: - debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.") + debug.check(len(self.all_ports)<=2, + "Bank layout cannot handle more than two ports.") self.create_layout() self.add_boundary() - def create_netlist(self): self.compute_sizes() self.add_modules() self.add_pins() # Must create the replica bitcell array first self.create_instances() - def create_layout(self): self.place_instances() @@ -66,35 +65,34 @@ class bank(design.design): self.route_layout() # Can remove the following, but it helps for debug! - #self.add_lvs_correspondence_points() + # self.add_lvs_correspondence_points() # Remember the bank center for further placement - self.bank_array_ll = self.offset_all_coordinates().scale(-1,-1) + self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1) self.bank_array_ur = self.bitcell_array_inst.ur() self.bank_array_ul = self.bitcell_array_inst.ul() - self.DRC_LVS() def add_pins(self): """ Adding pins for Bank module""" for port in self.read_ports: for bit in range(self.word_size): - self.add_pin("dout{0}_{1}".format(port,bit),"OUTPUT") + self.add_pin("dout{0}_{1}".format(port, bit), "OUTPUT") for port in self.all_ports: - self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]),"OUTPUT") + self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT") for port in self.write_ports: for bit in range(self.word_size): - self.add_pin("din{0}_{1}".format(port,bit),"INPUT") + self.add_pin("din{0}_{1}".format(port,bit), "INPUT") for port in self.all_ports: for bit in range(self.addr_size): - self.add_pin("addr{0}_{1}".format(port,bit),"INPUT") + self.add_pin("addr{0}_{1}".format(port,bit), "INPUT") # For more than one bank, we have a bank select and name # the signals gated_*. if self.num_banks > 1: for port in self.all_ports: - self.add_pin("bank_sel{}".format(port),"INPUT") + self.add_pin("bank_sel{}".format(port), "INPUT") for port in self.read_ports: self.add_pin("s_en{0}".format(port), "INPUT") for port in self.all_ports: @@ -102,12 +100,11 @@ class bank(design.design): for port in self.write_ports: self.add_pin("w_en{0}".format(port), "INPUT") for bit in range(self.num_wmasks): - self.add_pin("bank_wmask{0}_{1}".format(port,bit),"INPUT") + self.add_pin("bank_wmask{0}_{1}".format(port, bit), "INPUT") for port in self.all_ports: self.add_pin("wl_en{0}".format(port), "INPUT") - self.add_pin("vdd","POWER") - self.add_pin("gnd","GROUND") - + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def route_layout(self): """ Create routing amoung the modules """ @@ -124,7 +121,7 @@ class bank(design.design): self.route_supplies() - def route_rbl(self,port): + def route_rbl(self, port): """ Route the rbl_bl and rbl_wl """ bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) @@ -135,8 +132,6 @@ class bank(design.design): height=bl_pin.height(), width=bl_pin.width()) - - def route_bitlines(self, port): """ Route the bitlines depending on the port type rw, w, or r. """ @@ -146,7 +141,6 @@ class bank(design.design): self.route_port_data_out(port) self.route_port_data_to_bitcell_array(port) - def create_instances(self): """ Create the instances of the netlist. """ @@ -161,19 +155,18 @@ class bank(design.design): Compute the empty instance offsets for port0 and port1 (if needed) """ - self.port_data_offsets = [None]*len(self.all_ports) - self.port_address_offsets = [None]*len(self.all_ports) - - self.column_decoder_offsets = [None]*len(self.all_ports) - self.bank_select_offsets = [None]*len(self.all_ports) + self.port_data_offsets = [None] * len(self.all_ports) + self.port_address_offsets = [None] * len(self.all_ports) + self.column_decoder_offsets = [None] * len(self.all_ports) + self.bank_select_offsets = [None] * len(self.all_ports) # The center point for these cells are the upper-right corner of # the bitcell array. # The port address decoder/driver logic is placed on the right and mirrored on Y-axis. # The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis. - self.bitcell_array_top = self.bitcell_array.height - self.bitcell_array_right = self.bitcell_array.width + self.bitcell_array_top = self.bitcell_array.height + self.bitcell_array_right = self.bitcell_array.width # These are the offsets of the main array (excluding dummy and replica rows/cols) self.main_bitcell_array_top = self.bitcell_array.bitcell_array_inst.uy() @@ -185,7 +178,6 @@ class bank(design.design): self.compute_instance_port0_offsets() if len(self.all_ports)==2: self.compute_instance_port1_offsets() - def compute_instance_port0_offsets(self): """ @@ -196,29 +188,30 @@ class bank(design.design): # UPPER RIGHT QUADRANT # Bitcell array is placed at (0,0) - self.bitcell_array_offset = vector(0,0) + self.bitcell_array_offset = vector(0, 0) # LOWER RIGHT QUADRANT # Below the bitcell array - self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width,0) + self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width, 0) # UPPER LEFT QUADRANT # To the left of the bitcell array - x_offset = self.m2_gap + self.port_address.width - self.port_address_offsets[port] = vector(-x_offset,self.main_bitcell_array_bottom) + x_offset = self.m2_gap + self.port_address.width + self.port_address_offsets[port] = vector(-x_offset, + self.main_bitcell_array_bottom) # LOWER LEFT QUADRANT - # Place the col decoder left aligned with wordline driver + # Place the col decoder left aligned with wordline driver # This is also placed so that it's supply rails do not align with the SRAM-level # control logic to allow control signals to easily pass over in M3 # by placing 1/2 a cell pitch down x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width - y_offset = 0.5*self.dff.height + self.column_decoder.height + y_offset = 0.5 * self.dff.height + self.column_decoder.height else: y_offset = 0 - self.column_decoder_offsets[port] = vector(-x_offset,-y_offset) + self.column_decoder_offsets[port] = vector(-x_offset, -y_offset) # Bank select gets placed below the column decoder (x_offset doesn't change) if self.col_addr_size > 0: @@ -227,7 +220,7 @@ class bank(design.design): y_offset = self.port_address_offsets[port].y if self.num_banks > 1: y_offset += self.bank_select.height + drc("well_to_well") - self.bank_select_offsets[port] = vector(-x_offset,-y_offset) + self.bank_select_offsets[port] = vector(-x_offset, -y_offset) def compute_instance_port1_offsets(self): """ @@ -246,18 +239,19 @@ class bank(design.design): # LOWER RIGHT QUADRANT # To the left of the bitcell array x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap - self.port_address_offsets[port] = vector(x_offset,self.main_bitcell_array_bottom) + self.port_address_offsets[port] = vector(x_offset, + self.main_bitcell_array_bottom) # UPPER RIGHT QUADRANT - # Place the col decoder right aligned with wordline driver + # Place the col decoder right aligned with wordline driver # Above the bitcell array with a well spacing - x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width + x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width if self.col_addr_size > 0: x_offset += self.column_decoder.width + self.col_addr_bus_width - y_offset = self.bitcell_array_top + 0.5*self.dff.height + self.column_decoder.height + y_offset = self.bitcell_array_top + 0.5 * self.dff.height + self.column_decoder.height else: y_offset = self.bitcell_array_top - self.column_decoder_offsets[port] = vector(x_offset,y_offset) + self.column_decoder_offsets[port] = vector(x_offset, y_offset) # Bank select gets placed above the column decoder (x_offset doesn't change) if self.col_addr_size > 0: @@ -265,7 +259,7 @@ class bank(design.design): self.port_data[port].column_mux_offset.y + self.port_data[port].column_mux_array.height) else: y_offset = self.port_address_offsets[port].y - self.bank_select_offsets[port] = vector(x_offset,y_offset) + self.bank_select_offsets[port] = vector(x_offset, y_offset) def place_instances(self): """ Place the instances. """ @@ -281,19 +275,20 @@ class bank(design.design): self.place_column_decoder(self.column_decoder_offsets) self.place_bank_select(self.bank_select_offsets) - def compute_sizes(self): """ Computes the required sizes to create the bank """ - self.num_cols = int(self.words_per_row*self.word_size) + self.num_cols = int(self.words_per_row * self.word_size) self.num_rows = int(self.num_words / self.words_per_row) self.row_addr_size = int(log(self.num_rows, 2)) self.col_addr_size = int(log(self.words_per_row, 2)) self.addr_size = self.col_addr_size + self.row_addr_size - debug.check(self.num_rows*self.num_cols==self.word_size*self.num_words,"Invalid bank sizes.") - debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.") + debug.check(self.num_rows * self.num_cols==self.word_size * self.num_words, + "Invalid bank sizes.") + debug.check(self.addr_size==self.col_addr_size + self.row_addr_size, + "Invalid address break down.") # The order of the control signals on the control bus: self.input_control_signals = [] @@ -312,13 +307,13 @@ class bank(design.design): self.num_control_lines = [len(x) for x in self.input_control_signals] # The width of this bus is needed to place other modules (e.g. decoder) for each port - self.central_bus_width = [self.m2_pitch*x + self.m2_width for x in self.num_control_lines] + self.central_bus_width = [self.m2_pitch * x + self.m2_width for x in self.num_control_lines] # These will be outputs of the gaters if this is multibank, if not, normal signals. self.control_signals = [] for port in self.all_ports: if self.num_banks > 1: - self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]]) + self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]]) else: self.control_signals.append(self.input_control_signals[port]) @@ -326,19 +321,18 @@ class bank(design.design): if self.col_addr_size>0: self.num_col_addr_lines = 2**self.col_addr_size else: - self.num_col_addr_lines = 0 - self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines + self.num_col_addr_lines = 0 + self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines # A space for wells or jogging m2 - self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"), - 3*self.m2_pitch) - + self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"), + 3 * self.m2_pitch) def add_modules(self): """ Add all the modules using the class loader """ # create arrays of bitline and bitline_bar names for read, write, or all ports - self.bitcell = factory.create(module_type="bitcell") + self.bitcell = factory.create(module_type="bitcell") self.bl_names = self.bitcell.get_all_bl_names() self.br_names = self.bitcell.get_all_br_names() self.wl_names = self.bitcell.get_all_wl_names() @@ -352,13 +346,11 @@ class bank(design.design): self.port_data.append(temp_pre) self.add_mod(self.port_data[port]) - self.port_address = factory.create(module_type="port_address", cols=self.num_cols, rows=self.num_rows) self.add_mod(self.port_address) - self.port_rbl_map = self.all_ports self.num_rbl = len(self.all_ports) @@ -370,22 +362,20 @@ class bank(design.design): bitcell_ports=self.all_ports) self.add_mod(self.bitcell_array) - if(self.num_banks > 1): self.bank_select = factory.create(module_type="bank_select") self.add_mod(self.bank_select) - def create_bitcell_array(self): """ Creating Bitcell Array """ - self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array", + self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array", mod=self.bitcell_array) temp = [] for col in range(self.num_cols): for bitline in self.bitline_names: - temp.append("{0}_{1}".format(bitline,col)) + temp.append("{0}_{1}".format(bitline, col)) for rbl in range(self.num_rbl): rbl_bl_name=self.bitcell_array.get_rbl_bl_name(rbl) temp.append(rbl_bl_name) @@ -393,23 +383,21 @@ class bank(design.design): temp.append(rbl_br_name) for row in range(self.num_rows): for wordline in self.wl_names: - temp.append("{0}_{1}".format(wordline,row)) + temp.append("{0}_{1}".format(wordline, row)) for port in self.all_ports: temp.append("wl_en{0}".format(port)) temp.append("vdd") temp.append("gnd") self.connect_inst(temp) - def place_bitcell_array(self, offset): """ Placing Bitcell Array """ self.bitcell_array_inst.place(offset) - def create_port_data(self): """ Creating Port Data """ - self.port_data_inst = [None]*len(self.all_ports) + self.port_data_inst = [None] * len(self.all_ports) for port in self.all_ports: self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port), mod=self.port_data[port]) @@ -419,17 +407,17 @@ class bank(design.design): rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port]) temp.append(rbl_bl_name) temp.append(rbl_br_name) - for col in range(self.num_cols): - temp.append("{0}_{1}".format(self.bl_names[port],col)) - temp.append("{0}_{1}".format(self.br_names[port],col)) + for col in range(self.num_cols): + temp.append("{0}_{1}".format(self.bl_names[port], col)) + temp.append("{0}_{1}".format(self.br_names[port], col)) if port in self.read_ports: for bit in range(self.word_size): - temp.append("dout{0}_{1}".format(port,bit)) + temp.append("dout{0}_{1}".format(port, bit)) if port in self.write_ports: for bit in range(self.word_size): - temp.append("din{0}_{1}".format(port,bit)) + temp.append("din{0}_{1}".format(port, bit)) # Will be empty if no col addr lines - sel_names = ["sel{0}_{1}".format(port,x) for x in range(self.num_col_addr_lines)] + sel_names = ["sel{0}_{1}".format(port, x) for x in range(self.num_col_addr_lines)] temp.extend(sel_names) if port in self.read_ports: temp.append("s_en{0}".format(port)) @@ -438,17 +426,16 @@ class bank(design.design): temp.append("w_en{0}".format(port)) for bit in range(self.num_wmasks): temp.append("bank_wmask{0}_{1}".format(port, bit)) - temp.extend(["vdd","gnd"]) + temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def place_port_data(self, offsets): """ Placing Port Data """ for port in self.all_ports: # Top one is unflipped, bottom is flipped along X direction - if port%2 == 1: + if port % 2 == 1: mirror = "R0" else: mirror = "MX" @@ -457,42 +444,40 @@ class bank(design.design): def create_port_address(self): """ Create the hierarchical row decoder """ - self.port_address_inst = [None]*len(self.all_ports) + self.port_address_inst = [None] * len(self.all_ports) for port in self.all_ports: - self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port), + self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port), mod=self.port_address) temp = [] for bit in range(self.row_addr_size): - temp.append("addr{0}_{1}".format(port,bit+self.col_addr_size)) + temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size)) temp.append("wl_en{0}".format(port)) for row in range(self.num_rows): - temp.append("{0}_{1}".format(self.wl_names[port],row)) + temp.append("{0}_{1}".format(self.wl_names[port], row)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def place_port_address(self, offsets): """ Place the hierarchical row decoder """ debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.") - # The address and control bus will be in between decoder and the main memory array - # This bus will route address bits to the decoder input and column mux inputs. + # The address and control bus will be in between decoder and the main memory array + # This bus will route address bits to the decoder input and column mux inputs. # The wires are actually routed after we placed the stuff on both sides. # The predecoder is below the x-axis and the main decoder is above the x-axis # The address flop and decoder are aligned in the x coord. for port in self.all_ports: - if port%2: + if port % 2: mirror = "MY" else: mirror = "R0" self.port_address_inst[port].place(offset=offsets[port], mirror=mirror) - def create_column_decoder(self): - """ + """ Create a 2:4 or 3:8 column address decoder. """ @@ -510,40 +495,38 @@ class bank(design.design): self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=self.dff.height) else: # No error checking before? - debug.error("Invalid column decoder?",-1) + debug.error("Invalid column decoder?", -1) self.add_mod(self.column_decoder) - self.column_decoder_inst = [None]*len(self.all_ports) + self.column_decoder_inst = [None] * len(self.all_ports) for port in self.all_ports: - self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port), + self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port), mod=self.column_decoder) temp = [] for bit in range(self.col_addr_size): - temp.append("addr{0}_{1}".format(port,bit)) + temp.append("addr{0}_{1}".format(port, bit)) for bit in range(self.num_col_addr_lines): - temp.append("sel{0}_{1}".format(port,bit)) + temp.append("sel{0}_{1}".format(port, bit)) temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def place_column_decoder(self, offsets): - """ + """ Place a 2:4 or 3:8 column address decoder. """ if self.col_addr_size == 0: return - debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.") + debug.check(len(offsets)>=len(self.all_ports), + "Insufficient offsets to place column decoder.") for port in self.all_ports: - if port%2 == 1: + if port % 2 == 1: mirror = "XY" else: mirror = "R0" self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror) - - def create_bank_select(self): """ Create the bank select logic. """ @@ -551,7 +534,7 @@ class bank(design.design): if not self.num_banks > 1: return - self.bank_select_inst = [None]*len(self.all_ports) + self.bank_select_inst = [None] * len(self.all_ports) for port in self.all_ports: self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port), mod=self.bank_select) @@ -563,24 +546,23 @@ class bank(design.design): temp.extend(["vdd", "gnd"]) self.connect_inst(temp) - def place_bank_select(self, offsets): """ Place the bank select logic. """ if not self.num_banks > 1: return - debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place bank select logic.") + debug.check(len(offsets)>=len(self.all_ports), + "Insufficient offsets to place bank select logic.") for port in self.all_ports: self.bank_select_inst[port].place(offsets[port]) - def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ for inst in self.insts: - self.copy_power_pins(inst,"vdd") - self.copy_power_pins(inst,"gnd") + self.copy_power_pins(inst, "vdd") + self.copy_power_pins(inst, "gnd") def route_bank_select(self, port): """ Route the bank select logic. """ @@ -595,7 +577,7 @@ class bank(design.design): bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"] gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"] - copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)] + copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)] for signal in range(len(copy_control_signals)): self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal]) @@ -604,25 +586,24 @@ class bank(design.design): out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc() name = self.control_signals[port][signal] bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y) - self.add_path("m3",[out_pos, bus_pos]) + self.add_path("m3", [out_pos, bus_pos]) self.add_via_center(layers=self.m2_stack, offset=bus_pos) self.add_via_center(layers=self.m1_stack, offset=out_pos) self.add_via_center(layers=self.m2_stack, offset=out_pos) - def setup_routing_constraints(self): - """ + """ After the modules are instantiated, find the dimensions for the - control bus, power ring, etc. + control bus, power ring, etc. """ - self.max_y_offset = max([x.uy() for x in self.insts]) + 3*self.m1_width + self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width self.min_y_offset = min([x.by() for x in self.insts]) - self.max_x_offset = max([x.rx() for x in self.insts]) + 3*self.m1_width + self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width self.min_x_offset = min([x.lx() for x in self.insts]) # # Create the core bbox for the power rings @@ -632,7 +613,6 @@ class bank(design.design): self.height = ur.y - ll.y self.width = ur.x - ll.x - def route_central_bus(self): """ Create the address, supply, and control signal central bus lines. """ @@ -640,13 +620,13 @@ class bank(design.design): # Overall central bus width. It includes all the column mux lines, # and control lines. - self.bus_xoffset = [None]*len(self.all_ports) + self.bus_xoffset = [None] * len(self.all_ports) # Port 0 # The bank is at (0,0), so this is to the left of the y-axis. - # 2 pitches on the right for vias/jogs to access the inputs + # 2 pitches on the right for vias/jogs to access the inputs control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_pitch, self.min_y_offset) # The control bus is routed up to two pitches below the bitcell array - control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2*self.m1_pitch + control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch self.bus_xoffset[0] = self.create_bus(layer="m2", pitch=self.m2_pitch, offset=control_bus_offset, @@ -658,7 +638,7 @@ class bank(design.design): # Port 1 if len(self.all_ports)==2: # The other control bus is routed up to two pitches above the bitcell array - control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2*self.m1_pitch + control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch, self.max_y_offset - control_bus_length) # The bus for the right port is reversed so that the rbl_wl is closest to the array @@ -669,7 +649,6 @@ class bank(design.design): length=control_bus_length, vertical=True, make_pins=(self.num_banks==1)) - def route_port_data_to_bitcell_array(self, port): """ Routing of BL and BR between port data and bitcell array """ @@ -677,11 +656,11 @@ class bank(design.design): # Connect the regular bitlines inst2 = self.port_data_inst[port] inst1 = self.bitcell_array_inst - inst1_bl_name = self.bl_names[port]+"_{}" - inst1_br_name = self.br_names[port]+"_{}" + inst1_bl_name = self.bl_names[port] + "_{}" + inst1_br_name = self.br_names[port] + "_{}" - inst2_bl_name = inst2.mod.get_bl_names()+"_{}" - inst2_br_name = inst2.mod.get_br_names()+"_{}" + inst2_bl_name = inst2.mod.get_bl_names() + "_{}" + inst2_br_name = inst2.mod.get_br_names() + "_{}" self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, @@ -693,20 +672,17 @@ class bank(design.design): self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl") self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br") - - def route_port_data_out(self, port): """ Add pins for the port data out """ for bit in range(self.word_size): data_pin = self.port_data_inst[port].get_pin("dout_{0}".format(bit)) - self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port,bit), - layer=data_pin.layer, + self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port, bit), + layer=data_pin.layer, offset=data_pin.center(), height=data_pin.height(), width=data_pin.width()) - def route_port_address_in(self, port): """ Routes the row decoder inputs and supplies """ @@ -714,17 +690,15 @@ class bank(design.design): for row in range(self.row_addr_size): addr_idx = row + self.col_addr_size decoder_name = "addr_{}".format(row) - addr_name = "addr{0}_{1}".format(port,addr_idx) + addr_name = "addr{0}_{1}".format(port, addr_idx) self.copy_layout_pin(self.port_address_inst[port], decoder_name, addr_name) - - def route_port_data_in(self, port): """ Connecting port data in """ for row in range(self.word_size): data_name = "din_{}".format(row) - din_name = "din{0}_{1}".format(port,row) + din_name = "din{0}_{1}".format(port, row) self.copy_layout_pin(self.port_data_inst[port], data_name, din_name) if self.word_size: @@ -732,8 +706,6 @@ class bank(design.design): wmask_name = "bank_wmask_{}".format(row) bank_wmask_name = "bank_wmask{0}_{1}".format(port, row) self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name) - - def channel_route_bitlines(self, inst1, inst2, num_bits, inst1_bl_name="bl_{}", inst1_br_name="br_{}", @@ -751,19 +723,18 @@ class bank(design.design): (bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name) (top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name) - # Channel route each mux separately since we don't minimize the number # of tracks in teh channel router yet. If we did, we could route all the bits at once! - offset = bottom_inst.ul() + vector(0,self.m1_pitch) + offset = bottom_inst.ul() + vector(0, self.m1_pitch) for bit in range(num_bits): bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))] - top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))] + top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))] route_map = list(zip(bottom_names, top_names)) self.create_horizontal_channel_route(route_map, offset, self.m1_stack) def connect_bitline(self, inst1, inst2, inst1_name, inst2_name): """ - Connect two pins of two modules. + Connect two pins of two modules. This assumes that they have sufficient space to create a jog in the middle between the two modules (if needed). """ @@ -784,11 +755,13 @@ class bank(design.design): bottom_loc = bottom_pin.uc() top_loc = top_pin.bc() - yoffset = 0.5*(top_loc.y+bottom_loc.y) - self.add_path(top_pin.layer,[bottom_loc, vector(bottom_loc.x,yoffset), - vector(top_loc.x,yoffset), top_loc]) + yoffset = 0.5 * (top_loc.y + bottom_loc.y) + self.add_path(top_pin.layer, + [bottom_loc, + vector(bottom_loc.x, yoffset), + vector(top_loc.x, yoffset), + top_loc]) - def connect_bitlines(self, inst1, inst2, num_bits, inst1_bl_name, inst1_br_name, inst2_bl_name, inst2_br_name): @@ -799,14 +772,13 @@ class bank(design.design): for col in range(num_bits): self.connect_bitline(inst1, inst2, inst1_bl_name.format(col), inst2_bl_name.format(col)) self.connect_bitline(inst1, inst2, inst1_br_name.format(col), inst2_br_name.format(col)) - def route_port_address(self, port): """ Connect Wordline driver to bitcell array wordline """ self.route_port_address_in(port) - if port%2: + if port % 2: self.route_port_address_right(port) else: self.route_port_address_left(port) @@ -817,21 +789,20 @@ class bank(design.design): for row in range(self.num_rows): # The mid guarantees we exit the input cell to the right. driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).rc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).lc() - mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].rx() + 0.5*self.bitcell_array_inst.lx(),0) - mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0.5,1) + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).lc() + mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].rx() + 0.5 * self.bitcell_array_inst.lx(), 0) + mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1) self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) - def route_port_address_right(self, port): """ Connecting Wordline driver output to Bitcell WL connection """ for row in range(self.num_rows): # The mid guarantees we exit the input cell to the right. driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).lc() - bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).rc() - mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].lx() + 0.5*self.bitcell_array_inst.rx(),0) - mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0,1) + bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).rc() + mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].lx() + 0.5 * self.bitcell_array_inst.rx(), 0) + mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1) self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) def route_column_address_lines(self, port): @@ -845,7 +816,7 @@ class bank(design.design): decode_names = ["Zb", "Z"] # The Address LSB - self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) + self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) elif self.col_addr_size > 1: decode_names = [] @@ -854,11 +825,11 @@ class bank(design.design): for i in range(self.col_addr_size): decoder_name = "in_{}".format(i) - addr_name = "addr{0}_{1}".format(port,i) + addr_name = "addr{0}_{1}".format(port, i) self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) - if port%2: - offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0) + if port % 2: + offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_pitch, 0) else: offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0) @@ -871,7 +842,8 @@ class bank(design.design): self.create_vertical_channel_route(route_map, offset, self.m1_stack) def add_lvs_correspondence_points(self): - """ This adds some points for easier debugging if LVS goes wrong. + """ + 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. """ @@ -880,7 +852,7 @@ class bank(design.design): wl_name = "wl_{}".format(i) wl_pin = self.bitcell_array_inst.get_pin(wl_name) self.add_label(text=wl_name, - layer="m1", + layer="m1", offset=wl_pin.center()) # Add the bitline names @@ -890,35 +862,35 @@ class bank(design.design): bl_pin = self.bitcell_array_inst.get_pin(bl_name) br_pin = self.bitcell_array_inst.get_pin(br_name) self.add_label(text=bl_name, - layer="m2", + layer="m2", offset=bl_pin.center()) self.add_label(text=br_name, - layer="m2", + layer="m2", offset=br_pin.center()) - # # Add the data output names to the sense amp output + # # Add the data output names to the sense amp output # for i in range(self.word_size): # data_name = "data_{}".format(i) # data_pin = self.sense_amp_array_inst.get_pin(data_name) # self.add_label(text="sa_out_{}".format(i), - # layer="m2", + # layer="m2", # offset=data_pin.center()) # Add labels on the decoder for port in self.write_ports: for i in range(self.word_size): data_name = "dec_out_{}".format(i) - pin_name = "in_{}".format(i) + pin_name = "in_{}".format(i) data_pin = self.wordline_driver_inst[port].get_pin(pin_name) self.add_label(text=data_name, - layer="m1", + layer="m1", offset=data_pin.center()) def route_control_lines(self, port): """ Route the control lines of the entire bank """ # Make a list of tuples that we will connect. - # From control signal to the module pin + # From control signal to the module pin # Connection from the central bus to the main control block crosses # pre-decoder and this connection is in metal3 connection = [] @@ -943,58 +915,59 @@ class bank(design.design): for (control_signal, pin_pos) in connection: control_mid_pos = self.bus_xoffset[port][control_signal] - control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y) + control_pos = vector(self.bus_xoffset[port][control_signal].x, pin_pos.y) self.add_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos]) self.add_via_center(layers=self.m1_stack, offset=control_pos) - # clk to wordline_driver - control_signal = self.prefix+"wl_en{}".format(port) - if port%2: + control_signal = self.prefix + "wl_en{}".format(port) + if port % 2: pin_pos = self.port_address_inst[port].get_pin("wl_en").uc() - mid_pos = pin_pos + vector(0,2*self.m2_gap) # to route down to the top of the bus + mid_pos = pin_pos + vector(0, 2 * self.m2_gap) # to route down to the top of the bus else: pin_pos = self.port_address_inst[port].get_pin("wl_en").bc() - mid_pos = pin_pos - vector(0,2*self.m2_gap) # to route down to the top of the bus + mid_pos = pin_pos - vector(0, 2 * self.m2_gap) # to route down to the top of the bus control_x_offset = self.bus_xoffset[port][control_signal].x control_pos = vector(control_x_offset, mid_pos.y) - self.add_wire(self.m1_stack,[pin_pos, mid_pos, control_pos]) + self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos]) self.add_via_center(layers=self.m1_stack, offset=control_pos) def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): """Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline""" - #Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption + # Decoder is assumed to have settled before the negative edge of the clock. + # Delay model relies on this assumption stage_effort_list = [] wordline_cout = self.bitcell_array.get_wordline_cin() + external_cout - stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout,inp_is_rise) + stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout, + inp_is_rise) return stage_effort_list def get_wl_en_cin(self): """Get the relative capacitance of all the clk connections in the bank""" - #wl_en only used in the wordline driver. + # wl_en only used in the wordline driver. return self.port_address.wordline_driver.get_wl_en_cin() def get_w_en_cin(self): """Get the relative capacitance of all the clk connections in the bank""" - #wl_en only used in the wordline driver. - port = self.write_ports[0] + # wl_en only used in the wordline driver. + port = self.write_ports[0] return self.port_data[port].write_driver.get_w_en_cin() def get_clk_bar_cin(self): """Get the relative capacitance of all the clk_bar connections in the bank""" - #Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array. + # Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array. - #Precharges are the all the same in Mulitport, one is picked + # Precharges are the all the same in Mulitport, one is picked port = self.read_ports[0] return self.port_data[port].precharge_array.get_en_cin() def get_sen_cin(self): """Get the relative capacitance of all the sense amp enable connections in the bank""" - #Current bank only uses sen as an enable for the sense amps. - port = self.read_ports[0] + # Current bank only uses sen as an enable for the sense amps. + port = self.read_ports[0] return self.port_data[port].sense_amp_array.get_en_cin() def graph_exclude_precharge(self): @@ -1005,5 +978,7 @@ class bank(design.design): def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" - return self.bitcell_array_inst.mod.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col) + return self.bitcell_array_inst.mod.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, + row, + col) From 531262970243a7eb165c713960499b628164ddd2 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 5 Mar 2020 12:10:13 -0800 Subject: [PATCH 15/22] Remove jog in precharge. Jog is in port data --- compiler/pgates/precharge.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 3f0560f5..56382712 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -228,32 +228,25 @@ class precharge(design.design): layer_width = drc("minwidth_" + self.bitline_layer) layer_space = drc("{0}_to_{0}".format(self.bitline_layer)) - yoffset = self.initial_yoffset - 0.5 * self.m2_pitch # adds the BL self.bl_xoffset = layer_space + 0.5 * layer_width top_pos = vector(self.bl_xoffset, self.height) - mid1_pos = vector(self.bl_xoffset, yoffset) - mid2_pos = vector(self.bitcell_bl_pin.cx(), yoffset) - pin_pos = vector(self.bitcell_bl_pin.cx(), 0) - self.add_path(self.bitline_layer, - [top_pos, mid1_pos, mid2_pos, pin_pos]) + pin_pos = vector(self.bl_xoffset, 0) + self.add_path(self.bitline_layer, [top_pos, pin_pos]) self.bl_pin = self.add_layout_pin_segment_center(text="bl", layer=self.bitline_layer, start=pin_pos, - end=mid2_pos) + end=top_pos) # adds the BR self.br_xoffset = self.width - layer_space - 0.5 * layer_width top_pos = vector(self.br_xoffset, self.height) - mid1_pos = vector(self.br_xoffset, yoffset) - mid2_pos = vector(self.bitcell_br_pin.cx(), yoffset) - pin_pos = vector(self.bitcell_br_pin.cx(), 0) - self.add_path(self.bitline_layer, - [top_pos, mid1_pos, mid2_pos, pin_pos]) + pin_pos = vector(self.br_xoffset, 0) + self.add_path(self.bitline_layer, [top_pos, pin_pos]) self.br_pin = self.add_layout_pin_segment_center(text="br", layer=self.bitline_layer, start=pin_pos, - end=mid2_pos) + end=top_pos) def connect_to_bitlines(self): """ From 5b2365336989c2c7fd78a79a7d5da71e5c5b250a Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 5 Mar 2020 16:13:49 -0800 Subject: [PATCH 16/22] PEP8 Formatting --- compiler/sram/sram_1bank.py | 147 +++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 69 deletions(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 2f37d927..ae16b6bd 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -19,9 +19,9 @@ class sram_1bank(sram_base): sram_base.__init__(self, name, sram_config) def create_modules(self): - """ + """ This adds the modules for a single bank SRAM with control - logic. + logic. """ self.bank_inst=self.create_bank(0) @@ -40,7 +40,7 @@ class sram_1bank(sram_base): self.data_dff_insts = self.create_data_dff() def place_instances(self): - """ + """ This places the instances for a single bank SRAM with control logic and up to 2 ports. """ @@ -53,19 +53,19 @@ class sram_1bank(sram_base): # the sense amps/column mux and cell array) # The x-coordinate is placed to allow a single clock wire (plus an extra pitch) # up to the row address DFFs. - control_pos = [None]*len(self.all_ports) - row_addr_pos = [None]*len(self.all_ports) - col_addr_pos = [None]*len(self.all_ports) - wmask_pos = [None]*len(self.all_ports) - data_pos = [None]*len(self.all_ports) + control_pos = [None] * len(self.all_ports) + row_addr_pos = [None] * len(self.all_ports) + col_addr_pos = [None] * len(self.all_ports) + wmask_pos = [None] * len(self.all_ports) + data_pos = [None] * len(self.all_ports) if self.write_size: - max_gap_size = self.m3_pitch*self.word_size + 2*self.m1_pitch - max_gap_size_wmask = self.m2_pitch*max(self.num_wmasks+1,self.col_addr_size+1) + 2*self.m1_pitch + max_gap_size = self.m3_pitch * self.word_size + 2 * self.m1_pitch + max_gap_size_wmask = self.m2_pitch * max(self.num_wmasks + 1, self.col_addr_size + 1) + 2 * self.m1_pitch else: # This is M2 pitch even though it is on M1 to help stem via spacings on the trunk # The M1 pitch is for supply rail spacings - max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch + max_gap_size = self.m2_pitch * max(self.word_size + 1,self.col_addr_size + 1) + 2 * self.m1_pitch # Port 0 port = 0 @@ -79,7 +79,7 @@ class sram_1bank(sram_base): # Add the data flops below the write mask flops. data_pos[port] = vector(self.bank.bank_array_ll.x, - -max_gap_size - max_gap_size_wmask - 2*self.dff.height) + -max_gap_size - max_gap_size_wmask - 2 * self.dff.height) self.data_dff_insts[port].place(data_pos[port]) else: # Add the data flops below the bank to the right of the lower-left of bank array @@ -93,8 +93,7 @@ class sram_1bank(sram_base): self.data_dff_insts[port].place(data_pos[port]) else: wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0) - data_pos[port] = vector(self.bank.bank_array_ll.x,0) - + data_pos[port] = vector(self.bank.bank_array_ll.x, 0) # Add the col address flops below the bank to the left of the lower-left of bank array if self.col_addr_dff: @@ -106,11 +105,11 @@ class sram_1bank(sram_base): -max_gap_size - self.col_addr_dff_insts[port].height) self.col_addr_dff_insts[port].place(col_addr_pos[port]) else: - col_addr_pos[port] = vector(self.bank.bank_array_ll.x,0) + col_addr_pos[port] = vector(self.bank.bank_array_ll.x, 0) # This includes 2 M2 pitches for the row addr clock line. - control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch, - self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - 2*self.bank.m2_gap ) + control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch, + self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - 2 * self.bank.m2_gap) self.control_logic_insts[port].place(control_pos[port]) # The row address bits are placed above the control logic aligned on the right. @@ -133,7 +132,7 @@ class sram_1bank(sram_base): # Add the data flops below the write mask flops data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, - self.bank.height + max_gap_size_wmask + max_gap_size + 2*self.dff.height) + self.bank.height + max_gap_size_wmask + max_gap_size + 2 * self.dff.height) self.data_dff_insts[port].place(data_pos[port], mirror="MX") else: # Add the data flops above the bank to the left of the upper-right of bank array @@ -158,11 +157,10 @@ class sram_1bank(sram_base): col_addr_pos[port] = self.bank_inst.ur() # This includes 2 M2 pitches for the row addr clock line - control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch, - self.bank.bank_array_ur.y + self.control_logic_insts[port].height - + control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch, + self.bank.bank_array_ur.y + self.control_logic_insts[port].height - \ (self.control_logic_insts[port].height - self.control_logic_insts[port].mod.control_logic_center.y) - + 2*self.bank.m2_gap) - #import pdb; pdb.set_trace() + + 2 * self.bank.m2_gap) self.control_logic_insts[port].place(control_pos[port], mirror="XY") # The row address bits are placed above the control logic aligned on the left. @@ -172,7 +170,6 @@ class sram_1bank(sram_base): row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY") - def add_layout_pins(self): """ Add the top-level pins for a single bank SRAM with control. @@ -180,28 +177,39 @@ class sram_1bank(sram_base): for port in self.all_ports: # Connect the control pins as inputs for signal in self.control_logic_inputs[port] + ["clk"]: - self.copy_layout_pin(self.control_logic_insts[port], signal, signal+"{}".format(port)) + self.copy_layout_pin(self.control_logic_insts[port], + signal, + signal + "{}".format(port)) if port in self.read_ports: for bit in range(self.word_size): - self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "dout{0}[{1}]".format(port,bit)) + self.copy_layout_pin(self.bank_inst, + "dout{0}_{1}".format(port, bit), + "dout{0}[{1}]".format(port, bit)) # Lower address bits for bit in range(self.col_addr_size): - self.copy_layout_pin(self.col_addr_dff_insts[port], "din_{}".format(bit),"addr{0}[{1}]".format(port,bit)) + self.copy_layout_pin(self.col_addr_dff_insts[port], + "din_{}".format(bit), + "addr{0}[{1}]".format(port, bit)) # Upper address bits for bit in range(self.row_addr_size): - self.copy_layout_pin(self.row_addr_dff_insts[port], "din_{}".format(bit),"addr{0}[{1}]".format(port,bit+self.col_addr_size)) + self.copy_layout_pin(self.row_addr_dff_insts[port], + "din_{}".format(bit), + "addr{0}[{1}]".format(port, bit + self.col_addr_size)) if port in self.write_ports: for bit in range(self.word_size): - self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "din{0}[{1}]".format(port,bit)) + self.copy_layout_pin(self.data_dff_insts[port], + "din_{}".format(bit), + "din{0}[{1}]".format(port, bit)) if self.write_size: for bit in range(self.num_wmasks): - self.copy_layout_pin(self.wmask_dff_insts[port], "din_{}".format(bit), "wmask{0}[{1}]".format(port,bit)) + self.copy_layout_pin(self.wmask_dff_insts[port], + "din_{}".format(bit), + "wmask{0}[{1}]".format(port, bit)) - def route_layout(self): """ Route a single bank SRAM """ @@ -255,13 +263,15 @@ class sram_1bank(sram_base): offset=clk_steiner_pos) # Note, the via to the control logic is taken care of above - self.add_wire(("m3","via2","m2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos]) + self.add_wire(("m3", "via2", "m2"), + [row_addr_clk_pos, mid1_pos, clk_steiner_pos]) if self.col_addr_dff: dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") dff_clk_pos = dff_clk_pin.center() mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) - self.add_wire(("m3","via2","m2"),[dff_clk_pos, mid_pos, clk_steiner_pos]) + self.add_wire(("m3", "via2", "m2"), + [dff_clk_pos, mid_pos, clk_steiner_pos]) if port in self.write_ports: data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") @@ -269,8 +279,11 @@ class sram_1bank(sram_base): mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y) # In some designs, the steiner via will be too close to the mid_pos via # so make the wire as wide as the contacts - self.add_path("m2",[mid_pos, clk_steiner_pos], width=max(m2_via.width,m2_via.height)) - self.add_wire(("m3","via2","m2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos]) + self.add_path("m2", + [mid_pos, clk_steiner_pos], + width=max(m2_via.width, m2_via.height)) + self.add_wire(("m3", "via2", "m2"), + [data_dff_clk_pos, mid_pos, clk_steiner_pos]) if self.write_size: wmask_dff_clk_pin = self.wmask_dff_insts[port].get_pin("clk") @@ -280,7 +293,6 @@ class sram_1bank(sram_base): # so make the wire as wide as the contacts self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height)) self.add_wire(("m3", "via2", "m2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos]) - def route_control_logic(self): """ Route the control logic pins that are not inputs """ @@ -291,28 +303,28 @@ class sram_1bank(sram_base): if "clk" in signal: continue src_pin = self.control_logic_insts[port].get_pin(signal) - dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) + dest_pin = self.bank_inst.get_pin(signal + "{}".format(port)) self.connect_vbus_m2m3(src_pin, dest_pin) for port in self.all_ports: # Only input (besides pins) is the replica bitline src_pin = self.control_logic_insts[port].get_pin("rbl_bl") - dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port)) + dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port)) self.connect_vbus_m2m3(src_pin, dest_pin) - def route_row_addr_dff(self): """ Connect the output of the row flops to the bank pins """ for port in self.all_ports: for bit in range(self.row_addr_size): flop_name = "dout_{}".format(bit) - bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size) + bank_name = "addr{0}_{1}".format(port, bit + self.col_addr_size) flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name) bank_pin = self.bank_inst.get_pin(bank_name) flop_pos = flop_pin.center() bank_pos = bank_pin.center() - mid_pos = vector(bank_pos.x,flop_pos.y) - self.add_wire(("m3","via2","m2"),[flop_pos, mid_pos,bank_pos]) + mid_pos = vector(bank_pos.x, flop_pos.y) + self.add_wire(("m3", "via2", "m2"), + [flop_pos, mid_pos, bank_pos]) self.add_via_center(layers=self.m2_stack, offset=flop_pos) @@ -320,11 +332,11 @@ class sram_1bank(sram_base): """ Connect the output of the col flops to the bank pins """ for port in self.all_ports: if port%2: - offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size+2)*self.m1_pitch) + offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size + 2) * self.m1_pitch) else: - offset = self.col_addr_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) + offset = self.col_addr_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch) - bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)] + bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)] col_addr_bus_offsets = self.create_horizontal_bus(layer="m1", pitch=self.m1_pitch, offset=offset, @@ -335,10 +347,9 @@ class sram_1bank(sram_base): data_dff_map = zip(dff_names, bus_names) self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_insts[port], col_addr_bus_offsets) - bank_names = ["addr{0}_{1}".format(port,x) for x in range(self.col_addr_size)] + bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)] data_bank_map = zip(bank_names, bus_names) self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets) - def route_data_dff(self): """ Connect the output of the data flops to the write driver """ @@ -346,14 +357,14 @@ class sram_1bank(sram_base): for port in self.write_ports: if self.write_size: if port % 2: - offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2)*self.m3_pitch) + offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m3_pitch) else: offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m3_pitch) else: - if port%2: - offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) + if port % 2: + offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m1_pitch) else: - offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) + offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch) dff_names = ["dout_{}".format(x) for x in range(self.word_size)] dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] @@ -362,13 +373,13 @@ class sram_1bank(sram_base): pin_offset = self.data_dff_insts[port].get_pin(x).center() self.add_via_center(layers=self.m1_stack, offset=pin_offset, - directions = ("V", "V")) + directions=("V", "V")) self.add_via_center(layers=self.m2_stack, offset=pin_offset) self.add_via_center(layers=self.m3_stack, offset=pin_offset) - bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)] + bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size)] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] if self.write_size: for x in bank_names: @@ -397,7 +408,7 @@ class sram_1bank(sram_base): # This is where the channel will start (y-dimension at least) for port in self.write_ports: if port % 2: - offset = self.wmask_dff_insts[port].ll() - vector(0, (self.num_wmasks+2) * self.m1_pitch) + offset = self.wmask_dff_insts[port].ll() - vector(0, (self.num_wmasks + 2) * self.m1_pitch) else: offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch) @@ -416,16 +427,14 @@ class sram_1bank(sram_base): self.add_via_center(layers=self.m1_stack, offset=offset_pin) - route_map = list(zip(bank_pins, dff_pins)) self.create_horizontal_channel_route(netlist=route_map, offset=offset, layer_stack=self.m1_stack) - 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. """ @@ -438,7 +447,7 @@ class sram_1bank(sram_base): def graph_exclude_data_dff(self): """Removes data dff and wmask dff (if applicable) from search graph. """ - #Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay. + # Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay. for inst in self.data_dff_insts: self.graph_inst_exclude.add(inst) if self.write_size: @@ -447,7 +456,7 @@ class sram_1bank(sram_base): def graph_exclude_addr_dff(self): """Removes data dff from search graph. """ - #Address is considered not part of the critical path, subjectively removed + # Address is considered not part of the critical path, subjectively removed for inst in self.row_addr_dff_insts: self.graph_inst_exclude.add(inst) @@ -457,27 +466,27 @@ class sram_1bank(sram_base): def graph_exclude_ctrl_dffs(self): """Exclude dffs for CSB, WEB, etc from graph""" - #Insts located in control logic, exclusion function called here + # Insts located in control logic, exclusion function called here for inst in self.control_logic_insts: inst.mod.graph_exclude_dffs() def get_sen_name(self, sram_name, port=0): """Returns the s_en spice name.""" - #Naming scheme is hardcoded using this function, should be built into the - #graph in someway. + # Naming scheme is hardcoded using this function, should be built into the + # graph in someway. sen_name = "s_en{}".format(port) control_conns = self.get_conns(self.control_logic_insts[port]) - #Sanity checks + # Sanity checks if sen_name not in control_conns: - debug.error("Signal={} not contained in control logic connections={}"\ - .format(sen_name, control_conns)) + debug.error("Signal={} not contained in control logic connections={}".format(sen_name, + control_conns)) if sen_name in self.pins: - debug.error("Internal signal={} contained in port list. Name defined by the parent.") + debug.error("Internal signal={} contained in port list. Name defined by the parent.".format(sen_name)) return "X{}.{}".format(sram_name, sen_name) def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" - #Sanity check in case it was forgotten + # Sanity check in case it was forgotten if inst_name.find('x') != 0: - inst_name = 'x'+inst_name - return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col) + inst_name = 'x' + inst_name + return self.bank_inst.mod.get_cell_name(inst_name + '.x' + self.bank_inst.name, row, col) From 6506622dfb7dde6d300123e5b51cc5866698716d Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 5 Mar 2020 16:20:21 -0800 Subject: [PATCH 17/22] PEP8 Formatting --- compiler/sram/sram.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index dfde78f7..0efad5cc 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -5,13 +5,11 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import sys import datetime -import getpass import debug from globals import OPTS, print_time -from sram_config import sram_config - + + class sram(): """ This is not a design module, but contains an SRAM design instance. @@ -28,7 +26,7 @@ class sram(): from design import design design.name_map=[] - debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size, + debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size, self.num_words, self.num_banks)) start_time = datetime.datetime.now() @@ -40,30 +38,28 @@ class sram(): elif self.num_banks == 2: from sram_2bank import sram_2bank as sram else: - debug.error("Invalid number of banks.",-1) + debug.error("Invalid number of banks.", -1) - self.s = sram(name, sram_config) + self.s = sram(name, sram_config) self.s.create_netlist() if not OPTS.netlist_only: self.s.create_layout() if not OPTS.is_unit_test: print_time("SRAM creation", datetime.datetime.now(), start_time) - - def sp_write(self,name): + def sp_write(self, name): self.s.sp_write(name) - def lef_write(self,name): + def lef_write(self, name): self.s.lef_write(name) - def gds_write(self,name): + def gds_write(self, name): self.s.gds_write(name) - def verilog_write(self,name): + def verilog_write(self, name): self.s.verilog_write(name) - def save(self): """ Save all the output files while reporting time to do it as well. """ @@ -107,7 +103,6 @@ class sram(): debug.print_raw("LIB: Characterizing... ") lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) print_time("Characterization", datetime.datetime.now(), start_time) - # Write the config file start_time = datetime.datetime.now() From 05f9e809b403a363812394bae22f08645e1fcfe2 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 5 Mar 2020 16:27:35 -0800 Subject: [PATCH 18/22] PEP8 Formatting --- compiler/sram/sram_base.py | 150 ++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 84 deletions(-) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index c7c91e90..d2fbe9fc 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -5,20 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import sys import datetime -import getpass import debug -from datetime import datetime from importlib import reload from vector import vector from globals import OPTS, print_time -import logical_effort from design import design from verilog import verilog from lef import lef from sram_factory import factory -import logical_effort + class sram_base(design, verilog, lef): """ @@ -36,11 +32,11 @@ class sram_base(design, verilog, lef): self.bank_insts = [] if self.write_size: - self.num_wmasks = int(self.word_size/self.write_size) + self.num_wmasks = int(self.word_size / self.write_size) else: self.num_wmasks = 0 - #For logical effort delay calculations. + # For logical effort delay calculations. self.all_mods_except_control_done = False def add_pins(self): @@ -48,11 +44,11 @@ class sram_base(design, verilog, lef): for port in self.write_ports: for bit in range(self.word_size): - self.add_pin("din{0}[{1}]".format(port,bit),"INPUT") + self.add_pin("din{0}[{1}]".format(port, bit), "INPUT") for port in self.all_ports: for bit in range(self.addr_size): - self.add_pin("addr{0}[{1}]".format(port,bit),"INPUT") + self.add_pin("addr{0}[{1}]".format(port, bit), "INPUT") # These are used to create the physical pins self.control_logic_inputs = [] @@ -69,22 +65,21 @@ class sram_base(design, verilog, lef): self.control_logic_outputs.append(self.control_logic_r.get_outputs()) for port in self.all_ports: - self.add_pin("csb{}".format(port),"INPUT") + self.add_pin("csb{}".format(port), "INPUT") for port in self.readwrite_ports: - self.add_pin("web{}".format(port),"INPUT") + self.add_pin("web{}".format(port), "INPUT") for port in self.all_ports: - self.add_pin("clk{}".format(port),"INPUT") + self.add_pin("clk{}".format(port), "INPUT") # add the optional write mask pins for port in self.write_ports: for bit in range(self.num_wmasks): - self.add_pin("wmask{0}[{1}]".format(port,bit),"INPUT") + self.add_pin("wmask{0}[{1}]".format(port, bit), "INPUT") for port in self.read_ports: for bit in range(self.word_size): - self.add_pin("dout{0}[{1}]".format(port,bit),"OUTPUT") + self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT") - self.add_pin("vdd","POWER") - self.add_pin("gnd","GROUND") - + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def create_netlist(self): """ Netlist creation """ @@ -100,23 +95,21 @@ class sram_base(design, verilog, lef): self.width=0 self.height=0 - if not OPTS.is_unit_test: - print_time("Submodules",datetime.now(), start_time) - + print_time("Submodules", datetime.now(), start_time) def create_layout(self): - """ Layout creation """ + """ Layout creation """ start_time = datetime.now() self.place_instances() if not OPTS.is_unit_test: - print_time("Placement",datetime.now(), start_time) + print_time("Placement", datetime.now(), start_time) start_time = datetime.now() self.route_layout() self.route_supplies() if not OPTS.is_unit_test: - print_time("Routing",datetime.now(), start_time) + print_time("Routing", datetime.now(), start_time) self.add_lvs_correspondence_points() @@ -130,10 +123,10 @@ class sram_base(design, verilog, lef): # We only enable final verification if we have routed the design self.DRC_LVS(final_verification=OPTS.route_supplies, top_level=True) if not OPTS.is_unit_test: - print_time("Verification",datetime.now(), start_time) + print_time("Verification", datetime.now(), start_time) def create_modules(self): - debug.error("Must override pure virtual function.",-1) + debug.error("Must override pure virtual function.", -1) def route_supplies(self): """ Route the supply grid and connect the pins to them. """ @@ -141,8 +134,8 @@ class sram_base(design, verilog, lef): # Copy the pins to the top level # This will either be used to route or left unconnected. for inst in self.insts: - self.copy_power_pins(inst,"vdd") - self.copy_power_pins(inst,"gnd") + self.copy_power_pins(inst, "vdd") + self.copy_power_pins(inst, "gnd") import tech if not OPTS.route_supplies: @@ -164,33 +157,31 @@ class sram_base(design, verilog, lef): from supply_grid_router import supply_grid_router as router rtr=router(grid_stack, self) rtr.route() - def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """ # address size + control signals + one-hot bank select signals - self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks,2) + 1 + self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks, 2) + 1 # data bus size self.num_horizontal_line = self.word_size - self.vertical_bus_width = self.m2_pitch*self.num_vertical_line + self.vertical_bus_width = self.m2_pitch * self.num_vertical_line # vertical bus height depends on 2 or 4 banks - self.data_bus_height = self.m3_pitch*self.num_horizontal_line - self.data_bus_width = 2*(self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width + self.data_bus_height = self.m3_pitch * self.num_horizontal_line + self.data_bus_width = 2 * (self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width - self.control_bus_height = self.m1_pitch*(self.control_size+2) + self.control_bus_height = self.m1_pitch * (self.control_size + 2) self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width - self.supply_bus_height = self.m1_pitch*2 # 2 for vdd/gnd placed with control bus + self.supply_bus_height = self.m1_pitch * 2 # 2 for vdd/gnd placed with control bus self.supply_bus_width = self.data_bus_width # Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really) - debug.check(self.bank.width + self.vertical_bus_width > 0.9*self.control_logic.width, + debug.check(self.bank.width + self.vertical_bus_width > 0.9 * self.control_logic.width, "Bank is too small compared to control logic.") - def add_busses(self): """ Add the horizontal and vertical busses """ # Vertical bus @@ -213,24 +204,22 @@ class sram_base(design, verilog, lef): names=self.control_bus_names[port], length=self.vertical_bus_height) - self.addr_bus_names=["A{0}[{1}]".format(port,i) for i in range(self.addr_size)] + self.addr_bus_names=["A{0}[{1}]".format(port, i) for i in range(self.addr_size)] self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2", pitch=self.m2_pitch, offset=self.addr_bus_offset, names=self.addr_bus_names, length=self.addr_bus_height)) - - self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port,i) for i in range(self.num_banks)] + self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port, i) for i in range(self.num_banks)] self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2", pitch=self.m2_pitch, offset=self.bank_sel_bus_offset, names=self.bank_sel_bus_names, length=self.vertical_bus_height)) - # Horizontal data bus - self.data_bus_names = ["DATA{0}[{1}]".format(port,i) for i in range(self.word_size)] + self.data_bus_names = ["DATA{0}[{1}]".format(port, i) for i in range(self.word_size)] self.data_bus_positions = self.create_horizontal_pin_bus(layer="m3", pitch=self.m3_pitch, offset=self.data_bus_offset, @@ -249,7 +238,7 @@ class sram_base(design, verilog, lef): # the decoder in 4-bank SRAMs self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1", pitch=self.m1_pitch, - offset=self.supply_bus_offset+vector(0,self.m1_pitch), + offset=self.supply_bus_offset + vector(0, self.m1_pitch), names=["gnd"], length=self.supply_bus_width)) self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1", @@ -258,20 +247,17 @@ class sram_base(design, verilog, lef): names=self.control_bus_names[port], length=self.control_bus_width)) - - def add_multi_bank_modules(self): """ Create the multibank address flops and bank decoder """ from dff_buf_array import dff_buf_array self.msb_address = dff_buf_array(name="msb_address", rows=1, - columns=self.num_banks/2) + columns=self.num_banks / 2) self.add_mod(self.msb_address) if self.num_banks>2: self.msb_decoder = self.bank.decoder.pre2_4 self.add_mod(self.msb_decoder) - def add_modules(self): self.bitcell = factory.create(module_type=OPTS.bitcell) @@ -293,7 +279,6 @@ class sram_base(design, verilog, lef): if self.write_size: self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks) self.add_mod(self.wmask_dff) - # Create the bank module (up to four are instantiated) self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank") @@ -305,7 +290,8 @@ class sram_base(design, verilog, lef): self.bank_count = 0 - #The control logic can resize itself based on the other modules. Requires all other modules added before control logic. + # The control logic can resize itself based on the other modules. + # Requires all other modules added before control logic. self.all_mods_except_control_done = True c = reload(__import__(OPTS.control_logic)) @@ -320,40 +306,40 @@ class sram_base(design, verilog, lef): port_type="rw") self.add_mod(self.control_logic_rw) if len(self.writeonly_ports)>0: - self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, + self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, sram=self, port_type="w") self.add_mod(self.control_logic_w) if len(self.readonly_ports)>0: - self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, + self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, sram=self, port_type="r") self.add_mod(self.control_logic_r) - def create_bank(self,bank_num): - """ Create a bank """ + def create_bank(self, bank_num): + """ Create a bank """ self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num), mod=self.bank)) temp = [] for port in self.read_ports: for bit in range(self.word_size): - temp.append("dout{0}[{1}]".format(port,bit)) + temp.append("dout{0}[{1}]".format(port, bit)) for port in self.all_ports: temp.append("rbl_bl{0}".format(port)) for port in self.write_ports: for bit in range(self.word_size): - temp.append("bank_din{0}[{1}]".format(port,bit)) + temp.append("bank_din{0}[{1}]".format(port, bit)) for port in self.all_ports: for bit in range(self.bank_addr_size): - temp.append("a{0}[{1}]".format(port,bit)) + temp.append("a{0}[{1}]".format(port, bit)) if(self.num_banks > 1): for port in self.all_ports: - temp.append("bank_sel{0}[{1}]".format(port,bank_num)) + temp.append("bank_sel{0}[{1}]".format(port, bank_num)) for port in self.read_ports: temp.append("s_en{0}".format(port)) for port in self.all_ports: @@ -369,7 +355,6 @@ class sram_base(design, verilog, lef): return self.bank_insts[-1] - def place_bank(self, bank_inst, position, x_flip, y_flip): """ Place a bank at the given position with orientations """ @@ -400,7 +385,6 @@ class sram_base(design, verilog, lef): return bank_inst - def create_row_addr_dff(self): """ Add all address flops for the main decoder """ insts = [] @@ -412,13 +396,12 @@ class sram_base(design, verilog, lef): inputs = [] outputs = [] for bit in range(self.row_addr_size): - inputs.append("addr{}[{}]".format(port,bit+self.col_addr_size)) - outputs.append("a{}[{}]".format(port,bit+self.col_addr_size)) + inputs.append("addr{}[{}]".format(port, bit + self.col_addr_size)) + outputs.append("a{}[{}]".format(port, bit + self.col_addr_size)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) return insts - def create_col_addr_dff(self): """ Add and place all address flops for the column decoder """ @@ -431,14 +414,13 @@ class sram_base(design, verilog, lef): inputs = [] outputs = [] for bit in range(self.col_addr_size): - inputs.append("addr{}[{}]".format(port,bit)) - outputs.append("a{}[{}]".format(port,bit)) + inputs.append("addr{}[{}]".format(port, bit)) + outputs.append("a{}[{}]".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) return insts - def create_data_dff(self): """ Add and place all data flops """ insts = [] @@ -454,8 +436,8 @@ class sram_base(design, verilog, lef): inputs = [] outputs = [] for bit in range(self.word_size): - inputs.append("din{}[{}]".format(port,bit)) - outputs.append("bank_din{}[{}]".format(port,bit)) + inputs.append("din{}[{}]".format(port, bit)) + outputs.append("bank_din{}[{}]".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) @@ -483,7 +465,6 @@ class sram_base(design, verilog, lef): return insts - def create_control_logic(self): """ Add control logic instances """ @@ -516,12 +497,13 @@ class sram_base(design, verilog, lef): return insts - def connect_vbus_m2m3(self, src_pin, dest_pin): - """ Helper routine to connect an instance to a vertical bus. + """ + Helper routine to connect an instance to a vertical bus. Routes horizontal then vertical L shape. Dest pin is assumed to be on M2. - Src pin can be on M1/M2/M3.""" + Src pin can be on M1/M2/M3. + """ if src_pin.cx() Date: Fri, 6 Mar 2020 09:03:52 -0800 Subject: [PATCH 19/22] Route RBL to edge of bank. --- compiler/modules/bank.py | 31 ++++++++++++++++------ compiler/sram/sram_1bank.py | 5 ++-- compiler/sram/sram_base.py | 51 +++++++++++++++++++++++++++++++------ 3 files changed, 69 insertions(+), 18 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 60c84a1e..7ba33230 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -126,11 +126,22 @@ class bank(design.design): bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name) - self.add_layout_pin(text="rbl_bl{0}".format(port), - layer=bl_pin.layer, - offset=bl_pin.ll(), - height=bl_pin.height(), - width=bl_pin.width()) + # This will ensure the pin is only on the top or bottom edge + if port % 2: + via_offset = bl_pin.uc() + left_right_offset = vector(self.max_x_offset, via_offset.y) + else: + via_offset = bl_pin.bc() + left_right_offset = vector(self.min_x_offset, via_offset.y) + if bl_pin == "m1": + self.add_via_center(layers=self.m1_stack, + offset=via_offset) + self.add_via_center(layers=self.m2_stack, + offset=via_offset) + self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port), + layer="m3", + start=left_right_offset, + end=via_offset) def route_bitlines(self, port): """ Route the bitlines depending on the port type rw, w, or r. """ @@ -662,9 +673,13 @@ class bank(design.design): inst2_bl_name = inst2.mod.get_bl_names() + "_{}" inst2_br_name = inst2.mod.get_br_names() + "_{}" - self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, - inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, - inst2_bl_name=inst2_bl_name, inst2_br_name=inst2_br_name) + self.connect_bitlines(inst1=inst1, + inst2=inst2, + num_bits=self.num_cols, + inst1_bl_name=inst1_bl_name, + inst1_br_name=inst1_br_name, + inst2_bl_name=inst2_bl_name, + inst2_br_name=inst2_br_name) # Connect the replica bitlines rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index ae16b6bd..76e9f805 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -310,8 +310,9 @@ class sram_1bank(sram_base): # Only input (besides pins) is the replica bitline src_pin = self.control_logic_insts[port].get_pin("rbl_bl") dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port)) - self.connect_vbus_m2m3(src_pin, dest_pin) - + + self.connect_hbus_m2m3(src_pin, dest_pin) + def route_row_addr_dff(self): """ Connect the output of the row flops to the bank pins """ for port in self.all_ports: diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index d2fbe9fc..e8a91486 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -84,7 +84,7 @@ class sram_base(design, verilog, lef): def create_netlist(self): """ Netlist creation """ - start_time = datetime.now() + start_time = datetime.datetime.now() # Must create the control logic before pins to get the pins self.add_modules() @@ -96,20 +96,20 @@ class sram_base(design, verilog, lef): self.height=0 if not OPTS.is_unit_test: - print_time("Submodules", datetime.now(), start_time) + print_time("Submodules", datetime.datetime.now(), start_time) def create_layout(self): """ Layout creation """ - start_time = datetime.now() + start_time = datetime.datetime.now() self.place_instances() if not OPTS.is_unit_test: - print_time("Placement", datetime.now(), start_time) + print_time("Placement", datetime.datetime.now(), start_time) - start_time = datetime.now() + start_time = datetime.datetime.now() self.route_layout() self.route_supplies() if not OPTS.is_unit_test: - print_time("Routing", datetime.now(), start_time) + print_time("Routing", datetime.datetime.now(), start_time) self.add_lvs_correspondence_points() @@ -119,11 +119,11 @@ class sram_base(design, verilog, lef): self.width = highest_coord[0] self.height = highest_coord[1] - start_time = datetime.now() + 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) if not OPTS.is_unit_test: - print_time("Verification", datetime.now(), start_time) + print_time("Verification", datetime.datetime.now(), start_time) def create_modules(self): debug.error("Must override pure virtual function.", -1) @@ -526,6 +526,41 @@ class sram_base(design, verilog, lef): self.add_via_center(layers=self.m2_stack, offset=in_pos) + def connect_hbus_m2m3(self, src_pin, dest_pin): + """ + Helper routine to connect an instance to a horizontal bus. + Routes horizontal then vertical L shape. + Dest pin is on M1/M2/M3. + Src pin can be on M1/M2/M3. + """ + + if src_pin.cx() Date: Fri, 6 Mar 2020 09:48:20 -0800 Subject: [PATCH 20/22] Move rbl route away from bitcell array --- compiler/modules/bank.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 7ba33230..5e6ce004 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -128,10 +128,10 @@ class bank(design.design): bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name) # This will ensure the pin is only on the top or bottom edge if port % 2: - via_offset = bl_pin.uc() + via_offset = bl_pin.uc() + vector(0, self.m2_pitch) left_right_offset = vector(self.max_x_offset, via_offset.y) else: - via_offset = bl_pin.bc() + via_offset = bl_pin.bc() - vector(0, self.m2_pitch) left_right_offset = vector(self.min_x_offset, via_offset.y) if bl_pin == "m1": self.add_via_center(layers=self.m1_stack, From 23501c7b352a32b9f87b77d7eda43d92a5c1bc51 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 6 Mar 2020 13:26:40 -0800 Subject: [PATCH 21/22] Convert pnand+pinv to pand in decoders. --- compiler/modules/hierarchical_decoder.py | 163 ++++++----------- compiler/modules/hierarchical_predecode.py | 167 +++++++----------- compiler/modules/hierarchical_predecode2x4.py | 24 ++- compiler/modules/hierarchical_predecode3x8.py | 32 ++-- compiler/pgates/pnand3.py | 6 +- 5 files changed, 143 insertions(+), 249 deletions(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index f9c21619..6e98c5e0 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -20,8 +20,7 @@ class hierarchical_decoder(design.design): def __init__(self, name, rows): design.design.__init__(self, name) - self.NAND_FORMAT = "DEC_NAND_{0}" - self.INV_FORMAT = "DEC_INV_{0}" + self.AND_FORMAT = "DEC_AND_{0}" self.pre2x4_inst = [] self.pre3x8_inst = [] @@ -58,12 +57,12 @@ class hierarchical_decoder(design.design): self.inv = factory.create(module_type="pinv", height=self.cell_height) self.add_mod(self.inv) - self.nand2 = factory.create(module_type="pnand2", + self.and2 = factory.create(module_type="pand2", + height=self.cell_height) + self.add_mod(self.and2) + self.and3 = factory.create(module_type="pand3", height=self.cell_height) - self.add_mod(self.nand2) - self.nand3 = factory.create(module_type="pnand3", - height=self.cell_height) - self.add_mod(self.nand3) + self.add_mod(self.and3) self.add_decoders() @@ -143,9 +142,9 @@ class hierarchical_decoder(design.design): # Calculates height and width of row-decoder if (self.num_inputs == 4 or self.num_inputs == 5): - nand_width = self.nand2.width + nand_width = self.and2.width else: - nand_width = self.nand3.width + 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 @@ -309,33 +308,32 @@ class hierarchical_decoder(design.design): self.pre3x8_inst[num].place(offset) def create_row_decoder(self): - """ Create the row-decoder by placing NAND2/NAND3 and Inverters + """ Create the row-decoder by placing AND2/AND3 and Inverters and add the primary decoder output pins. """ if (self.num_inputs >= 4): - self.create_decoder_nand_array() - self.create_decoder_inv_array() + self.create_decoder_and_array() - def create_decoder_nand_array(self): - """ Add a column of NAND gates for final decode """ + def create_decoder_and_array(self): + """ Add a column of AND gates for final decode """ - self.nand_inst = [] + self.and_inst = [] - # Row Decoder NAND GATE array for address inputs <5. + # Row Decoder AND GATE array for address inputs <5. 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.rows): - name = self.NAND_FORMAT.format(row) - self.nand_inst.append(self.add_inst(name=name, - mod=self.nand2)) + name = self.AND_FORMAT.format(row) + 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])), - "Z_{0}".format(row), + "decode_{0}".format(row), "vdd", "gnd"] self.connect_inst(pins) - # Row Decoder NAND GATE array for address inputs >5. + # Row Decoder AND GATE array for address inputs >5. elif (self.num_inputs > 5): for i in range(len(self.predec_groups[0])): for j in range(len(self.predec_groups[1])): @@ -344,104 +342,57 @@ class hierarchical_decoder(design.design): + len(self.predec_groups[0]) * j + i if (row < self.rows): - name = self.NAND_FORMAT.format(row) - self.nand_inst.append(self.add_inst(name=name, - mod=self.nand3)) + name = self.AND_FORMAT.format(row) + 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])), - "Z_{0}".format(row), + "decode_{0}".format(row), "vdd", "gnd"] self.connect_inst(pins) - def create_decoder_inv_array(self): - """ - Add a column of INV gates for the decoder. - """ - - self.inv_inst = [] - for row in range(self.rows): - name = self.INV_FORMAT.format(row) - self.inv_inst.append(self.add_inst(name=name, - mod=self.inv)) - self.connect_inst(args=["Z_{0}".format(row), - "decode_{0}".format(row), - "vdd", "gnd"]) - - def place_decoder_inv_array(self): - """ - Place the column of INV gates for the decoder above the predecoders - and to the right of the NAND decoders. - """ - - if (self.num_inputs == 4 or self.num_inputs == 5): - x_off = self.internal_routing_width + self.nand2.width - else: - x_off = self.internal_routing_width + self.nand3.width - - for row in range(self.rows): - if (row % 2 == 0): - inv_row_height = self.inv.height * row - mirror = "R0" - else: - inv_row_height = self.inv.height * (row + 1) - mirror = "MX" - y_off = inv_row_height - offset = vector(x_off, y_off) - self.inv_inst[row].place(offset=offset, - mirror=mirror) - def place_row_decoder(self): """ - Place the row-decoder by placing NAND2/NAND3 and Inverters + Place the row-decoder by placing AND2/AND3 and Inverters and add the primary decoder output pins. """ if (self.num_inputs >= 4): - self.place_decoder_nand_array() - self.place_decoder_inv_array() + self.place_decoder_and_array() self.route_decoder() - def place_decoder_nand_array(self): - """ Add a column of NAND gates for final decode """ + def place_decoder_and_array(self): + """ Add a column of AND gates for final decode """ - # Row Decoder NAND GATE array for address inputs <5. + # Row Decoder AND GATE array for address inputs <5. if (self.num_inputs == 4 or self.num_inputs == 5): - self.place_nand_array(nand_mod=self.nand2) + self.place_and_array(and_mod=self.and2) - # Row Decoder NAND GATE array for address inputs >5. + # Row Decoder AND GATE array for address inputs >5. # FIXME: why this correct offset?) elif (self.num_inputs > 5): - self.place_nand_array(nand_mod=self.nand3) + self.place_and_array(and_mod=self.and3) - def place_nand_array(self, nand_mod): - """ Add a column of NAND gates for the decoder above the predecoders.""" + 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): if ((row % 2) == 0): - y_off = nand_mod.height * row + y_off = and_mod.height * row mirror = "R0" else: - y_off = nand_mod.height * (row + 1) + y_off = and_mod.height * (row + 1) mirror = "MX" - self.nand_inst[row].place(offset=[self.internal_routing_width, y_off], - mirror=mirror) + self.and_inst[row].place(offset=[self.internal_routing_width, y_off], + mirror=mirror) def route_decoder(self): - """ Route the nand to inverter in the decoder and add the pins. """ + """ Add the pins. """ for row in range(self.rows): - - # route nand output to output inv input - zr_pos = self.nand_inst[row].get_pin("Z").rc() - al_pos = self.inv_inst[row].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]) - - z_pin = self.inv_inst[row].get_pin("Z") + z_pin = self.and_inst[row].get_pin("Z") self.add_layout_pin(text="decode_{0}".format(row), layer="m1", offset=z_pin.ll(), @@ -484,12 +435,12 @@ class hierarchical_decoder(design.design): self.route_predecode_rail_m3(predecode_name, pin) def route_rails_to_decoder(self): - """ Use the self.predec_groups to determine the connections to the decoder NAND gates. - Inputs of NAND2/NAND3 gates come from different groups. + """ 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 NAND3 inputs are connected to - [0,4,8] and second NAND3 is connected to [0,4,9] ........... and the - 128th NAND3 is connected to [3,7,15] + [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] """ row_index = 0 if (self.num_inputs == 4 or self.num_inputs == 5): @@ -498,9 +449,9 @@ class hierarchical_decoder(design.design): # FIXME: convert to connect_bus? if (row_index < self.rows): predecode_name = "predecode_{}".format(index_A) - self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) - predecode_name = "predecode_{}".format(index_B) - self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) + self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A")) + 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 elif (self.num_inputs > 5): @@ -510,35 +461,25 @@ class hierarchical_decoder(design.design): # FIXME: convert to connect_bus? if (row_index < self.rows): predecode_name = "predecode_{}".format(index_A) - self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) + self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A")) predecode_name = "predecode_{}".format(index_B) - self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) + self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B")) predecode_name = "predecode_{}".format(index_C) - self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C")) + self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C")) row_index = row_index + 1 def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ # The vias will be placed in the center and right of the cells, respectively. - xoffset = self.nand_inst[0].rx() + xoffset = self.and_inst[0].rx() for num in range(0, self.rows): for pin_name in ["vdd", "gnd"]: # The nand and inv are the same height rows... - supply_pin = self.nand_inst[num].get_pin(pin_name) + supply_pin = self.and_inst[num].get_pin(pin_name) pin_pos = vector(xoffset, supply_pin.cy()) self.add_power_pin(name=pin_name, loc=pin_pos) - - # Make a redundant rail too - for num in range(0, self.rows, 2): - for pin_name in ["vdd", "gnd"]: - start = self.nand_inst[num].get_pin(pin_name).lc() - end = self.inv_inst[num].get_pin(pin_name).rc() - mid = (start + end).scale(0.5, 0.5) - self.add_rect_center(layer="m1", - offset=mid, - width=end.x - start.x) # Copy the pins from the predecoders for pre in self.pre2x4_inst + self.pre3x8_inst: diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 88bae534..ccee34a9 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -32,61 +32,58 @@ class hierarchical_predecode(design.design): self.add_pin("gnd", "GROUND") def add_modules(self): - """ Add the INV and NAND gate modules """ + """ Add the INV and AND gate modules """ self.inv = factory.create(module_type="pinv", height=self.cell_height) self.add_mod(self.inv) - self.add_nand(self.number_of_inputs) - self.add_mod(self.nand) + self.add_and(self.number_of_inputs) + self.add_mod(self.and_mod) - def add_nand(self, inputs): + def add_and(self, inputs): """ Create the NAND for the predecode input stage """ if inputs==2: - self.nand = factory.create(module_type="pnand2", - height=self.cell_height) + self.and_mod = factory.create(module_type="pand2", + height=self.cell_height) elif inputs==3: - self.nand = factory.create(module_type="pnand3", - height=self.cell_height) + self.and_mod = factory.create(module_type="pand3", + height=self.cell_height) else: debug.error("Invalid number of predecode inputs: {}".format(inputs), -1) def setup_layout_constraints(self): - self.height = self.number_of_outputs * self.nand.height + self.height = self.number_of_outputs * self.and_mod.height # x offset for input inverters self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch - # x offset to NAND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches - self.x_off_nand = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch + # x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches + self.x_off_and = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch # x offset to output inverters - self.x_off_inv_2 = self.x_off_nand + self.nand.width - - # Height width are computed - self.width = self.x_off_inv_2 + self.inv.width + self.width = self.x_off_and + self.and_mod.width def route_rails(self): """ Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """ input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)] - offset = vector(0.5*self.m2_width,2*self.m1_width) + offset = vector(0.5 * self.m2_width, self.m1_pitch) self.input_rails = self.create_vertical_pin_bus(layer="m2", pitch=self.m2_pitch, offset=offset, names=input_names, - length=self.height - 2*self.m1_width) + length=self.height - 2 * self.m1_pitch) invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)] non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)] decode_names = invert_names + non_invert_names - offset = vector(self.x_off_inv_1 + self.inv.width + 2*self.m2_pitch, 2*self.m1_width) + offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m1_pitch) self.decode_rails = self.create_vertical_bus(layer="m2", pitch=self.m2_pitch, offset=offset, names=decode_names, - length=self.height - 2*self.m1_width) + length=self.height - 2 * self.m1_pitch) def create_input_inverters(self): """ Create the input inverters to invert input signals for the decode stage. """ @@ -112,62 +109,35 @@ class hierarchical_predecode(design.design): self.in_inst[inv_num].place(offset=offset, mirror=mirror) - def create_output_inverters(self): - """ Create inverters for the inverted output decode signals. """ - self.inv_inst = [] - for inv_num in range(self.number_of_outputs): - name = "pre_nand_inv_{}".format(inv_num) - self.inv_inst.append(self.add_inst(name=name, - mod=self.inv)) - self.connect_inst(["Z_{}".format(inv_num), - "out_{}".format(inv_num), - "vdd", "gnd"]) + def create_and_array(self, connections): + """ Create the AND stage for the decodes """ + self.and_inst = [] + for and_input in range(self.number_of_outputs): + inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs) + name = "Xpre{0}_and_{1}".format(inout, and_input) + self.and_inst.append(self.add_inst(name=name, + mod=self.and_mod)) + self.connect_inst(connections[and_input]) - - def place_output_inverters(self): - """ Place inverters for the inverted output decode signals. """ - for inv_num in range(self.number_of_outputs): - if (inv_num % 2 == 0): - y_off = inv_num * self.inv.height + def place_and_array(self): + """ Place the AND stage for the decodes """ + for and_input in range(self.number_of_outputs): + # inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs) + if (and_input % 2 == 0): + y_off = and_input * self.and_mod.height mirror = "R0" else: - y_off =(inv_num + 1)*self.inv.height + y_off = (and_input + 1) * self.and_mod.height mirror = "MX" - offset = vector(self.x_off_inv_2, y_off) - self.inv_inst[inv_num].place(offset=offset, - mirror=mirror) - - def create_nand_array(self,connections): - """ Create the NAND stage for the decodes """ - self.nand_inst = [] - for nand_input in range(self.number_of_outputs): - inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs) - name = "Xpre{0}_nand_{1}".format(inout,nand_input) - self.nand_inst.append(self.add_inst(name=name, - mod=self.nand)) - self.connect_inst(connections[nand_input]) - - - def place_nand_array(self): - """ Place the NAND stage for the decodes """ - for nand_input in range(self.number_of_outputs): - inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs) - if (nand_input % 2 == 0): - y_off = nand_input * self.inv.height - mirror = "R0" - else: - y_off = (nand_input + 1) * self.inv.height - mirror = "MX" - offset = vector(self.x_off_nand, y_off) - self.nand_inst[nand_input].place(offset=offset, - mirror=mirror) - + offset = vector(self.x_off_and, y_off) + self.and_inst[and_input].place(offset=offset, + mirror=mirror) def route(self): self.route_input_inverters() self.route_inputs_to_rails() - self.route_nand_to_rails() - self.route_output_inverters() + self.route_and_to_rails() + self.route_output_and() self.route_vdd_gnd() def route_inputs_to_rails(self): @@ -175,39 +145,30 @@ class hierarchical_predecode(design.design): for num in range(self.number_of_inputs): # route one signal next to each vdd/gnd rail since this is # typically where the p/n devices are and there are no - # pins in the nand gates. - y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space - in_pin = "in_{}".format(num) + # pins in the and gates. + y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space + in_pin = "in_{}".format(num) a_pin = "A_{}".format(num) - in_pos = vector(self.input_rails[in_pin].x,y_offset) - a_pos = vector(self.decode_rails[a_pin].x,y_offset) - self.add_path("m1",[in_pos, a_pos]) - self.add_via_center(layers = self.m1_stack, + in_pos = vector(self.input_rails[in_pin].x, y_offset) + a_pos = vector(self.decode_rails[a_pin].x, y_offset) + self.add_path("m1", [in_pos, a_pos]) + self.add_via_center(layers=self.m1_stack, offset=[self.input_rails[in_pin].x, y_offset]) - self.add_via_center(layers = self.m1_stack, + self.add_via_center(layers=self.m1_stack, offset=[self.decode_rails[a_pin].x, y_offset]) - def route_output_inverters(self): + def route_output_and(self): """ - Route all conections of the outputs inverters + Route all conections of the outputs and gates """ for num in range(self.number_of_outputs): - # route nand output to output inv input - zr_pos = self.nand_inst[num].get_pin("Z").rc() - al_pos = self.inv_inst[num].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]) - - z_pin = self.inv_inst[num].get_pin("Z") + z_pin = self.and_inst[num].get_pin("Z") self.add_layout_pin(text="out_{}".format(num), layer="m1", offset=z_pin.ll(), height=z_pin.height(), width=z_pin.width()) - def route_input_inverters(self): """ @@ -219,7 +180,7 @@ class hierarchical_predecode(design.design): #add output so that it is just below the vdd or gnd rail # since this is where the p/n devices are and there are no - # pins in the nand gates. + # pins in the and gates. y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc() right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0) @@ -236,13 +197,12 @@ class hierarchical_predecode(design.design): self.add_via_center(layers=self.m1_stack, offset=in_pos) - - def route_nand_to_rails(self): - # This 2D array defines the connection mapping - nand_input_line_combination = self.get_nand_input_line_combination() + def route_and_to_rails(self): + # This 2D array defines the connection mapping + and_input_line_combination = self.get_and_input_line_combination() for k in range(self.number_of_outputs): - # create x offset list - index_lst= nand_input_line_combination[k] + # create x offset list + index_lst= and_input_line_combination[k] if self.number_of_inputs == 2: gate_lst = ["A","B"] @@ -251,35 +211,32 @@ class hierarchical_predecode(design.design): # this will connect pins A,B or A,B,C for rail_pin,gate_pin in zip(index_lst,gate_lst): - pin_pos = self.nand_inst[k].get_pin(gate_pin).lc() + pin_pos = self.and_inst[k].get_pin(gate_pin).lc() rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) self.add_path("m1", [rail_pos, pin_pos]) self.add_via_center(layers=self.m1_stack, offset=rail_pos) - - - def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ # Find the x offsets for where the vias/pins should be placed in_xoffset = self.in_inst[0].rx() + self.m1_space - out_xoffset = self.inv_inst[0].lx() - self.m1_space - for num in range(0,self.number_of_outputs): + # out_xoffset = self.and_inst[0].cx() + self.m1_space + for num in range(0, self.number_of_outputs): # this will result in duplicate polygons for rails, but who cares # Route both supplies for n in ["vdd", "gnd"]: - nand_pin = self.nand_inst[num].get_pin(n) - supply_offset = nand_pin.ll().scale(0,1) + and_pin = self.and_inst[num].get_pin(n) + supply_offset = and_pin.ll().scale(0, 1) self.add_rect(layer="m1", offset=supply_offset, - width=self.inv_inst[num].rx()) + width=self.and_inst[num].rx()) # Add pins in two locations - for xoffset in [in_xoffset, out_xoffset]: - pin_pos = vector(xoffset, nand_pin.cy()) + for xoffset in [in_xoffset]: + pin_pos = vector(xoffset, and_pin.cy()) self.add_power_pin(n, pin_pos) diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index f05f54b0..b1aacc5a 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -27,33 +27,31 @@ class hierarchical_predecode2x4(hierarchical_predecode): self.add_pins() self.add_modules() self.create_input_inverters() - self.create_output_inverters() - connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"], - ["in_0", "inbar_1", "Z_1", "vdd", "gnd"], - ["inbar_0", "in_1", "Z_2", "vdd", "gnd"], - ["in_0", "in_1", "Z_3", "vdd", "gnd"]] - self.create_nand_array(connections) + connections =[["inbar_0", "inbar_1", "out_0", "vdd", "gnd"], + ["in_0", "inbar_1", "out_1", "vdd", "gnd"], + ["inbar_0", "in_1", "out_2", "vdd", "gnd"], + ["in_0", "in_1", "out_3", "vdd", "gnd"]] + self.create_and_array(connections) def create_layout(self): """ The general organization is from left to right: 1) a set of M2 rails for input signals 2) a set of inverters to invert input signals 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs - 4) a set of NAND gates for inversion + 4) a set of AND gates for inversion """ self.setup_layout_constraints() self.route_rails() self.place_input_inverters() - self.place_output_inverters() - self.place_nand_array() + self.place_and_array() self.route() self.add_boundary() - self.DRC_LVS() + self.DRC_LVS() - def get_nand_input_line_combination(self): - """ These are the decoder connections of the NAND gates to the A,B pins """ + def get_and_input_line_combination(self): + """ These are the decoder connections of the AND gates to the A,B pins """ combination = [["Abar_0", "Abar_1"], ["A_0", "Abar_1"], ["Abar_0", "A_1"], ["A_0", "A_1"]] - return combination \ No newline at end of file + return combination diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index 20b629bb..4f2294f1 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -20,26 +20,25 @@ class hierarchical_predecode3x8(hierarchical_predecode): hierarchical_predecode.__init__(self, name, 3, height) self.create_netlist() - if not OPTS.netlist_only: + if not OPTS.netlist_only: self.create_layout() def create_netlist(self): self.add_pins() self.add_modules() self.create_input_inverters() - self.create_output_inverters() - connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"], - ["in_0", "inbar_1", "inbar_2", "Z_1", "vdd", "gnd"], - ["inbar_0", "in_1", "inbar_2", "Z_2", "vdd", "gnd"], - ["in_0", "in_1", "inbar_2", "Z_3", "vdd", "gnd"], - ["inbar_0", "inbar_1", "in_2", "Z_4", "vdd", "gnd"], - ["in_0", "inbar_1", "in_2", "Z_5", "vdd", "gnd"], - ["inbar_0", "in_1", "in_2", "Z_6", "vdd", "gnd"], - ["in_0", "in_1", "in_2", "Z_7", "vdd", "gnd"]] - self.create_nand_array(connections) + connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"], + ["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"], + ["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"], + ["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"], + ["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"], + ["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]] + self.create_and_array(connections) def create_layout(self): - """ + """ The general organization is from left to right: 1) a set of M2 rails for input signals 2) a set of inverters to invert input signals @@ -49,20 +48,19 @@ class hierarchical_predecode3x8(hierarchical_predecode): self.setup_layout_constraints() self.route_rails() self.place_input_inverters() - self.place_output_inverters() - self.place_nand_array() + self.place_and_array() self.route() self.add_boundary() self.DRC_LVS() - def get_nand_input_line_combination(self): + def get_and_input_line_combination(self): """ These are the decoder connections of the NAND gates to the A,B,C pins """ combination = [["Abar_0", "Abar_1", "Abar_2"], ["A_0", "Abar_1", "Abar_2"], ["Abar_0", "A_1", "Abar_2"], ["A_0", "A_1", "Abar_2"], - ["Abar_0", "Abar_1", "A_2"], + ["Abar_0", "Abar_1", "A_2"], ["A_0", "Abar_1", "A_2"], ["Abar_0", "A_1", "A_2"], ["A_0", "A_1", "A_2"]] - return combination \ No newline at end of file + return combination diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index a9fd19b7..2bbdaf91 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -185,6 +185,7 @@ class pnand3(pgate.pgate): def route_inputs(self): """ Route the A and B and C inputs """ + m1_pitch = self.m1_space + contact.m1_via.first_layer_height # Put B right on the well line self.inputB_yoffset = self.nwell_y_offset self.route_input_gate(self.pmos2_inst, @@ -193,20 +194,19 @@ class pnand3(pgate.pgate): "B", position="center") - self.inputC_yoffset = self.inputB_yoffset - self.m1_pitch + self.inputC_yoffset = self.inputB_yoffset - m1_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, "C", position="center") - self.inputA_yoffset = self.inputB_yoffset + self.m1_pitch + self.inputA_yoffset = self.inputB_yoffset + m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A", position="center") - def route_output(self): """ Route the Z output """ From c5a1be703c45dc1cda282915237d45820d3bf5bb Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 6 Mar 2020 13:39:46 -0800 Subject: [PATCH 22/22] Rotate via and PEP8 formatting --- compiler/modules/wordline_driver.py | 16 ++++++++-------- compiler/pgates/pnand2.py | 4 ++-- compiler/pgates/pnand3.py | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver.py index 35a2b8bf..d84457ba 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -115,7 +115,7 @@ class wordline_driver(design.design): "vdd", "gnd"]) def place_drivers(self): - nand2_xoffset = 2*self.m1_width + 5*self.m1_space + nand2_xoffset = 2 * self.m1_width + 5 * self.m1_space inv2_xoffset = nand2_xoffset + self.nand2.width self.width = inv2_xoffset + self.inv.width @@ -123,10 +123,10 @@ class wordline_driver(design.design): for row in range(self.rows): if (row % 2): - y_offset = self.inv.height*(row + 1) + y_offset = self.inv.height * (row + 1) inst_mirror = "MX" else: - y_offset = self.inv.height*row + y_offset = self.inv.height * row inst_mirror = "R0" nand2_offset = [nand2_xoffset, y_offset] @@ -168,8 +168,8 @@ class wordline_driver(design.design): 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) + 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 @@ -180,7 +180,7 @@ class wordline_driver(design.design): up_or_down = self.m2_space if row % 2 else -self.m2_space input_offset = vector(0, b_pos.y + up_or_down) base_offset = vector(clk_offset.x, input_offset.y) - contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0) + contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0) mid_via_offset = base_offset + contact_offset # must under the clk line in M1 @@ -207,7 +207,7 @@ class wordline_driver(design.design): end=wl_offset - vector(self.m1_width, 0)) def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): - """ + """ Follows the clk_buf to a wordline signal adding each stages stage effort to a list. """ @@ -224,7 +224,7 @@ class wordline_driver(design.design): return stage_effort_list def get_wl_en_cin(self): - """ + """ Get the relative capacitance of all the enable connections in the bank """ diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 05eabbe0..8ecc115e 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -234,8 +234,8 @@ class pnand2(pgate.pgate): self.add_layout_pin_rect_center(text="Z", layer="m1", offset=out_offset, - width=contact.m1_via.first_layer_height, - height=contact.m1_via.first_layer_width) + width=contact.m1_via.first_layer_width, + height=contact.m1_via.first_layer_height) def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 2bbdaf91..927ac6fd 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -259,7 +259,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"""