From 1b6634bb9742ebc3be700e93d1fe6d50033f2d21 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 29 Apr 2020 15:48:15 -0700 Subject: [PATCH 01/51] port data routing fix --- compiler/base/hierarchy_layout.py | 19 +++++++++++++++---- compiler/modules/port_data.py | 19 +++++++++---------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index e6f9ac9f..03d0116e 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -905,8 +905,11 @@ class layout(): max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) + max_x_lc = max([pin.lc().x for pin in pins]) + min_x_rc = min([pin.rc().x for pin in pins]) + # if we are less than a pitch, just create a non-preferred layer jog - if max_x-min_x <= pitch: + if max_x - min_x <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] # Add the horizontal trunk on the vertical layer! @@ -927,7 +930,15 @@ class layout(): # Route each pin to the trunk for pin in pins: - mid = vector(pin.center().x, trunk_offset.y) + # If there is sufficient space, Route from the edge of the pins + # Otherwise, route from the center of the pins + if max_x_lc - min_x_rc > pitch: + if pin.center().x == max_x: + mid = vector(pin.lc().x, trunk_offset.y) + else: + mid = vector(pin.rc().x, trunk_offset.y) + else: + mid = vector(pin.center().x, trunk_offset.y) self.add_path(self.vertical_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid) @@ -1014,7 +1025,7 @@ class layout(): def vcg_pin_overlap(pin1, pin2, vertical, pitch): """ Check for vertical or horizontal overlap of the two pins """ - + # FIXME: If the pins are not in a row, this may break. # However, a top pin shouldn't overlap another top pin, # for example, so the @@ -1095,7 +1106,7 @@ class layout(): # list of routes to do while vcg: # from pprint import pformat - # print("VCG:\n",pformat(vcg)) + # print("VCG:\n", pformat(vcg)) # get a route from conflict graph with empty fanout set net_name = None for net_name, conflicts in vcg.items(): diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index bdf10f39..641a1349 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -19,7 +19,7 @@ class port_data(design.design): """ def __init__(self, sram_config, port, name=""): - + sram_config.set_local_config(self) self.port = port if self.write_size is not None: @@ -444,7 +444,7 @@ class port_data(design.design): 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), @@ -521,10 +521,10 @@ class port_data(design.design): insn2_start_bit = 1 if self.port == 0 else 0 - self.connect_bitlines(inst1=inst1, - inst2=inst2, - num_bits=self.num_cols, - inst2_start_bit=insn2_start_bit) + self.channel_route_bitlines(inst1=inst1, + inst2=inst2, + 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 """ @@ -703,7 +703,7 @@ class port_data(design.design): bitline_dirs = ("H", "V") elif bottom_names[0].layer == "m1": bitline_dirs = ("V", "H") - + route_map = list(zip(bottom_names, top_names)) self.create_horizontal_channel_route(route_map, offset, self.m1_stack, bitline_dirs) @@ -717,7 +717,7 @@ class port_data(design.design): This assumes that they have sufficient space to create a jog in the middle between the two modules (if needed). """ - + bot_inst_group, top_inst_group = self._group_bitline_instances( inst1, inst2, num_bits, inst1_bls_template, inst1_start_bit, @@ -738,9 +738,8 @@ class port_data(design.design): 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""" if self.precharge_array_inst: self.graph_inst_exclude.add(self.precharge_array_inst) - From 91dbbed9ba404bc951711d51f59023821e034508 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Tue, 5 May 2020 12:18:26 -0700 Subject: [PATCH 02/51] added horizontal trunk route edit to vertical trunk route --- compiler/base/hierarchy_layout.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 03d0116e..4f74185b 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -955,6 +955,9 @@ class layout(): max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) + max_y_uc = max([pin.uc().y for pin in pins]) + min_y_bc = min([pin.bc().y for pin in pins]) + # if we are less than a pitch, just create a non-preferred layer jog if max_y - min_y <= pitch: @@ -978,7 +981,15 @@ class layout(): # Route each pin to the trunk for pin in pins: - mid = vector(trunk_offset.x, pin.center().y) + # If there is sufficient space, Route from the edge of the pins + # Otherwise, route from the center of the pins + if max_y_uc - min_y_bc > pitch: + if pin.center().y == max_y: + mid = vector(trunk_offset.x, pin.bc().y) + else: + mid = vector(trunk_offset.x, pin.uc().y) + else: + mid = vector(trunk_offset.x, pin.center().y) self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid) From e642b8521b01df689d9a55a5e6de4e72f4820d7a Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 6 May 2020 13:02:33 -0700 Subject: [PATCH 03/51] increase col_mux bitline spacing to fix cyclic vcg --- compiler/base/hierarchy_layout.py | 14 +++++++++----- compiler/pgates/single_level_column_mux.py | 15 +++++++++++++-- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 4f74185b..ffb35151 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -905,11 +905,13 @@ class layout(): max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) + # max_x_lc & min_x_rc are for routing to/from the edge of the pins + # to increase spacing between contacts of different nets max_x_lc = max([pin.lc().x for pin in pins]) min_x_rc = min([pin.rc().x for pin in pins]) # if we are less than a pitch, just create a non-preferred layer jog - if max_x - min_x <= pitch: + if max_x_lc - min_x_rc <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] # Add the horizontal trunk on the vertical layer! @@ -955,11 +957,13 @@ class layout(): max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) - max_y_uc = max([pin.uc().y for pin in pins]) - min_y_bc = min([pin.bc().y for pin in pins]) + # max_y_bc & min_y_uc are for routing to/from the edge of the pins + # to reduce spacing between contacts of different nets + max_y_bc = max([pin.bc().y for pin in pins]) + min_y_uc = min([pin.uc().y for pin in pins]) # if we are less than a pitch, just create a non-preferred layer jog - if max_y - min_y <= pitch: + if max_y_bc - min_y_uc <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] @@ -983,7 +987,7 @@ class layout(): for pin in pins: # If there is sufficient space, Route from the edge of the pins # Otherwise, route from the center of the pins - if max_y_uc - min_y_bc > pitch: + if max_y_bc - min_y_uc > pitch: if pin.center().y == max_y: mid = vector(trunk_offset.x, pin.bc().y) else: diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 9e38287f..0629a96c 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -11,7 +11,7 @@ from tech import drc, layer from vector import vector from sram_factory import factory import logical_effort - +from utils import round_to_grid class single_level_column_mux(pgate.pgate): """ @@ -75,6 +75,17 @@ class single_level_column_mux(pgate.pgate): bl_pos = vector(bl_pin.lx(), 0) br_pos = vector(br_pin.lx(), 0) + # The bitline input/output pins must be a least as wide as the metal pitch + # so that there is enough space to route to/from the pins. + # FIXME: bitline_metal_pitch should be greater than the horizontal metal pitch used in port_data + bitline_metal_pitch = self.width / 2 + bitline_width = br_pos.x - bl_pos.x + if bitline_width < bitline_metal_pitch: + bitline_width_increase_bl = round_to_grid((bitline_metal_pitch - bitline_width) / 2) + bitline_width_increase_br = round_to_grid((bitline_metal_pitch - bitline_width) - bitline_width_increase_bl) + bl_pos = bl_pos + vector(-bitline_width_increase_bl, 0) + br_pos = br_pos + vector( bitline_width_increase_br, 0) + # bl and br self.add_layout_pin(text="bl", layer=bl_pin.layer, @@ -133,7 +144,7 @@ class single_level_column_mux(pgate.pgate): def connect_bitlines(self): """ Connect the bitlines to the mux transistors """ - + # If li exists, use li and m1 for the mux, otherwise use m1 and m2 if "li" in layer: self.col_mux_stack = self.li_stack From c96a6d0b9d9d7d5d5461b11592fac0ad2a2feaee Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 11 May 2020 16:22:08 -0700 Subject: [PATCH 04/51] Add no well option. Add stack gates vertical option. --- compiler/pgates/pand2.py | 80 +++++++++++++++++++++++++------- compiler/pgates/pand3.py | 76 ++++++++++++++++++++++++------ compiler/pgates/pbuf.py | 3 +- compiler/pgates/pdriver.py | 5 +- compiler/pgates/pgate.py | 18 +++++-- compiler/pgates/pinv.py | 7 +-- compiler/pgates/pnand2.py | 9 ++-- compiler/pgates/pnand3.py | 7 +-- compiler/pgates/pnor2.py | 11 +++-- compiler/tests/04_pand2_test.py | 4 ++ compiler/tests/04_pand3_test.py | 4 ++ compiler/tests/04_pnand2_test.py | 5 ++ compiler/tests/04_pnand3_test.py | 5 ++ compiler/tests/04_pnor2_test.py | 4 ++ 14 files changed, 187 insertions(+), 51 deletions(-) diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 69af1e62..6df877fd 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -15,14 +15,14 @@ class pand2(pgate.pgate): """ This is a simple buffer used for driving loads. """ - def __init__(self, name, size=1, height=None): - debug.info(1, "Creating pnand2 {}".format(name)) + def __init__(self, name, size=1, height=None, vertical=False): + debug.info(1, "Creating pand2 {}".format(name)) self.add_comment("size: {}".format(size)) - + + self.vertical = vertical self.size = size - - # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + + pgate.pgate.__init__(self, name) def create_netlist(self): self.add_pins() @@ -30,17 +30,22 @@ class pand2(pgate.pgate): self.create_insts() def create_modules(self): - self.nand = factory.create(module_type="pnand2", height=self.height) + self.nand = factory.create(module_type="pnand2", + height=self.height) self.add_mod(self.nand) self.inv = factory.create(module_type="pdriver", - neg_polarity=True, - fanout=self.size, + size_list=[self.size], height=self.height) self.add_mod(self.inv) def create_layout(self): - self.width = self.nand.width + self.inv.width + if self.vertical: + self.height = 2 * self.nand.height + self.width = max(self.nand.width, self.inv.width) + else: + self.width = self.nand.width + self.inv.width + self.place_insts() self.add_wires() self.add_layout_pins() @@ -68,17 +73,60 @@ class pand2(pgate.pgate): # Add NAND to the right self.nand_inst.place(offset=vector(0, 0)) - # Add INV to the right - self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + if self.vertical: + # Add INV above + self.inv_inst.place(offset=vector(self.inv.width, + 2 * self.nand.height), + mirror="XY") + else: + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + + # Second gnd of the inverter gate + if self.vertical: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + if self.vertical: + # Shared between two gates + y_offset = 0.5 * self.height + else: + y_offset = self.height + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, y_offset), + width=self.width) + def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") a2_pin = self.inv_inst.get_pin("A") - mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy()) - mid2_point = vector(mid1_point, a2_pin.cy()) - self.add_path(self.route_layer, - [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) + if self.vertical: + route_layer = "m2" + self.add_via_stack_center(offset=z1_pin.center(), + from_layer=z1_pin.layer, + to_layer=route_layer) + self.add_zjog(route_layer, + z1_pin.uc(), + a2_pin.bc(), + "V") + self.add_via_stack_center(offset=a2_pin.center(), + from_layer=a2_pin.layer, + to_layer=route_layer) + else: + route_layer = self.route_layer + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) def add_layout_pins(self): pin = self.inv_inst.get_pin("Z") diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index 841ac69d..5e2e83f8 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -15,10 +15,11 @@ class pand3(pgate.pgate): """ This is a simple buffer used for driving loads. """ - def __init__(self, name, size=1, height=None): + def __init__(self, name, size=1, height=None, vertical=False): debug.info(1, "Creating pand3 {}".format(name)) self.add_comment("size: {}".format(size)) - + + self.vertical = vertical self.size = size # Creates the netlist and layout @@ -31,16 +32,22 @@ class pand3(pgate.pgate): def create_modules(self): # Shield the cap, but have at least a stage effort of 4 - self.nand = factory.create(module_type="pnand3", height=self.height) + self.nand = factory.create(module_type="pnand3", + height=self.height) self.add_mod(self.nand) - self.inv = factory.create(module_type="pinv", - size=self.size, + self.inv = factory.create(module_type="pdriver", + size_list=[self.size], height=self.height) self.add_mod(self.inv) def create_layout(self): - self.width = self.nand.width + self.inv.width + if self.vertical: + self.height = 2 * self.nand.height + self.width = max(self.nand.width, self.inv.width) + else: + self.width = self.nand.width + self.inv.width + self.place_insts() self.add_wires() self.add_layout_pins() @@ -69,18 +76,61 @@ class pand3(pgate.pgate): # Add NAND to the right self.nand_inst.place(offset=vector(0, 0)) - # Add INV to the right - self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + if self.vertical: + # Add INV above + self.inv_inst.place(offset=vector(self.inv.width, + 2 * self.nand.height), + mirror="XY") + else: + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + + # Second gnd of the inverter gate + if self.vertical: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + if self.vertical: + # Shared between two gates + y_offset = 0.5 * self.height + else: + y_offset = self.height + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, y_offset), + width=self.width) + def add_wires(self): # nand Z to inv A z1_pin = self.nand_inst.get_pin("Z") a2_pin = self.inv_inst.get_pin("A") - mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) - mid2_point = vector(mid1_point, a2_pin.cy()) - self.add_path(z1_pin.layer, - [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) - + if self.vertical: + route_layer = "m2" + self.add_via_stack_center(offset=z1_pin.center(), + from_layer=z1_pin.layer, + to_layer=route_layer) + self.add_zjog(route_layer, + z1_pin.uc(), + a2_pin.bc(), + "V") + self.add_via_stack_center(offset=a2_pin.center(), + from_layer=a2_pin.layer, + to_layer=route_layer) + else: + route_layer = self.route_layer + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + def add_layout_pins(self): pin = self.inv_inst.get_pin("Z") self.add_layout_pin_rect_center(text="Z", diff --git a/compiler/pgates/pbuf.py b/compiler/pgates/pbuf.py index 4d90286d..8b9c4eab 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/pgates/pbuf.py @@ -56,7 +56,8 @@ class pbuf(pgate.pgate): self.inv2 = factory.create(module_type="pinv", size=self.size, - height=self.height) + height=self.height, + add_wells=False) self.add_mod(self.inv2) def create_insts(self): diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 9bba7aee..b36d3e95 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -87,10 +87,13 @@ class pdriver(pgate.pgate): def add_modules(self): self.inv_list = [] + add_well = True for size in self.size_list: temp_inv = factory.create(module_type="pinv", size=size, - height=self.height) + height=self.height, + add_wells=add_well) + add_well=False self.inv_list.append(temp_inv) self.add_mod(temp_inv) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 5351be07..be317f48 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -24,7 +24,7 @@ class pgate(design.design): functions for parameterized gates. """ - def __init__(self, name, height=None): + def __init__(self, name, height=None, add_wells=True): """ Creates a generic cell """ design.design.__init__(self, name) @@ -33,7 +33,8 @@ class pgate(design.design): elif not height: # By default, something simple self.height = 14 * self.m1_pitch - + self.add_wells = add_wells + if "li" in layer: self.route_layer = "li" else: @@ -150,7 +151,7 @@ class pgate(design.design): # This should match the cells in the cell library self.nwell_y_offset = 0.48 * self.height - full_height = self.height + 0.5* self.m1_width + full_height = self.height + 0.5 * self.m1_width # FIXME: float rounding problem if "nwell" in layer: @@ -302,10 +303,17 @@ 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 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 + if self.add_wells: + self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width + # Height is an input parameter, so it is not recomputed. + else: + max_active_xoffset = self.find_highest_layer_coords("active").x + max_route_xoffset = self.find_highest_layer_coords(self.route_layer).x + 0.5 * self.m1_space + self.width = max(max_active_xoffset, max_route_xoffset) + self.well_width = self.width + 2 * self.nwell_enclose_active - # Height is an input parameter, so it is not recomputed. @staticmethod def bin_width(tx_type, target_width): diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index e0e1ce81..214e66dc 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -32,7 +32,7 @@ class pinv(pgate.pgate): from center of rail to rail. """ - def __init__(self, name, size=1, beta=parameter["beta"], height=None): + def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True): debug.info(2, "creating pinv structure {0} with size of {1}".format(name, @@ -44,7 +44,7 @@ class pinv(pgate.pgate): self.pmos_size = beta * size self.beta = beta - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): """ Calls all functions related to the generation of the netlist """ @@ -56,7 +56,8 @@ class pinv(pgate.pgate): def create_layout(self): """ Calls all functions related to the generation of the layout """ self.place_ptx() - self.add_well_contacts() + if self.add_wells: + self.add_well_contacts() self.determine_width() self.extend_wells() self.route_supply_rails() diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 5d20201e..59995133 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -20,7 +20,7 @@ class pnand2(pgate.pgate): This module generates gds of a parametrically sized 2-input nand. This model use ptx to generate a 2-input nand within a cetrain height. """ - def __init__(self, name, size=1, height=None): + def __init__(self, name, size=1, height=None, add_wells=True): """ Creates a cell for a simple 2 input nand """ debug.info(2, @@ -43,7 +43,7 @@ class pnand2(pgate.pgate): (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): self.add_pins() @@ -55,13 +55,14 @@ class pnand2(pgate.pgate): self.setup_layout_constants() self.place_ptx() - self.add_well_contacts() + if self.add_wells: + self.add_well_contacts() + self.route_output() self.determine_width() self.route_supply_rails() self.connect_rails() self.extend_wells() self.route_inputs() - self.route_output() self.add_boundary() def add_pins(self): diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 454b75dd..446b8212 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -19,7 +19,7 @@ class pnand3(pgate.pgate): This module generates gds of a parametrically sized 2-input nand. This model use ptx to generate a 2-input nand within a cetrain height. """ - def __init__(self, name, size=1, height=None): + def __init__(self, name, size=1, height=None, add_wells=True): """ Creates a cell for a simple 3 input nand """ debug.info(2, @@ -45,7 +45,7 @@ class pnand3(pgate.pgate): (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def add_pins(self): """ Adds pins for spice netlist """ @@ -63,7 +63,8 @@ class pnand3(pgate.pgate): self.setup_layout_constants() self.place_ptx() - self.add_well_contacts() + if self.add_wells: + self.add_well_contacts() self.determine_width() self.route_supply_rails() self.connect_rails() diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index f522578e..2126e86c 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -19,7 +19,7 @@ class pnor2(pgate.pgate): This module generates gds of a parametrically sized 2-input nor. This model use ptx to generate a 2-input nor within a cetrain height. """ - def __init__(self, name, size=1, height=None): + def __init__(self, name, size=1, height=None, add_wells=True): """ Creates a cell for a simple 2 input nor """ debug.info(2, @@ -42,7 +42,7 @@ class pnor2(pgate.pgate): (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): self.add_pins() @@ -54,13 +54,14 @@ class pnor2(pgate.pgate): self.setup_layout_constants() self.place_ptx() - self.add_well_contacts() + if self.add_wells: + self.add_well_contacts() + self.route_inputs() + self.route_output() self.determine_width() self.route_supply_rails() self.connect_rails() self.extend_wells() - self.route_inputs() - self.route_output() self.add_boundary() def add_pins(self): diff --git a/compiler/tests/04_pand2_test.py b/compiler/tests/04_pand2_test.py index f7e5f304..21a0a38e 100755 --- a/compiler/tests/04_pand2_test.py +++ b/compiler/tests/04_pand2_test.py @@ -29,6 +29,10 @@ class pand2_test(openram_test): a = pand2.pand2(name="pand2x4", size=4) self.local_check(a) + debug.info(2, "Testing vertical pand2 gate 4x") + a = pand2.pand2(name="pand2x4", size=4, vertical=True) + self.local_check(a) + globals.end_openram() # instantiate a copdsay of the class to actually run the test diff --git a/compiler/tests/04_pand3_test.py b/compiler/tests/04_pand3_test.py index e58f1ee9..f851077b 100755 --- a/compiler/tests/04_pand3_test.py +++ b/compiler/tests/04_pand3_test.py @@ -29,6 +29,10 @@ class pand3_test(openram_test): a = pand3.pand3(name="pand3x4", size=4) self.local_check(a) + debug.info(2, "Testing vertical pand3 gate 4x") + a = pand3.pand3(name="pand3x4", size=4, vertical=True) + self.local_check(a) + globals.end_openram() # instantiate a copdsay of the class to actually run the test diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py index f939738a..77a6932c 100755 --- a/compiler/tests/04_pnand2_test.py +++ b/compiler/tests/04_pnand2_test.py @@ -25,6 +25,11 @@ class pnand2_test(openram_test): tx = factory.create(module_type="pnand2", size=1) self.local_check(tx) + debug.info(2, "Checking 2-input nand gate") + tx = factory.create(module_type="pnand2", size=1, add_wells=False) + # Only DRC because well contacts will fail LVS + self.local_drc_check(tx) + globals.end_openram() diff --git a/compiler/tests/04_pnand3_test.py b/compiler/tests/04_pnand3_test.py index f1af19ac..82bf1846 100755 --- a/compiler/tests/04_pnand3_test.py +++ b/compiler/tests/04_pnand3_test.py @@ -25,6 +25,11 @@ class pnand3_test(openram_test): tx = factory.create(module_type="pnand3", size=1) self.local_check(tx) + debug.info(2, "Checking 3-input nand gate") + tx = factory.create(module_type="pnand3", size=1, add_wells=False) + # Only DRC because well contacts will fail LVS + self.local_drc_check(tx) + globals.end_openram() diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py index ea0d6dbc..1e8abaa4 100755 --- a/compiler/tests/04_pnor2_test.py +++ b/compiler/tests/04_pnor2_test.py @@ -25,6 +25,10 @@ class pnor2_test(openram_test): tx = factory.create(module_type="pnor2", size=1) self.local_check(tx) + debug.info(2, "Checking 2-input nor gate") + tx = factory.create(module_type="pnor2", size=1, add_wells=False) + self.local_drc_check(tx) + globals.end_openram() # run the test from the command line From 848241a3ad501ef78df9ac05a99e3ba4ef411d67 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 11 May 2020 16:22:17 -0700 Subject: [PATCH 05/51] PEP8 cleanup --- compiler/base/geometry.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index cfcfa73a..96da3d99 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -66,14 +66,17 @@ class geometry: self.compute_boundary(self.offset, self.mirror, self.rotate) def compute_boundary(self, offset=vector(0, 0), mirror="", rotate=0): - """ Transform with offset, mirror and rotation to get the absolute pin location. - We must then re-find the ll and ur. The master is the cell instance. """ + """ + Transform with offset, mirror and rotation to get the absolute pin location. + We must then re-find the ll and ur. The master is the cell instance. + """ if OPTS.netlist_only: - self.boundary = [vector(0,0), vector(0,0)] + self.boundary = [vector(0, 0), vector(0, 0)] return (ll, ur) = [vector(0, 0), vector(self.width, self.height)] + # Mirroring is performed before rotation if mirror == "MX": ll = ll.scale(1, -1) ur = ur.scale(1, -1) @@ -195,14 +198,13 @@ class instance(geometry): blockages = [] blockages = self.mod.gds.getBlockages(lpp) for b in blockages: - new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) + new_blockages.append(self.transform_coords(b, self.offset, mirr, angle)) else: blockages = self.mod.get_blockages(lpp) for b in blockages: - new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) + new_blockages.append(self.transform_coords(b, self.offset, mirr, angle)) return new_blockages - def gds_write_file(self, new_layout): """Recursively writes all the sub-modules in this instance""" debug.info(4, "writing instance: " + self.name) @@ -225,26 +227,25 @@ class instance(geometry): self.update_boundary() debug.info(3, "placing instance {}".format(self)) - - def get_pin(self,name,index=-1): + def get_pin(self, name, index=-1): """ Return an absolute pin that is offset and transformed based on this instance location. Index will return one of several pins.""" import copy if index == -1: pin = copy.deepcopy(self.mod.get_pin(name)) - pin.transform(self.offset,self.mirror,self.rotate) + pin.transform(self.offset, self.mirror, self.rotate) return pin else: pins = copy.deepcopy(self.mod.get_pin(name)) - pin.transform(self.offset,self.mirror,self.rotate) + pins.transform(self.offset, self.mirror, self.rotate) return pin[index] def get_num_pins(self, name): """ Return the number of pins of a given name """ return len(self.mod.get_pins(name)) - def get_pins(self,name): + def get_pins(self, name): """ Return an absolute pin that is offset and transformed based on this instance location. """ @@ -253,7 +254,7 @@ class instance(geometry): new_pins = [] for p in pin: - p.transform(self.offset,self.mirror,self.rotate) + p.transform(self.offset, self.mirror, self.rotate) new_pins.append(p) return new_pins @@ -265,6 +266,7 @@ class instance(geometry): """ override print function output """ return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")" + class path(geometry): """Represents a Path""" @@ -322,7 +324,7 @@ class label(geometry): self.size = 0 - debug.info(4,"creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset)) + debug.info(4, "creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset)) def gds_write_file(self, new_layout): """Writes the text label to GDS""" @@ -340,7 +342,7 @@ class label(geometry): def __str__(self): """ override print function output """ - return "label: " + self.text + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose) + return "label: " + self.text + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose) def __repr__(self): """ override print function output """ From 617bf302d18f1863da212073d8e80d71097d7dae Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 13 May 2020 14:46:42 -0700 Subject: [PATCH 06/51] Add option to remove wells. Save area in pgates with redundant wells. --- compiler/pgates/pand2.py | 13 ++++++++----- compiler/pgates/pand3.py | 15 ++++++++++----- compiler/pgates/pdriver.py | 8 ++++---- compiler/pgates/pgate.py | 17 +++++++++-------- compiler/pgates/pinv.py | 4 ++-- compiler/pgates/pnand2.py | 1 - compiler/pgates/single_level_column_mux.py | 4 ++-- compiler/tests/04_pnor2_test.py | 4 ---- .../04_single_level_column_mux_pbitcell_test.py | 0 compiler/tests/05_replica_bitcell_array_test.py | 0 ...ngle_level_column_mux_array_pbitcell_test.py | 0 .../tests/08_wordline_driver_pbitcell_test.py | 0 .../tests/09_sense_amp_array_test_pbitcell.py | 0 .../10_write_driver_array_pbitcell_test.py | 0 ...10_write_driver_array_wmask_pbitcell_test.py | 0 .../10_write_mask_and_array_pbitcell_test.py | 0 compiler/tests/19_bank_select_pbitcell_test.py | 0 17 files changed, 35 insertions(+), 31 deletions(-) mode change 100644 => 100755 compiler/tests/04_single_level_column_mux_pbitcell_test.py mode change 100644 => 100755 compiler/tests/05_replica_bitcell_array_test.py mode change 100644 => 100755 compiler/tests/07_single_level_column_mux_array_pbitcell_test.py mode change 100644 => 100755 compiler/tests/08_wordline_driver_pbitcell_test.py mode change 100644 => 100755 compiler/tests/09_sense_amp_array_test_pbitcell.py mode change 100644 => 100755 compiler/tests/10_write_driver_array_pbitcell_test.py mode change 100644 => 100755 compiler/tests/10_write_driver_array_wmask_pbitcell_test.py mode change 100644 => 100755 compiler/tests/10_write_mask_and_array_pbitcell_test.py mode change 100644 => 100755 compiler/tests/19_bank_select_pbitcell_test.py diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 6df877fd..dfbdc3e7 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -15,14 +15,14 @@ class pand2(pgate.pgate): """ This is a simple buffer used for driving loads. """ - def __init__(self, name, size=1, height=None, vertical=False): + def __init__(self, name, size=1, height=None, vertical=False, add_wells=True): debug.info(1, "Creating pand2 {}".format(name)) self.add_comment("size: {}".format(size)) self.vertical = vertical self.size = size - pgate.pgate.__init__(self, name) + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): self.add_pins() @@ -31,12 +31,15 @@ class pand2(pgate.pgate): def create_modules(self): self.nand = factory.create(module_type="pnand2", - height=self.height) - self.add_mod(self.nand) + height=self.height, + add_wells=False) self.inv = factory.create(module_type="pdriver", size_list=[self.size], - height=self.height) + height=self.height, + add_wells=self.add_wells) + + self.add_mod(self.nand) self.add_mod(self.inv) def create_layout(self): diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index 5e2e83f8..d7f0b00e 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -15,7 +15,7 @@ class pand3(pgate.pgate): """ This is a simple buffer used for driving loads. """ - def __init__(self, name, size=1, height=None, vertical=False): + def __init__(self, name, size=1, height=None, vertical=False, add_wells=True): debug.info(1, "Creating pand3 {}".format(name)) self.add_comment("size: {}".format(size)) @@ -23,7 +23,7 @@ class pand3(pgate.pgate): self.size = size # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def create_netlist(self): self.add_pins() @@ -33,12 +33,17 @@ class pand3(pgate.pgate): def create_modules(self): # Shield the cap, but have at least a stage effort of 4 self.nand = factory.create(module_type="pnand3", - height=self.height) - self.add_mod(self.nand) + height=self.height, + add_wells=False) + # Add the well tap to the inverter because when stacked + # vertically it is sometimes narrower self.inv = factory.create(module_type="pdriver", size_list=[self.size], - height=self.height) + height=self.height, + add_wells=self.add_wells) + + self.add_mod(self.nand) self.add_mod(self.inv) def create_layout(self): diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index b36d3e95..266400c9 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -17,7 +17,7 @@ class pdriver(pgate.pgate): sized for driving a load. """ - def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None): + def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None, add_wells=True): debug.info(1, "creating pdriver {}".format(name)) @@ -35,7 +35,7 @@ class pdriver(pgate.pgate): debug.error("Cannot specify both size_list and neg_polarity.", -1) # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.pgate.__init__(self, name, height, add_wells) def compute_sizes(self): # size_list specified @@ -73,9 +73,9 @@ class pdriver(pgate.pgate): self.place_modules() self.route_wires() self.add_layout_pins() - self.width = self.inv_inst_list[-1].rx() self.height = self.inv_inst_list[0].height + self.extend_wells() self.route_supply_rails() self.add_boundary() @@ -87,7 +87,7 @@ class pdriver(pgate.pgate): def add_modules(self): self.inv_list = [] - add_well = True + add_well = self.add_wells for size in self.size_list: temp_inv = factory.create(module_type="pinv", size=size, diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index be317f48..cdb89fcb 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -162,12 +162,12 @@ class pgate(design.design): nwell_height = nwell_max_offset - self.nwell_y_offset self.add_rect(layer="nwell", offset=nwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=nwell_height) if "vtg" in layer: self.add_rect(layer="vtg", offset=nwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=nwell_height) # Start this half a rail width below the cell @@ -178,12 +178,12 @@ class pgate(design.design): pwell_height = self.nwell_y_offset - pwell_position.y self.add_rect(layer="pwell", offset=pwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=pwell_height) if "vtg" in layer: self.add_rect(layer="vtg", offset=pwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=pwell_height) def add_nwell_contact(self, pmos, pmos_pos): @@ -304,16 +304,17 @@ class pgate(design.design): def determine_width(self): """ Determine the width based on the well contacts (assumed to be on the right side) """ + # It was already set or is left as default (minimum) # Width is determined by well contact and spacing and allowing a supply via between each cell if self.add_wells: - self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width + width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width # Height is an input parameter, so it is not recomputed. else: max_active_xoffset = self.find_highest_layer_coords("active").x max_route_xoffset = self.find_highest_layer_coords(self.route_layer).x + 0.5 * self.m1_space - self.width = max(max_active_xoffset, max_route_xoffset) - - self.well_width = self.width + 2 * self.nwell_enclose_active + width = max(max_active_xoffset, max_route_xoffset) + + self.width = width @staticmethod def bin_width(tx_type, target_width): diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 214e66dc..465f7a58 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -60,14 +60,14 @@ class pinv(pgate.pgate): self.add_well_contacts() self.determine_width() self.extend_wells() - self.route_supply_rails() - self.connect_rails() self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", position="farleft") self.route_outputs() + self.route_supply_rails() + self.connect_rails() self.add_boundary() def add_pins(self): diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 59995133..51581e61 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -5,7 +5,6 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import contact import pgate import debug from tech import drc, parameter, spice diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index 9e38287f..e1d19534 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -50,7 +50,7 @@ class single_level_column_mux(pgate.pgate): self.connect_poly() self.add_bitline_pins() self.connect_bitlines() - self.add_wells() + self.add_pn_wells() def add_modules(self): self.bitcell = factory.create(module_type="bitcell") @@ -218,7 +218,7 @@ class single_level_column_mux(pgate.pgate): self.add_path(self.col_mux_stack[2], [br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()]) - def add_wells(self): + def add_pn_wells(self): """ Add a well and implant over the whole cell. Also, add the pwell contact (if it exists) diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py index 1e8abaa4..ea0d6dbc 100755 --- a/compiler/tests/04_pnor2_test.py +++ b/compiler/tests/04_pnor2_test.py @@ -25,10 +25,6 @@ class pnor2_test(openram_test): tx = factory.create(module_type="pnor2", size=1) self.local_check(tx) - debug.info(2, "Checking 2-input nor gate") - tx = factory.create(module_type="pnor2", size=1, add_wells=False) - self.local_drc_check(tx) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/04_single_level_column_mux_pbitcell_test.py b/compiler/tests/04_single_level_column_mux_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/05_replica_bitcell_array_test.py b/compiler/tests/05_replica_bitcell_array_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/08_wordline_driver_pbitcell_test.py b/compiler/tests/08_wordline_driver_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/09_sense_amp_array_test_pbitcell.py b/compiler/tests/09_sense_amp_array_test_pbitcell.py old mode 100644 new mode 100755 diff --git a/compiler/tests/10_write_driver_array_pbitcell_test.py b/compiler/tests/10_write_driver_array_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/10_write_mask_and_array_pbitcell_test.py b/compiler/tests/10_write_mask_and_array_pbitcell_test.py old mode 100644 new mode 100755 diff --git a/compiler/tests/19_bank_select_pbitcell_test.py b/compiler/tests/19_bank_select_pbitcell_test.py old mode 100644 new mode 100755 From f8bcc543385ff8da82cf2d04c477714bcb8fc85e Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 13 May 2020 16:04:38 -0700 Subject: [PATCH 07/51] Determine width after routing with no well contacts. --- compiler/pgates/pnand3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 446b8212..85b3f0b6 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -65,12 +65,12 @@ class pnand3(pgate.pgate): self.place_ptx() if self.add_wells: self.add_well_contacts() + self.route_inputs() + self.route_output() self.determine_width() self.route_supply_rails() self.connect_rails() self.extend_wells() - self.route_inputs() - self.route_output() self.add_boundary() def add_ptx(self): From 4b526f0d5fd970a87628aadf85183045ab06cbe0 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 13 May 2020 16:54:26 -0700 Subject: [PATCH 08/51] Check min size inverter. --- compiler/modules/write_mask_and_array.py | 2 +- compiler/pgates/pinv.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 4d4e0c50..e660d519 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -60,7 +60,7 @@ class write_mask_and_array(design.design): # Size the AND gate for the number of write drivers it drives, which is equal to the write size. # Assume stage effort of 3 to compute the size self.and2 = factory.create(module_type="pand2", - size=self.write_size / 4.0) + size=max(self.write_size / 4.0, 1)) self.add_mod(self.and2) def create_and2_array(self): diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 465f7a58..5e1aab7b 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -40,6 +40,7 @@ class pinv(pgate.pgate): self.add_comment("size: {}".format(size)) self.size = size + debug.check(self.size >= 1, "Must have a size greater than or equal to 1.") self.nmos_size = size self.pmos_size = beta * size self.beta = beta From a305d788d7635b4536ea43e0b2486660a59e5682 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 13 May 2020 16:54:35 -0700 Subject: [PATCH 09/51] Vertical gates need both well contacts. --- compiler/pgates/pand2.py | 2 +- compiler/pgates/pand3.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index dfbdc3e7..41189876 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -32,7 +32,7 @@ class pand2(pgate.pgate): def create_modules(self): self.nand = factory.create(module_type="pnand2", height=self.height, - add_wells=False) + add_wells=self.vertical) self.inv = factory.create(module_type="pdriver", size_list=[self.size], diff --git a/compiler/pgates/pand3.py b/compiler/pgates/pand3.py index d7f0b00e..92429921 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/pgates/pand3.py @@ -34,7 +34,7 @@ class pand3(pgate.pgate): # Shield the cap, but have at least a stage effort of 4 self.nand = factory.create(module_type="pnand3", height=self.height, - add_wells=False) + add_wells=self.vertical) # Add the well tap to the inverter because when stacked # vertically it is sometimes narrower From 7505fa5aef116220975e3670091e927cdd93ca37 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 27 May 2020 20:03:11 -0700 Subject: [PATCH 10/51] update for end caps --- compiler/bitcells/col_cap_bitcell_1rw_1r.py | 43 ++++++ compiler/bitcells/row_cap_bitcell_1rw_1r.py | 43 ++++++ compiler/modules/bitcell_base_array.py | 15 +- compiler/modules/col_cap_array.py | 103 +++++++++++++ compiler/modules/replica_bitcell_array.py | 138 ++++++++++-------- compiler/modules/replica_column.py | 84 ++++++++--- compiler/modules/row_cap_array.py | 128 ++++++++++++++++ compiler/options.py | 21 +-- .../14_replica_bitcell_1rw_1r_array_test.py | 12 +- 9 files changed, 486 insertions(+), 101 deletions(-) create mode 100644 compiler/bitcells/col_cap_bitcell_1rw_1r.py create mode 100644 compiler/bitcells/row_cap_bitcell_1rw_1r.py create mode 100644 compiler/modules/col_cap_array.py create mode 100644 compiler/modules/row_cap_array.py diff --git a/compiler/bitcells/col_cap_bitcell_1rw_1r.py b/compiler/bitcells/col_cap_bitcell_1rw_1r.py new file mode 100644 index 00000000..315ad23f --- /dev/null +++ b/compiler/bitcells/col_cap_bitcell_1rw_1r.py @@ -0,0 +1,43 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +import utils +from tech import GDS, layer +from tech import cell_properties as props +import bitcell_base + + +class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base): + """ + todo""" + + pin_names = [props.bitcell.cell_1rw1r.pin.bl0, + props.bitcell.cell_1rw1r.pin.br0, + props.bitcell.cell_1rw1r.pin.bl1, + props.bitcell.cell_1rw1r.pin.br1, + props.bitcell.cell_1rw1r.pin.vdd] + + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", + "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("col_cap_cell_1rw_1r", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, + "col_cap_cell_1rw_1r", + GDS["unit"]) + + def __init__(self, name=""): + # Ignore the name argument + bitcell_base.bitcell_base.__init__(self, "col_cap_cell_1rw_1r") + debug.info(2, "Create col_cap bitcell 1rw+1r object") + + self.width = col_cap_bitcell_1rw_1r.width + self.height = col_cap_bitcell_1rw_1r.height + self.pin_map = col_cap_bitcell_1rw_1r.pin_map + self.add_pin_types(self.type_list) diff --git a/compiler/bitcells/row_cap_bitcell_1rw_1r.py b/compiler/bitcells/row_cap_bitcell_1rw_1r.py new file mode 100644 index 00000000..b50629f0 --- /dev/null +++ b/compiler/bitcells/row_cap_bitcell_1rw_1r.py @@ -0,0 +1,43 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +import utils +from tech import GDS, layer +from tech import cell_properties as props +import bitcell_base + + +class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base): + """ + A single bit cell which is forced to store a 0. + This module implements the single memory cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. """ + + pin_names = [props.bitcell.cell_1rw1r.pin.wl0, + props.bitcell.cell_1rw1r.pin.wl1, + props.bitcell.cell_1rw1r.pin.gnd] + + type_list = ["INPUT", "INPUT", "GROUND"] + + (width, height) = utils.get_libcell_size("row_cap_cell_1rw_1r", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, + "row_cap_cell_1rw_1r", + GDS["unit"]) + + def __init__(self, name=""): + # Ignore the name argument + bitcell_base.bitcell_base.__init__(self, "row_cap_cell_1rw_1r") + debug.info(2, "Create row_cap bitcell 1rw+1r object") + + self.width = row_cap_bitcell_1rw_1r.width + self.height = row_cap_bitcell_1rw_1r.height + self.pin_map = row_cap_bitcell_1rw_1r.pin_map + self.add_pin_types(self.type_list) diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index a8829fc3..9b46a192 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -108,16 +108,23 @@ class bitcell_base_array(design.design): except AttributeError: bitcell_power_pin_directions = None + # For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps. + try: + bitcell_no_vdd_pin = cell_properties.bitcell.no_vdd_via + except AttributeError: + bitcell_no_vdd_pin = False + # Add vdd/gnd via stacks for row in range(self.row_size): for col in range(self.column_size): inst = self.cell_inst[row,col] for pin_name in ["vdd", "gnd"]: for pin in inst.get_pins(pin_name): - self.add_power_pin(name=pin_name, - loc=pin.center(), - directions=bitcell_power_pin_directions, - start_layer=pin.layer) + if not (pin_name == "vdd" and bitcell_no_vdd_pin): + self.add_power_pin(name=pin_name, + loc=pin.center(), + directions=bitcell_power_pin_directions, + start_layer=pin.layer) def _adjust_x_offset(self, xoffset, col, col_offset): tempx = xoffset diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py new file mode 100644 index 00000000..3119f4e5 --- /dev/null +++ b/compiler/modules/col_cap_array.py @@ -0,0 +1,103 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +from bitcell_base_array import bitcell_base_array +from sram_factory import factory +from globals import OPTS +from tech import cell_properties + +class col_cap_array(bitcell_base_array): + """ + Generate a dummy row/column for the replica array. + """ + def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): + super().__init__(cols, rows, name, column_offset) + self.mirror = mirror + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + """ Create and connect the netlist """ + self.add_modules() + self.add_pins() + self.create_instances() + + def create_layout(self): + + self.place_array("dummy_r{0}_c{1}", self.mirror) + self.add_layout_pins() + self.add_boundary() + self.DRC_LVS() + + def add_modules(self): + """ Add the modules used in this design """ + # self.dummy_cell = factory.create(module_type="col_cap_bitcell_1rw_1r") # TODO: make module_type generic + self.dummy_cell = factory.create(module_type="col_cap_bitcell") + self.add_mod(self.dummy_cell) + + self.cell = factory.create(module_type="bitcell") + + def create_instances(self): + """ Create the module instances used in this design """ + self.cell_inst = {} + for col in range(self.column_size): + for row in range(self.row_size): + name = "bit_r{0}_c{1}".format(row, col) + self.cell_inst[row,col]=self.add_inst(name=name, + mod=self.dummy_cell) + self.connect_inst(self.get_bitcell_pins(col, row)) + + def get_bitcell_pins(self, col, row): + """ + Creates a list of connections in the bitcell, + indexed by column and row, for instance use in bitcell_array + """ + + pin_name = cell_properties.bitcell.cell_1rw1r.pin + bitcell_pins = ["{0}_{1}".format(pin_name.bl0, col), + "{0}_{1}".format(pin_name.br0, col), + "{0}_{1}".format(pin_name.bl1, col), + "{0}_{1}".format(pin_name.br1, col), + "vdd"] + + return bitcell_pins + + def add_layout_pins(self): + """ Add the layout pins """ + + column_list = self.cell.get_all_bitline_names() + + for col in range(self.column_size): + for cell_column in column_list: + bl_pin = self.cell_inst[0,col].get_pin(cell_column) + self.add_layout_pin(text=cell_column+"_{0}".format(col), + layer=bl_pin.layer, + offset=bl_pin.ll().scale(1,0), + width=bl_pin.width(), + height=self.height) + + # Add vdd/gnd via stacks + for row in range(self.row_size): + for col in range(self.column_size): + inst = self.cell_inst[row,col] + for pin_name in ["vdd", "gnd"]: + for pin in inst.get_pins(pin_name): + self.add_power_pin(name=pin.name, + loc=pin.center(), + start_layer=pin.layer) + + + # def input_load(self): + # wl_wire = self.gen_wl_wire() + # return wl_wire.return_input_cap() + # + # def get_wordline_cin(self): + # """Get the relative input capacitance from the wordline connections in all the bitcell""" + # #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns + # bitcell_wl_cin = self.cell.get_wl_cin() + # total_cin = bitcell_wl_cin * self.column_size + # return total_cin diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index c88dbe6d..ff91f7fd 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -1,12 +1,12 @@ # 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 debug import design -from tech import drc, spice +from tech import drc, spice, cell_properties from vector import vector from globals import OPTS from sram_factory import factory @@ -34,11 +34,11 @@ class replica_bitcell_array(design.design): debug.check(left_rbl+right_rbl==len(self.all_ports),"Invalid number of RBLs for port configuration.") debug.check(left_rbl+right_rbl==len(self.bitcell_ports),"Bitcell ports must match total RBLs.") - + # Two dummy rows/cols plus replica for each port self.extra_rows = 2 + left_rbl + right_rbl self.extra_cols = 2 + left_rbl + right_rbl - + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -46,8 +46,8 @@ class replica_bitcell_array(design.design): # We don't offset this because we need to align # the replica bitcell in the control logic #self.offset_all_coordinates() - - + + def create_netlist(self): """ Create and connect the netlist """ self.add_modules() @@ -55,15 +55,15 @@ class replica_bitcell_array(design.design): self.create_instances() def add_modules(self): - """ Array and dummy/replica columns + """ Array and dummy/replica columns d or D = dummy cell (caps to distinguish grouping) r or R = replica cell (caps to distinguish grouping) - b or B = bitcell - replica columns 1 + b or B = bitcell + replica columns 1 v v - bdDDDDDDDDDDDDDDdb <- Dummy row - bdDDDDDDDDDDDDDDrb <- Dummy row + bdDDDDDDDDDDDDDDdb <- Dummy row + bdDDDDDDDDDDDDDDrb <- Dummy row br--------------rb br| Array |rb br| row x col |rb @@ -106,7 +106,7 @@ class replica_bitcell_array(design.design): column_offset=column_offset, replica_bit=replica_bit) self.add_mod(self.replica_columns[bit]) - + # Dummy row self.dummy_row = factory.create(module_type="dummy_array", cols=self.column_size, @@ -116,15 +116,35 @@ class replica_bitcell_array(design.design): mirror=0) self.add_mod(self.dummy_row) - # Dummy col (mirror starting at first if odd replica+dummy rows) - self.dummy_col_left = factory.create(module_type="dummy_array", + + # If there are bitcell end caps, replace the dummy cells on the edge of the bitcell array with end caps. + try: + end_caps_enabled = cell_properties.bitcell.end_caps + except AttributeError: + end_caps_enabled = False + + # Dummy Row or Col Cap, depending on bitcell array properties + edge_row_module_type = ("col_cap_array" if end_caps_enabled else "dummy_array") + + self.edge_row = factory.create(module_type=edge_row_module_type, + cols=self.column_size, + rows=1, + # dummy column + left replica column + column_offset=1 + self.left_rbl, + mirror=0) + self.add_mod(self.edge_row) + + # Dummy Col or Row Cap, depending on bitcell array properties + edge_col_module_type = ("row_cap_array" if end_caps_enabled else "dummy_array") + + self.edge_col_left = factory.create(module_type=edge_col_module_type, cols=1, column_offset=0, rows=self.row_size + self.extra_rows, mirror=(self.left_rbl+1)%2) - self.add_mod(self.dummy_col_left) + self.add_mod(self.edge_col_left) - self.dummy_col_right = factory.create(module_type="dummy_array", + self.edge_col_right = factory.create(module_type=edge_col_module_type, cols=1, # dummy column # + left replica column @@ -133,9 +153,7 @@ class replica_bitcell_array(design.design): column_offset=1 + self.left_rbl + self.column_size + self.right_rbl, rows=self.row_size + self.extra_rows, mirror=(self.left_rbl+1)%2) - self.add_mod(self.dummy_col_right) - - + self.add_mod(self.edge_col_right) def add_pins(self): self.bitcell_array_wl_names = self.bitcell_array.get_all_wordline_names() @@ -150,7 +168,7 @@ class replica_bitcell_array(design.design): self.rbl_bl_names = {} self.rbl_br_names = {} self.rbl_wl_names = {} - + # Create the full WL names include dummy, replica, and regular bit cells self.replica_col_wl_names = [] self.replica_col_wl_names.extend(["{0}_bot".format(x) for x in self.dummy_cell_wl_names]) @@ -193,7 +211,7 @@ class replica_bitcell_array(design.design): wl_names = ["rbl_{0}_{1}".format(x,port) for x in self.cell.get_all_wl_names()] #wl_names[port] = "rbl_wl{}".format(port) self.replica_wl_names[port] = wl_names - + # External pins self.add_pin_list(self.bitcell_array_bl_names, "INOUT") @@ -204,22 +222,22 @@ class replica_bitcell_array(design.design): self.add_pin(bl_name,"OUTPUT") self.add_pin(br_name,"OUTPUT") self.add_pin_list(self.bitcell_array_wl_names, "INPUT") - # Need to sort by port order since dictionary values may not be in order + # Need to sort by port order since dictionary values may not be in order wl_names = [self.rbl_wl_names[x] for x in sorted(self.rbl_wl_names.keys())] for pin_name in wl_names: self.add_pin(pin_name,"INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - + def create_instances(self): """ Create the module instances used in this design """ supplies = ["vdd", "gnd"] - + # Used for names/dimensions only self.cell = factory.create(module_type="bitcell") - + # Main array self.bitcell_array_inst=self.add_inst(name="bitcell_array", mod=self.bitcell_array) @@ -231,35 +249,33 @@ class replica_bitcell_array(design.design): self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port), mod=self.replica_columns[port]) self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies) - - + + # Dummy rows under the bitcell array (connected with with the replica cell wl) self.dummy_row_replica_inst = {} for port in range(self.left_rbl+self.right_rbl): self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port), mod=self.dummy_row) self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names[port] + supplies) - - - # Top/bottom dummy rows + + + # Top/bottom dummy rows or col caps self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot", - mod=self.dummy_row) + mod=self.edge_row) self.connect_inst(self.dummy_row_bl_names + [x+"_bot" for x in self.dummy_cell_wl_names] + supplies) self.dummy_row_top_inst=self.add_inst(name="dummy_row_top", - mod=self.dummy_row) + mod=self.edge_row) self.connect_inst(self.dummy_row_bl_names + [x+"_top" for x in self.dummy_cell_wl_names] + supplies) # Left/right Dummy columns self.dummy_col_left_inst=self.add_inst(name="dummy_col_left", - mod=self.dummy_col_left) + mod=self.edge_col_left) self.connect_inst([x+"_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) self.dummy_col_right_inst=self.add_inst(name="dummy_col_right", - mod=self.dummy_col_right) + mod=self.edge_col_right) self.connect_inst([x+"_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) - - def create_layout(self): self.height = (self.row_size+self.extra_rows)*self.dummy_row.height @@ -267,7 +283,7 @@ class replica_bitcell_array(design.design): # This is a bitcell x bitcell offset to scale offset = vector(self.cell.width, self.cell.height) - + self.bitcell_array_inst.place(offset=[0,0]) # To the left of the bitcell array @@ -276,7 +292,6 @@ class replica_bitcell_array(design.design): # To the right of the bitcell array for bit in range(self.right_rbl): self.replica_col_inst[self.left_rbl+bit].place(offset=offset.scale(bit,-self.left_rbl-1)+self.bitcell_array_inst.lr()) - # Far top dummy row (first row above array is NOT flipped) flip_dummy = self.right_rbl%2 @@ -298,17 +313,17 @@ class replica_bitcell_array(design.design): for bit in range(self.right_rbl): self.dummy_row_replica_inst[self.left_rbl+bit].place(offset=offset.scale(0,bit+bit%2)+self.bitcell_array_inst.ul(), mirror="MX" if bit%2 else "R0") - + self.translate_all(offset.scale(-1-self.left_rbl,-1-self.left_rbl)) - + self.add_layout_pins() - + self.add_boundary() - + self.DRC_LVS() - + def add_layout_pins(self): """ Add the layout pins """ @@ -341,10 +356,10 @@ class replica_bitcell_array(design.design): for (pin_name,wl_name) in zip(self.cell.get_all_wl_names(),self.replica_wl_names[port]): # +1 for dummy row pin_bit = port+1 - # +row_size if above the array + # +row_size if above the array if port>=self.left_rbl: pin_bit += self.row_size - + pin_name += "_{}".format(pin_bit) pin = inst.get_pin(pin_name) if wl_name in self.rbl_wl_names.values(): @@ -368,20 +383,27 @@ class replica_bitcell_array(design.design): offset=pin.ll().scale(1, 0), width=pin.width(), height=self.height) - + + # For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps. + try: + bitcell_no_vdd_pin = cell_properties.bitcell.no_vdd_via + except AttributeError: + bitcell_no_vdd_pin = False + for pin_name in ["vdd", "gnd"]: for inst in self.insts: pin_list = inst.get_pins(pin_name) for pin in pin_list: - self.add_power_pin(name=pin_name, - loc=pin.center(), - directions=("V", "V"), - start_layer=pin.layer) + if not (pin_name == "vdd" and bitcell_no_vdd_pin): + self.add_power_pin(name=pin_name, + loc=pin.center(), + directions=("V", "V"), + start_layer=pin.layer) def get_rbl_wl_name(self, port): """ Return the WL for the given RBL port """ return self.rbl_wl_names[port] - + def get_rbl_bl_name(self, port): """ Return the BL for the given RBL port """ return self.rbl_bl_names[port] @@ -393,17 +415,17 @@ class replica_bitcell_array(design.design): def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" from tech import drc, parameter - + # Dynamic Power from Bitline bl_wire = self.gen_bl_wire() cell_load = 2 * bl_wire.return_input_cap() bl_swing = OPTS.rbl_delay_percentage freq = spice["default_event_frequency"] bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) - - #Calculate the bitcell power which currently only includes leakage + + #Calculate the bitcell power which currently only includes leakage cell_power = self.cell.analytical_power(corner, load) - + #Leakage power grows with entire array and bitlines. total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, cell_power.leakage * self.column_size * self.row_size) @@ -429,13 +451,13 @@ class replica_bitcell_array(design.design): def graph_exclude_bits(self, targ_row, targ_col): """Excludes bits in column from being added to graph except target""" self.bitcell_array.graph_exclude_bits(targ_row, targ_col) - + def graph_exclude_replica_col_bits(self): """Exclude all replica/dummy cells in the replica columns except the replica bit.""" - + for port in range(self.left_rbl+self.right_rbl): self.replica_columns[port].exclude_all_but_replica() def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" - return self.bitcell_array.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col) + return self.bitcell_array.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index d874ba8c..2e3d207e 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -1,11 +1,11 @@ # 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 debug import design -from tech import drc +from tech import drc, cell_properties import contact from sram_factory import factory from vector import vector @@ -16,7 +16,7 @@ class replica_column(design.design): Generate a replica bitline column for the replica array. Rows is the total number of rows i the main array. Left_rbl and right_rbl are the number of left and right replica bitlines. - Replica bit specifies which replica column this is (to determine where to put the + Replica bit specifies which replica column this is (to determine where to put the replica cell. """ @@ -31,15 +31,15 @@ class replica_column(design.design): # left, right, regular rows plus top/bottom dummy cells self.total_size = self.left_rbl+rows+self.right_rbl+2 self.column_offset = column_offset - + debug.check(replica_bit!=0 and replica_bit!=rows,"Replica bit cannot be the dummy row.") debug.check(replica_bit<=left_rbl or replica_bit>=self.total_size-right_rbl-1, - "Replica bit cannot be in the regular array.") + "Replica bit cannot be in the regular array.") self.create_netlist() if not OPTS.netlist_only: self.create_layout() - + def create_netlist(self): self.add_modules() self.add_pins() @@ -47,7 +47,7 @@ class replica_column(design.design): def create_layout(self): self.height = self.total_size*self.cell.height - self.width = self.cell.width + self.width = self.cell.width self.place_instances() self.add_layout_pins() @@ -55,7 +55,7 @@ class replica_column(design.design): self.DRC_LVS() def add_pins(self): - + for bl_name in self.cell.get_all_bitline_names(): # In the replica column, these are only outputs! self.add_pin("{0}_{1}".format(bl_name,0), "OUTPUT") @@ -63,7 +63,7 @@ class replica_column(design.design): for row in range(self.total_size): for wl_name in self.cell.get_all_wl_names(): self.add_pin("{0}_{1}".format(wl_name,row), "INPUT") - + self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -72,27 +72,49 @@ class replica_column(design.design): self.add_mod(self.replica_cell) self.dummy_cell = factory.create(module_type="dummy_bitcell") self.add_mod(self.dummy_cell) + try: + edge_module_type = ("col_cap_bitcell" if cell_properties.bitcell.end_caps else "dummy_bitcell") + except AttributeError: + edge_module_type = "dummy_bitcell" + self.edge_cell = factory.create(module_type=edge_module_type) + self.add_mod(self.edge_cell) # Used for pin names only self.cell = factory.create(module_type="bitcell") - + def create_instances(self): + + try: + end_caps_enabled = cell_properties.bitcell.end_caps + except AttributeError: + end_caps_enabled = False + self.cell_inst = {} for row in range(self.total_size): name="rbc_{0}".format(row) # Top/bottom cell are always dummy cells. # Regular array cells are replica cells (>left_rbl and self.left_rbl and row Date: Thu, 28 May 2020 20:31:21 -0700 Subject: [PATCH 11/51] fix for replica column mirroring over y --- compiler/modules/replica_bitcell_array.py | 6 ++++-- compiler/tests/14_replica_bitcell_1rw_1r_array_test.py | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index ff91f7fd..ff2260d3 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -91,14 +91,16 @@ class replica_bitcell_array(design.design): # Replica bitlines self.replica_columns = {} for bit in range(self.left_rbl+self.right_rbl): + # Creating left_rbl if bit Date: Thu, 14 May 2020 11:20:37 -0700 Subject: [PATCH 12/51] Thin-cell decoder changes. Add hard decoder gates (nand, inv, pnan) Add conditions for routing using LI layer in s8. Generalize bus layers for decoders. Move custom cells to own directory. Fixed via directions, etc. Add 4x16 hierarchical decoder and test case --- .gitlab-ci.yml | 11 +- compiler/base/contact.py | 8 +- compiler/base/geometry.py | 14 +- compiler/base/hierarchy_layout.py | 58 +-- compiler/base/hierarchy_spice.py | 1 + compiler/bitcells/bitcell.py | 2 +- compiler/{modules => custom}/dff.py | 4 +- compiler/custom/inv_dec.py | 80 ++++ compiler/custom/nand2_dec.py | 77 ++++ compiler/custom/nand3_dec.py | 77 ++++ compiler/custom/nand4_dec.py | 77 ++++ compiler/custom/pand2_dec.py | 138 +++++++ compiler/custom/pand3_dec.py | 149 +++++++ compiler/custom/pand4_dec.py | 149 +++++++ compiler/{modules => custom}/tri_gate.py | 0 compiler/{modules => custom}/write_driver.py | 0 compiler/modules/hierarchical_decoder.py | 373 ++++++++---------- compiler/modules/hierarchical_predecode.py | 260 ++++++++---- compiler/modules/hierarchical_predecode2x4.py | 20 +- compiler/modules/hierarchical_predecode3x8.py | 21 +- .../modules/hierarchical_predecode4x16.py | 64 +++ compiler/modules/port_address.py | 27 +- compiler/modules/port_data.py | 59 ++- ...ine_driver.py => wordline_driver_array.py} | 153 +++---- compiler/options.py | 4 + compiler/pgates/pand2.py | 2 +- compiler/pgates/pdriver.py | 12 +- compiler/pgates/pgate.py | 1 - compiler/pgates/pinv_dec.py | 217 ++++++++++ compiler/pgates/pnand3.py | 3 + compiler/pgates/ptx.py | 10 +- compiler/pgates/wordline_driver.py | 155 ++++++++ compiler/sram_factory.py | 4 +- compiler/tests/02_library_lvs_test.py | 2 +- compiler/tests/04_pand2_dec_test.py | 39 ++ compiler/tests/04_pand3_dec_test.py | 39 ++ compiler/tests/04_pand4_dec_test.py | 39 ++ compiler/tests/04_pdriver_test.py | 4 +- compiler/tests/04_pinv_dec_1x_test.py | 35 ++ ...ver_test.py => 04_wordline_driver_test.py} | 2 +- .../tests/06_hierarchical_decoder_test.py | 8 +- .../06_hierarchical_predecode2x4_test.py | 7 +- .../06_hierarchical_predecode3x8_test.py | 7 +- .../06_hierarchical_predecode4x16_test.py | 42 ++ ...08_wordline_driver_array_pbitcell_test.py} | 5 +- .../tests/08_wordline_driver_array_test.py | 37 ++ compiler/tests/18_port_data_1rw_1r_test.py | 6 +- compiler/tests/19_single_bank_test.py | 40 +- compiler/tests/testutils.py | 1 + 49 files changed, 1991 insertions(+), 552 deletions(-) rename compiler/{modules => custom}/dff.py (95%) create mode 100644 compiler/custom/inv_dec.py create mode 100644 compiler/custom/nand2_dec.py create mode 100644 compiler/custom/nand3_dec.py create mode 100644 compiler/custom/nand4_dec.py create mode 100644 compiler/custom/pand2_dec.py create mode 100644 compiler/custom/pand3_dec.py create mode 100644 compiler/custom/pand4_dec.py rename compiler/{modules => custom}/tri_gate.py (100%) rename compiler/{modules => custom}/write_driver.py (100%) create mode 100644 compiler/modules/hierarchical_predecode4x16.py rename compiler/modules/{wordline_driver.py => wordline_driver_array.py} (50%) create mode 100644 compiler/pgates/pinv_dec.py create mode 100644 compiler/pgates/wordline_driver.py create mode 100755 compiler/tests/04_pand2_dec_test.py create mode 100755 compiler/tests/04_pand3_dec_test.py create mode 100755 compiler/tests/04_pand4_dec_test.py create mode 100755 compiler/tests/04_pinv_dec_1x_test.py rename compiler/tests/{08_wordline_driver_test.py => 04_wordline_driver_test.py} (93%) create mode 100755 compiler/tests/06_hierarchical_predecode4x16_test.py rename compiler/tests/{08_wordline_driver_pbitcell_test.py => 08_wordline_driver_array_pbitcell_test.py} (87%) create mode 100755 compiler/tests/08_wordline_driver_array_test.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 27c341aa..27431cb2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ before_script: - . /home/gitlab-runner/setup-paths.sh - export OPENRAM_HOME="`pwd`/compiler" - - export OPENRAM_TECH="`pwd`/technology" + - export OPENRAM_TECH="`pwd`/technology:/home/PDKs/skywater-tech" stages: - test @@ -25,6 +25,15 @@ scn4m_subm: - .coverage.* expire_in: 1 week +# s8: +# stage: test +# script: +# - coverage run -p $OPENRAM_HOME/tests/regress.py -t s8 +# artifacts: +# paths: +# - .coverage.* +# expire_in: 1 week + coverage: stage: coverage script: diff --git a/compiler/base/contact.py b/compiler/base/contact.py index f4fda552..14dbf76e 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -53,6 +53,10 @@ class contact(hierarchy_design.hierarchy_design): first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V" second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V" self.directions = (first_dir, second_dir) + # Preferred directions + elif directions == "pref": + self.directions = (tech.preferred_directions[layer_stack[0]], + tech.preferred_directions[layer_stack[2]]) # User directions elif directions: self.directions = directions @@ -149,7 +153,7 @@ class contact(hierarchy_design.hierarchy_design): self.first_layer_vertical_enclosure = max(self.first_layer_enclosure, (self.first_layer_minwidth - self.contact_array_height) / 2) else: - debug.error("Invalid first layer direction.", -1) + debug.error("Invalid first layer direction: ".format(self.directions[0]), -1) # In some technologies, the minimum width may be larger # than the overlap requirement around the via, so @@ -165,7 +169,7 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_vertical_enclosure = max(self.second_layer_enclosure, (self.second_layer_minwidth - self.contact_array_width) / 2) else: - debug.error("Invalid second layer direction.", -1) + debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1) def create_contact_array(self): """ Create the contact array at the origin""" diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 96da3d99..32af7ee9 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -86,8 +86,14 @@ class geometry: elif mirror == "XY": ll = ll.scale(-1, -1) ur = ur.scale(-1, -1) + elif mirror == "" or mirror == "R0": + pass + else: + debug.error("Invalid mirroring: {}".format(mirror), -1) - if rotate == 90: + if rotate == 0: + pass + elif rotate == 90: ll = ll.rotate_scale(-1, 1) ur = ur.rotate_scale(-1, 1) elif rotate == 180: @@ -96,6 +102,8 @@ class geometry: elif rotate == 270: ll = ll.rotate_scale(1, -1) ur = ur.rotate_scale(1, -1) + else: + debug.error("Invalid rotation: {}".format(rotate), -1) self.boundary = [offset + ll, offset + ur] self.normalize() @@ -139,6 +147,10 @@ class geometry: def cy(self): """ Return the center y """ return 0.5 * (self.boundary[0].y + self.boundary[1].y) + + def center(self): + """ Return the center coordinate """ + return vector(self.cx(), self.cy()) class instance(geometry): diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index df7095df..4d0345a3 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -244,25 +244,27 @@ class layout(): height)) return self.objs[-1] - def add_segment_center(self, layer, start, end): + def add_segment_center(self, layer, start, end, width=None): """ Add a min-width rectanglular segment using center line on the start to end point """ - minwidth_layer = drc["minwidth_{}".format(layer)] + if not width: + width = drc["minwidth_{}".format(layer)] + if start.x != end.x and start.y != end.y: debug.error("Nonrectilinear center rect!", -1) elif start.x != end.x: - offset = vector(0, 0.5 * minwidth_layer) + offset = vector(0, 0.5 * width) return self.add_rect(layer, start - offset, end.x - start.x, - minwidth_layer) + width) else: - offset = vector(0.5 * minwidth_layer, 0) + offset = vector(0.5 * width, 0) return self.add_rect(layer, start - offset, - minwidth_layer, + width, end.y - start.y) def get_pin(self, text): @@ -322,7 +324,7 @@ class layout(): for pin_name in self.pin_map.keys(): self.copy_layout_pin(instance, pin_name, prefix + pin_name) - def add_layout_pin_segment_center(self, text, layer, start, end): + def add_layout_pin_segment_center(self, text, layer, start, end, width=None): """ Creates a path like pin with center-line convention """ @@ -331,27 +333,27 @@ class layout(): self.gds_write(file_name) debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1) - minwidth_layer = drc["minwidth_{}".format(layer)] + if not width: + layer_width = drc["minwidth_{}".format(layer)] + else: + layer_width = width # one of these will be zero - width = max(start.x, end.x) - min(start.x, end.x) - height = max(start.y, end.y) - min(start.y, end.y) + bbox_width = max(start.x, end.x) - min(start.x, end.x) + bbox_height = max(start.y, end.y) - min(start.y, end.y) ll_offset = vector(min(start.x, end.x), min(start.y, end.y)) # Shift it down 1/2 a width in the 0 dimension - if height == 0: - ll_offset -= vector(0, 0.5 * minwidth_layer) - if width == 0: - ll_offset -= vector(0.5 * minwidth_layer, 0) - # This makes sure it is long enough, but also it is not 0 width! - height = max(minwidth_layer, height) - width = max(minwidth_layer, width) + if bbox_height == 0: + ll_offset -= vector(0, 0.5 * layer_width) + if bbox_width == 0: + ll_offset -= vector(0.5 * layer_width, 0) - return self.add_layout_pin(text, - layer, - ll_offset, - width, - height) + return self.add_layout_pin(text=text, + layer=layer, + offset=ll_offset, + width=bbox_width, + height=bbox_height) def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None): """ Creates a path like pin with center-line convention """ @@ -692,6 +694,8 @@ class layout(): boundary_layer = "stdc" boundary = [self.find_lowest_coords(), self.find_highest_coords()] + debug.check(boundary[0] and boundary[1], "No shapes to make a boundary.") + height = boundary[1][1] - boundary[0][1] width = boundary[1][0] - boundary[0][0] (layer_number, layer_purpose) = techlayer[boundary_layer] @@ -1306,16 +1310,16 @@ class layout(): which vias are needed. """ - via = self.add_via_stack_center(from_layer=start_layer, - to_layer=self.pwr_grid_layer, - size=size, - offset=loc, - directions=directions) if start_layer == self.pwr_grid_layer: self.add_layout_pin_rect_center(text=name, layer=self.pwr_grid_layer, offset=loc) else: + via = self.add_via_stack_center(from_layer=start_layer, + to_layer=self.pwr_grid_layer, + size=size, + offset=loc, + directions=directions) # Hack for min area if OPTS.tech_name == "s8": width = round_to_grid(sqrt(drc["minarea_m3"])) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 8091af63..00d7ad44 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -208,6 +208,7 @@ class spice(): # parses line into ports and remove subckt self.pins = subckt_line.split(" ")[2:] else: + debug.info(4, "no spfile {0}".format(self.sp_file)) self.spice = [] # We don't define self.lvs and will use self.spice if dynamically created diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index b0e79208..4ed2d053 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -7,7 +7,7 @@ # import debug import utils -from tech import GDS, layer, parameter +from tech import GDS, layer from tech import cell_properties as props import bitcell_base diff --git a/compiler/modules/dff.py b/compiler/custom/dff.py similarity index 95% rename from compiler/modules/dff.py rename to compiler/custom/dff.py index e319459e..c8fdb4b0 100644 --- a/compiler/modules/dff.py +++ b/compiler/custom/dff.py @@ -61,7 +61,7 @@ class dff(design.design): #Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width. return parameter["dff_clk_cin"] - def build_graph(self, graph, inst_name, port_nets): + def build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" - self.add_graph_edges(graph, port_nets) + self.add_graph_edges(graph, port_nets) diff --git a/compiler/custom/inv_dec.py b/compiler/custom/inv_dec.py new file mode 100644 index 00000000..80fdb74e --- /dev/null +++ b/compiler/custom/inv_dec.py @@ -0,0 +1,80 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils +import debug + + +class inv_dec(design.design): + """ + INV for address decoders. + """ + + pin_names = ["A", "Z", "vdd", "gnd"] + type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("inv_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "inv_dec", GDS["unit"]) + + def __init__(self, name="inv_dec", height=None): + design.design.__init__(self, name) + + self.width = inv_dec.width + self.height = inv_dec.height + self.pin_map = inv_dec.pin_map + self.add_pin_types(self.type_list) + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + c_eff = self.calculate_effective_capacitance(load) + freq = spice["default_event_frequency"] + power_dyn = self.calc_dynamic_power(corner, c_eff, freq) + power_leak = spice["inv_leakage"] + + total_power = self.return_power(power_dyn, power_leak) + return total_power + + def calculate_effective_capacitance(self, load): + """Computes effective capacitance. Results in fF""" + c_load = load + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) + + return transition_prob * (c_load + c_para) + + def input_load(self): + """ + Return the capacitance of the gate connection in generic capacitive + units relative to the minimum width of a transistor + """ + return self.nmos_size + self.pmos_size + + def get_stage_effort(self, cout, inp_is_rise=True): + """ + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 1 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) diff --git a/compiler/custom/nand2_dec.py b/compiler/custom/nand2_dec.py new file mode 100644 index 00000000..30a018a1 --- /dev/null +++ b/compiler/custom/nand2_dec.py @@ -0,0 +1,77 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils + + +class nand2_dec(design.design): + """ + 2-input NAND decoder for address decoders. + """ + + pin_names = ["A", "B", "Z", "vdd", "gnd"] + type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("nand2_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "nand2_dec", GDS["unit"]) + + def __init__(self, name="nand2_dec", height=None): + design.design.__init__(self, name) + + self.width = nand2_dec.width + self.height = nand2_dec.height + self.pin_map = nand2_dec.pin_map + self.add_pin_types(self.type_list) + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + c_eff = self.calculate_effective_capacitance(load) + freq = spice["default_event_frequency"] + power_dyn = self.calc_dynamic_power(corner, c_eff, freq) + power_leak = spice["nand2_leakage"] + + total_power = self.return_power(power_dyn, power_leak) + return total_power + + def calculate_effective_capacitance(self, load): + """Computes effective capacitance. Results in fF""" + c_load = load + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) + transition_prob = 0.1875 + return transition_prob * (c_load + c_para) + + def input_load(self): + """Return the relative input capacitance of a single input""" + return self.nmos_size + self.pmos_size + + def get_stage_effort(self, cout, inp_is_rise=True): + """ + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 2 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) + diff --git a/compiler/custom/nand3_dec.py b/compiler/custom/nand3_dec.py new file mode 100644 index 00000000..d700062c --- /dev/null +++ b/compiler/custom/nand3_dec.py @@ -0,0 +1,77 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils + + +class nand3_dec(design.design): + """ + 3-input NAND decoder for address decoders. + """ + + pin_names = ["A", "B", "C", "Z", "vdd", "gnd"] + type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("nand3_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "nand3_dec", GDS["unit"]) + + def __init__(self, name="nand3_dec", height=None): + design.design.__init__(self, name) + + self.width = nand3_dec.width + self.height = nand3_dec.height + self.pin_map = nand3_dec.pin_map + self.add_pin_types(self.type_list) + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + c_eff = self.calculate_effective_capacitance(load) + freq = spice["default_event_frequency"] + power_dyn = self.calc_dynamic_power(corner, c_eff, freq) + power_leak = spice["nand3_leakage"] + + total_power = self.return_power(power_dyn, power_leak) + return total_power + + def calculate_effective_capacitance(self, load): + """Computes effective capacitance. Results in fF""" + c_load = load + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) + transition_prob = 0.1875 + return transition_prob * (c_load + c_para) + + def input_load(self): + """Return the relative input capacitance of a single input""" + return self.nmos_size + self.pmos_size + + def get_stage_effort(self, cout, inp_is_rise=True): + """ + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 2 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) + diff --git a/compiler/custom/nand4_dec.py b/compiler/custom/nand4_dec.py new file mode 100644 index 00000000..5c2bb882 --- /dev/null +++ b/compiler/custom/nand4_dec.py @@ -0,0 +1,77 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils + + +class nand4_dec(design.design): + """ + 2-input NAND decoder for address decoders. + """ + + pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"] + type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("nand4_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "nand4_dec", GDS["unit"]) + + def __init__(self, name="nand4_dec", height=None): + design.design.__init__(self, name) + + self.width = nand4_dec.width + self.height = nand4_dec.height + self.pin_map = nand4_dec.pin_map + self.add_pin_types(self.type_list) + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + c_eff = self.calculate_effective_capacitance(load) + freq = spice["default_event_frequency"] + power_dyn = self.calc_dynamic_power(corner, c_eff, freq) + power_leak = spice["nand4_leakage"] + + total_power = self.return_power(power_dyn, power_leak) + return total_power + + def calculate_effective_capacitance(self, load): + """Computes effective capacitance. Results in fF""" + c_load = load + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) + transition_prob = 0.1875 + return transition_prob * (c_load + c_para) + + def input_load(self): + """Return the relative input capacitance of a single input""" + return self.nmos_size + self.pmos_size + + def get_stage_effort(self, cout, inp_is_rise=True): + """ + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 2 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) + diff --git a/compiler/custom/pand2_dec.py b/compiler/custom/pand2_dec.py new file mode 100644 index 00000000..0db490ff --- /dev/null +++ b/compiler/custom/pand2_dec.py @@ -0,0 +1,138 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from vector import vector +import pgate +from sram_factory import factory +from globals import OPTS + + +class pand2_dec(pgate.pgate): + """ + This is an AND with configurable drive strength. + """ + def __init__(self, name, size=1, height=None, add_wells=True): + debug.info(1, "Creating pand2_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + pgate.pgate.__init__(self, name, height, add_wells) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand2_dec") + else: + self.nand = factory.create(module_type="nand2_dec", + height=self.height) + + self.inv = factory.create(module_type="inv_dec", + height=self.height, + size=self.size) + + self.add_mod(self.nand) + self.add_mod(self.inv) + + def create_layout(self): + self.width = self.nand.width + self.inv.width + + self.place_insts() + self.add_wires() + self.add_layout_pins() + self.route_supply_rails() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="pand2_dec_nand", + mod=self.nand) + self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) + + self.inv_inst = self.add_inst(name="pand2_dec_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) + + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.inv_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + if OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + + def add_layout_pins(self): + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A", "B"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/custom/pand3_dec.py b/compiler/custom/pand3_dec.py new file mode 100644 index 00000000..3ec1eb45 --- /dev/null +++ b/compiler/custom/pand3_dec.py @@ -0,0 +1,149 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from vector import vector +import pgate +from sram_factory import factory +from globals import OPTS + + +class pand3_dec(pgate.pgate): + """ + This is an AND with configurable drive strength. + """ + def __init__(self, name, size=1, height=None, add_wells=True): + debug.info(1, "Creating pand3_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + pgate.pgate.__init__(self, name, height, add_wells) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand3_dec") + else: + self.nand = factory.create(module_type="nand3_dec", + height=self.height) + + self.inv = factory.create(module_type="inv_dec", + height=self.height, + size=self.size) + + self.add_mod(self.nand) + self.add_mod(self.inv) + + def create_layout(self): + self.width = self.nand.width + self.inv.width + + self.place_insts() + self.add_wires() + self.add_layout_pins() + self.route_supply_rails() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("C", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="pand3_dec_nand", + mod=self.nand) + self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"]) + + self.inv_inst = self.add_inst(name="pand3_dec_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) + + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.inv_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + if OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + + def add_layout_pins(self): + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A", "B", "C"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + def analytical_delay(self, corner, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + nand_delay = self.nand.analytical_delay(corner, + slew=slew, + load=self.inv.input_load()) + inv_delay = self.inv.analytical_delay(corner, + slew=nand_delay.slew, + load=load) + return nand_delay + inv_delay + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/custom/pand4_dec.py b/compiler/custom/pand4_dec.py new file mode 100644 index 00000000..ee65a349 --- /dev/null +++ b/compiler/custom/pand4_dec.py @@ -0,0 +1,149 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from vector import vector +import pgate +from sram_factory import factory +from globals import OPTS + + +class pand4_dec(pgate.pgate): + """ + This is an AND with configurable drive strength. + """ + def __init__(self, name, size=1, height=None, add_wells=True): + debug.info(1, "Creating pand4_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + pgate.pgate.__init__(self, name, height, add_wells) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand4_dec") + else: + self.nand = factory.create(module_type="nand4_dec", + height=self.height) + + self.inv = factory.create(module_type="inv_dec", + size=self.size) + + self.add_mod(self.nand) + self.add_mod(self.inv) + + def create_layout(self): + self.width = self.nand.width + self.inv.width + + self.place_insts() + self.add_wires() + self.add_layout_pins() + self.route_supply_rails() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("C", "INPUT") + self.add_pin("D", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="pand4_dec_nand", + mod=self.nand) + self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"]) + + self.inv_inst = self.add_inst(name="pand4_dec_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) + + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.inv_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + if OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + + def add_layout_pins(self): + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A", "B", "C"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + def analytical_delay(self, corner, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + nand_delay = self.nand.analytical_delay(corner, + slew=slew, + load=self.inv.input_load()) + inv_delay = self.inv.analytical_delay(corner, + slew=nand_delay.slew, + load=load) + return nand_delay + inv_delay + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/modules/tri_gate.py b/compiler/custom/tri_gate.py similarity index 100% rename from compiler/modules/tri_gate.py rename to compiler/custom/tri_gate.py diff --git a/compiler/modules/write_driver.py b/compiler/custom/write_driver.py similarity index 100% rename from compiler/modules/write_driver.py rename to compiler/custom/write_driver.py diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index c1593932..3cb5d35c 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -12,7 +12,6 @@ from sram_factory import factory from vector import vector from globals import OPTS from errors import drc_error -from tech import cell_properties, layer class hierarchical_decoder(design.design): @@ -28,12 +27,8 @@ class hierarchical_decoder(design.design): self.pre3x8_inst = [] b = factory.create(module_type="bitcell") - try: - self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple - except AttributeError: - self.cell_multiple = 1 - self.cell_height = self.cell_multiple * b.height - + self.cell_height = b.height + self.num_outputs = num_outputs self.num_inputs = math.ceil(math.log(self.num_outputs, 2)) (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) @@ -41,41 +36,6 @@ class hierarchical_decoder(design.design): self.create_netlist() if not OPTS.netlist_only: self.create_layout() - - def find_decoder_height(self): - """ - Dead code. This would dynamically determine the bitcell multiple, - but I just decided to hard code it in the tech file if it is not 1 - because a DRC tool would be required even to run in front-end mode. - """ - b = factory.create(module_type="bitcell") - - # Old behavior - if OPTS.netlist_only: - return (b.height, 1) - - # Search for the smallest multiple that works - cell_multiple = 1 - while cell_multiple < 5: - cell_height = cell_multiple * b.height - # debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height)) - try: - and3 = factory.create(module_type="pand3", - height=cell_height) - except drc_error: - # debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height)) - pass - else: - (drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) - total_errors = drc_errors + lvs_errors - if total_errors == 0: - debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple)) - return (cell_height, cell_multiple) - - cell_multiple += 1 - - else: - debug.error("Couldn't find a valid decoder height multiple.", -1) def create_netlist(self): self.add_modules() @@ -97,15 +57,22 @@ class hierarchical_decoder(design.design): self.DRC_LVS() def add_modules(self): - self.inv = factory.create(module_type="pinv", - height=self.cell_height) - self.add_mod(self.inv) - self.and2 = factory.create(module_type="pand2", - height=self.cell_height) + if OPTS.tech_name == "s8": + self.and2 = factory.create(module_type="pand2_dec") + else: + self.and2 = factory.create(module_type="pand2_dec", + height=self.cell_height) self.add_mod(self.and2) - self.and3 = factory.create(module_type="pand3", - height=self.cell_height) + if OPTS.tech_name == "s8": + self.and3 = factory.create(module_type="pand3_dec") + else: + self.and3 = factory.create(module_type="pand3_dec", + height=self.cell_height) + self.add_mod(self.and3) + # TBD + # self.and4 = factory.create(module_type="pand4_dec") + # self.add_mod(self.and4) self.add_decoders() @@ -176,56 +143,63 @@ class hierarchical_decoder(design.design): -1) # Calculates height and width of pre-decoder, - if self.no_of_pre3x8 > 0: + # FIXME: Update with 4x16 + if self.no_of_pre3x8 > 0 and self.no_of_pre2x4 > 0: + self.predecoder_width = max(self.pre3_8.width, self.pre2_4.width) + elif self.no_of_pre3x8 > 0: 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 - # We may have more than one bitcell per decoder row - self.num_rows = math.ceil(self.num_outputs / self.cell_multiple) - # We will place this many final decoders per row - self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows) - # We will need to use M2 and M3 in the vertical bus if we have multiple decoders per row - if self.decoders_per_row == 1: - self.decoder_bus_pitch = self.m2_pitch - elif self.decoders_per_row == 2: - self.decoder_bus_pitch = self.m3_pitch + # How much space between each predecoder + self.predecoder_spacing = self.and2.height + self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 \ + + (self.no_of_pre2x4 + self.no_of_pre3x8 - 1) * self.predecoder_spacing + + # Inputs to cells are on input layer + # Outputs from cells are on output layer + if OPTS.tech_name == "s8": + self.bus_layer = "m1" + self.bus_directions = "nonpref" + self.bus_pitch = self.m1_pitch + self.bus_space = self.m2_space + self.input_layer = "m2" + self.output_layer = "li" + self.output_layer_pitch = self.li_pitch else: - debug.error("Insufficient layers for multi-bit height decoder.", -1) + self.bus_layer = "m2" + self.bus_directions = "pref" + self.bus_pitch = self.m2_pitch + self.bus_space = self.m2_space + # These two layers being the same requires a special jog + # to ensure to conflicts with the output layers + self.input_layer = "m1" + self.output_layer = "m3" + self.output_layer_pitch = self.m3_pitch + + # Two extra pitches between modules on left and right + self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch + self.row_decoder_height = self.and2.height * self.num_outputs + # Extra bus space for supply contacts + self.input_routing_width = self.num_inputs * self.bus_pitch + self.bus_space # Calculates height and width of row-decoder - if (self.num_inputs == 4 or self.num_inputs == 5): - nand_width = self.and2.width - nand_inputs = 2 - else: - nand_width = self.and3.width - nand_inputs = 3 - self.internal_routing_width = self.decoder_bus_pitch * (self.total_number_of_predecoder_outputs + 1) - self.row_decoder_height = self.inv.height * self.num_rows - - decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m2_pitch - # print(self.decoders_per_row, nand_inputs) - # print(decoder_input_wire_height, self.cell_height) - if decoder_input_wire_height > self.cell_height: - debug.warning("Cannot fit multi-bit decoder routes per row.") - # debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.") - - self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch - # Calculates height and width of hierarchical decoder # Add extra pitch for good measure - self.height = max(self.predecoder_height, self.row_decoder_height) + self.m2_pitch - self.width = self.input_routing_width + self.predecoder_width \ + self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space + if (self.num_inputs == 4 or self.num_inputs == 5): + self.nand_width = self.and2.width + else: + self.nand_width = self.and3.width + + self.width = self.input_routing_width \ + + self.predecoder_width \ + self.internal_routing_width \ - + self.decoders_per_row * nand_width + self.inv.width - + + self.nand_width \ + + self.m1_space + def route_inputs(self): """ Create input bus for the predecoders """ - # inputs should be as high as the decoders - input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height - # Find the left-most predecoder min_x = 0 if self.no_of_pre2x4 > 0: @@ -235,10 +209,10 @@ class hierarchical_decoder(design.design): input_offset=vector(min_x - self.input_routing_width, 0) input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] - self.input_bus = self.create_vertical_pin_bus(layer="m2", + self.input_bus = self.create_vertical_pin_bus(layer=self.bus_layer, offset=input_offset, names=input_bus_names, - length=input_height) + length=self.predecoder_height) self.route_input_to_predecodes() @@ -253,9 +227,7 @@ class hierarchical_decoder(design.design): in_name = "in_{}".format(i) decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) - # 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 + self.m1_pitch)) + decoder_offset = decoder_pin.center() input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) self.route_input_bus(decoder_offset, input_offset) @@ -269,9 +241,7 @@ class hierarchical_decoder(design.design): in_name = "in_{}".format(i) decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) - # 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 + self.m1_pitch)) + decoder_offset = decoder_pin.center() input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) self.route_input_bus(decoder_offset, input_offset) @@ -282,13 +252,14 @@ class hierarchical_decoder(design.design): vertical M2 coordinate to the predecode inputs """ - self.add_via_stack_center(from_layer="m2", - to_layer="m3", + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.input_layer, offset=input_offset) - self.add_via_stack_center(from_layer="m2", - to_layer="m3", - offset=output_offset) - self.add_path("m3", [input_offset, output_offset]) + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.input_layer, + offset=output_offset, + directions=self.bus_directions) + self.add_path(self.input_layer, [input_offset, output_offset]) def add_pins(self): """ Add the module pins """ @@ -363,19 +334,19 @@ class hierarchical_decoder(design.design): if (self.num_inputs == 2): base = vector(-self.pre2_4.width, 0) else: - base= vector(-self.pre2_4.width, num * self.pre2_4.height) + base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing)) - self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0)) + self.pre2x4_inst[num].place(base) def place_pre3x8(self, num): """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ if (self.num_inputs == 3): offset = vector(-self.pre_3_8.width, 0) else: - height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height + height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) + num * (self.pre3_8.height + self.predecoder_spacing) offset = vector(-self.pre3_8.width, height) - self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0)) + self.pre3x8_inst[num].place(offset) def create_row_decoder(self): """ Create the row-decoder by placing AND2/AND3 and Inverters @@ -431,7 +402,6 @@ class hierarchical_decoder(design.design): if (self.num_inputs >= 4): self.place_decoder_and_array() - def place_decoder_and_array(self): """ Add a column of AND gates for final decode. @@ -452,9 +422,7 @@ class hierarchical_decoder(design.design): Add a column of AND gates for the decoder above the predecoders. """ - for inst_index in range(self.num_outputs): - row = math.floor(inst_index / self.decoders_per_row) - dec = inst_index % self.decoders_per_row + for row in range(self.num_outputs): if ((row % 2) == 0): y_off = and_mod.height * row mirror = "R0" @@ -462,32 +430,16 @@ class hierarchical_decoder(design.design): y_off = and_mod.height * (row + 1) mirror = "MX" - x_off = self.internal_routing_width + dec * and_mod.width - self.and_inst[inst_index].place(offset=vector(x_off, y_off), - mirror=mirror) + x_off = self.internal_routing_width + self.and_inst[row].place(offset=vector(x_off, y_off), + mirror=mirror) def route_outputs(self): """ Add the pins. """ - max_xoffset = max(x.rx() for x in self.and_inst) - - for output_index in range(self.num_outputs): - row_remainder = (output_index % self.decoders_per_row) - - and_inst = self.and_inst[output_index] - z_pin = and_inst.get_pin("Z") - if row_remainder == 0 and self.decoders_per_row > 1: - layer = "m3" - self.add_via_stack_center(from_layer=z_pin.layer, - to_layer="m3", - offset=z_pin.center()) - else: - layer = z_pin.layer - - self.add_layout_pin_segment_center(text="decode_{0}".format(output_index), - layer=layer, - start=z_pin.center(), - end=vector(max_xoffset, z_pin.cy())) + for row in range(self.num_outputs): + and_inst = self.and_inst[row] + self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row)) def route_decoder_bus(self): """ @@ -498,9 +450,9 @@ class hierarchical_decoder(design.design): if (self.num_inputs >= 4): # This leaves an offset for the predecoder output jogs input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)] - self.predecode_bus = self.create_vertical_pin_bus(layer="m2", - pitch=self.decoder_bus_pitch, - offset=vector(0, 0), + self.predecode_bus = self.create_vertical_pin_bus(layer=self.bus_layer, + pitch=self.bus_pitch, + offset=vector(self.bus_pitch, 0), names=input_bus_names, length=self.height) @@ -518,8 +470,9 @@ 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) - x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch - self.route_predecode_bus_inputs(predecode_name, pin, x_offset) + x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch + y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height + self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) # FIXME: convert to connect_bus for pre_num in range(self.no_of_pre3x8): @@ -527,8 +480,9 @@ 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) - x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch - self.route_predecode_bus_inputs(predecode_name, pin, x_offset) + x_offset = self.pre3x8_inst[pre_num].rx() + self.output_layer_pitch + y_offset = self.pre3x8_inst[pre_num].by() + i * self.cell_height + self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) def route_bus_to_decoder(self): """ @@ -542,12 +496,6 @@ class hierarchical_decoder(design.design): and the 128th AND3 is connected to [3,7,15] """ output_index = 0 - - if "li" in layer: - self.decoder_layers = [self.m1_stack, self.m2_stack[::-1]] - else: - self.decoder_layers = [self.m2_stack[::-1]] - debug.check(self.decoders_per_row <= len(self.decoder_layers), "Must have more layers for multi-height decoder.") if (self.num_inputs == 4 or self.num_inputs == 5): for index_B in self.predec_groups[1]: @@ -557,13 +505,11 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(index_A) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("A"), - output_index, - 0) + output_index) predecode_name = "predecode_{}".format(index_B) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("B"), - output_index, - 1) + output_index) output_index = output_index + 1 elif (self.num_inputs > 5): @@ -575,18 +521,15 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(index_A) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("A"), - output_index, - 0) + output_index) predecode_name = "predecode_{}".format(index_B) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("B"), - output_index, - 1) + output_index) predecode_name = "predecode_{}".format(index_C) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("C"), - output_index, - 2) + output_index) output_index = output_index + 1 def route_vdd_gnd(self): @@ -594,90 +537,90 @@ class hierarchical_decoder(design.design): Add a pin for each row of vdd/gnd which are must-connects next level up. """ + + if OPTS.tech_name == "s8": + for n in ["vdd", "gnd"]: + pins = self.and_inst[0].get_pins(n) + for pin in pins: + self.add_rect(layer=pin.layer, + offset=pin.ll() + vector(0, self.bus_space), + width=pin.width(), + height=self.height - 2 * self.bus_space) - # The vias will be placed at the right of the cells. - xoffset = max(x.rx() for x in self.and_inst) - for num in range(0, self.num_outputs): - # Only add the power pin for the 1st in each row - if num % self.decoders_per_row: - continue - - for pin_name in ["vdd", "gnd"]: - # The nand and inv are the same height rows... - supply_pin = self.and_inst[num].get_pin(pin_name) - pin_pos = vector(xoffset, supply_pin.cy()) - self.add_path(supply_pin.layer, - [supply_pin.lc(), vector(xoffset, supply_pin.cy())]) - self.add_power_pin(name=pin_name, - loc=pin_pos, - start_layer=supply_pin.layer) - - # 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") + # This adds power vias at the top of each cell + # (except the last to keep them inside the boundary) + for i in self.and_inst[:-1]: + pins = i.get_pins(n) + for pin in pins: + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + + for i in self.pre2x4_inst + self.pre3x8_inst: + self.copy_layout_pin(i, n) + else: + # The vias will be placed at the right of the cells. + xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space + for row in range(0, self.num_outputs): + for pin_name in ["vdd", "gnd"]: + # The nand and inv are the same height rows... + supply_pin = self.and_inst[row].get_pin(pin_name) + pin_pos = vector(xoffset, supply_pin.cy()) + self.add_power_pin(name=pin_name, + loc=pin_pos, + start_layer=supply_pin.layer) + + # Copy the pins from the predecoders + for pre in self.pre2x4_inst + self.pre3x8_inst: + for pin_name in ["vdd", "gnd"]: + self.copy_layout_pin(pre, pin_name) - def route_predecode_bus_outputs(self, rail_name, pin, output_index, pin_index): + def route_predecode_bus_outputs(self, rail_name, pin, row): """ Connect the routing rail to the given metal1 pin using a routing track at the given y_offset """ - row_index = math.floor(output_index / self.decoders_per_row) - row_remainder = (output_index % self.decoders_per_row) - row_offset = row_index * self.and_inst[0].height - pin_pos = pin.center() - - # y_offset is the same for both the M2 and M4 routes so that the rail - # contacts align and don't cause problems - if pin_index == 0: - # Bottom pitch - y_offset = row_offset - elif pin_index == 1: - # One pitch from top - y_offset = row_offset + self.and_inst[0].height - self.m3_pitch - elif pin_index == 2: - # One pitch from bottom - y_offset = row_offset + self.m3_pitch - else: - debug.error("Invalid decoder pitch.") - - rail_pos = vector(self.predecode_bus[rail_name].x, y_offset) - mid_pos = vector(pin_pos.x, rail_pos.y) - self.add_wire(self.decoder_layers[row_remainder], [rail_pos, mid_pos, pin_pos]) + rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) + self.add_path(self.input_layer, [rail_pos, pin_pos]) - self.add_via_stack_center(from_layer="m2", - to_layer=self.decoder_layers[row_remainder][0], - offset=rail_pos) + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.input_layer, + offset=rail_pos, + directions=self.bus_directions) self.add_via_stack_center(from_layer=pin.layer, - to_layer=self.decoder_layers[row_remainder][2], + to_layer=self.input_layer, offset=pin_pos, directions=("H", "H")) - def route_predecode_bus_inputs(self, rail_name, pin, x_offset): + def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset): """ Connect the routing rail to the given metal1 pin using a jog to the right of the cell at the given x_offset. """ # This routes the pin up to the rail, basically, to avoid conflicts. # It would be fixed with a channel router. - # pin_pos = pin.center() - # mid_point1 = vector(x_offset, pin_pos.y) - # mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2) - # rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y) - # self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos]) + pin_pos = pin.rc() + mid_point1 = vector(x_offset, pin_pos.y) + mid_point2 = vector(x_offset, y_offset) + rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y) + self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos]) - pin_pos = pin.center() - rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) - self.add_path("m1", [pin_pos, rail_pos]) + # pin_pos = pin.center() + # rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) + # self.add_path(self.output_layer, [pin_pos, rail_pos]) self.add_via_stack_center(from_layer=pin.layer, - to_layer="m1", + to_layer=self.output_layer, offset=pin_pos) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", - offset=rail_pos) + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.output_layer, + offset=rail_pos, + directions=self.bus_directions) def input_load(self): if self.determine_predecodes(self.num_inputs)[1]==0: diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 9a658546..8244028e 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -8,29 +8,24 @@ import debug import design import math -import contact from vector import vector from sram_factory import factory -from tech import cell_properties +from globals import OPTS class hierarchical_predecode(design.design): """ - Pre 2x4 and 3x8 decoder shared code. + Pre 2x4 and 3x8 and TBD 4x16 decoder shared code. """ def __init__(self, name, input_number, height=None): self.number_of_inputs = input_number if not height: b = factory.create(module_type="bitcell") - try: - self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple - except AttributeError: - self.cell_multiple = 1 - self.cell_height = self.cell_multiple * b.height + self.cell_height = b.height else: self.cell_height = height - + self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) design.design.__init__(self, name) @@ -44,34 +39,73 @@ class hierarchical_predecode(design.design): def add_modules(self): """ 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_and(self.number_of_inputs) - self.add_mod(self.and_mod) - def add_and(self, inputs): - """ Create the NAND for the predecode input stage """ - if inputs==2: - self.and_mod = factory.create(module_type="pand2", + # FIXME: Default parms are required for hard cells for now. + if self.number_of_inputs == 2: + self.and_mod = factory.create(module_type="pand2_dec", height=self.cell_height) - elif inputs==3: - self.and_mod = factory.create(module_type="pand3", + elif self.number_of_inputs == 3: + self.and_mod = factory.create(module_type="pand3_dec", + height=self.cell_height) + elif self.number_of_inputs == 4: + self.and_mod = factory.create(module_type="pand4_dec", height=self.cell_height) else: - debug.error("Invalid number of predecode inputs: {}".format(inputs), -1) - + debug.error("Invalid number of predecode inputs: {}".format(self.number_of_inputs), -1) + self.add_mod(self.and_mod) + + # This uses the pinv_dec parameterized cell + self.inv = factory.create(module_type="inv_dec", + height=self.cell_height, + size=1) + self.add_mod(self.inv) + + + 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 AND gates for inversion + """ + self.setup_layout_constraints() + self.route_rails() + self.place_input_inverters() + self.place_and_array() + self.route() + self.add_boundary() + self.DRC_LVS() + def setup_layout_constraints(self): + # Inputs to cells are on input layer + # Outputs from cells are on output layer + if OPTS.tech_name == "s8": + self.bus_layer = "m1" + self.bus_directions = None + self.bus_pitch = self.m1_pitch + self.bus_space = 1.5 * self.m1_space + self.input_layer = "li" + self.output_layer = "m2" + self.output_layer_pitch = self.m2_pitch + else: + self.bus_layer = "m2" + self.bus_directions = None + self.bus_pitch = self.m2_pitch + self.bus_space = self.m2_space + # This requires a special jog to ensure to conflicts with the output layers + self.input_layer = "m1" + self.output_layer = "m1" + self.output_layer_pitch = self.m1_pitch + 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 + self.m2_space + # +1 input for spacing for supply rail contacts + self.x_off_inv_1 = (self.number_of_inputs + 1) * self.bus_pitch + self.bus_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 AND decoder includes the left rails, mid rails and inverters, plus two extra bus pitches + self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.bus_pitch # x offset to output inverters self.width = self.x_off_and + self.and_mod.width @@ -79,28 +113,30 @@ class hierarchical_predecode(design.design): 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, self.m3_pitch) - self.input_rails = self.create_vertical_pin_bus(layer="m2", - offset=offset, - names=input_names, - length=self.height - 2 * self.m1_pitch) + # Offsets for the perimeter spacing to other modules + # This uses m3 pitch to leave space for power routes + offset = vector(self.bus_pitch, self.bus_pitch) + self.input_rails = self.create_vertical_bus(layer=self.bus_layer, + offset=offset, + names=input_names, + length=self.height - 2 * self.bus_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, self.m3_pitch) - self.decode_rails = self.create_vertical_bus(layer="m2", + offset = vector(self.x_off_inv_1 + self.inv.width + self.bus_pitch, self.bus_pitch) + self.decode_rails = self.create_vertical_bus(layer=self.bus_layer, offset=offset, names=decode_names, - length=self.height - 2 * self.m1_pitch) + length=self.height - 2 * self.bus_pitch) def create_input_inverters(self): """ Create the input inverters to invert input signals for the decode stage. """ - self.in_inst = [] + self.inv_inst = [] for inv_num in range(self.number_of_inputs): name = "pre_inv_{0}".format(inv_num) - self.in_inst.append(self.add_inst(name=name, - mod=self.inv)) + self.inv_inst.append(self.add_inst(name=name, + mod=self.inv)) self.connect_inst(["in_{0}".format(inv_num), "inbar_{0}".format(inv_num), "vdd", "gnd"]) @@ -108,6 +144,7 @@ class hierarchical_predecode(design.design): def place_input_inverters(self): """ Place the input inverters to invert input signals for the decode stage. """ for inv_num in range(self.number_of_inputs): + if (inv_num % 2 == 0): y_off = inv_num * (self.inv.height) mirror = "R0" @@ -115,8 +152,8 @@ class hierarchical_predecode(design.design): y_off = (inv_num + 1) * (self.inv.height) mirror="MX" offset = vector(self.x_off_inv_1, y_off) - self.in_inst[inv_num].place(offset=offset, - mirror=mirror) + self.inv_inst[inv_num].place(offset=offset, + mirror=mirror) def create_and_array(self, connections): """ Create the AND stage for the decodes """ @@ -151,21 +188,30 @@ class hierarchical_predecode(design.design): def route_inputs_to_rails(self): """ Route the uninverted inputs to the second set of rails """ + + top_and_gate = self.and_inst[-1] 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 and gates. - y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m2_via.width + self.m2_space + if num == 0: + pin = top_and_gate.get_pin("A") + elif num == 1: + pin = top_and_gate.get_pin("B") + elif num == 2: + pin = top_and_gate.get_pin("C") + elif num == 3: + pin = top_and_gate.get_pin("D") + else: + debug.error("Too many inputs for predecoder.", -1) + y_offset = pin.cy() 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_stack_center(from_layer="m1", - to_layer="m2", + self.add_path(self.input_layer, [in_pos, a_pos]) + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, offset=[self.input_rails[in_pin].x, y_offset]) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, offset=[self.decode_rails[a_pin].x, y_offset]) def route_output_and(self): @@ -188,31 +234,47 @@ class hierarchical_predecode(design.design): for inv_num in range(self.number_of_inputs): out_pin = "Abar_{}".format(inv_num) in_pin = "in_{}".format(inv_num) + + inv_out_pin = self.inv_inst[inv_num].get_pin("Z") + inv_out_pos = inv_out_pin.rc() # 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 and gates. - y_offset = (inv_num + 1) * self.inv.height - 3 * self.m1_space - inv_out_pin = self.in_inst[inv_num].get_pin("Z") - inv_out_pos = inv_out_pin.rc() - right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(), 0) - rail_pos = vector(self.decode_rails[out_pin].x, y_offset) - self.add_path(inv_out_pin.layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) + if OPTS.tech_name == "s8": + rail_pos = vector(self.decode_rails[out_pin].x, inv_out_pos.y) + self.add_path(self.output_layer, [inv_out_pos, rail_pos]) + else: + y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch + right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0) + rail_pos = vector(self.decode_rails[out_pin].x, y_offset) + self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) + self.add_via_stack_center(from_layer=inv_out_pin.layer, - to_layer="m2", - offset=rail_pos) + to_layer=self.output_layer, + offset=inv_out_pos) + self.add_via_stack_center(from_layer=self.output_layer, + to_layer=self.bus_layer, + offset=rail_pos, + directions=self.bus_directions) # route input - pin = self.in_inst[inv_num].get_pin("A") - inv_in_pos = pin.lc() + pin = self.inv_inst[inv_num].get_pin("A") + inv_in_pos = pin.center() in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y) - self.add_path("m1", [in_pos, inv_in_pos]) + self.add_path(self.input_layer, [in_pos, inv_in_pos]) self.add_via_stack_center(from_layer=pin.layer, - to_layer="m1", + to_layer=self.input_layer, offset=inv_in_pos) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", - offset=in_pos) + via=self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, + offset=in_pos) + # Create the input pin at this location on the rail + self.add_layout_pin_rect_center(text=in_pin, + layer=self.bus_layer, + offset=in_pos, + height=via.mod.second_layer_height, + width=via.mod.second_layer_width) def route_and_to_rails(self): # This 2D array defines the connection mapping @@ -231,43 +293,67 @@ class hierarchical_predecode(design.design): pin = self.and_inst[k].get_pin(gate_pin) pin_pos = pin.center() rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) - self.add_path("m1", [rail_pos, pin_pos]) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", - offset=rail_pos) + self.add_path(self.input_layer, [rail_pos, pin_pos]) + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, + offset=rail_pos, + directions=self.bus_directions) if gate_pin == "A": direction = None else: direction = ("H", "H") self.add_via_stack_center(from_layer=pin.layer, - to_layer="m1", + to_layer=self.input_layer, offset=pin_pos, directions=direction) 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.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 + # In s8, we use hand-made decoder cells with vertical power + if OPTS.tech_name == "s8": for n in ["vdd", "gnd"]: - and_pin = self.and_inst[num].get_pin(n) - supply_offset = and_pin.ll().scale(0, 1) - self.add_rect(layer=and_pin.layer, - offset=supply_offset, - width=self.and_inst[num].rx()) + # This makes a wire from top to bottom for both inv and and gates + for i in [self.inv_inst, self.and_inst]: + bot_pins = i[0].get_pins(n) + top_pins = i[-1].get_pins(n) + for (bot_pin, top_pin) in zip(bot_pins, top_pins): + self.add_rect(layer=bot_pin.layer, + offset=vector(bot_pin.lx(), self.bus_pitch), + width=bot_pin.width(), + height=top_pin.uy() - self.bus_pitch) + # This adds power vias at the top of each cell + # (except the last to keep them inside the boundary) + for i in self.inv_inst[:-1:2] + self.and_inst[:-1:2]: + pins = i.get_pins(n) + for pin in pins: + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + + # In other techs, we are using standard cell decoder cells with horizontal power + else: + for num in range(0, self.number_of_outputs): - # Add pins in two locations - for xoffset in [in_xoffset]: - pin_pos = vector(xoffset, and_pin.cy()) - self.add_power_pin(name=n, - loc=pin_pos, - start_layer=and_pin.layer) + # Route both supplies + for n in ["vdd", "gnd"]: + and_pins = self.and_inst[num].get_pins(n) + for and_pin in and_pins: + self.add_segment_center(layer=and_pin.layer, + start=vector(0, and_pin.cy()), + end=vector(self.width, and_pin.cy())) + + # Add pins in two locations + for xoffset in [self.inv_inst[0].lx() - self.bus_space, + self.and_inst[0].lx() - self.bus_space]: + pin_pos = vector(xoffset, and_pin.cy()) + self.add_power_pin(name=n, + loc=pin_pos, + start_layer=and_pin.layer) diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index b1aacc5a..9c7ddfa3 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -5,13 +5,10 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from tech import drc -import debug -import design -from vector import vector from hierarchical_predecode import hierarchical_predecode from globals import OPTS + class hierarchical_predecode2x4(hierarchical_predecode): """ Pre 2x4 decoder used in hierarchical_decoder. @@ -33,21 +30,6 @@ class hierarchical_predecode2x4(hierarchical_predecode): ["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 AND gates for inversion - """ - self.setup_layout_constraints() - self.route_rails() - self.place_input_inverters() - self.place_and_array() - self.route() - self.add_boundary() - self.DRC_LVS() - 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"], diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index 4f2294f1..e8c44e48 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -5,13 +5,10 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from tech import drc -import debug -import design -from vector import vector from hierarchical_predecode import hierarchical_predecode from globals import OPTS + class hierarchical_predecode3x8(hierarchical_predecode): """ Pre 3x8 decoder used in hierarchical_decoder. @@ -37,22 +34,6 @@ class hierarchical_predecode3x8(hierarchical_predecode): ["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 - 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs - 4) a set of NAND gates for inversion - """ - self.setup_layout_constraints() - self.route_rails() - self.place_input_inverters() - self.place_and_array() - self.route() - self.add_boundary() - self.DRC_LVS() - 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"], diff --git a/compiler/modules/hierarchical_predecode4x16.py b/compiler/modules/hierarchical_predecode4x16.py new file mode 100644 index 00000000..4a258bfb --- /dev/null +++ b/compiler/modules/hierarchical_predecode4x16.py @@ -0,0 +1,64 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from hierarchical_predecode import hierarchical_predecode +from globals import OPTS + + +class hierarchical_predecode4x16(hierarchical_predecode): + """ + Pre 4x16 decoder used in hierarchical_decoder. + """ + def __init__(self, name, height=None): + hierarchical_predecode.__init__(self, name, 4, height) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_input_inverters() + connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"], + ["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"], + ["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"], + ["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"], + ["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"], + ["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"], + ["inbar_0", "inbar_1", "inbar_2", "in_3", "out_0", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "in_3", "out_1", "vdd", "gnd"], + ["inbar_0", "in_1", "inbar_2", "in_3", "out_2", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "in_3", "out_3", "vdd", "gnd"], + ["inbar_0", "inbar_1", "in_2", "in_3", "out_4", "vdd", "gnd"], + ["in_0", "inbar_1", "in_2", "in_3", "out_5", "vdd", "gnd"], + ["inbar_0", "in_1", "in_2", "in_3", "out_6", "vdd", "gnd"], + ["in_0", "in_1", "in_2", "in_3", "out_7", "vdd", "gnd"] ] + + self.create_and_array(connections) + + 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", "Abar_2", "Abar_3"], + ["A_0", "Abar_1", "Abar_2", "Abar_3"], + ["Abar_0", "A_1", "Abar_2", "Abar_3"], + ["A_0", "A_1", "Abar_2", "Abar_3"], + ["Abar_0", "Abar_1", "A_2" , "Abar_3"], + ["A_0", "Abar_1", "A_2" , "Abar_3"], + ["Abar_0", "A_1", "A_2" , "Abar_3"], + ["A_0", "A_1", "A_2" , "Abar_3"], + ["Abar_0", "Abar_1", "Abar_2", "A_3"], + ["A_0", "Abar_1", "Abar_2", "A_3"], + ["Abar_0", "A_1", "Abar_2", "A_3"], + ["A_0", "A_1", "Abar_2", "A_3"], + ["Abar_0", "Abar_1", "A_2", "A_3"], + ["A_0", "Abar_1", "A_2", "A_3"], + ["Abar_0", "A_1", "A_2", "A_3"], + ["A_0", "A_1", "A_2", "A_3"]] + return combination diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index a27894ae..6293a79d 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -8,7 +8,7 @@ import debug import design from sram_factory import factory from vector import vector - +from tech import layer from globals import OPTS @@ -41,6 +41,10 @@ class port_address(design.design): self.create_wordline_driver() def create_layout(self): + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" self.place_instances() self.route_layout() self.DRC_LVS() @@ -85,11 +89,19 @@ class port_address(design.design): def route_internal(self): for row in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs - decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc() - driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc() - mid1 = decoder_out_pos.scale(0.5, 1) + driver_in_pos.scale(0.5, 0) - mid2 = decoder_out_pos.scale(0.5, 0) + driver_in_pos.scale(0.5, 1) - self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos]) + decoder_out_pin = self.row_decoder_inst.get_pin("decode_{}".format(row)) + decoder_out_pos = decoder_out_pin.rc() + driver_in_pin = self.wordline_driver_inst.get_pin("in_{}".format(row)) + driver_in_pos = driver_in_pin.lc() + self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos) + + self.add_via_stack_center(from_layer=decoder_out_pin.layer, + to_layer=self.route_layer, + offset=decoder_out_pos) + + self.add_via_stack_center(from_layer=driver_in_pin.layer, + to_layer=self.route_layer, + offset=driver_in_pos) def add_modules(self): @@ -97,7 +109,7 @@ class port_address(design.design): num_outputs=self.num_rows) self.add_mod(self.row_decoder) - self.wordline_driver = factory.create(module_type="wordline_driver", + self.wordline_driver = factory.create(module_type="wordline_driver_array", rows=self.num_rows, cols=self.num_cols) self.add_mod(self.wordline_driver) @@ -139,7 +151,6 @@ class port_address(design.design): row_decoder_offset = vector(0, 0) wordline_driver_offset = vector(self.row_decoder.width, 0) - self.wordline_driver_inst.place(wordline_driver_offset) self.row_decoder_inst.place(row_decoder_offset) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 91e20a45..8cc4e272 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -469,45 +469,37 @@ 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 """ + 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 + """ - inst1 = self.write_mask_and_array_inst - inst2 = self.write_driver_array_inst + wmask_inst = self.write_mask_and_array_inst + wdriver_inst = self.write_driver_array_inst - loc = 0 for bit in range(self.num_wmasks): # Bring write mask AND array output pin to port data level - self.copy_layout_pin(inst1, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit)) + self.copy_layout_pin(wmask_inst, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit)) - wmask_out_pin = inst1.get_pin("wmask_out_{0}".format(bit)) - wdriver_en_pin = inst2.get_pin("en_{0}".format(bit)) + wmask_out_pin = wmask_inst.get_pin("wmask_out_{0}".format(bit)) + wdriver_en_pin = wdriver_inst.get_pin("en_{0}".format(bit)) + + wmask_pos = wmask_out_pin.center() + wdriver_pos = wdriver_en_pin.rc() - vector(self.m2_pitch, 0) + mid_pos = vector(wdriver_pos.x, wmask_pos.y) - # The metal2 wdriver_sel_{} wire must hit the en_{} pin after the closest bitline pin that's right of the - # the wdriver_sel_{} pin in the write driver AND array. - if bit == 0: - # When the write mask output pin is right of the bitline, the target is found - 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.") - 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 - - beg_pos = wmask_out_pin.center() - middle_pos = vector(length, wmask_out_pin.cy()) - end_pos = vector(length, wdriver_en_pin.cy()) + # Add driver on mask output + self.add_via_center(layers=self.m1_stack, + offset=wmask_pos) # Add via for the write driver array's enable input self.add_via_center(layers=self.m1_stack, - offset=end_pos) + offset=wdriver_pos) # Route between write mask AND array and write driver array - self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos]) + self.add_wire(self.m1_stack, [wmask_pos, mid_pos, wdriver_pos]) def route_column_mux_to_precharge_array(self, port): """ Routing of BL and BR between col mux and precharge array """ @@ -516,15 +508,12 @@ class port_data(design.design): if self.col_addr_size==0: return - inst1 = self.column_mux_array_inst - inst2 = self.precharge_array_inst + start_bit = 1 if self.port == 0 else 0 - insn2_start_bit = 1 if self.port == 0 else 0 - - self.connect_bitlines(inst1=inst1, - inst2=inst2, + self.connect_bitlines(inst1=self.column_mux_array_inst, + inst2=self.precharge_array_inst, num_bits=self.num_cols, - inst2_start_bit=insn2_start_bit) + inst2_start_bit=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 """ diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver_array.py similarity index 50% rename from compiler/modules/wordline_driver.py rename to compiler/modules/wordline_driver_array.py index 55f5e707..e3d7c4f9 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver_array.py @@ -7,15 +7,12 @@ # import debug import design -import math -from tech import drc +from tech import drc, layer from vector import vector from sram_factory import factory from globals import OPTS -from tech import cell_properties - -class wordline_driver(design.design): +class wordline_driver_array(design.design): """ Creates a Wordline Driver Generates the wordline-driver to drive the bitcell @@ -26,21 +23,9 @@ class wordline_driver(design.design): debug.info(1, "Creating {0}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) - self.bitcell_rows = rows - self.bitcell_cols = cols + self.rows = rows + self.cols = cols - b = factory.create(module_type="bitcell") - try: - self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple - except AttributeError: - self.cell_multiple = 1 - self.cell_height = self.cell_multiple * b.height - - # We may have more than one bitcell per decoder row - self.num_rows = math.ceil(self.bitcell_rows / self.cell_multiple) - # We will place this many final decoders per row - self.decoders_per_row = math.ceil(self.bitcell_rows / self.num_rows) - self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -51,7 +36,10 @@ class wordline_driver(design.design): self.create_drivers() def create_layout(self): - self.setup_layout_constants() + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" self.place_drivers() self.route_layout() self.route_vdd_gnd() @@ -61,104 +49,99 @@ class wordline_driver(design.design): def add_pins(self): # inputs to wordline_driver. - for i in range(self.bitcell_rows): + for i in range(self.rows): self.add_pin("in_{0}".format(i), "INPUT") # Outputs from wordline_driver. - for i in range(self.bitcell_rows): + for i in range(self.rows): self.add_pin("wl_{0}".format(i), "OUTPUT") self.add_pin("en", "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") def add_modules(self): - self.and2 = factory.create(module_type="pand2", - height=self.cell_height, - size=self.bitcell_cols) - self.add_mod(self.and2) + self.wl_driver = factory.create(module_type="wordline_driver", + size=self.cols) + self.add_mod(self.wl_driver) 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 - xoffset_list = [self.and_inst[0].lx()] - for num in range(self.bitcell_rows): - # this will result in duplicate polygons for rails, but who cares - - # use the inverter offset even though it will be the and's too - (gate_offset, y_dir) = self.get_gate_offset(0, - self.and2.height, - num) - # Route both supplies + if OPTS.tech_name == "s8": for name in ["vdd", "gnd"]: - supply_pin = self.and_inst[num].get_pin(name) - - # Add pins in two locations - for xoffset in xoffset_list: - pin_pos = vector(xoffset, supply_pin.cy()) - self.add_power_pin(name, pin_pos) + supply_pins = self.wld_inst[0].get_pins(name) + for pin in supply_pins: + self.add_layout_pin_segment_center(text=name, + layer=pin.layer, + start=pin.bc(), + end=vector(pin.cx(), self.height)) + else: + # Find the x offsets for where the vias/pins should be placed + xoffset_list = [self.wld_inst[0].rx()] + for num in range(self.rows): + # this will result in duplicate polygons for rails, but who cares + + # use the inverter offset even though it will be the and's too + (gate_offset, y_dir) = self.get_gate_offset(0, + self.wl_driver.height, + num) + # Route both supplies + for name in ["vdd", "gnd"]: + supply_pin = self.wld_inst[num].get_pin(name) + + # Add pins in two locations + for xoffset in xoffset_list: + pin_pos = vector(xoffset, supply_pin.cy()) + self.add_power_pin(name, pin_pos) def create_drivers(self): - self.and_inst = [] - for row in range(self.bitcell_rows): + self.wld_inst = [] + for row in range(self.rows): name_and = "wl_driver_and{}".format(row) # add and2 - self.and_inst.append(self.add_inst(name=name_and, - mod=self.and2)) + self.wld_inst.append(self.add_inst(name=name_and, + mod=self.wl_driver)) self.connect_inst(["in_{0}".format(row), "en", "wl_{0}".format(row), "vdd", "gnd"]) - def setup_layout_constants(self): - # We may have more than one bitcell per decoder row - self.driver_rows = math.ceil(self.bitcell_rows / self.cell_multiple) - # We will place this many final decoders per row - self.decoders_per_row = math.ceil(self.bitcell_rows / self.driver_rows) - def place_drivers(self): + for row in range(self.rows): + if (row % 2): + y_offset = self.wl_driver.height * (row + 1) + inst_mirror = "MX" + else: + y_offset = self.wl_driver.height * row + inst_mirror = "R0" + + and2_offset = [self.wl_driver.width, y_offset] + + # add and2 + self.wld_inst[row].place(offset=and2_offset, + mirror=inst_mirror) + # Leave a well gap to separate the bitcell array well from this well well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") - - self.width = self.decoders_per_row * self.and2.width + well_gap - self.height = self.and2.height * self.driver_rows - - for inst_index in range(self.bitcell_rows): - row = math.floor(inst_index / self.decoders_per_row) - dec = inst_index % self.decoders_per_row - - if (row % 2): - y_offset = self.and2.height * (row + 1) - inst_mirror = "MX" - else: - y_offset = self.and2.height * row - inst_mirror = "R0" - - x_offset = dec * self.and2.width - and2_offset = [x_offset, y_offset] - - # add and2 - self.and_inst[inst_index].place(offset=and2_offset, - mirror=inst_mirror) + self.width = self.wl_driver.width + well_gap + self.height = self.wl_driver.height * self.rows def route_layout(self): """ Route all of the signals """ # Wordline enable connection - en_pin = self.and_inst[0].get_pin("B") + en_pin = self.wld_inst[0].get_pin("B") en_bottom_pos = vector(en_pin.lx(), 0) en_pin = self.add_layout_pin(text="en", layer="m2", offset=en_bottom_pos, height=self.height) - for inst_index in range(self.bitcell_rows): - and_inst = self.and_inst[inst_index] - row = math.floor(inst_index / self.decoders_per_row) + for row in range(self.rows): + and_inst = self.wld_inst[row] # Drop a via b_pin = and_inst.get_pin("B") @@ -167,18 +150,12 @@ class wordline_driver(design.design): offset=b_pin.center()) # connect the decoder input pin to and2 A - a_pin = and_inst.get_pin("A") - a_pos = a_pin.center() - # must under the clk line in M1 - self.add_layout_pin_segment_center(text="in_{0}".format(row), - layer="m1", - start=vector(0, a_pos.y), - end=a_pos) + self.copy_layout_pin(and_inst, "A", "in_{0}".format(row)) # output each WL on the right wl_offset = and_inst.get_pin("Z").rc() self.add_layout_pin_segment_center(text="wl_{0}".format(row), - layer="m1", + layer=self.route_layer, start=wl_offset, end=wl_offset - vector(self.m1_width, 0)) @@ -189,7 +166,7 @@ class wordline_driver(design.design): """ stage_effort_list = [] - stage1 = self.and2.get_stage_effort(external_cout, inp_is_rise) + stage1 = self.wl_driver.get_stage_effort(external_cout, inp_is_rise) stage_effort_list.append(stage1) return stage_effort_list @@ -200,5 +177,5 @@ class wordline_driver(design.design): the enable connections in the bank """ # The enable is connected to a and2 for every row. - total_cin = self.and2.get_cin() * self.rows + total_cin = self.wl_driver.get_cin() * self.rows return total_cin diff --git a/compiler/options.py b/compiler/options.py index d891ebd9..1c48d63d 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -134,6 +134,10 @@ class options(optparse.Values): dff_array = "dff_array" dff = "dff" dummy_bitcell = "dummy_bitcell" + inv_dec = "pinv" + nand2_dec = "pnand2" + nand3_dec = "pnand3" + nand4_dec = "pnand4" # Not available right now precharge_array = "precharge_array" ptx = "ptx" replica_bitcell = "replica_bitcell" diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 41189876..435ace1f 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -13,7 +13,7 @@ from sram_factory import factory class pand2(pgate.pgate): """ - This is a simple buffer used for driving loads. + This is an AND (or NAND) with configurable drive strength. """ def __init__(self, name, size=1, height=None, vertical=False, add_wells=True): debug.info(1, "Creating pand2 {}".format(name)) diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 266400c9..578a11c4 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -17,13 +17,13 @@ class pdriver(pgate.pgate): sized for driving a load. """ - def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None, add_wells=True): + def __init__(self, name, inverting=False, fanout=0, size_list=None, height=None, add_wells=True): debug.info(1, "creating pdriver {}".format(name)) self.stage_effort = 3 self.height = height - self.neg_polarity = neg_polarity + self.inverting = inverting self.size_list = size_list self.fanout = fanout @@ -31,8 +31,8 @@ class pdriver(pgate.pgate): debug.error("Either fanout or size list must be specified.", -1) if self.size_list and self.fanout != 0: debug.error("Cannot specify both size_list and fanout.", -1) - if self.size_list and self.neg_polarity: - debug.error("Cannot specify both size_list and neg_polarity.", -1) + if self.size_list and self.inverting: + debug.error("Cannot specify both size_list and inverting.", -1) # Creates the netlist and layout pgate.pgate.__init__(self, name, height, add_wells) @@ -47,9 +47,9 @@ class pdriver(pgate.pgate): int(round(self.fanout ** (1 / self.stage_effort)))) # Increase the number of stages if we need to fix polarity - if self.neg_polarity and (self.num_stages % 2 == 0): + if self.inverting and (self.num_stages % 2 == 0): self.num_stages += 1 - elif not self.neg_polarity and (self.num_stages % 2): + elif not self.inverting and (self.num_stages % 2): self.num_stages += 1 self.size_list = [] diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index cdb89fcb..a8c45641 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -44,7 +44,6 @@ class pgate(design.design): self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer)) # This is the space from a S/D contact to the supply rail - # Assume the contact starts at the active edge contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space # This is a poly-to-poly of a flipped cell poly_to_poly_gate_space = self.poly_extend_active + self.poly_space diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py new file mode 100644 index 00000000..efc21074 --- /dev/null +++ b/compiler/pgates/pinv_dec.py @@ -0,0 +1,217 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import contact +import pinv +import debug +from tech import drc, parameter +from vector import vector +from globals import OPTS +from sram_factory import factory + +if(OPTS.tech_name == "s8"): + from tech import nmos_bins, pmos_bins, accuracy_requirement + + +class pinv_dec(pinv.pinv): + """ + This is another version of pinv but with layout for the decoder. + Other stuff is the same (netlist, sizes, etc.) + """ + + def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True): + + debug.info(2, + "creating pinv_dec structure {0} with size of {1}".format(name, + size)) + if not height: + b = factory.create(module_type="bitcell") + self.cell_height = b.height + else: + self.cell_height = height + + # Inputs to cells are on input layer + # Outputs from cells are on output layer + if OPTS.tech_name == "s8": + self.supply_layer = "m1" + else: + self.supply_layer = "m2" + + pinv.pinv.__init__(self, name, size, beta, self.cell_height, add_wells) + + def determine_tx_mults(self): + """ + Determines the number of fingers needed to achieve the size within + the height constraint. This may fail if the user has a tight height. + """ + + # This is always 1 tx, because we have horizontal transistors. + self.tx_mults = 1 + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") + if OPTS.tech_name == "s8": + (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) + (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) + return + + # Over-ride the route input gate to call the horizontal version. + # Other top-level netlist and layout functions are not changed. + def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None): + """ + Route the input gate to the left side of the cell for access. + Position is actually ignored and is left to be compatible with the pinv. + """ + + nmos_gate_pin = nmos_inst.get_pin("G") + pmos_gate_pin = pmos_inst.get_pin("G") + + # Check if the gates are aligned and give an error if they aren't! + if nmos_gate_pin.ll().y != pmos_gate_pin.ll().y: + self.gds_write("unaliged_gates.gds") + debug.check(nmos_gate_pin.ll().y == pmos_gate_pin.ll().y, + "Connecting unaligned gates not supported. See unaligned_gates.gds.") + + # Pick point on the left of NMOS and up to PMOS + nmos_gate_pos = nmos_gate_pin.rc() + pmos_gate_pos = pmos_gate_pin.lc() + self.add_path("poly", [nmos_gate_pos, pmos_gate_pos]) + + # Center is completely symmetric. + contact_width = contact.poly_contact.width + contact_offset = nmos_gate_pin.lc() \ + - vector(self.poly_extend_active + 0.5 * contact_width, 0) + via = self.add_via_stack_center(from_layer="poly", + to_layer=self.route_layer, + offset=contact_offset, + directions=directions) + self.add_path("poly", [contact_offset, nmos_gate_pin.lc()]) + + self.add_layout_pin_rect_center(text=name, + layer=self.route_layer, + offset=contact_offset, + width=via.mod.second_layer_width, + height=via.mod.second_layer_height) + + def determine_width(self): + self.width = self.pmos_inst.rx() + self.well_extend_active + + def extend_wells(self): + """ Extend bottom to top for each well. """ + + from tech import layer + if "pwell" in layer: + ll = self.nmos_inst.ll() - self.nmos_inst.mod.active_offset + ur = self.nmos_inst.ur() + self.nmos_inst.mod.active_offset + self.add_rect(layer="pwell", + offset=ll, + width=ur.x - ll.x, + height=self.height - ll.y) + + if "nwell" in layer: + ll = self.pmos_inst.ll() - self.pmos_inst.mod.active_offset + ur = self.pmos_inst.ur() + self.pmos_inst.mod.active_offset + self.add_rect(layer="nwell", + offset=ll - vector(self.nwell_enclose_active, 0), + width=ur.x - ll.x + self.nwell_enclose_active, + height=self.height - ll.y + 2 * self.nwell_enclose_active) + + def place_ptx(self): + """ + """ + + # offset so that the input contact is over from the left edge by poly spacing + x_offset = self.nmos.active_offset.y + contact.poly_contact.width + self.poly_space + # center the transistor in the y-dimension + y_offset = self.nmos.width + self.active_space + self.nmos_pos = vector(x_offset, y_offset) + self.nmos_inst.place(self.nmos_pos) + self.nmos_inst.place(self.nmos_pos, + rotate=270) + # place PMOS so it is half a poly spacing down from the top + xoffset = self.nmos_inst.height + 2 * self.poly_extend_active + 2 * self.well_extend_active + drc("pwell_to_nwell") + self.pmos_pos = self.nmos_pos + vector(xoffset, 0) + self.pmos_inst.place(self.pmos_pos, + rotate=270) + + # Output position will be in between the PMOS and NMOS drains + pmos_drain_pos = self.pmos_inst.get_pin("D").center() + nmos_drain_pos = self.nmos_inst.get_pin("D").center() + self.output_pos = vector(0.5 * (pmos_drain_pos.x + nmos_drain_pos.x), nmos_drain_pos.y) + + def route_outputs(self): + """ + Route the output (drains) together. + Optionally, routes output to edge. + """ + + # Get the drain pin + nmos_drain_pin = self.nmos_inst.get_pin("D") + + # Pick point at right most of NMOS and connect over to PMOS + nmos_drain_pos = nmos_drain_pin.lc() + right_side = vector(self.width, nmos_drain_pos.y) + + self.add_layout_pin_segment_center("Z", + self.route_layer, + nmos_drain_pos, + right_side) + + def add_well_contacts(self): + """ Add n/p well taps to the layout and connect to supplies """ + + source_pos = self.pmos_inst.get_pin("S").center() + contact_pos = vector(source_pos.x, self.height) + self.nwell_contact = self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="n", + well_type="n") + self.add_via_stack_center(offset=contact_pos, + from_layer=self.active_stack[2], + to_layer=self.supply_layer) + + source_pos = self.nmos_inst.get_pin("S").center() + contact_pos = vector(source_pos.x, self.height) + self.pwell_contact= self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="p", + well_type="p") + self.add_via_stack_center(offset=contact_pos, + from_layer=self.active_stack[2], + to_layer=self.supply_layer) + + def route_supply_rails(self): + pin = self.nmos_inst.get_pin("S") + source_pos = pin.center() + bottom_pos = source_pos.scale(1, 0) + top_pos = bottom_pos + vector(0, self.height) + self.add_layout_pin_segment_center("gnd", + self.supply_layer, + start=bottom_pos, + end=top_pos) + + pin = self.pmos_inst.get_pin("S") + source_pos = pin.center() + bottom_pos = source_pos.scale(1, 0) + top_pos = bottom_pos + vector(0, self.height) + self.add_layout_pin_segment_center("vdd", + self.supply_layer, + start=bottom_pos, + end=top_pos) + + def connect_rails(self): + """ Connect the nmos and pmos to its respective power rails """ + + source_pos = self.nmos_inst.get_pin("S").center() + self.add_via_stack_center(offset=source_pos, + from_layer=self.route_layer, + to_layer=self.supply_layer) + + source_pos = self.pmos_inst.get_pin("S").center() + self.add_via_stack_center(offset=source_pos, + from_layer=self.route_layer, + to_layer=self.supply_layer) + diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 85b3f0b6..6cbd7cca 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -212,7 +212,10 @@ class pnand3(pgate.pgate): pmos_drain_bottom = self.pmos1_inst.get_pin("D").by() self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space + # This is a more compact offset, but the bottom one works better in the decoders to "center" the pins + # in the height of the gates self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space + # self.inputA_yoffset = self.output_yoffset - self.m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index c584e70b..f6716a1f 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -386,10 +386,12 @@ class ptx(design.design): well_ll = center_pos - vector(0.5 * self.well_width, 0.5 * self.well_height) if well_name in layer: - self.add_rect(layer=well_name, - offset=well_ll, - width=self.well_width, - height=self.well_height) + well = self.add_rect(layer=well_name, + offset=well_ll, + width=self.well_width, + height=self.well_height) + setattr(self, well_name, well) + if "vtg" in layer: self.add_rect(layer="vtg", offset=well_ll, diff --git a/compiler/pgates/wordline_driver.py b/compiler/pgates/wordline_driver.py new file mode 100644 index 00000000..1b035e20 --- /dev/null +++ b/compiler/pgates/wordline_driver.py @@ -0,0 +1,155 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from vector import vector +import design +from sram_factory import factory +from globals import OPTS +from tech import layer + + +class wordline_driver(design.design): + """ + This is an AND (or NAND) with configurable drive strength to drive the wordlines. + It is matched to the bitcell height. + """ + def __init__(self, name, size=1, height=None): + debug.info(1, "Creating wordline_driver {}".format(name)) + self.add_comment("size: {}".format(size)) + design.design.__init__(self, name) + + if height is None: + b = factory.create(module_type="bitcell") + self.height = b.height + else: + self.height = height + self.size = size + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand2_dec") + self.height = self.nand.height + else: + self.nand = factory.create(module_type="nand2_dec", + height=self.height) + + self.driver = factory.create(module_type="inv_dec", + size=self.size, + height=self.nand.height) + + self.add_mod(self.nand) + self.add_mod(self.driver) + + def create_layout(self): + self.width = self.nand.width + self.driver.width + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + + self.place_insts() + self.route_wires() + self.add_layout_pins() + self.route_supply_rails() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="wld_nand", + mod=self.nand) + self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) + + self.driver_inst = self.add_inst(name="wl_driver", + mod=self.driver) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) + + # Add INV to the right + self.driver_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.driver_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + + y_offset = self.height + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, y_offset), + width=self.width) + + def route_wires(self): + + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.driver_inst.get_pin("A") + if OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + + def add_layout_pins(self): + pin = self.driver_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A", "B"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.driver.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + + stage2 = self.driver.get_stage_effort(external_cout, stage1.is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 110dbe15..df6b9d99 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -77,7 +77,9 @@ class sram_factory: """ tech_module_type, tm_overridden = self.get_techmodule_type(module_type) user_module_type, um_overridden = self.get_usermodule_type(module_type) - + #print(module_type, tech_module_type, tm_overridden) + #print(module_type, user_module_type, um_overridden) + # overridden user modules have priority if um_overridden: real_module_type = user_module_type diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index 0acc8926..ed15770d 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -35,7 +35,7 @@ class library_lvs_test(openram_test): debug.error("Missing GDS file {}".format(gds_name)) if not os.path.isfile(sp_name): lvs_errors += 1 - debug.error("Missing SPICE file {}".format(gds_name)) + debug.error("Missing SPICE file {}".format(sp_name)) drc_errors += verify.run_drc(name, gds_name) lvs_errors += verify.run_lvs(f, gds_name, sp_name) diff --git a/compiler/tests/04_pand2_dec_test.py b/compiler/tests/04_pand2_dec_test.py new file mode 100755 index 00000000..92b80203 --- /dev/null +++ b/compiler/tests/04_pand2_dec_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class pand2_dec_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + global verify + import verify + + import pand2_dec + + debug.info(2, "Testing pand2 gate 4x") + a = pand2_dec.pand2_dec(name="pand2x4", size=4) + self.local_check(a) + + globals.end_openram() + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pand3_dec_test.py b/compiler/tests/04_pand3_dec_test.py new file mode 100755 index 00000000..78e576ed --- /dev/null +++ b/compiler/tests/04_pand3_dec_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class pand3_dec_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + global verify + import verify + + import pand3_dec + + debug.info(2, "Testing pand3 gate 4x") + a = pand3_dec.pand3_dec(name="pand3x4", size=4) + self.local_check(a) + + globals.end_openram() + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pand4_dec_test.py b/compiler/tests/04_pand4_dec_test.py new file mode 100755 index 00000000..78e576ed --- /dev/null +++ b/compiler/tests/04_pand4_dec_test.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class pand3_dec_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + global verify + import verify + + import pand3_dec + + debug.info(2, "Testing pand3 gate 4x") + a = pand3_dec.pand3_dec(name="pand3x4", size=4) + self.local_check(a) + + globals.end_openram() + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pdriver_test.py b/compiler/tests/04_pdriver_test.py index e65b6fad..41af86f6 100755 --- a/compiler/tests/04_pdriver_test.py +++ b/compiler/tests/04_pdriver_test.py @@ -32,13 +32,13 @@ class pdriver_test(openram_test): c = factory.create(module_type="pdriver", fanout = 50) self.local_check(c) - d = factory.create(module_type="pdriver", fanout = 50, neg_polarity = True) + d = factory.create(module_type="pdriver", fanout = 50, inverting = True) self.local_check(d) e = factory.create(module_type="pdriver", fanout = 64) self.local_check(e) - f = factory.create(module_type="pdriver", fanout = 64, neg_polarity = True) + f = factory.create(module_type="pdriver", fanout = 64, inverting = True) self.local_check(f) globals.end_openram() diff --git a/compiler/tests/04_pinv_dec_1x_test.py b/compiler/tests/04_pinv_dec_1x_test.py new file mode 100755 index 00000000..8876ab58 --- /dev/null +++ b/compiler/tests/04_pinv_dec_1x_test.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class pinv_dec_1x_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(2, "Checking 1x size decoder inverter") + tx = factory.create(module_type="pinv_dec", size=1) + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/04_wordline_driver_test.py similarity index 93% rename from compiler/tests/08_wordline_driver_test.py rename to compiler/tests/04_wordline_driver_test.py index 8a18a59d..ada65db7 100755 --- a/compiler/tests/08_wordline_driver_test.py +++ b/compiler/tests/04_wordline_driver_test.py @@ -25,7 +25,7 @@ class wordline_driver_test(openram_test): # check wordline driver for single port debug.info(2, "Checking driver") - tx = factory.create(module_type="wordline_driver", rows=8, cols=32) + tx = factory.create(module_type="wordline_driver") self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 07d995f9..7d155b01 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -21,11 +21,17 @@ class hierarchical_decoder_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + # Checks 2x4 and 2-input NAND decoder debug.info(1, "Testing 16 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) self.local_check(a) - + # Checks 2x4 and 2-input NAND decoder with non-power-of-two debug.info(1, "Testing 17 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=17) diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index ebb06330..94fd4838 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -21,7 +21,12 @@ class hierarchical_predecode2x4_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # checking hierarchical precode 2x4 for single port + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + debug.info(1, "Testing sample for hierarchy_predecode2x4") a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index 63acc416..33f94d9d 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -21,7 +21,12 @@ class hierarchical_predecode3x8_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # checking hierarchical precode 3x8 for single port + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + debug.info(1, "Testing sample for hierarchy_predecode3x8") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode4x16_test.py b/compiler/tests/06_hierarchical_predecode4x16_test.py new file mode 100755 index 00000000..b4ebda38 --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode4x16_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +@unittest.skip("SKIPPING hierarchical_predecode4x16_test") +class hierarchical_predecode4x16_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(1, "Testing sample for hierarchy_predecode4x16") + a = factory.create(module_type="hierarchical_predecode4x16") + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_wordline_driver_pbitcell_test.py b/compiler/tests/08_wordline_driver_array_pbitcell_test.py similarity index 87% rename from compiler/tests/08_wordline_driver_pbitcell_test.py rename to compiler/tests/08_wordline_driver_array_pbitcell_test.py index 3dd5933d..267aaddf 100755 --- a/compiler/tests/08_wordline_driver_pbitcell_test.py +++ b/compiler/tests/08_wordline_driver_array_pbitcell_test.py @@ -15,9 +15,8 @@ from globals import OPTS from sram_factory import factory import debug -#@unittest.skip("SKIPPING 04_driver_test") -class wordline_driver_pbitcell_test(openram_test): +class wordline_driver_array_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -31,7 +30,7 @@ class wordline_driver_pbitcell_test(openram_test): factory.reset() debug.info(2, "Checking driver (multi-port case)") - tx = factory.create(module_type="wordline_driver", rows=8, cols=64) + tx = factory.create(module_type="wordline_driver_array", rows=8, cols=64) self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/08_wordline_driver_array_test.py b/compiler/tests/08_wordline_driver_array_test.py new file mode 100755 index 00000000..3491cc4f --- /dev/null +++ b/compiler/tests/08_wordline_driver_array_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class wordline_driver_array_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check wordline driver for single port + debug.info(2, "Checking driver") + tx = factory.create(module_type="wordline_driver_array", rows=8, cols=32) + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index 6201de6a..e9e282a5 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -20,10 +20,10 @@ class port_data_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1w_1r" - OPTS.num_rw_ports = 0 + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 + OPTS.num_w_ports = 0 c = sram_config(word_size=4, num_words=16) diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index 38a847a9..6cff481b 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -25,28 +25,28 @@ class single_bank_test(openram_test): c = sram_config(word_size=4, num_words=16) - # c.words_per_row=1 - # factory.reset() - # c.recompute_sizes() - # debug.info(1, "No column mux") - # a = factory.create("bank", sram_config=c) - # self.local_check(a) + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) - # c.num_words=32 - # c.words_per_row=2 - # factory.reset() - # c.recompute_sizes() - # debug.info(1, "Two way column mux") - # a = factory.create("bank", sram_config=c) - # self.local_check(a) + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) - # c.num_words=64 - # c.words_per_row=4 - # factory.reset() - # c.recompute_sizes() - # debug.info(1, "Four way column mux") - # a = factory.create("bank", sram_config=c) - # self.local_check(a) + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) c.word_size=2 c.num_words=128 diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 99d534d0..4cae1a9c 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -60,6 +60,7 @@ class openram_test(unittest.TestCase): #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) debug.warning("DRC failed but LVS passed: {}".format(a.name)) + #self.fail("DRC failed but LVS passed: {}".format(a.name)) elif drc_result != 0: #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) From b00163e4e1e22883ed910826ed7ed5e880cc9088 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Fri, 29 May 2020 13:50:34 -0700 Subject: [PATCH 13/51] lvs fix for regression tests --- compiler/modules/replica_column.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 2e3d207e..4ea1b7df 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -114,7 +114,6 @@ class replica_column(design.design): mod=self.dummy_cell) self.connect_inst(self.get_bitcell_pins(0, row)) - def place_instances(self): from tech import cell_properties # Flip the mirrors if we have an odd number of replica+dummy rows at the bottom @@ -149,20 +148,30 @@ class replica_column(design.design): self.cell_inst[row].place(offset=offset, mirror=dir_key) - - def add_layout_pins(self): """ Add the layout pins """ for bl_name in self.cell.get_all_bitline_names(): - bl_pin = self.cell_inst[1].get_pin(bl_name) + bl_pin = self.cell_inst[0].get_pin(bl_name) self.add_layout_pin(text=bl_name, layer=bl_pin.layer, offset=bl_pin.ll(), width=bl_pin.width(), height=self.height) - for row in range(1, self.total_size - 1): + try: + end_caps_enabled = cell_properties.bitcell.end_caps + except AttributeError: + end_caps_enabled = False + + if end_caps_enabled: + row_range_max = self.total_size - 1 + row_range_min = 1 + else: + row_range_max = self.total_size + row_range_min = 0 + + for row in range(row_range_min, row_range_max): for wl_name in self.cell.get_all_wl_names(): wl_pin = self.cell_inst[row].get_pin(wl_name) self.add_layout_pin(text="{0}_{1}".format(wl_name,row), @@ -172,7 +181,7 @@ class replica_column(design.design): height=wl_pin.height()) # For every second row and column, add a via for gnd and vdd - for row in range(1, self.total_size - 1): + for row in range(row_range_min, row_range_max): inst = self.cell_inst[row] for pin_name in ["vdd", "gnd"]: self.copy_layout_pin(inst, pin_name) From 82dc937768e2e88fae741600d8c604348fd7093a Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 29 May 2020 16:53:47 -0700 Subject: [PATCH 14/51] Add missing vias by using via stack function --- compiler/modules/port_data.py | 74 ++++++++++++++++----------------- compiler/pgates/precharge.py | 77 +++++++++++++---------------------- 2 files changed, 65 insertions(+), 86 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 8cc4e272..44bb8ed8 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -534,11 +534,14 @@ class port_data(design.design): else: start_bit=0 - self.channel_route_bitlines(inst1=inst1, - inst1_bls_template=inst1_bls_templ, - inst2=inst2, - num_bits=self.word_size, - inst1_start_bit=start_bit) + # This could be a channel route, but in some techs the bitlines + # are too close together. + print("SA to precharge") + self.connect_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + 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 """ @@ -558,10 +561,13 @@ class port_data(design.design): else: start_bit=0 - self.channel_route_bitlines(inst1=inst1, inst2=inst2, - num_bits=self.word_size, - inst1_bls_template=inst1_bls_templ, - inst1_start_bit=start_bit) + # This could be a channel route, but in some techs the bitlines + # are too close together. + print("WD to precharge") + self.connect_bitlines(inst1=inst1, inst2=inst2, + num_bits=self.word_size, + 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 """ @@ -569,11 +575,12 @@ class port_data(design.design): inst1 = self.write_driver_array_inst inst2 = self.sense_amp_array_inst - # These should be pitch matched in the cell library, - # but just in case, do a channel route. - self.channel_route_bitlines(inst1=inst1, - inst2=inst2, - num_bits=self.word_size) + # This could be a channel route, but in some techs the bitlines + # are too close together. + print("WD to SA") + self.connect_bitlines(inst1=inst1, + inst2=inst2, + num_bits=self.word_size) def route_bitline_pins(self): """ Add the bitline pins for the given port """ @@ -676,10 +683,9 @@ class port_data(design.design): Route the bl and br of two modules using the channel router. """ - 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) + 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) # 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! @@ -688,13 +694,8 @@ class port_data(design.design): bottom_names = self._get_bitline_pins(bot_inst_group, bit) top_names = self._get_bitline_pins(top_inst_group, bit) - if bottom_names[0].layer == "m2": - bitline_dirs = ("H", "V") - elif bottom_names[0].layer == "m1": - bitline_dirs = ("V", "H") - route_map = list(zip(bottom_names, top_names)) - self.create_horizontal_channel_route(route_map, offset, self.m1_stack, bitline_dirs) + self.create_horizontal_channel_route(route_map, offset, self.m1_stack) def connect_bitlines(self, inst1, inst2, num_bits, inst1_bls_template="{inst}_{bit}", @@ -707,26 +708,23 @@ class port_data(design.design): in the middle between the two modules (if needed). """ - 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) + 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) for col in range(num_bits): + print(col) bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col) top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col) 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]) + + print("BL", bot_bl, top_bl) + print(bot_bl_pin, top_bl_pin) + print("BR", bot_br, top_br) + print(bot_br_pin, top_br_pin) + self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V") + self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V") def graph_exclude_precharge(self): """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index d47e445f..ae314452 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -105,21 +105,19 @@ class precharge(design.design): # center of vdd rail pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) - self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos]) + self.add_path(self.bitline_layer, [pmos_pin.center(), pmos_vdd_pos]) - # if enable is not on M1, the supply can be - if self.en_layer != "m1": - self.add_via_center(layers=self.m1_stack, - offset=pmos_vdd_pos) - + self.add_via_stack_center(from_layer=self.bitline_layer, + to_layer=self.en_layer, + offset=pmos_vdd_pos) self.add_power_pin("vdd", self.well_contact_pos, directions=("V", "V")) - - # Hack for li layers - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=self.well_contact_pos) + + self.add_via_stack_center(from_layer=pmos_pin.layer, + to_layer=self.bitline_layer, + offset=pmos_pin.center(), + directions=("V", "V")) def create_ptx(self): """ @@ -199,14 +197,9 @@ class precharge(design.design): # midway in the 4 M2 tracks offset = self.lower_pmos_inst.get_pin("G").ul() \ + vector(0, 0.5 * self.m2_pitch) - self.add_via_center(layers=self.poly_stack, - offset=offset) - if self.en_layer == "m2": - self.add_via_center(layers=self.m1_stack, - offset=offset) - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=offset) + self.add_via_stack_center(from_layer="poly", + to_layer=self.en_layer, + offset=offset) # adds the en rail on metal1 self.add_layout_pin_segment_center(text="en_bar", @@ -225,13 +218,13 @@ class precharge(design.design): self.nwell_extend_active self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \ vector(0, offset_height) - self.add_via_center(layers=self.active_stack, - offset=self.well_contact_pos, - implant_type="n", - well_type="n") - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=self.well_contact_pos) + self.well_contact = self.add_via_center(layers=self.active_stack, + offset=self.well_contact_pos, + implant_type="n", + well_type="n") + self.add_via_stack_center(from_layer=self.active_stack[2], + to_layer=self.bitline_layer, + offset=self.well_contact_pos) self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space @@ -288,31 +281,19 @@ class precharge(design.design): 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") - self.lower_via = self.add_via_center(layers=self.m1_stack, - offset=lower_pin.center(), - directions=("V", "V")) + for lower_pin in [self.lower_pmos_inst.get_pin("S"), self.lower_pmos_inst.get_pin("D")]: + self.add_via_stack_center(from_layer=lower_pin.layer, + to_layer=self.bitline_layer, + offset=lower_pin.center(), + 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")) - - 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")) + for upper_pin in [self.upper_pmos1_inst.get_pin("S"), self.upper_pmos2_inst.get_pin("D")]: + self.add_via_stack_center(from_layer=upper_pin.layer, + to_layer=self.bitline_layer, + offset=upper_pin.center(), + directions=("V", "V")) def connect_pmos(self, pmos_pin, bit_xoffset): """ From 496a24389c4e95f780a71a1f73959366a9744f4d Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 29 May 2020 16:57:47 -0700 Subject: [PATCH 15/51] Remove prints --- compiler/modules/port_data.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 44bb8ed8..d544b6be 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -536,7 +536,6 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - print("SA to precharge") self.connect_bitlines(inst1=inst1, inst1_bls_template=inst1_bls_templ, inst2=inst2, @@ -563,7 +562,6 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - print("WD to precharge") self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, inst1_bls_template=inst1_bls_templ, @@ -577,7 +575,6 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - print("WD to SA") self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size) @@ -713,16 +710,11 @@ class port_data(design.design): inst2_bls_template, inst2_start_bit) for col in range(num_bits): - print(col) bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col) top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col) bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc() top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc() - print("BL", bot_bl, top_bl) - print(bot_bl_pin, top_bl_pin) - print("BR", bot_br, top_br) - print(bot_br_pin, top_br_pin) self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V") self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V") From b3b03d4d397e82dbeb357ac01f381018226e2b27 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 1 Jun 2020 16:46:00 -0700 Subject: [PATCH 16/51] Hard cells can accept height parameter too. --- compiler/custom/{pand2_dec.py => and2_dec.py} | 33 +++++++++++------- compiler/custom/{pand3_dec.py => and3_dec.py} | 29 ++++++++++------ compiler/custom/{pand4_dec.py => and4_dec.py} | 34 ++++++++++++------- compiler/custom/nand2_dec.py | 10 +++++- compiler/custom/nand3_dec.py | 10 +++++- compiler/custom/nand4_dec.py | 10 +++++- compiler/modules/hierarchical_decoder.py | 19 ++++------- compiler/modules/hierarchical_predecode.py | 6 ++-- compiler/pgates/wordline_driver.py | 8 ++--- ..._pand3_dec_test.py => 04_and2_dec_test.py} | 8 ++--- ..._pand4_dec_test.py => 04_and3_dec_test.py} | 8 ++--- ..._pand2_dec_test.py => 04_and4_dec_test.py} | 8 ++--- 12 files changed, 112 insertions(+), 71 deletions(-) rename compiler/custom/{pand2_dec.py => and2_dec.py} (89%) rename compiler/custom/{pand3_dec.py => and3_dec.py} (90%) rename compiler/custom/{pand4_dec.py => and4_dec.py} (89%) rename compiler/tests/{04_pand3_dec_test.py => 04_and2_dec_test.py} (85%) rename compiler/tests/{04_pand4_dec_test.py => 04_and3_dec_test.py} (85%) rename compiler/tests/{04_pand2_dec_test.py => 04_and4_dec_test.py} (85%) diff --git a/compiler/custom/pand2_dec.py b/compiler/custom/and2_dec.py similarity index 89% rename from compiler/custom/pand2_dec.py rename to compiler/custom/and2_dec.py index 0db490ff..b764bb83 100644 --- a/compiler/custom/pand2_dec.py +++ b/compiler/custom/and2_dec.py @@ -7,34 +7,37 @@ # import debug from vector import vector -import pgate +import design from sram_factory import factory from globals import OPTS +from tech import layer -class pand2_dec(pgate.pgate): +class and2_dec(design.design): """ This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - debug.info(1, "Creating pand2_dec {}".format(name)) + + design.design.__init__(self, name) + + debug.info(1, "Creating and2_dec {}".format(name)) self.add_comment("size: {}".format(size)) - self.size = size - - pgate.pgate.__init__(self, name, height, add_wells) - + self.height = height + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + def create_netlist(self): self.add_pins() self.create_modules() self.create_insts() def create_modules(self): - if OPTS.tech_name == "s8": - self.nand = factory.create(module_type="nand2_dec") - else: - self.nand = factory.create(module_type="nand2_dec", - height=self.height) + self.nand = factory.create(module_type="nand2_dec", + height=self.height) self.inv = factory.create(module_type="inv_dec", height=self.height, @@ -44,7 +47,13 @@ class pand2_dec(pgate.pgate): self.add_mod(self.inv) def create_layout(self): + + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" self.width = self.nand.width + self.inv.width + self.height = self.nand.height self.place_insts() self.add_wires() diff --git a/compiler/custom/pand3_dec.py b/compiler/custom/and3_dec.py similarity index 90% rename from compiler/custom/pand3_dec.py rename to compiler/custom/and3_dec.py index 3ec1eb45..89cc84f8 100644 --- a/compiler/custom/pand3_dec.py +++ b/compiler/custom/and3_dec.py @@ -7,22 +7,26 @@ # import debug from vector import vector -import pgate +import design from sram_factory import factory from globals import OPTS +from tech import layer -class pand3_dec(pgate.pgate): +class and3_dec(design.design): """ This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - debug.info(1, "Creating pand3_dec {}".format(name)) + design.design.__init__(self, name) + debug.info(1, "Creating and3_dec {}".format(name)) self.add_comment("size: {}".format(size)) - self.size = size + self.height = height - pgate.pgate.__init__(self, name, height, add_wells) + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): self.add_pins() @@ -30,11 +34,8 @@ class pand3_dec(pgate.pgate): self.create_insts() def create_modules(self): - if OPTS.tech_name == "s8": - self.nand = factory.create(module_type="nand3_dec") - else: - self.nand = factory.create(module_type="nand3_dec", - height=self.height) + self.nand = factory.create(module_type="nand3_dec", + height=self.height) self.inv = factory.create(module_type="inv_dec", height=self.height, @@ -44,8 +45,14 @@ class pand3_dec(pgate.pgate): self.add_mod(self.inv) def create_layout(self): - self.width = self.nand.width + self.inv.width + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + self.width = self.nand.width + self.inv.width + self.height = self.nand.height + self.place_insts() self.add_wires() self.add_layout_pins() diff --git a/compiler/custom/pand4_dec.py b/compiler/custom/and4_dec.py similarity index 89% rename from compiler/custom/pand4_dec.py rename to compiler/custom/and4_dec.py index ee65a349..f99c048f 100644 --- a/compiler/custom/pand4_dec.py +++ b/compiler/custom/and4_dec.py @@ -7,22 +7,28 @@ # import debug from vector import vector -import pgate +import design from sram_factory import factory from globals import OPTS +from tech import layer -class pand4_dec(pgate.pgate): +class and4_dec(design.design): """ This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - debug.info(1, "Creating pand4_dec {}".format(name)) - self.add_comment("size: {}".format(size)) - - self.size = size - pgate.pgate.__init__(self, name, height, add_wells) + design.design.__init__(self, name) + + debug.info(1, "Creating and4_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + self.size = size + self.height = height + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() def create_netlist(self): self.add_pins() @@ -30,20 +36,24 @@ class pand4_dec(pgate.pgate): self.create_insts() def create_modules(self): - if OPTS.tech_name == "s8": - self.nand = factory.create(module_type="nand4_dec") - else: - self.nand = factory.create(module_type="nand4_dec", - height=self.height) + self.nand = factory.create(module_type="nand4_dec", + height=self.height) self.inv = factory.create(module_type="inv_dec", + height=self.height, size=self.size) self.add_mod(self.nand) self.add_mod(self.inv) def create_layout(self): + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + self.width = self.nand.width + self.inv.width + self.height = self.nand.height self.place_insts() self.add_wires() diff --git a/compiler/custom/nand2_dec.py b/compiler/custom/nand2_dec.py index 30a018a1..c806bf5a 100644 --- a/compiler/custom/nand2_dec.py +++ b/compiler/custom/nand2_dec.py @@ -6,7 +6,7 @@ # All rights reserved. # import design -from tech import GDS, layer, spice, parameter +from tech import GDS, layer, spice, parameter, drc import logical_effort import utils @@ -31,6 +31,14 @@ class nand2_dec(design.design): self.height = nand2_dec.height self.pin_map = nand2_dec.pin_map self.add_pin_types(self.type_list) + + # FIXME: For now... + size = 1 + self.size = size + self.nmos_size = 2 * size + self.pmos_size = parameter["beta"] * size + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" diff --git a/compiler/custom/nand3_dec.py b/compiler/custom/nand3_dec.py index d700062c..5eea68de 100644 --- a/compiler/custom/nand3_dec.py +++ b/compiler/custom/nand3_dec.py @@ -6,7 +6,7 @@ # All rights reserved. # import design -from tech import GDS, layer, spice, parameter +from tech import GDS, layer, spice, parameter, drc import logical_effort import utils @@ -31,6 +31,14 @@ class nand3_dec(design.design): self.height = nand3_dec.height self.pin_map = nand3_dec.pin_map self.add_pin_types(self.type_list) + + # FIXME: For now... + size = 1 + self.size = size + self.nmos_size = 2 * size + self.pmos_size = parameter["beta"] * size + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" diff --git a/compiler/custom/nand4_dec.py b/compiler/custom/nand4_dec.py index 5c2bb882..df3eee14 100644 --- a/compiler/custom/nand4_dec.py +++ b/compiler/custom/nand4_dec.py @@ -6,7 +6,7 @@ # All rights reserved. # import design -from tech import GDS, layer, spice, parameter +from tech import GDS, layer, spice, parameter, drc import logical_effort import utils @@ -31,6 +31,14 @@ class nand4_dec(design.design): self.height = nand4_dec.height self.pin_map = nand4_dec.pin_map self.add_pin_types(self.type_list) + + # FIXME: For now... + size = 1 + self.size = size + self.nmos_size = 2 * size + self.pmos_size = parameter["beta"] * size + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") def analytical_power(self, corner, load): """Returns dynamic and leakage power. Results in nW""" diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 3cb5d35c..8b711031 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -57,21 +57,15 @@ class hierarchical_decoder(design.design): self.DRC_LVS() def add_modules(self): - if OPTS.tech_name == "s8": - self.and2 = factory.create(module_type="pand2_dec") - else: - self.and2 = factory.create(module_type="pand2_dec", - height=self.cell_height) + self.and2 = factory.create(module_type="and2_dec", + height=self.cell_height) self.add_mod(self.and2) - if OPTS.tech_name == "s8": - self.and3 = factory.create(module_type="pand3_dec") - else: - self.and3 = factory.create(module_type="pand3_dec", - height=self.cell_height) - + + self.and3 = factory.create(module_type="and3_dec", + height=self.cell_height) self.add_mod(self.and3) # TBD - # self.and4 = factory.create(module_type="pand4_dec") + # self.and4 = factory.create(module_type="and4_dec") # self.add_mod(self.and4) self.add_decoders() @@ -180,6 +174,7 @@ class hierarchical_decoder(design.design): # Two extra pitches between modules on left and right self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch self.row_decoder_height = self.and2.height * self.num_outputs + # Extra bus space for supply contacts self.input_routing_width = self.num_inputs * self.bus_pitch + self.bus_space diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 8244028e..fa44b080 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -42,13 +42,13 @@ class hierarchical_predecode(design.design): # FIXME: Default parms are required for hard cells for now. if self.number_of_inputs == 2: - self.and_mod = factory.create(module_type="pand2_dec", + self.and_mod = factory.create(module_type="and2_dec", height=self.cell_height) elif self.number_of_inputs == 3: - self.and_mod = factory.create(module_type="pand3_dec", + self.and_mod = factory.create(module_type="and3_dec", height=self.cell_height) elif self.number_of_inputs == 4: - self.and_mod = factory.create(module_type="pand4_dec", + self.and_mod = factory.create(module_type="and4_dec", height=self.cell_height) else: debug.error("Invalid number of predecode inputs: {}".format(self.number_of_inputs), -1) diff --git a/compiler/pgates/wordline_driver.py b/compiler/pgates/wordline_driver.py index 1b035e20..a817941b 100644 --- a/compiler/pgates/wordline_driver.py +++ b/compiler/pgates/wordline_driver.py @@ -40,12 +40,8 @@ class wordline_driver(design.design): self.create_insts() def create_modules(self): - if OPTS.tech_name == "s8": - self.nand = factory.create(module_type="nand2_dec") - self.height = self.nand.height - else: - self.nand = factory.create(module_type="nand2_dec", - height=self.height) + self.nand = factory.create(module_type="nand2_dec", + height=self.height) self.driver = factory.create(module_type="inv_dec", size=self.size, diff --git a/compiler/tests/04_pand3_dec_test.py b/compiler/tests/04_and2_dec_test.py similarity index 85% rename from compiler/tests/04_pand3_dec_test.py rename to compiler/tests/04_and2_dec_test.py index 78e576ed..355d3b15 100755 --- a/compiler/tests/04_pand3_dec_test.py +++ b/compiler/tests/04_and2_dec_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class pand3_dec_test(openram_test): +class and2_dec_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -23,10 +23,10 @@ class pand3_dec_test(openram_test): global verify import verify - import pand3_dec + import and2_dec - debug.info(2, "Testing pand3 gate 4x") - a = pand3_dec.pand3_dec(name="pand3x4", size=4) + debug.info(2, "Testing and2 gate 4x") + a = and2_dec.and2_dec(name="and2x4", size=4) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_pand4_dec_test.py b/compiler/tests/04_and3_dec_test.py similarity index 85% rename from compiler/tests/04_pand4_dec_test.py rename to compiler/tests/04_and3_dec_test.py index 78e576ed..7794f36b 100755 --- a/compiler/tests/04_pand4_dec_test.py +++ b/compiler/tests/04_and3_dec_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class pand3_dec_test(openram_test): +class and3_dec_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -23,10 +23,10 @@ class pand3_dec_test(openram_test): global verify import verify - import pand3_dec + import and3_dec - debug.info(2, "Testing pand3 gate 4x") - a = pand3_dec.pand3_dec(name="pand3x4", size=4) + debug.info(2, "Testing and3 gate 4x") + a = and3_dec.and3_dec(name="and3x4", size=4) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_pand2_dec_test.py b/compiler/tests/04_and4_dec_test.py similarity index 85% rename from compiler/tests/04_pand2_dec_test.py rename to compiler/tests/04_and4_dec_test.py index 92b80203..7794f36b 100755 --- a/compiler/tests/04_pand2_dec_test.py +++ b/compiler/tests/04_and4_dec_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class pand2_dec_test(openram_test): +class and3_dec_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -23,10 +23,10 @@ class pand2_dec_test(openram_test): global verify import verify - import pand2_dec + import and3_dec - debug.info(2, "Testing pand2 gate 4x") - a = pand2_dec.pand2_dec(name="pand2x4", size=4) + debug.info(2, "Testing and3 gate 4x") + a = and3_dec.and3_dec(name="and3x4", size=4) self.local_check(a) globals.end_openram() From 9ecf98a4c32c52bde08010d2abb0e8b26ac38054 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 1 Jun 2020 16:46:22 -0700 Subject: [PATCH 17/51] SRAM factory uses default name for first instance even if it has arguments. --- compiler/sram_factory.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index df6b9d99..46eed3d4 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -111,11 +111,12 @@ class sram_factory: return obj_item # If no prefered module name is provided, we generate one. - if module_name is None: - # Use the default name if there are default arguments + if not module_name: + # Use the default name for the first cell. # This is especially for library cells so that the # spice and gds files can be found. - if len(kwargs) > 0: + # Subsequent objects will get unique names to help with GDS limitation. + if len(self.objects[real_module_type]) > 0: # Create a unique name and increment the index module_name = "{0}_{1}".format(real_module_type, self.module_indices[real_module_type]) From b39579c109848845c753bcef74b87cf30af7ad50 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Mon, 1 Jun 2020 20:55:15 -0700 Subject: [PATCH 18/51] temp drc fix for regression tests --- compiler/base/hierarchy_layout.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 0da2c4b0..722ac2f1 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1078,15 +1078,18 @@ class layout(): # Route each pin to the trunk for pin in pins: - # If there is sufficient space, Route from the edge of the pins - # Otherwise, route from the center of the pins - if max_y_bc - min_y_uc > pitch: - if pin.center().y == max_y: - mid = vector(trunk_offset.x, pin.bc().y) - else: - mid = vector(trunk_offset.x, pin.uc().y) - else: - mid = vector(trunk_offset.x, pin.center().y) + # This code block currently causes drc violations for the topmost + # port when using multiport, TODO: fix or remove this block + # # If there is sufficient space, Route from the edge of the pins + # # Otherwise, route from the center of the pins + # if max_y_bc - min_y_uc > pitch: + # if pin.center().y == max_y: + # mid = vector(trunk_offset.x, pin.bc().y) + # else: + # mid = vector(trunk_offset.x, pin.uc().y) + # else: + # mid = vector(trunk_offset.x, pin.center().y) + mid = vector(trunk_offset.x, pin.center().y) self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid) From b0aa70ffda436e9b22f5f51c249671e2660c0ed6 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 09:23:27 -0700 Subject: [PATCH 19/51] Fix precharge vdd route layer --- compiler/pgates/precharge.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index ae314452..8b84a1b8 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -105,17 +105,14 @@ class precharge(design.design): # center of vdd rail pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) - self.add_path(self.bitline_layer, [pmos_pin.center(), pmos_vdd_pos]) + self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos]) - self.add_via_stack_center(from_layer=self.bitline_layer, - to_layer=self.en_layer, - offset=pmos_vdd_pos) self.add_power_pin("vdd", self.well_contact_pos, directions=("V", "V")) self.add_via_stack_center(from_layer=pmos_pin.layer, - to_layer=self.bitline_layer, + to_layer=self.en_layer, offset=pmos_pin.center(), directions=("V", "V")) From e69b665689c0b2b29ba0a124b15e6ba505199fca Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 09:31:43 -0700 Subject: [PATCH 20/51] Flatten pbitcell_1 too --- technology/scn4m_subm/mag_lib/setup.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/technology/scn4m_subm/mag_lib/setup.tcl b/technology/scn4m_subm/mag_lib/setup.tcl index 95e7dbea..09bbea27 100644 --- a/technology/scn4m_subm/mag_lib/setup.tcl +++ b/technology/scn4m_subm/mag_lib/setup.tcl @@ -6,7 +6,7 @@ equate class {-circuit1 pfet} {-circuit2 p} flatten class {-circuit1 dummy_cell_6t} flatten class {-circuit1 dummy_cell_1rw_1r} flatten class {-circuit1 dummy_cell_1w_1r} -flatten class {-circuit1 bitcell_array_0} +flatten class {-circuit1 pbitcell} flatten class {-circuit1 pbitcell_0} flatten class {-circuit1 pbitcell_1} property {-circuit1 nfet} remove as ad ps pd From 620604603c9949627df77b5f887f04e334d84e40 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 10:08:37 -0700 Subject: [PATCH 21/51] Fixed offset jogs --- compiler/base/hierarchy_layout.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 4d0345a3..f659aed4 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -450,7 +450,7 @@ class layout(): path=coordinates, layer_widths=layer_widths) - def add_zjog(self, layer, start, end, first_direction="H"): + def add_zjog(self, layer, start, end, first_direction="H", fixed_offset=None): """ Add a simple jog at the halfway point. If layer is a single value, it is a path. @@ -459,11 +459,17 @@ class layout(): # vertical first if first_direction == "V": - mid1 = vector(start.x, 0.5 * start.y + 0.5 * end.y) + if fixed_offset: + mid1 = vector(start.x, fixed_offset) + else: + mid1 = vector(start.x, 0.5 * start.y + 0.5 * end.y) mid2 = vector(end.x, mid1.y) # horizontal first elif first_direction == "H": - mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y) + if fixed_offset: + mid1 = vector(fixed_offset, start.y) + else: + mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y) mid2 = vector(mid1, end.y) else: debug.error("Invalid direction for jog -- must be H or V.") From a1c7474f80aaa35060f503b7bcb886e14f88b2c7 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 10:08:53 -0700 Subject: [PATCH 22/51] Revert to channel route of bitlines --- compiler/modules/port_data.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index d544b6be..0ad0dd86 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -536,11 +536,11 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - self.connect_bitlines(inst1=inst1, - inst1_bls_template=inst1_bls_templ, - inst2=inst2, - num_bits=self.word_size, - inst1_start_bit=start_bit) + self.channel_route_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + 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 """ @@ -562,10 +562,10 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - self.connect_bitlines(inst1=inst1, inst2=inst2, - num_bits=self.word_size, - inst1_bls_template=inst1_bls_templ, - inst1_start_bit=start_bit) + self.channel_route_bitlines(inst1=inst1, inst2=inst2, + num_bits=self.word_size, + 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 """ @@ -714,9 +714,10 @@ class port_data(design.design): top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col) bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc() top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc() - - self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V") - self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V") + + layer_pitch = getattr(self, "{}_pitch".format(top_bl_pin.layer)) + self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V", top_bl_pin.by() - layer_pitch) + self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V", top_bl_pin.by() - 2 * layer_pitch) def graph_exclude_precharge(self): """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" From 45b0601e4b0789f84c1d38230d2ee9a890fafe34 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 11:43:31 -0700 Subject: [PATCH 23/51] Fix via directions in s8 col mux --- .../modules/single_level_column_mux_array.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 5dc9cbf8..1514d64a 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -7,7 +7,7 @@ # import design import debug -from tech import layer +from tech import layer, preferred_directions from vector import vector from sram_factory import factory from globals import OPTS @@ -33,10 +33,15 @@ class single_level_column_mux_array(design.design): if "li" in layer: self.col_mux_stack = self.li_stack - self.col_mux_stack_pitch = self.li_pitch + self.col_mux_stack_pitch = self.m1_pitch else: self.col_mux_stack = self.m1_stack self.col_mux_stack_pitch = self.m1_pitch + + if preferred_directions[self.col_mux_stack[0]] == "V": + self.via_directions = ("H", "H") + else: + self.via_directions = "pref" self.create_netlist() if not OPTS.netlist_only: @@ -173,7 +178,8 @@ class single_level_column_mux_array(design.design): self.get_pin("sel_{}".format(sel_index)).cy()) # Add the poly contact with a shift to account for the rotation self.add_via_center(layers=self.poly_stack, - offset=offset) + offset=offset, + directions=self.via_directions) self.add_path("poly", [offset, gate_offset]) def route_bitlines(self): @@ -224,11 +230,13 @@ class single_level_column_mux_array(design.design): # This via is on the right of the wire self.add_via_center(layers=self.col_mux_stack, - offset=bl_out_offset) + offset=bl_out_offset, + directions=self.via_directions) # This via is on the left of the wire self.add_via_center(layers=self.col_mux_stack, - offset=br_out_offset) + offset=br_out_offset, + directions=self.via_directions) else: self.add_path(self.col_mux_stack[2], [bl_out_offset, bl_offset]) @@ -236,10 +244,12 @@ class single_level_column_mux_array(design.design): # This via is on the right of the wire self.add_via_center(layers=self.col_mux_stack, - offset=bl_out_offset) + offset=bl_out_offset, + directions=self.via_directions) # This via is on the left of the wire self.add_via_center(layers=self.col_mux_stack, - offset=br_out_offset) + offset=br_out_offset, + directions=self.via_directions) def get_drain_cin(self): """Get the relative capacitance of the drain of the NMOS pass TX""" From f1b7b91b1a4958a90b198fce69cd7f21aa9f7ea7 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 11:43:57 -0700 Subject: [PATCH 24/51] Use non-channel route for s8 port_data --- compiler/modules/port_data.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 0ad0dd86..904c82eb 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -180,6 +180,7 @@ class port_data(design.design): # Precharge will be shifted left if needed self.precharge_array = factory.create(module_type="precharge_array", columns=self.num_cols + 1, + port=self.port, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) self.add_mod(self.precharge_array) @@ -250,6 +251,7 @@ class port_data(design.design): # module, which happens before we create the real precharge_array self.precharge_array = factory.create(module_type="precharge_array", columns=self.num_cols + 1, + port=self.port, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) @@ -536,11 +538,18 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - self.channel_route_bitlines(inst1=inst1, - inst1_bls_template=inst1_bls_templ, - inst2=inst2, - num_bits=self.word_size, - inst1_start_bit=start_bit) + if OPTS.tech_name == "s8": + self.connect_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + num_bits=self.word_size, + inst1_start_bit=start_bit) + else: + self.channel_route_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + 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 """ @@ -562,11 +571,17 @@ class port_data(design.design): # This could be a channel route, but in some techs the bitlines # are too close together. - self.channel_route_bitlines(inst1=inst1, inst2=inst2, - num_bits=self.word_size, - inst1_bls_template=inst1_bls_templ, - inst1_start_bit=start_bit) - + if OPTS.tech_name == "s8": + self.connect_bitlines(inst1=inst1, inst2=inst2, + num_bits=self.word_size, + inst1_bls_template=inst1_bls_templ, + inst1_start_bit=start_bit) + else: + self.channel_route_bitlines(inst1=inst1, inst2=inst2, + num_bits=self.word_size, + 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 """ From fdf51c5a00ca61f8601ae344ee7997be8bd2a5a6 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 11:44:22 -0700 Subject: [PATCH 25/51] Add port option to precharge array --- compiler/modules/precharge_array.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index af612af4..3c64e85f 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -19,13 +19,14 @@ class precharge_array(design.design): of bit line columns, height is the height of the bit-cell array. """ - def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br"): + def __init__(self, name, columns, port, size=1, bitcell_bl="bl", bitcell_br="br"): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br)) self.columns = columns self.size = size + self.port = port self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br @@ -106,7 +107,7 @@ class precharge_array(design.design): xoffset = 0 for i in range(self.columns): tempx = xoffset - if cell_properties.bitcell.mirror.y and (i + 1) % 2: + if cell_properties.bitcell.mirror.y and (i + 1 + self.port) % 2: mirror = "MY" tempx = tempx + self.pc_cell.width else: From fce8e878b9ac55d57865ef2e7a4883f901d3daf4 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 13:57:41 -0700 Subject: [PATCH 26/51] Add port to col mux and simplify route with computation to fix mirror bug. --- .../modules/single_level_column_mux_array.py | 86 +++++++------------ .../07_single_level_column_mux_array_test.py | 6 +- 2 files changed, 34 insertions(+), 58 deletions(-) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 1514d64a..3d80525a 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -20,12 +20,13 @@ class single_level_column_mux_array(design.design): Array of column mux to read the bitlines through the 6T. """ - def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br"): + def __init__(self, name, columns, port, word_size, bitcell_bl="bl", bitcell_br="br"): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br)) self.columns = columns + self.port = port self.word_size = word_size self.words_per_row = int(self.columns / self.word_size) self.bitcell_bl = bitcell_bl @@ -117,7 +118,7 @@ class single_level_column_mux_array(design.design): # For every column, add a pass gate for col_num in range(self.columns): xoffset = col_num * self.mux.width - if cell_properties.bitcell.mirror.y and col_num % 2: + if cell_properties.bitcell.mirror.y and (col_num + self.port) % 2: mirror = "MY" xoffset = xoffset + self.mux.width else: @@ -184,72 +185,47 @@ class single_level_column_mux_array(design.design): def route_bitlines(self): """ Connect the output bit-lines to form the appropriate width mux """ - from tech import cell_properties for j in range(self.columns): - bl_offset = self.mux_inst[j].get_pin("bl_out").bc() - br_offset = self.mux_inst[j].get_pin("br_out").bc() - bl_out_offset = bl_offset - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch) - br_out_offset = br_offset - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch) + bl_offset_begin = self.mux_inst[j].get_pin("bl_out").bc() + br_offset_begin = self.mux_inst[j].get_pin("br_out").bc() - bl_out_offset_end = bl_out_offset + vector(0, self.route_height) - br_out_offset_end = br_out_offset + vector(0, self.route_height) + bl_out_offset_begin = bl_offset_begin - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch) + br_out_offset_begin = br_offset_begin - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch) - if cell_properties.bitcell.mirror.y and j % 2: - tmp_bl_out_end = br_out_offset_end - tmp_br_out_end = bl_out_offset_end - else: - tmp_bl_out_end = bl_out_offset_end - tmp_br_out_end = br_out_offset_end - - if (j % self.words_per_row) == 0: - # Create the metal1 to connect the n-way mux output from the pass gate - # These will be located below the select lines. Yes, these are M2 width - # to ensure vias are enclosed and M1 min width rules. - width = self.m2_width + self.mux.width * (self.words_per_row - 1) - - if cell_properties.bitcell.mirror.y and (j % 2) == 0: - bl = self.mux.get_pin("bl") - br = self.mux.get_pin("br") - dist = abs(bl.ll().x - br.ll().x) - else: - dist = 0 - - self.add_path(self.col_mux_stack[0], [bl_out_offset, bl_out_offset + vector(width + dist, 0)]) - self.add_path(self.col_mux_stack[0], [br_out_offset, br_out_offset + vector(width - dist, 0)]) + # Add the horizontal wires for the first bit + if j % self.words_per_row == 0: + bl_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("bl_out").bc() + br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc() + bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch) + br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch) + + self.add_path(self.col_mux_stack[0], [bl_out_offset_begin, bl_out_offset_end]) + self.add_path(self.col_mux_stack[0], [br_out_offset_begin, br_out_offset_end]) # Extend the bitline output rails and gnd downward on the first bit of each n-way mux self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)), layer=self.col_mux_stack[2], - start=bl_out_offset, - end=tmp_bl_out_end) + start=bl_offset_begin, + end=bl_out_offset_begin) self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)), layer=self.col_mux_stack[2], - start=br_out_offset, - end=tmp_br_out_end) - - # This via is on the right of the wire - self.add_via_center(layers=self.col_mux_stack, - offset=bl_out_offset, - directions=self.via_directions) - - # This via is on the left of the wire - self.add_via_center(layers=self.col_mux_stack, - offset=br_out_offset, - directions=self.via_directions) + start=br_offset_begin, + end=br_out_offset_begin) else: - self.add_path(self.col_mux_stack[2], [bl_out_offset, bl_offset]) - self.add_path(self.col_mux_stack[2], [br_out_offset, br_offset]) + self.add_path(self.col_mux_stack[2], [bl_out_offset_begin, bl_offset_begin]) + self.add_path(self.col_mux_stack[2], [br_out_offset_begin, br_offset_begin]) - # This via is on the right of the wire - self.add_via_center(layers=self.col_mux_stack, - offset=bl_out_offset, - directions=self.via_directions) - # This via is on the left of the wire - self.add_via_center(layers=self.col_mux_stack, - offset=br_out_offset, - directions=self.via_directions) + # This via is on the right of the wire + self.add_via_center(layers=self.col_mux_stack, + offset=bl_out_offset_begin, + directions=self.via_directions) + + # This via is on the left of the wire + self.add_via_center(layers=self.col_mux_stack, + offset=br_out_offset_begin, + directions=self.via_directions) def get_drain_cin(self): """Get the relative capacitance of the drain of the NMOS pass TX""" diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index c5d48689..fb608f68 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -23,15 +23,15 @@ class single_level_column_mux_test(openram_test): # check single level column mux array in single port debug.info(1, "Testing sample for 2-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8) + a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=8) self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4) + a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=4) self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4) + a = factory.create(module_type="single_level_column_mux_array", columns=32, port=0, word_size=4) self.local_check(a) globals.end_openram() From 34209dac3d6aa9680e1311ed34cac7cbb44199a3 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 2 Jun 2020 16:50:07 -0700 Subject: [PATCH 27/51] A port option for correct mirroring in port_data. --- compiler/modules/hierarchical_predecode.py | 2 -- compiler/modules/port_data.py | 1 + compiler/modules/precharge_array.py | 3 +-- .../07_single_level_column_mux_array_pbitcell_test.py | 8 ++++---- compiler/tests/08_precharge_array_1rw1r_test.py | 2 +- compiler/tests/08_precharge_array_test.py | 2 +- compiler/tests/18_port_data_1rw_1r_test.py | 2 +- 7 files changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index fa44b080..d998d882 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -40,7 +40,6 @@ class hierarchical_predecode(design.design): def add_modules(self): """ Add the INV and AND gate modules """ - # FIXME: Default parms are required for hard cells for now. if self.number_of_inputs == 2: self.and_mod = factory.create(module_type="and2_dec", height=self.cell_height) @@ -60,7 +59,6 @@ class hierarchical_predecode(design.design): size=1) self.add_mod(self.inv) - def create_layout(self): """ The general organization is from left to right: 1) a set of M2 rails for input signals diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 904c82eb..ac6022f7 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -196,6 +196,7 @@ class port_data(design.design): if self.col_addr_size > 0: self.column_mux_array = factory.create(module_type="column_mux_array", columns=self.num_cols, + port=self.port, word_size=self.word_size, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 3c64e85f..2acb1063 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -7,7 +7,6 @@ # import design import debug -from tech import drc from vector import vector from sram_factory import factory from globals import OPTS @@ -26,7 +25,7 @@ class precharge_array(design.design): self.columns = columns self.size = size - self.port = port + self.port = port self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br diff --git a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py index 84e62a96..7feb067e 100755 --- a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py +++ b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py @@ -29,19 +29,19 @@ class single_level_column_mux_pbitcell_test(openram_test): factory.reset() debug.info(1, "Testing sample for 2-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=32, port=0, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2") + a = factory.create(module_type="single_level_column_mux_array", columns=32, port=3, word_size=4, bitcell_bl="bl2", bitcell_br="br2") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/08_precharge_array_1rw1r_test.py b/compiler/tests/08_precharge_array_1rw1r_test.py index 8f51c2d4..1381b93a 100755 --- a/compiler/tests/08_precharge_array_1rw1r_test.py +++ b/compiler/tests/08_precharge_array_1rw1r_test.py @@ -29,7 +29,7 @@ class precharge_test(openram_test): factory.reset() debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") - pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0") + pc = factory.create(module_type="precharge_array", columns=3, port=0, bitcell_bl="bl0", bitcell_br="br0") self.local_check(pc) # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index b301a909..c3b823b3 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -23,7 +23,7 @@ class precharge_test(openram_test): # check precharge array in single port debug.info(2, "Checking 3 column precharge") - pc = factory.create(module_type="precharge_array", columns=3) + pc = factory.create(module_type="precharge_array", columns=3, port=0) self.local_check(pc) globals.end_openram() diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index e9e282a5..8081416e 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -36,7 +36,7 @@ class port_data_1rw_1r_test(openram_test): self.local_check(a) a = factory.create("port_data", sram_config=c, port=1) self.local_check(a) - + c.num_words=32 c.words_per_row=2 factory.reset() From 38f5e8b865e41577959f9eb243d63a466f6cc4f1 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 10:01:02 -0700 Subject: [PATCH 28/51] Add col mux tests for multiport --- .../04_single_level_column_mux_1rw_1r_test.py | 45 +++++++++++++++++++ .../tests/04_single_level_column_mux_test.py | 1 - ...ngle_level_column_mux_array_1rw_1r_test.py | 44 ++++++++++++++++++ .../07_single_level_column_mux_array_test.py | 2 - 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100755 compiler/tests/04_single_level_column_mux_1rw_1r_test.py create mode 100755 compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py diff --git a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py new file mode 100755 index 00000000..525191d7 --- /dev/null +++ b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class single_level_column_mux_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(2, "Checking column mux port 0") + tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + debug.info(2, "Checking column mux port 1") + tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_single_level_column_mux_test.py b/compiler/tests/04_single_level_column_mux_test.py index 2b437987..20dfe968 100755 --- a/compiler/tests/04_single_level_column_mux_test.py +++ b/compiler/tests/04_single_level_column_mux_test.py @@ -15,7 +15,6 @@ from globals import OPTS from sram_factory import factory import debug -#@unittest.skip("SKIPPING 04_driver_test") class single_level_column_mux_test(openram_test): diff --git a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py new file mode 100755 index 00000000..a8d804c9 --- /dev/null +++ b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class single_level_column_mux_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(1, "Testing sample for 4-way column_mux_array port 0") + a = factory.create(module_type="single_level_column_mux_array", columns=8, port=0, word_size=2, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) + + debug.info(1, "Testing sample for 4-way column_mux_array port 1") + a = factory.create(module_type="single_level_column_mux_array", columns=8, port=0, word_size=2, bitcell_bl="bl1", bitcell_br="br1") + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index fb608f68..ff7ec1c9 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -19,9 +19,7 @@ class single_level_column_mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - import single_level_column_mux_array - # check single level column mux array in single port debug.info(1, "Testing sample for 2-way column_mux_array") a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=8) self.local_check(a) From 7a602b75a47280399260235fb8ef0edd6e3d4f4b Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 3 Jun 2020 12:54:15 -0700 Subject: [PATCH 29/51] keep dev routing changes to hierarchy_layout --- compiler/base/hierarchy_layout.py | 37 ++++--------------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 36505294..c6f6ff49 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -701,7 +701,7 @@ class layout(): boundary = [self.find_lowest_coords(), self.find_highest_coords()] debug.check(boundary[0] and boundary[1], "No shapes to make a boundary.") - + height = boundary[1][1] - boundary[0][1] width = boundary[1][0] - boundary[0][0] (layer_number, layer_purpose) = techlayer[boundary_layer] @@ -1008,13 +1008,8 @@ class layout(): max_x = max([pin.center().x for pin in pins]) min_x = min([pin.center().x for pin in pins]) - # max_x_lc & min_x_rc are for routing to/from the edge of the pins - # to increase spacing between contacts of different nets - max_x_lc = max([pin.lc().x for pin in pins]) - min_x_rc = min([pin.rc().x for pin in pins]) - # if we are less than a pitch, just create a non-preferred layer jog - if max_x_lc - min_x_rc <= pitch: + if max_x - min_x <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] # Add the horizontal trunk on the vertical layer! @@ -1035,15 +1030,7 @@ class layout(): # Route each pin to the trunk for pin in pins: - # If there is sufficient space, Route from the edge of the pins - # Otherwise, route from the center of the pins - if max_x_lc - min_x_rc > pitch: - if pin.center().x == max_x: - mid = vector(pin.lc().x, trunk_offset.y) - else: - mid = vector(pin.rc().x, trunk_offset.y) - else: - mid = vector(pin.center().x, trunk_offset.y) + mid = vector(pin.center().x, trunk_offset.y) self.add_path(self.vertical_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, offset=mid) @@ -1060,13 +1047,8 @@ class layout(): max_y = max([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins]) - # max_y_bc & min_y_uc are for routing to/from the edge of the pins - # to reduce spacing between contacts of different nets - max_y_bc = max([pin.bc().y for pin in pins]) - min_y_uc = min([pin.uc().y for pin in pins]) - # if we are less than a pitch, just create a non-preferred layer jog - if max_y_bc - min_y_uc <= pitch: + if max_y - min_y <= pitch: half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] @@ -1088,17 +1070,6 @@ class layout(): # Route each pin to the trunk for pin in pins: - # This code block currently causes drc violations for the topmost - # port when using multiport, TODO: fix or remove this block - # # If there is sufficient space, Route from the edge of the pins - # # Otherwise, route from the center of the pins - # if max_y_bc - min_y_uc > pitch: - # if pin.center().y == max_y: - # mid = vector(trunk_offset.x, pin.bc().y) - # else: - # mid = vector(trunk_offset.x, pin.uc().y) - # else: - # mid = vector(trunk_offset.x, pin.center().y) mid = vector(trunk_offset.x, pin.center().y) self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_via_center(layers=layer_stack, From e93f3f1d2efed0b55ff7222846126e7533573e1c Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 14:30:15 -0700 Subject: [PATCH 30/51] Add 1rw_1r tests --- .../06_hierarchical_decoder_1rw_1r_test.py | 68 ++++++++++++++++++ .../tests/06_hierarchical_decoder_test.py | 6 -- ...6_hierarchical_predecode2x4_1rw_1r_test.py | 42 +++++++++++ .../06_hierarchical_predecode2x4_test.py | 6 -- ...6_hierarchical_predecode3x8_1rw_1r_test.py | 42 +++++++++++ .../06_hierarchical_predecode3x8_test.py | 6 -- .../08_wordline_driver_array_1rw_1r_test.py | 43 ++++++++++++ compiler/tests/18_port_address_1rw_1r_test.py | 40 +++++++++++ compiler/tests/18_port_address_test.py | 1 + .../tests/19_single_bank_wmask_1rw_1r_test.py | 69 +++++++++++++++++++ 10 files changed, 305 insertions(+), 18 deletions(-) create mode 100755 compiler/tests/06_hierarchical_decoder_1rw_1r_test.py create mode 100755 compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py create mode 100755 compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py create mode 100755 compiler/tests/08_wordline_driver_array_1rw_1r_test.py create mode 100755 compiler/tests/18_port_address_1rw_1r_test.py create mode 100755 compiler/tests/19_single_bank_wmask_1rw_1r_test.py diff --git a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py new file mode 100755 index 00000000..5b317e6e --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + # Checks 2x4 and 2-input NAND decoder + debug.info(1, "Testing 16 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=16) + self.local_check(a) + + # Checks 2x4 and 2-input NAND decoder with non-power-of-two + debug.info(1, "Testing 17 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=17) + self.local_check(a) + + # Checks 2x4 with 3x8 and 2-input NAND decoder + debug.info(1, "Testing 32 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=32) + self.local_check(a) + + # Checks 3 x 2x4 and 3-input NAND decoder + debug.info(1, "Testing 64 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=64) + self.local_check(a) + + # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two + debug.info(1, "Testing 132 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=132) + self.local_check(a) + + # Checks 3 x 3x8 and 3-input NAND decoder + debug.info(1, "Testing 512 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=512) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 7d155b01..e2b34cba 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -21,12 +21,6 @@ class hierarchical_decoder_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - # Checks 2x4 and 2-input NAND decoder debug.info(1, "Testing 16 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) diff --git a/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py new file mode 100755 index 00000000..6512238f --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_predecode2x4_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(1, "Testing sample for hierarchy_predecode2x4") + a = factory.create(module_type="hierarchical_predecode2x4") + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index 94fd4838..c2b51f10 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -21,12 +21,6 @@ class hierarchical_predecode2x4_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - debug.info(1, "Testing sample for hierarchy_predecode2x4") a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py new file mode 100755 index 00000000..466a7e40 --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_predecode3x8_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(1, "Testing sample for hierarchy_predecode3x8") + a = factory.create(module_type="hierarchical_predecode3x8") + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index 33f94d9d..c1471a40 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -21,12 +21,6 @@ class hierarchical_predecode3x8_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - debug.info(1, "Testing sample for hierarchy_predecode3x8") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/08_wordline_driver_array_1rw_1r_test.py b/compiler/tests/08_wordline_driver_array_1rw_1r_test.py new file mode 100755 index 00000000..7c97ff75 --- /dev/null +++ b/compiler/tests/08_wordline_driver_array_1rw_1r_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class wordline_driver_array_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + # check wordline driver for single port + debug.info(2, "Checking driver") + tx = factory.create(module_type="wordline_driver_array", rows=8, cols=32) + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_1rw_1r_test.py new file mode 100755 index 00000000..402b30a0 --- /dev/null +++ b/compiler/tests/18_port_address_1rw_1r_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_address_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + debug.info(1, "Port address 16 rows") + a = factory.create("port_address", cols=16, rows=16) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_address_test.py b/compiler/tests/18_port_address_test.py index 23f35540..a2a8bf0b 100755 --- a/compiler/tests/18_port_address_test.py +++ b/compiler/tests/18_port_address_test.py @@ -13,6 +13,7 @@ from globals import OPTS from sram_factory import factory import debug + class port_address_test(openram_test): def runTest(self): diff --git a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py new file mode 100755 index 00000000..d08ff8cc --- /dev/null +++ b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class single_bank_wmask_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from sram_config import sram_config + + + c = sram_config(word_size=8, + write_size=4, + num_words=16, + num_banks=1) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) From 3b1fe26d252ded614cd61a9efa4ec4e50c8fde62 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 14:33:30 -0700 Subject: [PATCH 31/51] Spacing between decoder and driver for s8 --- compiler/modules/hierarchical_decoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 8b711031..2b624ddc 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -191,7 +191,7 @@ class hierarchical_decoder(design.design): + self.predecoder_width \ + self.internal_routing_width \ + self.nand_width \ - + self.m1_space + + 2 * self.m1_pitch def route_inputs(self): """ Create input bus for the predecoders """ From 4bc1e9a026cb412374b90945ac6a22642635ae1e Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 15:47:03 -0700 Subject: [PATCH 32/51] Fix the bitline spacing in the column mux to a constant. --- compiler/pgates/single_level_column_mux.py | 101 ++++++------------ .../04_single_level_column_mux_1rw_1r_test.py | 2 +- 2 files changed, 32 insertions(+), 71 deletions(-) diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/single_level_column_mux.py index bdd03e55..96935158 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/single_level_column_mux.py @@ -13,6 +13,7 @@ from sram_factory import factory import logical_effort from utils import round_to_grid + class single_level_column_mux(pgate.pgate): """ This module implements the columnmux bitline cell used in the design. @@ -44,9 +45,18 @@ class single_level_column_mux(pgate.pgate): def create_layout(self): - self.pin_height = 2 * self.m2_width + # If li exists, use li and m1 for the mux, otherwise use m1 and m2 + if "li" in layer: + self.col_mux_stack = self.li_stack + else: + self.col_mux_stack = self.m1_stack + self.pin_layer = self.bitcell.get_pin(self.bitcell_bl).layer + self.pin_pitch = getattr(self, "{}_pitch".format(self.pin_layer)) + self.pin_width = getattr(self, "{}_width".format(self.pin_layer)) + self.pin_height = 2 * self.pin_width self.width = self.bitcell.width self.height = self.nmos_upper.uy() + self.pin_height + self.connect_poly() self.add_bitline_pins() self.connect_bitlines() @@ -58,9 +68,7 @@ class single_level_column_mux(pgate.pgate): # Adds nmos_lower,nmos_upper to the module self.ptx_width = self.tx_size * drc("minwidth_tx") self.nmos = factory.create(module_type="ptx", - width=self.ptx_width, - add_source_contact=False, - add_drain_contact=False) + width=self.ptx_width) self.add_mod(self.nmos) def add_pins(self): @@ -69,40 +77,26 @@ class single_level_column_mux(pgate.pgate): def add_bitline_pins(self): """ Add the top and bottom pins to this cell """ - bl_pin=self.bitcell.get_pin(self.bitcell_bl) - br_pin=self.bitcell.get_pin(self.bitcell_br) - - bl_pos = vector(bl_pin.lx(), 0) - br_pos = vector(br_pin.lx(), 0) - - # The bitline input/output pins must be a least as wide as the metal pitch - # so that there is enough space to route to/from the pins. - # FIXME: bitline_metal_pitch should be greater than the horizontal metal pitch used in port_data - bitline_metal_pitch = self.width / 2 - bitline_width = br_pos.x - bl_pos.x - if bitline_width < bitline_metal_pitch: - bitline_width_increase_bl = round_to_grid((bitline_metal_pitch - bitline_width) / 2) - bitline_width_increase_br = round_to_grid((bitline_metal_pitch - bitline_width) - bitline_width_increase_bl) - bl_pos = bl_pos + vector(-bitline_width_increase_bl, 0) - br_pos = br_pos + vector( bitline_width_increase_br, 0) + bl_pos = vector(self.pin_pitch, 0) + br_pos = vector(self.width - self.pin_pitch, 0) # bl and br self.add_layout_pin(text="bl", - layer=bl_pin.layer, + layer=self.pin_layer, offset=bl_pos + vector(0, self.height - self.pin_height), height=self.pin_height) self.add_layout_pin(text="br", - layer=br_pin.layer, + layer=self.pin_layer, offset=br_pos + vector(0, self.height - self.pin_height), height=self.pin_height) # bl_out and br_out self.add_layout_pin(text="bl_out", - layer=bl_pin.layer, + layer=self.pin_layer, offset=bl_pos, height=self.pin_height) self.add_layout_pin(text="br_out", - layer=br_pin.layer, + layer=self.pin_layer, offset=br_pos, height=self.pin_height) @@ -110,7 +104,7 @@ class single_level_column_mux(pgate.pgate): """ Create the two pass gate NMOS transistors to switch the bitlines""" # Space it in the center - nmos_lower_position = self.nmos.active_offset.scale(0,1) \ + nmos_lower_position = self.nmos.active_offset.scale(0, 1) \ + vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0) self.nmos_lower = self.add_inst(name="mux_tx1", mod=self.nmos, @@ -145,62 +139,29 @@ class single_level_column_mux(pgate.pgate): def connect_bitlines(self): """ Connect the bitlines to the mux transistors """ - # If li exists, use li and m1 for the mux, otherwise use m1 and m2 - if "li" in layer: - self.col_mux_stack = self.li_stack - else: - self.col_mux_stack = self.m1_stack - - # These are on metal2 bl_pin = self.get_pin("bl") br_pin = self.get_pin("br") bl_out_pin = self.get_pin("bl_out") br_out_pin = self.get_pin("br_out") - # These are on metal1 nmos_lower_s_pin = self.nmos_lower.get_pin("S") nmos_lower_d_pin = self.nmos_lower.get_pin("D") nmos_upper_s_pin = self.nmos_upper.get_pin("S") nmos_upper_d_pin = self.nmos_upper.get_pin("D") # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D - self.add_via_center(layers=self.col_mux_stack, - offset=bl_pin.bc(), - directions=("V", "V")) - self.add_via_center(layers=self.col_mux_stack, - offset=br_out_pin.uc(), - directions=("V", "V")) - self.add_via_center(layers=self.col_mux_stack, - offset=nmos_upper_s_pin.center(), - directions=("V", "V")) - self.add_via_center(layers=self.col_mux_stack, - offset=nmos_lower_d_pin.center(), - directions=("V", "V")) - - # Add diffusion contacts - # These were previously omitted with the options: add_source_contact=False, add_drain_contact=False - # They are added now and not previously so that they do not include m1 (which is usually included by default) - # This is only a concern when the local interconnect (li) layer is being used - self.add_via_center(layers=self.active_stack, - offset=nmos_upper_d_pin.center(), - directions=("V", "V"), - implant_type="n", - well_type="nwell") - self.add_via_center(layers=self.active_stack, - offset=nmos_lower_s_pin.center(), - directions=("V", "V"), - implant_type="n", - well_type="nwell") - self.add_via_center(layers=self.active_stack, - offset=nmos_upper_s_pin.center(), - directions=("V", "V"), - implant_type="n", - well_type="nwell") - self.add_via_center(layers=self.active_stack, - offset=nmos_lower_d_pin.center(), - directions=("V", "V"), - implant_type="n", - well_type="nwell") + self.add_via_stack_center(from_layer=bl_pin.layer, + to_layer=self.col_mux_stack[0], + offset=bl_pin.bc()) + self.add_via_stack_center(from_layer=br_out_pin.layer, + to_layer=self.col_mux_stack[0], + offset=br_out_pin.uc()) + self.add_via_stack_center(from_layer=nmos_upper_s_pin.layer, + to_layer=self.col_mux_stack[2], + offset=nmos_upper_s_pin.center()) + self.add_via_stack_center(from_layer=nmos_lower_d_pin.layer, + to_layer=self.col_mux_stack[2], + offset=nmos_lower_d_pin.center()) # bl -> nmos_upper/D on metal1 # bl_out -> nmos_upper/S on metal2 diff --git a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py index 525191d7..69b31cc6 100755 --- a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py +++ b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py @@ -32,7 +32,7 @@ class single_level_column_mux_1rw_1r_test(openram_test): self.local_check(tx) debug.info(2, "Checking column mux port 1") - tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl1", bitcell_br="br1") self.local_check(tx) globals.end_openram() From 4183638f0354cb7ac909c1d6752b67a137d1191d Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 16:05:57 -0700 Subject: [PATCH 33/51] Align precharge bitlines with col mux --- compiler/pgates/precharge.py | 12 ++++++------ ...w1r_test.py => 08_precharge_array_1rw_1r_test.py} | 0 2 files changed, 6 insertions(+), 6 deletions(-) rename compiler/tests/{08_precharge_array_1rw1r_test.py => 08_precharge_array_1rw_1r_test.py} (100%) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 8b84a1b8..278fef3d 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -235,11 +235,11 @@ class precharge(design.design): """ Adds both bit-line and bit-line-bar to the module """ - layer_width = drc("minwidth_" + self.bitline_layer) - layer_space = drc("{0}_to_{0}".format(self.bitline_layer)) + layer_pitch = getattr(self, "{}_pitch".format(self.bitline_layer)) + layer_width = getattr(self, "{}_width".format(self.bitline_layer)) - # adds the BL - self.bl_xoffset = layer_space + 0.5 * layer_width + # adds the BL so it aligns with the col mux + self.bl_xoffset = layer_pitch + 0.5 * layer_width top_pos = vector(self.bl_xoffset, self.height) pin_pos = vector(self.bl_xoffset, 0) self.add_path(self.bitline_layer, [top_pos, pin_pos]) @@ -248,8 +248,8 @@ class precharge(design.design): start=pin_pos, end=top_pos) - # adds the BR - self.br_xoffset = self.width - layer_space - 0.5 * layer_width + # adds the BR so it aligns with the col mux + self.br_xoffset = self.width - layer_pitch - 0.5 * layer_width top_pos = vector(self.br_xoffset, self.height) pin_pos = vector(self.br_xoffset, 0) self.add_path(self.bitline_layer, [top_pos, pin_pos]) diff --git a/compiler/tests/08_precharge_array_1rw1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py similarity index 100% rename from compiler/tests/08_precharge_array_1rw1r_test.py rename to compiler/tests/08_precharge_array_1rw_1r_test.py From b2b7e7800bd01ac27e4051a6f78764d549086205 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 16:39:05 -0700 Subject: [PATCH 34/51] Undo same bitline pitch --- compiler/pgates/precharge.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 278fef3d..893736f4 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -236,10 +236,9 @@ class precharge(design.design): Adds both bit-line and bit-line-bar to the module """ layer_pitch = getattr(self, "{}_pitch".format(self.bitline_layer)) - layer_width = getattr(self, "{}_width".format(self.bitline_layer)) - # adds the BL so it aligns with the col mux - self.bl_xoffset = layer_pitch + 0.5 * layer_width + # adds the BL + self.bl_xoffset = layer_pitch top_pos = vector(self.bl_xoffset, self.height) pin_pos = vector(self.bl_xoffset, 0) self.add_path(self.bitline_layer, [top_pos, pin_pos]) @@ -248,8 +247,8 @@ class precharge(design.design): start=pin_pos, end=top_pos) - # adds the BR so it aligns with the col mux - self.br_xoffset = self.width - layer_pitch - 0.5 * layer_width + # adds the BR + self.br_xoffset = self.width - layer_pitch top_pos = vector(self.br_xoffset, self.height) pin_pos = vector(self.br_xoffset, 0) self.add_path(self.bitline_layer, [top_pos, pin_pos]) From 3927c62e3269f9fa4a0860fdf0a259bc3a14477b Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 16:39:33 -0700 Subject: [PATCH 35/51] Undo extra space due to nwell spacing --- compiler/modules/hierarchical_decoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 2b624ddc..8b711031 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -191,7 +191,7 @@ class hierarchical_decoder(design.design): + self.predecoder_width \ + self.internal_routing_width \ + self.nand_width \ - + 2 * self.m1_pitch + + self.m1_space def route_inputs(self): """ Create input bus for the predecoders """ From 77c95b28da19b37ae638a50b85d53c7bb06bb206 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 16:39:46 -0700 Subject: [PATCH 36/51] Rename precharge test --- compiler/tests/08_precharge_array_1rw_1r_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/tests/08_precharge_array_1rw_1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py index 1381b93a..dde36b23 100755 --- a/compiler/tests/08_precharge_array_1rw_1r_test.py +++ b/compiler/tests/08_precharge_array_1rw_1r_test.py @@ -15,7 +15,7 @@ from globals import OPTS from sram_factory import factory import debug -class precharge_test(openram_test): +class precharge_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) From 249b5355ba8ed083e011c000c3f3fb3ed6f8b01f Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 3 Jun 2020 17:08:04 -0700 Subject: [PATCH 37/51] Adjust rbl route --- 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 393631e5..2aa4e871 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() + vector(0, 1.5 * self.m2_pitch) + 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() - vector(0, 1.5 * self.m2_pitch) + via_offset = bl_pin.bc() - vector(0, self.m2_pitch) left_right_offset = vector(self.min_x_offset, via_offset.y) self.add_via_stack_center(from_layer=bl_pin.layer, to_layer="m3", From 7aafa438978cd4f38afa501d40357ddea0593ce6 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 4 Jun 2020 10:22:52 -0700 Subject: [PATCH 38/51] Connect RBL to bottom of precharge cell --- compiler/modules/bank.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 2aa4e871..92e4e8cd 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -123,23 +123,22 @@ class bank(design.design): 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]) - bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name) - # This will ensure the pin is only on the top or bottom edge + + # Connect the rbl to the port data pin + bl_pin = self.port_data_inst[port].get_pin("rbl_bl") if port % 2: - via_offset = bl_pin.uc() + vector(0, self.m2_pitch) - left_right_offset = vector(self.max_x_offset, via_offset.y) + pin_offset = bl_pin.uc() + left_right_offset = vector(self.max_x_offset, pin_offset.y) else: - via_offset = bl_pin.bc() - vector(0, self.m2_pitch) - left_right_offset = vector(self.min_x_offset, via_offset.y) + pin_offset = bl_pin.bc() + left_right_offset = vector(self.min_x_offset, pin_offset.y) self.add_via_stack_center(from_layer=bl_pin.layer, to_layer="m3", - offset=via_offset) + offset=pin_offset) self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port), layer="m3", start=left_right_offset, - end=via_offset) + end=pin_offset) def route_bitlines(self, port): """ Route the bitlines depending on the port type rw, w, or r. """ From 717188f85c47dc8dccaa822ef5d7b20e5cdec2b5 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 4 Jun 2020 11:03:39 -0700 Subject: [PATCH 39/51] Change L shape of rbl route --- compiler/sram/sram_1bank.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 8dbcfaf4..f9a40545 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -320,7 +320,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_hbus(src_pin, dest_pin) + self.add_wire(self.m2_stack[::-1], + [src_pin.center(), vector(src_pin.cx(), dest_pin.cy()), dest_pin.rc()]) + # self.connect_hbus(src_pin, dest_pin) def route_row_addr_dff(self): """ Connect the output of the row flops to the bank pins """ From e06dc3810a333819ba1fdb21258c77cfcbf94f41 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 4 Jun 2020 12:12:19 -0700 Subject: [PATCH 40/51] Move precharge pin to bottom --- compiler/pgates/precharge.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 893736f4..cfc2a688 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -191,14 +191,15 @@ class precharge(design.design): """ # 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, 0.5 * self.m2_pitch) + pin_offset = self.lower_pmos_inst.get_pin("G").lr() + # This is an extra space down for some techs with contact to active spacing + offset = pin_offset - vector(0, self.poly_space) self.add_via_stack_center(from_layer="poly", to_layer=self.en_layer, offset=offset) - - # adds the en rail on metal1 + self.add_path("poly", + [self.lower_pmos_inst.get_pin("G").bc(), offset]) + # adds the en rail self.add_layout_pin_segment_center(text="en_bar", layer=self.en_layer, start=offset.scale(0, 1), From 2fcecb7227e7de15fe78a3c9f3ab292062b9ae0c Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 4 Jun 2020 16:01:32 -0700 Subject: [PATCH 41/51] Variable zjog. 512 port address test. s8 port address working. --- compiler/base/hierarchy_layout.py | 7 +++--- compiler/modules/hierarchical_decoder.py | 22 ++++++------------- compiler/modules/port_address.py | 2 +- compiler/tests/18_port_address_1rw_1r_test.py | 4 ++++ compiler/tests/18_port_address_test.py | 4 ++++ 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index c6f6ff49..12a9826e 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -450,26 +450,27 @@ class layout(): path=coordinates, layer_widths=layer_widths) - def add_zjog(self, layer, start, end, first_direction="H", fixed_offset=None): + def add_zjog(self, layer, start, end, first_direction="H", var_offset=0.5, fixed_offset=None): """ Add a simple jog at the halfway point. If layer is a single value, it is a path. If layer is a tuple, it is a wire with preferred directions. """ + neg_offset = 1.0 - var_offset # vertical first if first_direction == "V": if fixed_offset: mid1 = vector(start.x, fixed_offset) else: - mid1 = vector(start.x, 0.5 * start.y + 0.5 * end.y) + mid1 = vector(start.x, neg_offset * start.y + var_offset * end.y) mid2 = vector(end.x, mid1.y) # horizontal first elif first_direction == "H": if fixed_offset: mid1 = vector(fixed_offset, start.y) else: - mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y) + mid1 = vector(neg_offset * start.x + var_offset * end.x, start.y) mid2 = vector(mid1, end.y) else: debug.error("Invalid direction for jog -- must be H or V.") diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 8b711031..fbf8ef29 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -48,11 +48,18 @@ class hierarchical_decoder(design.design): self.setup_layout_constants() self.place_pre_decoder() self.place_row_decoder() + + self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space + self.route_inputs() self.route_outputs() self.route_decoder_bus() self.route_vdd_gnd() + self.offset_all_coordinates() + + self.width = self.and_inst[0].rx() + self.m1_space + self.add_boundary() self.DRC_LVS() @@ -178,21 +185,6 @@ class hierarchical_decoder(design.design): # Extra bus space for supply contacts self.input_routing_width = self.num_inputs * self.bus_pitch + self.bus_space - # Calculates height and width of row-decoder - # Calculates height and width of hierarchical decoder - # Add extra pitch for good measure - self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space - if (self.num_inputs == 4 or self.num_inputs == 5): - self.nand_width = self.and2.width - else: - self.nand_width = self.and3.width - - self.width = self.input_routing_width \ - + self.predecoder_width \ - + self.internal_routing_width \ - + self.nand_width \ - + self.m1_space - def route_inputs(self): """ Create input bus for the predecoders """ # Find the left-most predecoder diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 6293a79d..b0c24e1e 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -93,7 +93,7 @@ class port_address(design.design): decoder_out_pos = decoder_out_pin.rc() driver_in_pin = self.wordline_driver_inst.get_pin("in_{}".format(row)) driver_in_pos = driver_in_pin.lc() - self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos) + self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos, var_offset=0.3) self.add_via_stack_center(from_layer=decoder_out_pin.layer, to_layer=self.route_layer, diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_1rw_1r_test.py index 402b30a0..33bff4ed 100755 --- a/compiler/tests/18_port_address_1rw_1r_test.py +++ b/compiler/tests/18_port_address_1rw_1r_test.py @@ -29,6 +29,10 @@ class port_address_1rw_1r_test(openram_test): debug.info(1, "Port address 16 rows") a = factory.create("port_address", cols=16, rows=16) self.local_check(a) + + debug.info(1, "Port address 512 rows") + a = factory.create("port_address", cols=256, rows=512) + self.local_check(a) globals.end_openram() diff --git a/compiler/tests/18_port_address_test.py b/compiler/tests/18_port_address_test.py index a2a8bf0b..11da333e 100755 --- a/compiler/tests/18_port_address_test.py +++ b/compiler/tests/18_port_address_test.py @@ -24,6 +24,10 @@ class port_address_test(openram_test): a = factory.create("port_address", cols=16, rows=16) self.local_check(a) + debug.info(1, "Port address 512 rows") + a = factory.create("port_address", cols=256, rows=512) + self.local_check(a) + globals.end_openram() # run the test from the command line From e14deff3d144f497126cab50675b0074bb7ae247 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 4 Jun 2020 16:03:39 -0700 Subject: [PATCH 42/51] Fixed offset in port_data --- compiler/modules/port_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 34ad4ca9..5e7ed8e5 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -732,8 +732,8 @@ class port_data(design.design): top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc() layer_pitch = getattr(self, "{}_pitch".format(top_bl_pin.layer)) - self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V", top_bl_pin.by() - layer_pitch) - self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V", top_bl_pin.by() - 2 * layer_pitch) + self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V", fixed_offset=top_bl_pin.by() - layer_pitch) + self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V", fixed_offset=top_bl_pin.by() - 2 * layer_pitch) def graph_exclude_precharge(self): """Precharge adds a loop between bitlines, can be excluded to reduce complexity""" From 08f6bd8d241bd6aaa591a134002d8c1cf307ae41 Mon Sep 17 00:00:00 2001 From: jcirimel Date: Fri, 5 Jun 2020 02:53:03 -0700 Subject: [PATCH 43/51] optimize tx binning for area --- compiler/pgates/pgate.py | 146 ++++++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 57 deletions(-) diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 918cd17b..8e18d4d2 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -17,22 +17,39 @@ from globals import OPTS if(OPTS.tech_name == "s8"): from tech import nmos_bins, pmos_bins, accuracy_requirement + class pgate(design.design): """ This is a module that implements some shared functions for parameterized gates. """ - def __init__(self, name, height=None): + def __init__(self, name, height=None, add_wells=True): """ Creates a generic cell """ design.design.__init__(self, name) if height: self.height = height elif not height: - # By default, we make it 10 M1 pitch tall - self.height = 10*self.m1_pitch - + # By default, something simple + self.height = 14 * self.m1_pitch + self.add_wells = add_wells + + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + self.route_layer_width = getattr(self, "{}_width".format(self.route_layer)) + self.route_layer_space = getattr(self, "{}_space".format(self.route_layer)) + self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer)) + + # This is the space from a S/D contact to the supply rail + contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space + # This is a poly-to-poly of a flipped cell + poly_to_poly_gate_space = self.poly_extend_active + self.poly_space + self.top_bottom_space = max(contact_to_vdd_rail_space, + poly_to_poly_gate_space) + self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -47,27 +64,27 @@ class pgate(design.design): """ Pure virtual function """ debug.error("Must over-ride create_layout.", -1) - def connect_pin_to_rail(self, inst, pin, supply): + def connect_pin_to_rail(self, inst, pin_name, supply_name): """ Connects a ptx pin to a supply rail. """ - source_pin = inst.get_pin(pin) - supply_pin = self.get_pin(supply) - if supply_pin.overlaps(source_pin): - return - - if supply == "gnd": - height = supply_pin.by() - source_pin.by() - elif supply == "vdd": - height = supply_pin.uy() - source_pin.by() - else: - debug.error("Invalid supply name.", -1) + supply_pin = self.get_pin(supply_name) - if abs(height) > 0: - self.add_rect(layer="m1", + source_pins = inst.get_pins(pin_name) + for source_pin in source_pins: + + if supply_name == "gnd": + height = supply_pin.by() - source_pin.by() + elif supply_name == "vdd": + height = supply_pin.uy() - source_pin.by() + else: + debug.error("Invalid supply name.", -1) + + debug.check(supply_pin.layer == source_pin.layer, "Supply pin is not on correct layer.") + self.add_rect(layer=source_pin.layer, offset=source_pin.ll(), height=height, width=source_pin.width()) - def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left"): + def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None): """ Route the input gate to the left side of the cell for access. Position specifies to place the contact the left, center, or @@ -93,8 +110,6 @@ class pgate(design.design): # Center is completely symmetric. contact_width = contact.poly_contact.width - contact_m1_width = contact.poly_contact.second_layer_width - contact_m1_height = contact.poly_contact.second_layer_height if position == "center": contact_offset = left_gate_offset \ @@ -111,18 +126,16 @@ class pgate(design.design): else: debug.error("Invalid contact placement option.", -1) - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=contact_offset) - - self.add_via_center(layers=self.poly_stack, - offset=contact_offset) + via = self.add_via_stack_center(from_layer="poly", + to_layer=self.route_layer, + offset=contact_offset, + directions=directions) self.add_layout_pin_rect_center(text=name, - layer="m1", + layer=self.route_layer, offset=contact_offset, - width=contact_m1_width, - height=contact_m1_height) + width=via.mod.second_layer_width, + height=via.mod.second_layer_height) # This is to ensure that the contact is # connected to the gate mid_point = contact_offset.scale(0.5, 1) \ @@ -137,7 +150,7 @@ class pgate(design.design): # This should match the cells in the cell library self.nwell_y_offset = 0.48 * self.height - full_height = self.height + 0.5* self.m1_width + full_height = self.height + 0.5 * self.m1_width # FIXME: float rounding problem if "nwell" in layer: @@ -148,12 +161,12 @@ class pgate(design.design): nwell_height = nwell_max_offset - self.nwell_y_offset self.add_rect(layer="nwell", offset=nwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=nwell_height) if "vtg" in layer: self.add_rect(layer="vtg", offset=nwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=nwell_height) # Start this half a rail width below the cell @@ -164,12 +177,12 @@ class pgate(design.design): pwell_height = self.nwell_y_offset - pwell_position.y self.add_rect(layer="pwell", offset=pwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=pwell_height) if "vtg" in layer: self.add_rect(layer="vtg", offset=pwell_position, - width=self.well_width, + width=self.width + 2 * self.well_extend_active, height=pwell_height) def add_nwell_contact(self, pmos, pmos_pos): @@ -194,12 +207,10 @@ class pgate(design.design): self.nwell_contact = self.add_via_center(layers=layer_stack, offset=contact_offset, implant_type="n", - well_type="n") - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=contact_offset) + well_type="n", + directions=("V", "V")) - self.add_rect_center(layer="m1", + self.add_rect_center(layer=self.route_layer, offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)), width=self.nwell_contact.mod.second_layer_width, height=self.height - contact_offset.y) @@ -249,13 +260,10 @@ class pgate(design.design): self.pwell_contact= self.add_via_center(layers=layer_stack, offset=contact_offset, implant_type="p", - well_type="p") - - if hasattr(self, "li_stack"): - self.add_via_center(layers=self.li_stack, - offset=contact_offset) - - self.add_rect_center(layer="m1", + well_type="p", + directions=("V", "V")) + + self.add_rect_center(layer=self.route_layer, offset=contact_offset.scale(1, 0.5), width=self.pwell_contact.mod.second_layer_width, height=contact_offset.y) @@ -279,13 +287,33 @@ class pgate(design.design): # offset=implant_offset, # width=implant_width, # height=implant_height) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top and bottom. """ + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) def determine_width(self): """ Determine the width based on the well contacts (assumed to be on the right side) """ + + # It was already set or is left as default (minimum) # 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. + if self.add_wells: + width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width + # Height is an input parameter, so it is not recomputed. + else: + max_active_xoffset = self.find_highest_layer_coords("active").x + max_route_xoffset = self.find_highest_layer_coords(self.route_layer).x + 0.5 * self.m1_space + width = max(max_active_xoffset, max_route_xoffset) + + self.width = width @staticmethod def bin_width(tx_type, target_width): @@ -307,16 +335,20 @@ class pgate(design.design): base_bins = [] scaled_bins = [] scaling_factors = [] - scaled_bins.append(bins[-1]) - base_bins.append(bins[-1]) - scaling_factors.append(1) - for width in bins[0:-1]: + + for width in bins: m = math.ceil(target_width / width) base_bins.append(width) scaling_factors.append(m) scaled_bins.append(m * width) - - select = bisect_left(scaled_bins, target_width) + + select = -1 + for i in reversed(range(0, len(scaled_bins))): + if abs(target_width - scaled_bins[i])/target_width <= 1-accuracy_requirement: + select = i + break + if select == -1: + debug.error("failed to bin tx size {}, try reducing accuracy requirement".format(target_width), 1) scaling_factor = scaling_factors[select] scaled_bin = scaled_bins[select] selected_bin = base_bins[select] @@ -346,4 +378,4 @@ class pgate(design.design): return(scaled_bins) def bin_accuracy(self, ideal_width, width): - return abs(1-(ideal_width - width)/ideal_width) \ No newline at end of file + return abs(1-(ideal_width - width)/ideal_width) From fb3acae9087de2da778d2d032a962649e681cf79 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 09:44:30 -0700 Subject: [PATCH 44/51] PEP8 format testutils. --- compiler/tests/testutils.py | 136 ++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 4cae1a9c..eb75fc44 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -5,23 +5,21 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import unittest,warnings -import pdb,traceback -import sys,os,glob,copy -import shutil +import unittest +import sys, os, glob sys.path.append(os.getenv("OPENRAM_HOME")) from globals import OPTS import debug + class openram_test(unittest.TestCase): """ Base unit test that we have some shared classes in. """ - def local_drc_check(self, w): self.reset() - tempgds = "{0}{1}.gds".format(OPTS.openram_temp,w.name) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp, w.name) w.gds_write(tempgds) import verify @@ -36,8 +34,8 @@ class openram_test(unittest.TestCase): self.reset() - tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name) - tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name) + tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name) a.lvs_write(tempspice) # cannot write gds in netlist_only mode @@ -56,34 +54,36 @@ class openram_test(unittest.TestCase): # Only allow DRC to fail and LVS to pass if we are using magic if "magic" in OPTS.drc_exe and lvs_result == 0 and drc_result != 0: - #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) - #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) - #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + # import shutil + # zip_file = "/tmp/{0}_{1}".format(a.name, os.getpid()) + # debug.info(0, "Archiving failed files to {}.zip".format(zip_file)) + # shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) debug.warning("DRC failed but LVS passed: {}".format(a.name)) - #self.fail("DRC failed but LVS passed: {}".format(a.name)) + # self.fail("DRC failed but LVS passed: {}".format(a.name)) elif drc_result != 0: - #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) - #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) - #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + # import shutil + # zip_file = "/tmp/{0}_{1}".format(a.name, os.getpid()) + # debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + # shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("DRC failed: {}".format(a.name)) if lvs_result != 0: - #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) - #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) - #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + # import shutil + # zip_file = "/tmp/{0}_{1}".format(a.name, os.getpid()) + # debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) + # shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) self.fail("LVS mismatch: {}".format(a.name)) - # For debug... - #import pdb; pdb.set_trace() + # import pdb; pdb.set_trace() if OPTS.purge_temp: self.cleanup() def run_pex(self, a, output=None): if output == None: output = OPTS.openram_temp + a.name + ".pex.netlist" - tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name) - tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name) + tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name) import verify result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False) @@ -97,8 +97,8 @@ class openram_test(unittest.TestCase): """ debug.info(1, "Finding feasible period for current test.") delay_obj.set_load_slew(load, slew) - test_port = delay_obj.read_ports[0] #Only test one port, assumes other ports have similar period. - delay_obj.analysis_init(probe_address="1"*sram.addr_size, probe_data=(sram.word_size-1)) + test_port = delay_obj.read_ports[0] # Only test one port, assumes other ports have similar period. + delay_obj.analysis_init(probe_address="1" * sram.addr_size, probe_data=sram.word_size - 1) delay_obj.find_feasible_period_one_port(test_port) return delay_obj.period @@ -130,29 +130,27 @@ class openram_test(unittest.TestCase): for k in data.keys(): if type(data[k])==list: for i in range(len(data[k])): - if not self.isclose(k,data[k][i],golden_data[k][i],error_tolerance): + if not self.isclose(k, data[k][i], golden_data[k][i], error_tolerance): data_matches = False else: - if not self.isclose(k,data[k],golden_data[k],error_tolerance): + if not self.isclose(k, data[k], golden_data[k], error_tolerance): data_matches = False if not data_matches: import pprint data_string=pprint.pformat(data) - debug.error("Results exceeded {:.1f}% tolerance compared to golden results:\n".format(error_tolerance*100)+data_string) + debug.error("Results exceeded {:.1f}% tolerance compared to golden results:\n".format(error_tolerance * 100) + data_string) return data_matches - - - def isclose(self,key,value,actual_value,error_tolerance=1e-2): + def isclose(self, key, value, actual_value, error_tolerance=1e-2): """ This is used to compare relative values. """ import debug - relative_diff = self.relative_diff(value,actual_value) + relative_diff = self.relative_diff(value, actual_value) check = relative_diff <= error_tolerance if check: - debug.info(2,"CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100)) + debug.info(2, "CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key, value, actual_value, relative_diff * 100)) return True else: - debug.error("NOT CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100)) + debug.error("NOT CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key, value, actual_value, relative_diff * 100)) return False def relative_diff(self, value1, value2): @@ -169,18 +167,14 @@ class openram_test(unittest.TestCase): # Get normalization value norm_value = abs(max(value1, value2)) - # Edge case where greater is a zero - if norm_value == 0: - min_value = abs(min(value1, value2)) return abs(value1 - value2) / norm_value - - def relative_compare(self, value,actual_value,error_tolerance): + def relative_compare(self, value, actual_value, error_tolerance): """ This is used to compare relative values. """ if (value==actual_value): # if we don't need a relative comparison! return True - return (abs(value - actual_value) / max(value,actual_value) <= error_tolerance) + return (abs(value - actual_value) / max(value, actual_value) <= error_tolerance) def isapproxdiff(self, filename1, filename2, error_tolerance=0.001): """Compare two files. @@ -218,23 +212,22 @@ class openram_test(unittest.TestCase): line_num+=1 line1 = fp1.readline().decode('utf-8') line2 = fp2.readline().decode('utf-8') - #print("line1:",line1) - #print("line2:",line2) + # print("line1:", line1) + # print("line2:", line2) # 1. Find all of the floats using a regex line1_floats=rx.findall(line1) line2_floats=rx.findall(line2) - debug.info(3,"line1_floats: "+str(line1_floats)) - debug.info(3,"line2_floats: "+str(line2_floats)) - + debug.info(3, "line1_floats: " + str(line1_floats)) + debug.info(3, "line2_floats: " + str(line2_floats)) # 2. Remove the floats from the string for f in line1_floats: - line1=line1.replace(f,"",1) + line1=line1.replace(f, "", 1) for f in line2_floats: - line2=line2.replace(f,"",1) - #print("line1:",line1) - #print("line2:",line2) + line2=line2.replace(f, "", 1) + # print("line1:", line1) + # print("line2:", line2) # 3. Convert to floats rather than strings line1_floats = [float(x) for x in line1_floats] @@ -242,29 +235,29 @@ class openram_test(unittest.TestCase): # 4. Check if remaining string matches if line1 != line2: - #Uncomment if you want to see all the individual chars of the two lines - #print(str([i for i in line1])) - #print(str([i for i in line2])) + # Uncomment if you want to see all the individual chars of the two lines + # print(str([i for i in line1])) + # print(str([i for i in line2])) if mismatches==0: - debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1, filename2)) mismatches += 1 - debug.error("MISMATCH Line ({0}):\n{1}\n!=\n{2}".format(line_num,line1.rstrip('\n'),line2.rstrip('\n'))) + debug.error("MISMATCH Line ({0}):\n{1}\n!=\n{2}".format(line_num, line1.rstrip('\n'), line2.rstrip('\n'))) # 5. Now compare that the floats match elif len(line1_floats)!=len(line2_floats): if mismatches==0: - debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1, filename2)) mismatches += 1 - debug.error("MISMATCH Line ({0}) Length {1} != {2}".format(line_num,len(line1_floats),len(line2_floats))) + debug.error("MISMATCH Line ({0}) Length {1} != {2}".format(line_num, len(line1_floats), len(line2_floats))) else: - for (float1,float2) in zip(line1_floats,line2_floats): - relative_diff = self.relative_diff(float1,float2) + for (float1, float2) in zip(line1_floats, line2_floats): + relative_diff = self.relative_diff(float1, float2) check = relative_diff <= error_tolerance if not check: if mismatches==0: - debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1, filename2)) mismatches += 1 - debug.error("MISMATCH Line ({0}) Float {1} != {2} diff: {3:.1f}%".format(line_num,float1,float2,relative_diff*100)) + debug.error("MISMATCH Line ({0}) Float {1} != {2} diff: {3:.1f}%".format(line_num, float1, float2, relative_diff * 100)) # Only show the first 10 mismatch lines if not line1 and not line2 or mismatches>10: @@ -275,19 +268,18 @@ class openram_test(unittest.TestCase): # Never reached return False - - def isdiff(self,filename1,filename2): + def isdiff(self, filename1, filename2): """ This is used to compare two files and display the diff if they are different.. """ import debug import filecmp import difflib - check = filecmp.cmp(filename1,filename2) + check = filecmp.cmp(filename1, filename2) if not check: - debug.error("MISMATCH file1={0} file2={1}".format(filename1,filename2)) - f1 = open(filename1,mode="r",encoding='utf-8') + debug.error("MISMATCH file1={0} file2={1}".format(filename1, filename2)) + f1 = open(filename1, mode="r", encoding='utf-8') s1 = f1.readlines() f1.close() - f2 = open(filename2,mode="r",encoding='utf-8') + f2 = open(filename2, mode="r", encoding='utf-8') s2 = f2.readlines() f2.close() mismatches=0 @@ -302,10 +294,13 @@ class openram_test(unittest.TestCase): return False return False else: - debug.info(2,"MATCH {0} {1}".format(filename1,filename2)) + debug.info(2, "MATCH {0} {1}".format(filename1, filename2)) return True + def dbg(): + import pdb; pdb.set_trace() + def header(filename, technology): # Skip the header for gitlab regression import getpass @@ -319,14 +314,18 @@ def header(filename, technology): print("|=========" + tst.center(60) + "=========|") print("|=========" + technology.center(60) + "=========|") print("|=========" + filename.center(60) + "=========|") - from globals import OPTS + from globals import OPTS print("|=========" + OPTS.openram_temp.center(60) + "=========|") print("|==============================================================================|") + def debugTestRunner(post_mortem=None): """unittest runner doing post mortem debugging on failing tests""" + import pdb + import traceback if post_mortem is None and not OPTS.purge_temp: post_mortem = pdb.post_mortem + class DebugTestResult(unittest.TextTestResult): def addError(self, test, err): # called before tearDown() @@ -334,9 +333,10 @@ def debugTestRunner(post_mortem=None): if post_mortem: post_mortem(err[2]) super(DebugTestResult, self).addError(test, err) + def addFailure(self, test, err): traceback.print_exception(*err) - if post_mortem: + if post_mortem: post_mortem(err[2]) super(DebugTestResult, self).addFailure(test, err) return unittest.TextTestRunner(resultclass=DebugTestResult) From 68ffb94d2e36ae22f5ff3198577d0489798b6bdd Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 09:55:57 -0700 Subject: [PATCH 45/51] Rename 05 test to 14 --- ...a_pbitcell_array_test.py => 14_replica_pbitcell_array_test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler/tests/{05_replica_pbitcell_array_test.py => 14_replica_pbitcell_array_test.py} (100%) diff --git a/compiler/tests/05_replica_pbitcell_array_test.py b/compiler/tests/14_replica_pbitcell_array_test.py similarity index 100% rename from compiler/tests/05_replica_pbitcell_array_test.py rename to compiler/tests/14_replica_pbitcell_array_test.py From 2e7f9395f2e3022b5cd086ddf461888b9f8dbf04 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 09:57:16 -0700 Subject: [PATCH 46/51] Rename 05 test to 14 --- .../tests/05_replica_bitcell_array_test.py | 38 ------------------- .../tests/14_replica_bitcell_array_test.py | 9 ++++- 2 files changed, 7 insertions(+), 40 deletions(-) delete mode 100755 compiler/tests/05_replica_bitcell_array_test.py diff --git a/compiler/tests/05_replica_bitcell_array_test.py b/compiler/tests/05_replica_bitcell_array_test.py deleted file mode 100755 index d229b99c..00000000 --- a/compiler/tests/05_replica_bitcell_array_test.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2019 Regents of the University of California -# All rights reserved. -# -import unittest -from testutils import * -import sys,os -sys.path.append(os.getenv("OPENRAM_HOME")) -import globals -from globals import OPTS -from sram_factory import factory -import debug - -class replica_bitcell_array_test(openram_test): - - def runTest(self): - config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) - globals.init_openram(config_file) - - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 0 - OPTS.num_w_ports = 0 - - factory.reset() - debug.info(2, "Testing 4x4 array for bitcell") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0]) - self.local_check(a) - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/14_replica_bitcell_array_test.py b/compiler/tests/14_replica_bitcell_array_test.py index 19413cd5..d229b99c 100755 --- a/compiler/tests/14_replica_bitcell_array_test.py +++ b/compiler/tests/14_replica_bitcell_array_test.py @@ -19,10 +19,15 @@ class replica_bitcell_array_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(2, "Testing 4x4 array for 6t_cell") + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + + factory.reset() + debug.info(2, "Testing 4x4 array for bitcell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0]) self.local_check(a) - + globals.end_openram() # run the test from the command line From a62b85a6b154941158552b430166e4e9462299f5 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 11:29:31 -0700 Subject: [PATCH 47/51] Update mirroring in port_data for bitcell mirrored arrays --- compiler/modules/port_data.py | 21 ++++++++----------- compiler/modules/precharge_array.py | 6 +++--- compiler/modules/sense_amp_array.py | 20 ++++++++---------- .../modules/single_level_column_mux_array.py | 6 +++--- compiler/modules/write_driver_array.py | 5 +++-- compiler/modules/write_mask_and_array.py | 4 ++-- ...ngle_level_column_mux_array_1rw_1r_test.py | 4 ++-- ...le_level_column_mux_array_pbitcell_test.py | 8 +++---- .../tests/08_precharge_array_1rw_1r_test.py | 16 ++++++-------- compiler/tests/08_precharge_array_test.py | 3 +-- compiler/tests/09_sense_amp_array_test.py | 1 - compiler/tests/18_port_data_1rw_1r_test.py | 1 + 12 files changed, 43 insertions(+), 52 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 5e7ed8e5..106f2e85 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -178,14 +178,17 @@ class port_data(design.design): # Extra column +1 is for RBL # Precharge will be shifted left if needed + # Column offset is set to port so extra column can be on left or right + # and mirroring happens correctly self.precharge_array = factory.create(module_type="precharge_array", columns=self.num_cols + 1, - port=self.port, bitcell_bl=self.bl_names[self.port], - bitcell_br=self.br_names[self.port]) + bitcell_br=self.br_names[self.port], + column_offset=self.port - 1) self.add_mod(self.precharge_array) if self.port in self.read_ports: + # RBLs don't get a sense amp self.sense_amp_array = factory.create(module_type="sense_amp_array", word_size=self.word_size, words_per_row=self.words_per_row) @@ -194,9 +197,9 @@ class port_data(design.design): self.sense_amp_array = None if self.col_addr_size > 0: + # RBLs dont get a col mux self.column_mux_array = factory.create(module_type="column_mux_array", columns=self.num_cols, - port=self.port, word_size=self.word_size, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) @@ -205,17 +208,18 @@ class port_data(design.design): self.column_mux_array = None if self.port in self.write_ports: + # RBLs dont get a write driver self.write_driver_array = factory.create(module_type="write_driver_array", columns=self.num_cols, word_size=self.word_size, write_size=self.write_size) self.add_mod(self.write_driver_array) if self.write_size is not None: + # RBLs don't get a write mask self.write_mask_and_array = factory.create(module_type="write_mask_and_array", columns=self.num_cols, word_size=self.word_size, - write_size=self.write_size, - port = self.port) + write_size=self.write_size) self.add_mod(self.write_mask_and_array) else: self.write_mask_and_array = None @@ -248,13 +252,6 @@ class port_data(design.design): self.precharge = factory.create(module_type="precharge", bitcell_bl=self.bl_names[0], bitcell_br=self.br_names[0]) - # We create a dummy here to get bl/br names to add those pins to this - # module, which happens before we create the real precharge_array - self.precharge_array = factory.create(module_type="precharge_array", - columns=self.num_cols + 1, - port=self.port, - bitcell_bl=self.bl_names[self.port], - bitcell_br=self.br_names[self.port]) def create_precharge_array(self): """ Creating Precharge """ diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 2acb1063..84f27fba 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -18,16 +18,16 @@ class precharge_array(design.design): of bit line columns, height is the height of the bit-cell array. """ - def __init__(self, name, columns, port, size=1, bitcell_bl="bl", bitcell_br="br"): + def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br", column_offset=0): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br)) self.columns = columns self.size = size - self.port = port self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br + self.column_offset = column_offset self.create_netlist() if not OPTS.netlist_only: @@ -106,7 +106,7 @@ class precharge_array(design.design): xoffset = 0 for i in range(self.columns): tempx = xoffset - if cell_properties.bitcell.mirror.y and (i + 1 + self.port) % 2: + if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: mirror = "MY" tempx = tempx + self.pc_cell.width else: diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index cb413c45..c532c54a 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -20,7 +20,7 @@ class sense_amp_array(design.design): Dynamically generated sense amp array for all bitlines. """ - def __init__(self, name, word_size, words_per_row): + def __init__(self, name, word_size, words_per_row, column_offset=0): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("word_size {0}".format(word_size)) @@ -28,6 +28,7 @@ class sense_amp_array(design.design): self.word_size = word_size self.words_per_row = words_per_row + self.column_offset = column_offset self.row_size = self.word_size * self.words_per_row self.create_netlist() @@ -102,25 +103,22 @@ class sense_amp_array(design.design): def place_sense_amp_array(self): from tech import cell_properties if self.bitcell.width > self.amp.width: - amp_spacing = self.bitcell.width * self.words_per_row + amp_spacing = self.bitcell.width else: - amp_spacing = self.amp.width * self.words_per_row + amp_spacing = self.amp.width - for i in range(0, self.word_size): - xoffset = amp_spacing * i + for i in range(0, self.row_size, self.words_per_row): + index = int(i / self.words_per_row) + xoffset = i * amp_spacing - # align the xoffset to the grid of bitcells. This way we - # know when to do the mirroring. - grid_x = int(xoffset / self.amp.width) - - if cell_properties.bitcell.mirror.y and grid_x % 2: + if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: mirror = "MY" xoffset = xoffset + self.amp.width else: mirror = "" amp_position = vector(xoffset, 0) - self.local_insts[i].place(offset=amp_position, mirror=mirror) + self.local_insts[index].place(offset=amp_position, mirror=mirror) def add_layout_pins(self): for i in range(len(self.local_insts)): diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 3d80525a..cc38cb45 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -20,17 +20,17 @@ class single_level_column_mux_array(design.design): Array of column mux to read the bitlines through the 6T. """ - def __init__(self, name, columns, port, word_size, bitcell_bl="bl", bitcell_br="br"): + def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br", column_offset=0): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br)) self.columns = columns - self.port = port self.word_size = word_size self.words_per_row = int(self.columns / self.word_size) self.bitcell_bl = bitcell_bl self.bitcell_br = bitcell_br + self.column_offset = column_offset if "li" in layer: self.col_mux_stack = self.li_stack @@ -118,7 +118,7 @@ class single_level_column_mux_array(design.design): # For every column, add a pass gate for col_num in range(self.columns): xoffset = col_num * self.mux.width - if cell_properties.bitcell.mirror.y and (col_num + self.port) % 2: + if cell_properties.bitcell.mirror.y and (col_num + self.column_offset) % 2: mirror = "MY" xoffset = xoffset + self.mux.width else: diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 49e830e2..105cacc0 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -18,7 +18,7 @@ class write_driver_array(design.design): Dynamically generated write driver array of all bitlines. """ - def __init__(self, name, columns, word_size, write_size=None): + def __init__(self, name, columns, word_size, write_size=None, column_offset=0): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) @@ -27,6 +27,7 @@ class write_driver_array(design.design): self.columns = columns self.word_size = word_size self.write_size = write_size + self.column_offset = column_offset self.words_per_row = int(columns / word_size) if self.write_size: @@ -128,7 +129,7 @@ class write_driver_array(design.design): index = int(i / self.words_per_row) xoffset = i * self.driver_spacing - if cell_properties.bitcell.mirror.y and i % 2: + if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2: mirror = "MY" xoffset = xoffset + self.driver.width else: diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index e660d519..c949fe58 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -18,7 +18,7 @@ class write_mask_and_array(design.design): The write mask AND array goes between the write driver array and the sense amp array. """ - def __init__(self, name, columns, word_size, write_size, port=0): + def __init__(self, name, columns, word_size, write_size, column_offset=0): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) @@ -28,7 +28,7 @@ class write_mask_and_array(design.design): self.columns = columns self.word_size = word_size self.write_size = write_size - self.port = port + self.column_offset self.words_per_row = int(columns / word_size) self.num_wmasks = int(word_size / write_size) diff --git a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py index a8d804c9..2a3a8f87 100755 --- a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py +++ b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py @@ -26,11 +26,11 @@ class single_level_column_mux_test(openram_test): OPTS.num_w_ports = 0 debug.info(1, "Testing sample for 4-way column_mux_array port 0") - a = factory.create(module_type="single_level_column_mux_array", columns=8, port=0, word_size=2, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array port 1") - a = factory.create(module_type="single_level_column_mux_array", columns=8, port=0, word_size=2, bitcell_bl="bl1", bitcell_br="br1") + a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl1", bitcell_br="br1") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py index 7feb067e..663ff075 100755 --- a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py +++ b/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py @@ -29,19 +29,19 @@ class single_level_column_mux_pbitcell_test(openram_test): factory.reset() debug.info(1, "Testing sample for 2-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=8, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, port=0, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, port=3, word_size=4, bitcell_bl="bl2", bitcell_br="br2") + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2", column_offset=3) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/08_precharge_array_1rw_1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py index dde36b23..7d54e02b 100755 --- a/compiler/tests/08_precharge_array_1rw_1r_test.py +++ b/compiler/tests/08_precharge_array_1rw_1r_test.py @@ -28,18 +28,14 @@ class precharge_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 factory.reset() - debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") - pc = factory.create(module_type="precharge_array", columns=3, port=0, bitcell_bl="bl0", bitcell_br="br0") + debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 0)") + pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(pc) + + debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 1)") + pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0", column_offset=1) self.local_check(pc) - # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") - # pc = precharge_array.precharge_array(name="pre3", columns=3, bitcell_bl="bl0", bitcell_br="br0") - # self.local_check(pc) - - # debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)") - # pc = precharge_array.precharge_array(name="pre4", columns=3, bitcell_bl="bl2", bitcell_br="br2") - # self.local_check(pc) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index c3b823b3..47843ca3 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -21,9 +21,8 @@ class precharge_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # check precharge array in single port debug.info(2, "Checking 3 column precharge") - pc = factory.create(module_type="precharge_array", columns=3, port=0) + pc = factory.create(module_type="precharge_array", columns=3) self.local_check(pc) globals.end_openram() diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index 459113f2..c71a75e8 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -21,7 +21,6 @@ class sense_amp_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # check sense amp array for single port debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=1") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=1) self.local_check(a) diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index 8081416e..3d415ec0 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -13,6 +13,7 @@ from globals import OPTS from sram_factory import factory import debug + class port_data_1rw_1r_test(openram_test): def runTest(self): From 4fef632dceb4ad9cb5d6f4ab29bbdaacf7f837af Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 12:13:41 -0700 Subject: [PATCH 48/51] Fix syntax error --- compiler/modules/write_mask_and_array.py | 2 +- compiler/tests/07_single_level_column_mux_array_test.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index c949fe58..9c077ed7 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -28,7 +28,7 @@ class write_mask_and_array(design.design): self.columns = columns self.word_size = word_size self.write_size = write_size - self.column_offset + self.column_offset = column_offset self.words_per_row = int(columns / word_size) self.num_wmasks = int(word_size / write_size) diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index ff7ec1c9..b1f74eba 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -21,15 +21,15 @@ class single_level_column_mux_test(openram_test): globals.init_openram(config_file) debug.info(1, "Testing sample for 2-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=8) + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8) self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=16, port=0, word_size=4) + a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4) self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=32, port=0, word_size=4) + a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4) self.local_check(a) globals.end_openram() From 00b51f546478b6a509541b4b5713a460d1d922bd Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 13:49:32 -0700 Subject: [PATCH 49/51] PEP8 format replica_bitcell_array --- compiler/modules/replica_bitcell_array.py | 148 ++++++++++------------ 1 file changed, 69 insertions(+), 79 deletions(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index ff2260d3..05cb1ac6 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -32,8 +32,10 @@ class replica_bitcell_array(design.design): self.right_rbl = right_rbl self.bitcell_ports = bitcell_ports - debug.check(left_rbl+right_rbl==len(self.all_ports),"Invalid number of RBLs for port configuration.") - debug.check(left_rbl+right_rbl==len(self.bitcell_ports),"Bitcell ports must match total RBLs.") + debug.check(left_rbl + right_rbl == len(self.all_ports), + "Invalid number of RBLs for port configuration.") + debug.check(left_rbl + right_rbl == len(self.bitcell_ports), + "Bitcell ports must match total RBLs.") # Two dummy rows/cols plus replica for each port self.extra_rows = 2 + left_rbl + right_rbl @@ -45,8 +47,7 @@ class replica_bitcell_array(design.design): # We don't offset this because we need to align # the replica bitcell in the control logic - #self.offset_all_coordinates() - + # self.offset_all_coordinates() def create_netlist(self): """ Create and connect the netlist """ @@ -90,15 +91,15 @@ class replica_bitcell_array(design.design): # Replica bitlines self.replica_columns = {} - for bit in range(self.left_rbl+self.right_rbl): + for bit in range(self.left_rbl + self.right_rbl): # Creating left_rbl if bit1 port) for port in range(self.left_rbl): # Make names for all RBLs - wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.cell.get_all_wl_names()))] + wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))] # Keep track of the pin that is the RBL self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] self.replica_col_wl_names.extend(wl_names) # Regular WLs self.replica_col_wl_names.extend(self.bitcell_array_wl_names) # Right port WLs (one dummy for each port when we allow >1 port) - for port in range(self.left_rbl,self.left_rbl+self.right_rbl): + for port in range(self.left_rbl, self.left_rbl + self.right_rbl): # Make names for all RBLs - wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.cell.get_all_wl_names()))] + wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))] # Keep track of the pin that is the RBL self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] self.replica_col_wl_names.extend(wl_names) @@ -195,14 +195,13 @@ class replica_bitcell_array(design.design): # Left/right dummy columns are connected identically to the replica column self.dummy_col_wl_names = self.replica_col_wl_names - # Per port bitline names self.replica_bl_names = {} self.replica_wl_names = {} # Array of all port bitline names - for port in range(self.left_rbl+self.right_rbl): - left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x),port) for x in range(len(self.all_ports))] - right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x),port) for x in range(len(self.all_ports))] + for port in range(self.left_rbl + self.right_rbl): + left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x), port) for x in range(len(self.all_ports))] + right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x), port) for x in range(len(self.all_ports))] # Keep track of the left pins that are the RBL self.rbl_bl_names[port]=left_names[self.bitcell_ports[port]] self.rbl_br_names[port]=right_names[self.bitcell_ports[port]] @@ -210,28 +209,25 @@ class replica_bitcell_array(design.design): bl_names = [x for t in zip(left_names, right_names) for x in t] self.replica_bl_names[port] = bl_names - wl_names = ["rbl_{0}_{1}".format(x,port) for x in self.cell.get_all_wl_names()] - #wl_names[port] = "rbl_wl{}".format(port) + wl_names = ["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()] self.replica_wl_names[port] = wl_names - # External pins self.add_pin_list(self.bitcell_array_bl_names, "INOUT") # Need to sort by port order since dictionary values may not be in order bl_names = [self.rbl_bl_names[x] for x in sorted(self.rbl_bl_names.keys())] br_names = [self.rbl_br_names[x] for x in sorted(self.rbl_br_names.keys())] - for (bl_name,br_name) in zip(bl_names,br_names): - self.add_pin(bl_name,"OUTPUT") - self.add_pin(br_name,"OUTPUT") + for (bl_name, br_name) in zip(bl_names, br_names): + self.add_pin(bl_name, "OUTPUT") + self.add_pin(br_name, "OUTPUT") self.add_pin_list(self.bitcell_array_wl_names, "INPUT") # Need to sort by port order since dictionary values may not be in order wl_names = [self.rbl_wl_names[x] for x in sorted(self.rbl_wl_names.keys())] for pin_name in wl_names: - self.add_pin(pin_name,"INPUT") + self.add_pin(pin_name, "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - def create_instances(self): """ Create the module instances used in this design """ @@ -247,77 +243,75 @@ class replica_bitcell_array(design.design): # Replica columns self.replica_col_inst = {} - for port in range(self.left_rbl+self.right_rbl): + for port in range(self.left_rbl + self.right_rbl): self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port), - mod=self.replica_columns[port]) + mod=self.replica_columns[port]) self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies) - # Dummy rows under the bitcell array (connected with with the replica cell wl) self.dummy_row_replica_inst = {} - for port in range(self.left_rbl+self.right_rbl): + for port in range(self.left_rbl + self.right_rbl): self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port), mod=self.dummy_row) self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names[port] + supplies) - # Top/bottom dummy rows or col caps self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot", mod=self.edge_row) - self.connect_inst(self.dummy_row_bl_names + [x+"_bot" for x in self.dummy_cell_wl_names] + supplies) + self.connect_inst(self.dummy_row_bl_names + [x + "_bot" for x in self.dummy_cell_wl_names] + supplies) self.dummy_row_top_inst=self.add_inst(name="dummy_row_top", mod=self.edge_row) - self.connect_inst(self.dummy_row_bl_names + [x+"_top" for x in self.dummy_cell_wl_names] + supplies) - + self.connect_inst(self.dummy_row_bl_names + [x + "_top" for x in self.dummy_cell_wl_names] + supplies) # Left/right Dummy columns self.dummy_col_left_inst=self.add_inst(name="dummy_col_left", mod=self.edge_col_left) - self.connect_inst([x+"_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) + self.connect_inst([x + "_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) self.dummy_col_right_inst=self.add_inst(name="dummy_col_right", - mod=self.edge_col_right) - self.connect_inst([x+"_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) + mod=self.edge_col_right) + self.connect_inst([x + "_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) def create_layout(self): - self.height = (self.row_size+self.extra_rows)*self.dummy_row.height - self.width = (self.column_size+self.extra_cols)*self.cell.width + self.height = (self.row_size + self.extra_rows) * self.dummy_row.height + self.width = (self.column_size + self.extra_cols) * self.cell.width # This is a bitcell x bitcell offset to scale offset = vector(self.cell.width, self.cell.height) - self.bitcell_array_inst.place(offset=[0,0]) + self.bitcell_array_inst.place(offset=[0, 0]) # To the left of the bitcell array for bit in range(self.left_rbl): - self.replica_col_inst[bit].place(offset=offset.scale(-bit-1,-self.left_rbl-1)) + self.replica_col_inst[bit].place(offset=offset.scale(-bit - 1, -self.left_rbl - 1)) # To the right of the bitcell array for bit in range(self.right_rbl): - self.replica_col_inst[self.left_rbl+bit].place(offset=offset.scale(bit,-self.left_rbl-1)+self.bitcell_array_inst.lr()) + self.replica_col_inst[self.left_rbl + bit].place(offset=offset.scale(bit, -self.left_rbl - 1) + self.bitcell_array_inst.lr()) + # FIXME: These depend on the array size itself # Far top dummy row (first row above array is NOT flipped) - flip_dummy = self.right_rbl%2 - self.dummy_row_top_inst.place(offset=offset.scale(0,self.right_rbl+flip_dummy)+self.bitcell_array_inst.ul(), + flip_dummy = self.right_rbl % 2 + self.dummy_row_top_inst.place(offset=offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul(), mirror="MX" if flip_dummy else "R0") + # FIXME: These depend on the array size itself # Far bottom dummy row (first row below array IS flipped) - flip_dummy = (self.left_rbl+1)%2 - self.dummy_row_bot_inst.place(offset=offset.scale(0,-self.left_rbl-1+flip_dummy), + flip_dummy = (self.left_rbl + 1) % 2 + self.dummy_row_bot_inst.place(offset=offset.scale(0, -self.left_rbl - 1 + flip_dummy), mirror="MX" if flip_dummy else "R0") # Far left dummy col - self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl-1,-self.left_rbl-1)) + self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl - 1, -self.left_rbl - 1)) # Far right dummy col - self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl,-self.left_rbl-1)+self.bitcell_array_inst.lr()) + self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr()) # Replica dummy rows for bit in range(self.left_rbl): - self.dummy_row_replica_inst[bit].place(offset=offset.scale(0,-bit-bit%2), - mirror="R0" if bit%2 else "MX") + self.dummy_row_replica_inst[bit].place(offset=offset.scale(0, -bit - bit % 2), + mirror="R0" if bit % 2 else "MX") for bit in range(self.right_rbl): - self.dummy_row_replica_inst[self.left_rbl+bit].place(offset=offset.scale(0,bit+bit%2)+self.bitcell_array_inst.ul(), - mirror="MX" if bit%2 else "R0") + self.dummy_row_replica_inst[self.left_rbl + bit].place(offset=offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(), + mirror="MX" if bit % 2 else "R0") - - self.translate_all(offset.scale(-1-self.left_rbl,-1-self.left_rbl)) + self.translate_all(offset.scale(-1 - self.left_rbl, -1 - self.left_rbl)) self.add_layout_pins() @@ -325,7 +319,6 @@ class replica_bitcell_array(design.design): self.DRC_LVS() - def add_layout_pins(self): """ Add the layout pins """ @@ -338,7 +331,7 @@ class replica_bitcell_array(design.design): for pin in pin_list: self.add_layout_pin(text=pin_name, layer=pin.layer, - offset=pin.ll().scale(0,1), + offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) for bitline in self.bitcell_array_bl_names: @@ -347,17 +340,16 @@ class replica_bitcell_array(design.design): for pin in pin_list: self.add_layout_pin(text=pin_name, layer=pin.layer, - offset=pin.ll().scale(1,0), + offset=pin.ll().scale(1, 0), width=pin.width(), height=self.height) - # Replica wordlines - for port in range(self.left_rbl+self.right_rbl): + for port in range(self.left_rbl + self.right_rbl): inst = self.replica_col_inst[port] - for (pin_name,wl_name) in zip(self.cell.get_all_wl_names(),self.replica_wl_names[port]): + for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.replica_wl_names[port]): # +1 for dummy row - pin_bit = port+1 + pin_bit = port + 1 # +row_size if above the array if port>=self.left_rbl: pin_bit += self.row_size @@ -367,7 +359,7 @@ class replica_bitcell_array(design.design): if wl_name in self.rbl_wl_names.values(): self.add_layout_pin(text=wl_name, layer=pin.layer, - offset=pin.ll().scale(0,1), + offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) @@ -416,8 +408,6 @@ class replica_bitcell_array(design.design): def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" - from tech import drc, parameter - # Dynamic Power from Bitline bl_wire = self.gen_bl_wire() cell_load = 2 * bl_wire.return_input_cap() @@ -425,10 +415,10 @@ class replica_bitcell_array(design.design): freq = spice["default_event_frequency"] bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) - #Calculate the bitcell power which currently only includes leakage + # Calculate the bitcell power which currently only includes leakage cell_power = self.cell.analytical_power(corner, load) - #Leakage power grows with entire array and bitlines. + # Leakage power grows with entire array and bitlines. total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, cell_power.leakage * self.column_size * self.row_size) return total_power @@ -439,13 +429,13 @@ class replica_bitcell_array(design.design): else: height = self.height bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_m1")) + bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1")) bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell return bl_wire def get_wordline_cin(self): """Get the relative input capacitance from the wordline connections in all the bitcell""" - #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns + # A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns bitcell_wl_cin = self.cell.get_wl_cin() total_cin = bitcell_wl_cin * self.column_size return total_cin @@ -457,9 +447,9 @@ class replica_bitcell_array(design.design): def graph_exclude_replica_col_bits(self): """Exclude all replica/dummy cells in the replica columns except the replica bit.""" - for port in range(self.left_rbl+self.right_rbl): + for port in range(self.left_rbl + self.right_rbl): self.replica_columns[port].exclude_all_but_replica() def get_cell_name(self, inst_name, row, col): """Gets the spice name of the target bitcell.""" - return self.bitcell_array.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col) + return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col) From 551499670809b0293def390d77e8c28e3a30cfed Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 15:09:22 -0700 Subject: [PATCH 50/51] Auto-generate port dependent cell names. --- compiler/globals.py | 13 ++--- compiler/modules/col_cap_array.py | 4 +- compiler/modules/dummy_array.py | 9 ++-- compiler/modules/replica_column.py | 50 +++++++++---------- compiler/modules/row_cap_array.py | 2 +- compiler/options.py | 5 +- compiler/sram_factory.py | 4 +- .../04_single_level_column_mux_1rw_1r_test.py | 4 +- .../tests/05_bitcell_1rw_1r_array_test.py | 4 +- .../06_hierarchical_decoder_1rw_1r_test.py | 4 +- .../06_hierarchical_decoder_pbitcell_test.py | 4 +- ...6_hierarchical_predecode2x4_1rw_1r_test.py | 3 +- ...hierarchical_predecode2x4_pbitcell_test.py | 4 +- ...6_hierarchical_predecode3x8_1rw_1r_test.py | 4 +- ...hierarchical_predecode3x8_pbitcell_test.py | 4 +- ...ngle_level_column_mux_array_1rw_1r_test.py | 4 +- .../tests/08_precharge_array_1rw_1r_test.py | 2 +- .../08_wordline_driver_array_1rw_1r_test.py | 2 +- .../14_replica_bitcell_1rw_1r_array_test.py | 12 ++--- compiler/tests/18_port_address_1rw_1r_test.py | 4 +- compiler/tests/18_port_data_1rw_1r_test.py | 4 +- compiler/tests/19_single_bank_1rw_1r_test.py | 4 +- compiler/tests/19_single_bank_1w_1r_test.py | 5 +- .../tests/19_single_bank_wmask_1rw_1r_test.py | 4 ++ .../tests/20_psram_1bank_2mux_1rw_1w_test.py | 3 +- .../tests/20_psram_1bank_2mux_1w_1r_test.py | 3 +- compiler/tests/20_psram_1bank_2mux_test.py | 6 +-- .../tests/20_psram_1bank_4mux_1rw_1r_test.py | 3 +- .../tests/20_sram_1bank_2mux_1rw_1r_test.py | 4 +- .../tests/20_sram_1bank_2mux_1w_1r_test.py | 4 +- .../tests/20_sram_1bank_8mux_1rw_1r_test.py | 6 +-- .../tests/20_sram_1bank_nomux_1rw_1r_test.py | 4 +- .../22_sram_1rw_1r_1bank_nomux_func_test.py | 4 +- .../tests/22_sram_wmask_1w_1r_func_test.py | 7 +-- 34 files changed, 82 insertions(+), 121 deletions(-) diff --git a/compiler/globals.py b/compiler/globals.py index 92780c1a..c9469330 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -214,25 +214,18 @@ def setup_bitcell(): if OPTS.num_r_ports > 0: ports += "{}r".format(OPTS.num_r_ports) - OPTS.bitcell = "bitcell_"+ports - OPTS.replica_bitcell = "replica_bitcell_"+ports - OPTS.dummy_bitcell = "dummy_bitcell_"+ports - else: - OPTS.replica_bitcell = "replica_" + OPTS.bitcell - OPTS.replica_bitcell = "dummy_" + OPTS.bitcell + if ports != "": + OPTS.bitcell_suffix = "_" + ports + OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix # See if bitcell exists try: __import__(OPTS.bitcell) - __import__(OPTS.replica_bitcell) - __import__(OPTS.dummy_bitcell) except ImportError: # Use the pbitcell if we couldn't find a custom bitcell # or its custom replica bitcell # Use the pbitcell (and give a warning if not in unit test mode) OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell = "replica_pbitcell" - OPTS.replica_bitcell = "dummy_pbitcell" if not OPTS.is_unit_test: debug.warning("Using the parameterized bitcell which may have suboptimal density.") debug.info(1, "Using bitcell: {}".format(OPTS.bitcell)) diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 3119f4e5..d74ab80a 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -8,6 +8,7 @@ from sram_factory import factory from globals import OPTS from tech import cell_properties + class col_cap_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. @@ -35,8 +36,7 @@ class col_cap_array(bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ - # self.dummy_cell = factory.create(module_type="col_cap_bitcell_1rw_1r") # TODO: make module_type generic - self.dummy_cell = factory.create(module_type="col_cap_bitcell") + self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell)) self.add_mod(self.dummy_cell) self.cell = factory.create(module_type="bitcell") diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index de15d0ce..f4b240da 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -38,20 +38,19 @@ class dummy_array(bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ - self.dummy_cell = factory.create(module_type="dummy_bitcell") + self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell)) self.add_mod(self.dummy_cell) self.cell = factory.create(module_type="bitcell") - def create_instances(self): """ Create the module instances used in this design """ self.cell_inst = {} for col in range(self.column_size): for row in range(self.row_size): name = "bit_r{0}_c{1}".format(row, col) - self.cell_inst[row,col]=self.add_inst(name=name, - mod=self.dummy_cell) + self.cell_inst[row, col]=self.add_inst(name=name, + mod=self.dummy_cell) self.connect_inst(self.get_bitcell_pins(col, row)) def input_load(self): @@ -60,7 +59,7 @@ class dummy_array(bitcell_base_array): def get_wordline_cin(self): """Get the relative input capacitance from the wordline connections in all the bitcell""" - #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns + # A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns bitcell_wl_cin = self.cell.get_wl_cin() total_cin = bitcell_wl_cin * self.column_size return total_cin diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 4ea1b7df..afe87497 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -5,12 +5,12 @@ # import debug import design -from tech import drc, cell_properties -import contact +from tech import cell_properties from sram_factory import factory from vector import vector from globals import OPTS + class replica_column(design.design): """ Generate a replica bitline column for the replica array. @@ -29,11 +29,12 @@ class replica_column(design.design): self.right_rbl = right_rbl self.replica_bit = replica_bit # left, right, regular rows plus top/bottom dummy cells - self.total_size = self.left_rbl+rows+self.right_rbl+2 + self.total_size = self.left_rbl + rows + self.right_rbl + 2 self.column_offset = column_offset - debug.check(replica_bit!=0 and replica_bit!=rows,"Replica bit cannot be the dummy row.") - debug.check(replica_bit<=left_rbl or replica_bit>=self.total_size-right_rbl-1, + debug.check(replica_bit != 0 and replica_bit != rows, + "Replica bit cannot be the dummy row.") + debug.check(replica_bit <= left_rbl or replica_bit >= self.total_size - right_rbl - 1, "Replica bit cannot be in the regular array.") self.create_netlist() @@ -46,7 +47,7 @@ class replica_column(design.design): self.create_instances() def create_layout(self): - self.height = self.total_size*self.cell.height + self.height = self.total_size * self.cell.height self.width = self.cell.width self.place_instances() @@ -58,25 +59,25 @@ class replica_column(design.design): for bl_name in self.cell.get_all_bitline_names(): # In the replica column, these are only outputs! - self.add_pin("{0}_{1}".format(bl_name,0), "OUTPUT") + self.add_pin("{0}_{1}".format(bl_name, 0), "OUTPUT") for row in range(self.total_size): for wl_name in self.cell.get_all_wl_names(): - self.add_pin("{0}_{1}".format(wl_name,row), "INPUT") + self.add_pin("{0}_{1}".format(wl_name, row), "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") def add_modules(self): - self.replica_cell = factory.create(module_type="replica_bitcell") + self.replica_cell = factory.create(module_type="replica_{}".format(OPTS.bitcell)) self.add_mod(self.replica_cell) - self.dummy_cell = factory.create(module_type="dummy_bitcell") + self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell)) self.add_mod(self.dummy_cell) try: - edge_module_type = ("col_cap_bitcell" if cell_properties.bitcell.end_caps else "dummy_bitcell") + edge_module_type = ("col_cap" if cell_properties.bitcell.end_caps else "dummy") except AttributeError: - edge_module_type = "dummy_bitcell" - self.edge_cell = factory.create(module_type=edge_module_type) + edge_module_type = "dummy" + self.edge_cell = factory.create(module_type=edge_module_type + "_" + OPTS.bitcell) self.add_mod(self.edge_cell) # Used for pin names only self.cell = factory.create(module_type="bitcell") @@ -94,7 +95,7 @@ class replica_column(design.design): # Top/bottom cell are always dummy cells. # Regular array cells are replica cells (>left_rbl and self.left_rbl and row self.left_rbl and row < self.total_size - self.right_rbl - 1): self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell) self.connect_inst(self.get_bitcell_pins(0, row)) @@ -118,7 +119,7 @@ class replica_column(design.design): from tech import cell_properties # Flip the mirrors if we have an odd number of replica+dummy rows at the bottom # so that we will start with mirroring rather than not mirroring - rbl_offset = (self.left_rbl+1)%2 + rbl_offset = (self.left_rbl + 1) %2 # if our bitcells are mirrored on the y axis, check if we are in global # column that needs to be flipped. @@ -129,12 +130,10 @@ class replica_column(design.design): xoffset = self.replica_cell.width for row in range(self.total_size): - dir_x = False - name = "bit_r{0}_{1}".format(row,"rbl") - if cell_properties.bitcell.mirror.x and (row+rbl_offset)%2: - dir_x = True + # name = "bit_r{0}_{1}".format(row, "rbl") + dir_x = cell_properties.bitcell.mirror.x and (row + rbl_offset) % 2 - offset = vector(xoffset,self.cell.height*(row+(row+rbl_offset)%2)) + offset = vector(xoffset, self.cell.height * (row + (row + rbl_offset) % 2)) if dir_x and dir_y: dir_key = "XY" @@ -174,9 +173,9 @@ class replica_column(design.design): for row in range(row_range_min, row_range_max): for wl_name in self.cell.get_all_wl_names(): wl_pin = self.cell_inst[row].get_pin(wl_name) - self.add_layout_pin(text="{0}_{1}".format(wl_name,row), + self.add_layout_pin(text="{0}_{1}".format(wl_name, row), layer=wl_pin.layer, - offset=wl_pin.ll().scale(0,1), + offset=wl_pin.ll().scale(0, 1), width=self.width, height=wl_pin.height()) @@ -194,10 +193,10 @@ class replica_column(design.design): pin_names = self.cell.get_all_bitline_names() for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(col)) + bitcell_pins.append(pin + "_{0}".format(col)) pin_names = self.cell.get_all_wl_names() for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(row)) + bitcell_pins.append(pin + "_{0}".format(row)) bitcell_pins.append("vdd") bitcell_pins.append("gnd") @@ -211,12 +210,11 @@ class replica_column(design.design): pin_names = self.cell.get_all_bitline_names() for pin in pin_names: - bitcell_pins.append(pin+"_{0}".format(col)) + bitcell_pins.append(pin + "_{0}".format(col)) bitcell_pins.append("vdd") return bitcell_pins - def exclude_all_but_replica(self): """Excludes all bits except the replica cell (self.replica_bit).""" diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index c9a6b4f7..2c7d4677 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -35,7 +35,7 @@ class row_cap_array(bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ - self.dummy_cell = factory.create(module_type="row_cap_bitcell_1rw_1r") # TODO: make module_type generic + self.dummy_cell = factory.create(module_type="row_cap_{}".format(OPTS.bitcell)) self.add_mod(self.dummy_cell) self.cell = factory.create(module_type="bitcell") diff --git a/compiler/options.py b/compiler/options.py index 646b2a68..fb738169 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -124,26 +124,23 @@ class options(optparse.Values): purge_temp = True # These are the default modules that can be over-riden + bitcell_suffix = "" bank_select = "bank_select" bitcell_array = "bitcell_array" bitcell = "bitcell" - col_cap_bitcell = "col_cap_bitcell" column_mux_array = "single_level_column_mux_array" control_logic = "control_logic" decoder = "hierarchical_decoder" delay_chain = "delay_chain" dff_array = "dff_array" dff = "dff" - dummy_bitcell = "dummy_bitcell" inv_dec = "pinv" nand2_dec = "pnand2" nand3_dec = "pnand3" nand4_dec = "pnand4" # Not available right now precharge_array = "precharge_array" ptx = "ptx" - replica_bitcell = "replica_bitcell" replica_bitline = "replica_bitline" - row_cap_bitcell = "row_cap_bitcell" sense_amp_array = "sense_amp_array" sense_amp = "sense_amp" tri_gate_array = "tri_gate_array" diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 46eed3d4..0e9721c7 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -77,8 +77,8 @@ class sram_factory: """ tech_module_type, tm_overridden = self.get_techmodule_type(module_type) user_module_type, um_overridden = self.get_usermodule_type(module_type) - #print(module_type, tech_module_type, tm_overridden) - #print(module_type, user_module_type, um_overridden) + # print(module_type, tech_module_type, tm_overridden) + # print(module_type, user_module_type, um_overridden) # overridden user modules have priority if um_overridden: diff --git a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py index 69b31cc6..a7e79e9b 100755 --- a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py +++ b/compiler/tests/04_single_level_column_mux_1rw_1r_test.py @@ -22,11 +22,11 @@ class single_level_column_mux_1rw_1r_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + debug.info(2, "Checking column mux port 0") tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) diff --git a/compiler/tests/05_bitcell_1rw_1r_array_test.py b/compiler/tests/05_bitcell_1rw_1r_array_test.py index 3426f0c5..0683d127 100755 --- a/compiler/tests/05_bitcell_1rw_1r_array_test.py +++ b/compiler/tests/05_bitcell_1rw_1r_array_test.py @@ -23,12 +23,10 @@ class bitcell_1rw_1r_array_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() debug.info(2, "Testing 4x4 array for cell_1rw_1r") a = factory.create(module_type="bitcell_array", cols=4, rows=4) diff --git a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py index 5b317e6e..844160a6 100755 --- a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py @@ -23,11 +23,11 @@ class hierarchical_decoder_1rw_1r_test(openram_test): globals.init_openram(config_file) # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + # Checks 2x4 and 2-input NAND decoder debug.info(1, "Testing 16 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py index 094e8b2e..e1605067 100755 --- a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py @@ -21,11 +21,11 @@ class hierarchical_decoder_pbitcell_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) # check hierarchical decoder for multi-port - OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - + globals.setup_bitcell() + factory.reset() debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) diff --git a/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py index 6512238f..a3ae2ed2 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py @@ -22,11 +22,10 @@ class hierarchical_predecode2x4_1rw_1r_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() debug.info(1, "Testing sample for hierarchy_predecode2x4") a = factory.create(module_type="hierarchical_predecode2x4") diff --git a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py index b5532891..0f70ddb1 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py @@ -22,11 +22,11 @@ class hierarchical_predecode2x4_pbitcell_test(openram_test): globals.init_openram(config_file) # checking hierarchical precode 2x4 for multi-port - OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - + globals.setup_bitcell() + debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)") a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py index 466a7e40..c0653243 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py @@ -23,11 +23,11 @@ class hierarchical_predecode3x8_1rw_1r_test(openram_test): globals.init_openram(config_file) # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + debug.info(1, "Testing sample for hierarchy_predecode3x8") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py index 1d5ab41b..9170d5c0 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py @@ -22,11 +22,11 @@ class hierarchical_predecode3x8_pbitcell_test(openram_test): globals.init_openram(config_file) # checking hierarchical precode 3x8 for multi-port - OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 - + globals.setup_bitcell() + debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py index 2a3a8f87..c758e788 100755 --- a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py +++ b/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py @@ -20,11 +20,11 @@ class single_level_column_mux_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + debug.info(1, "Testing sample for 4-way column_mux_array port 0") a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) diff --git a/compiler/tests/08_precharge_array_1rw_1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py index 7d54e02b..c5efb59b 100755 --- a/compiler/tests/08_precharge_array_1rw_1r_test.py +++ b/compiler/tests/08_precharge_array_1rw_1r_test.py @@ -22,10 +22,10 @@ class precharge_1rw_1r_test(openram_test): globals.init_openram(config_file) # check precharge array in multi-port - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() factory.reset() debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 0)") diff --git a/compiler/tests/08_wordline_driver_array_1rw_1r_test.py b/compiler/tests/08_wordline_driver_array_1rw_1r_test.py index 7c97ff75..cf1810d8 100755 --- a/compiler/tests/08_wordline_driver_array_1rw_1r_test.py +++ b/compiler/tests/08_wordline_driver_array_1rw_1r_test.py @@ -23,10 +23,10 @@ class wordline_driver_array_1rw_1r_test(openram_test): globals.init_openram(config_file) # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() # check wordline driver for single port debug.info(2, "Checking driver") diff --git a/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py b/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py index d746eabc..3f869255 100755 --- a/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py +++ b/compiler/tests/14_replica_bitcell_1rw_1r_array_test.py @@ -19,21 +19,17 @@ class replica_bitcell_array_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" - OPTS.col_cap_bitcell="col_cap_bitcell_1rw_1r" - OPTS.row_cap_bitcell="row_cap_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + debug.info(2, "Testing 4x4 array for cell_1rw_1r") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0,1]) + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0, 1]) self.local_check(a) debug.info(2, "Testing 4x4 array for cell_1rw_1r") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0,1]) + a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0, 1]) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_1rw_1r_test.py index 33bff4ed..ff09dec9 100755 --- a/compiler/tests/18_port_address_1rw_1r_test.py +++ b/compiler/tests/18_port_address_1rw_1r_test.py @@ -21,11 +21,11 @@ class port_address_1rw_1r_test(openram_test): globals.init_openram(config_file) # Use the 2 port cell since it is usually bigger/easier - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + debug.info(1, "Port address 16 rows") a = factory.create("port_address", cols=16, rows=16) self.local_check(a) diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index 3d415ec0..3a7687d6 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -21,11 +21,11 @@ class port_data_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + c = sram_config(word_size=4, num_words=16) diff --git a/compiler/tests/19_single_bank_1rw_1r_test.py b/compiler/tests/19_single_bank_1rw_1r_test.py index b3e18407..22f83f29 100755 --- a/compiler/tests/19_single_bank_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_1rw_1r_test.py @@ -22,12 +22,10 @@ class single_bank_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=16) diff --git a/compiler/tests/19_single_bank_1w_1r_test.py b/compiler/tests/19_single_bank_1w_1r_test.py index c1228e5a..e3a2d886 100755 --- a/compiler/tests/19_single_bank_1w_1r_test.py +++ b/compiler/tests/19_single_bank_1w_1r_test.py @@ -22,13 +22,10 @@ class single_bank_1w_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1w_1r" - OPTS.replica_bitcell = "replica_bitcell_1w_1r" - OPTS.dummy_bitcell="dummy_bitcell_1w_1r" - OPTS.num_rw_ports = 0 OPTS.num_r_ports = 1 OPTS.num_w_ports = 1 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=16) diff --git a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py index d08ff8cc..ddb97905 100755 --- a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py @@ -22,6 +22,10 @@ class single_bank_wmask_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=8, write_size=4, diff --git a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py index 599cb2ce..f521851b 100755 --- a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py @@ -24,11 +24,10 @@ class psram_1bank_2mux_1rw_1w_test(openram_test): from sram_config import sram_config OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell="replica_pbitcell" - OPTS.dummy_bitcell="dummy_pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 1 OPTS.num_r_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, diff --git a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py index 30b951fb..35912823 100755 --- a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py @@ -24,11 +24,10 @@ class psram_1bank_2mux_1w_1r_test(openram_test): from sram_config import sram_config OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell="replica_pbitcell" - OPTS.dummy_bitcell="dummy_pbitcell" OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, diff --git a/compiler/tests/20_psram_1bank_2mux_test.py b/compiler/tests/20_psram_1bank_2mux_test.py index 44272b2d..92403cd1 100755 --- a/compiler/tests/20_psram_1bank_2mux_test.py +++ b/compiler/tests/20_psram_1bank_2mux_test.py @@ -22,14 +22,12 @@ class psram_1bank_2mux_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell="replica_pbitcell" - OPTS.dummy_bitcell="dummy_pbitcell" - # testing layout of sram using pbitcell with 1 RW port (a 6T-cell equivalent) + OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, diff --git a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py index 49e1a125..145d1723 100755 --- a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py +++ b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py @@ -24,11 +24,10 @@ class psram_1bank_4mux_1rw_1r_test(openram_test): from sram_config import sram_config OPTS.bitcell = "pbitcell" - OPTS.replica_bitcell="replica_pbitcell" - OPTS.dummy_bitcell="dummy_pbitcell" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=64, diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py index a8d635ba..0a2b7d32 100755 --- a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py @@ -22,12 +22,10 @@ class sram_1bank_2mux_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, diff --git a/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py index bf572700..2c4e29e6 100755 --- a/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py @@ -23,12 +23,10 @@ class psram_1bank_2mux_1w_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1w_1r" - OPTS.replica_bitcell="replica_bitcell_1w_1r" - OPTS.dummy_bitcell="dummy_bitcell_1w_1r" OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=32, diff --git a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py index 69a623d2..1e4df34d 100755 --- a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py @@ -22,13 +22,11 @@ class sram_1bank_8mux_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 - + globals.setup_bitcell() + c = sram_config(word_size=2, num_words=128, num_banks=1) diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py index f9b96b84..a516b4f0 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py @@ -22,12 +22,10 @@ class sram_1bank_nomux_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell = "dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=16, diff --git a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py index d271d1e5..f2958f9f 100755 --- a/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py +++ b/compiler/tests/22_sram_1rw_1r_1bank_nomux_func_test.py @@ -24,12 +24,10 @@ class psram_1bank_nomux_func_test(openram_test): OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.replica_bitcell = "replica_bitcell_1rw_1r" - OPTS.dummy_bitcell="dummy_bitcell_1rw_1r" OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 + globals.setup_bitcell() # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload diff --git a/compiler/tests/22_sram_wmask_1w_1r_func_test.py b/compiler/tests/22_sram_wmask_1w_1r_func_test.py index 50acd5bf..b5d83654 100755 --- a/compiler/tests/22_sram_wmask_1w_1r_func_test.py +++ b/compiler/tests/22_sram_wmask_1w_1r_func_test.py @@ -26,14 +26,11 @@ class sram_wmask_1w_1r_func_test(openram_test): OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False - OPTS.bitcell = "bitcell_1w_1r" - OPTS.replica_bitcell = "replica_bitcell_1w_1r" - OPTS.dummy_bitcell = "dummy_bitcell_1w_1r" - OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 - + globals.setup_bitcell() + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer From 0837432d4538b75e74a522092a1d51ebc3667ff6 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 5 Jun 2020 16:47:22 -0700 Subject: [PATCH 51/51] Wordline route layers and (optional) via. --- compiler/modules/bank.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 92e4e8cd..d9659cd5 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -798,22 +798,34 @@ 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() + driver_wl_pin = self.port_address_inst[port].get_pin("wl_{}".format(row)) + driver_wl_pos = driver_wl_pin.rc() + bitcell_wl_pin = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)) + bitcell_wl_pos = bitcell_wl_pin.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]) + self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + self.add_via_stack_center(from_layer=driver_wl_pin.layer, + to_layer=bitcell_wl_pin.layer, + offset=bitcell_wl_pos, + directions=("H", "H")) 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() + driver_wl_pin = self.port_address_inst[port].get_pin("wl_{}".format(row)) + driver_wl_pos = driver_wl_pin.lc() + bitcell_wl_pin = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)) + bitcell_wl_pos = bitcell_wl_pin.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]) + self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + self.add_via_stack_center(from_layer=driver_wl_pin.layer, + to_layer=bitcell_wl_pin.layer, + offset=bitcell_wl_pos, + directions=("H", "H")) def route_column_address_lines(self, port): """ Connecting the select lines of column mux to the address bus """