From 1b6634bb9742ebc3be700e93d1fe6d50033f2d21 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 29 Apr 2020 15:48:15 -0700 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 7505fa5aef116220975e3670091e927cdd93ca37 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 27 May 2020 20:03:11 -0700 Subject: [PATCH 4/8] 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 5/8] 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: Fri, 29 May 2020 13:50:34 -0700 Subject: [PATCH 6/8] 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 b39579c109848845c753bcef74b87cf30af7ad50 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Mon, 1 Jun 2020 20:55:15 -0700 Subject: [PATCH 7/8] 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 7a602b75a47280399260235fb8ef0edd6e3d4f4b Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 3 Jun 2020 12:54:15 -0700 Subject: [PATCH 8/8] 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,