diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index f659aed4..c6f6ff49 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -42,14 +42,14 @@ class layout(): self.visited = [] # List of modules we have already visited self.is_library_cell = False # Flag for library cells self.gds_read() - + try: from tech import power_grid self.pwr_grid_layer = power_grid[0] except ImportError: self.pwr_grid_layer = "m3" - + ############################################################ # GDS layout @@ -473,7 +473,7 @@ class layout(): mid2 = vector(mid1, end.y) else: debug.error("Invalid direction for jog -- must be H or V.") - + if layer in layer_stacks: self.add_wire(layer, [start, mid1, mid2, end]) elif layer in techlayer: @@ -488,7 +488,7 @@ class layout(): mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y) mid2 = vector(mid1, end.y) self.add_path(layer, [start, mid1, mid2, end]) - + def add_wire(self, layers, coordinates, widen_short_wires=True): """Connects a routing path on given layer,coordinates,width. The layers are the (horizontal, via, vertical). """ @@ -628,7 +628,7 @@ class layout(): last_via=via, size=size) return via - + def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): """Adds a ptx module to the design.""" import ptx @@ -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] @@ -983,7 +983,7 @@ class layout(): self.add_via_stack_center(from_layer=vlayer, to_layer=dest_pin.layer, offset=out_pos) - + def get_layer_pitch(self, layer): """ Return the track pitch on a given layer """ try: @@ -995,7 +995,7 @@ class layout(): except AttributeError: debug.error("Cannot find layer pitch.", -1) return (nonpref_pitch, pitch, pitch - space, space) - + def add_horizontal_trunk_route(self, pins, trunk_offset, @@ -1113,7 +1113,7 @@ class layout(): pitch = self.horizontal_nonpref_pitch else: pitch = self.vertical_nonpref_pitch - + for pin1 in net1: for pin2 in net2: if vcg_pin_overlap(pin1, pin2, vertical, pitch): @@ -1123,7 +1123,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 extra comparison *shouldn't* matter. @@ -1157,7 +1157,7 @@ class layout(): layer_stuff = self.get_layer_pitch(self.vertical_layer) (self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff - + layer_stuff = self.get_layer_pitch(self.horizontal_layer) (self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff @@ -1182,7 +1182,7 @@ class layout(): # print("Nets:") # for net_name in nets: # print(net_name, [x.name for x in nets[net_name]]) - + # Find the vertical pin conflicts # FIXME: O(n^2) but who cares for now for net_name1 in nets: 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/port_data.py b/compiler/modules/port_data.py index ac6022f7..34ad4ca9 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: @@ -447,7 +447,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), @@ -488,7 +488,7 @@ class port_data(design.design): 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) @@ -720,7 +720,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, inst2_bls_template, inst2_start_bit) @@ -734,9 +734,8 @@ class port_data(design.design): 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""" if self.precharge_array_inst: self.graph_inst_exclude.add(self.precharge_array_inst) - diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index c88dbe6d..ff2260d3 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 @@ -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=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 +385,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 +417,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 +453,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..4ea1b7df 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,48 @@ 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