def add_wordline_pins(self): # Wordlines to ground self.gnd_wordline_names = [] for port in self.all_ports: for bit in self.all_ports: self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit)) if bit != port: self.gnd_wordline_names.append("rbl_wl_{0}_{1}".format(port, bit)) self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl] self.wordline_names = self.bitcell_array.wordline_names self.all_wordline_names = self.bitcell_array.all_wordline_names # All wordlines including dummy and RBL self.replica_array_wordline_names = [] if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap.get_wordline_names())) for bit in range(self.rbl[0]): self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[bit]]) self.replica_array_wordline_names.extend(self.all_wordline_names) for bit in range(self.rbl[1]): self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[self.rbl[0] + bit]]) if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap.get_wordline_names())) for port in range(self.rbl[0]): self.add_pin(self.rbl_wordline_names[port][port], "INPUT") self.add_pin_list(self.all_wordline_names, "INPUT") for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]): self.add_pin(self.rbl_wordline_names[port][port], "INPUT")
def add_layout_pins(self): """ Add the layout pins """ # Add the bitline metal, but not as pins since they are going to just be floating # For some reason, LVS has an issue if we don't add this metal bitline_names = self.cell.get_all_bitline_names() for col in range(self.column_size): for port in self.all_ports: bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port]) self.add_rect(layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) br_pin = self.cell_inst[0, col].get_pin( bitline_names[2 * port + 1]) self.add_rect(layer=br_pin.layer, offset=br_pin.ll().scale(1, 0), width=br_pin.width(), height=self.height) wl_names = self.cell.get_all_wl_names() if not props.compare_ports(props.bitcell.split_wl): for row in range(self.row_size): for port in self.all_ports: wl_pin = self.cell_inst[row, 0].get_pin(wl_names[port]) self.add_layout_pin(text="wl_{0}_{1}".format(port, row), layer=wl_pin.layer, offset=wl_pin.ll().scale(0, 1), width=self.width, height=wl_pin.height()) else: for row in range(self.row_size): for port in self.all_ports: for wl in range(len(wl_names)): wl_pin = self.cell_inst[row, 0].get_pin("wl{}".format(wl)) self.add_layout_pin(text="wl{0}_{1}_{2}".format( wl, port, row), layer=wl_pin.layer, offset=wl_pin.ll().scale(0, 1), width=self.width, height=wl_pin.height()) # Copy a vdd/gnd layout pin from every cell if not props.compare_ports( props.bitcell_array.use_custom_cell_arrangement): 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"]: self.copy_layout_pin(inst, pin_name) else: for row in range(self.row_size): for col in range(self.column_size): inst = self.cell_inst[row, col] for pin_name in ["vpwr", "vgnd"]: self.copy_layout_pin(inst, pin_name)
def create_layout(self): # We will need unused wordlines grounded, so we need to know their layer pin = self.cell.get_pin(self.cell.get_all_wl_names()[0]) pin_layer = pin.layer self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) self.unused_offset = vector(self.unused_pitch, 0) # Add extra width on the left and right for the unused WLs if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.height = (self.row_size + self.extra_rows) * self.dummy_row.height self.width = (self.column_size + self.extra_cols) * self.cell.width + 2 * self.unused_pitch else: self.width = self.row_cap_left.width + self.row_cap_right.width + self.col_cap_top.width for rbl in range(self.rbl[0] + self.rbl[1]): self.width += self.replica_col_insts[rbl].width self.height = self.row_cap_left.height # This is a bitcell x bitcell offset to scale self.bitcell_offset = vector(self.cell.width, self.cell.height) if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.strap_offset = vector(0, 0) self.col_end_offset = vector(self.cell.width, self.cell.height) self.row_end_offset = vector(self.cell.width, self.cell.height) else: self.strap_offset = vector(self.replica_col_insts[0].mod.strap1.width, self.replica_col_insts[0].mod.strap1.height) self.col_end_offset = vector(self.dummy_row_insts[0].mod.colend1.width, self.dummy_row_insts[0].mod.colend1.height) self.row_end_offset = vector(self.dummy_col_insts[0].mod.rowend1.width, self.dummy_col_insts[0].mod.rowend1.height) # Everything is computed with the main array at (self.unused_pitch, 0) to start self.bitcell_array_inst.place(offset=self.unused_offset) self.add_replica_columns() self.add_end_caps() # Array was at (0, 0) but move everything so it is at the lower left # We move DOWN the number of left RBL even if we didn't add the column to this bitcell array array_offset = self.bitcell_offset.scale(1 + len(self.left_rbl), 1 + self.rbl[0]) self.translate_all(array_offset.scale(-1, -1)) self.add_layout_pins() self.route_unused_wordlines() self.add_boundary() self.DRC_LVS()
def get_wl_name(self, port=0): """Get wl name""" if props.compare_ports(props.bitcell.split_wl): return "wl{}".format(port) else: debug.check(port == 0, "One port for bitcell only.") return props.bitcell.cell_6t.pin.wl
def get_all_wl_names(self): """ Creates a list of all wordline pin names """ if props.compare_ports(props.bitcell.split_wl): row_pins = ["wl0", "wl1"] else: row_pins = [props.bitcell.cell_6t.pin.wl] return row_pins
def __init__(self, name, rows, cols, column_offset): super().__init__(name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.column_size = cols self.row_size = rows self.column_offset = column_offset # Bitcell for port names only if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.cell = factory.create(module_type="bitcell") else: self.cell = factory.create(module_type="s8_bitcell", version="opt1") self.cell2 = factory.create(module_type="s8_bitcell", version="opt1a") self.strap = factory.create(module_type="s8_internal", version="wlstrap") self.strap2 = factory.create(module_type="s8_internal", version="wlstrap_p") self.wordline_names = [[] for port in self.all_ports] self.all_wordline_names = [] self.bitline_names = [[] for port in self.all_ports] self.all_bitline_names = [] self.rbl_bitline_names = [[] for port in self.all_ports] self.all_rbl_bitline_names = [] self.rbl_wordline_names = [[] for port in self.all_ports] self.all_rbl_wordline_names = []
class s8_dummy_bitcell(bitcell_base.bitcell_base): """ A single bit cell (6T, 8T, etc.) 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. """ if props.compare_ports(props.bitcell.split_wl): pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] else: pin_names = [ props.bitcell.cell_s8_6t.pin.bl, props.bitcell.cell_s8_6t.pin.br, props.bitcell.cell_s8_6t.pin.wl, "vpwr", "vgnd" ] def __init__(self, version, name=""): # Ignore the name argument if version == "opt1": self.name = "s8sram_cell_opt1" self.border_structure = "s8sram_cell" elif version == "opt1a": self.name = "s8sram_cell_opt1a" self.border_structure = "s8sram_cell" bitcell_base.bitcell_base.__init__(self, self.name) debug.info(2, "Create dummy bitcell") (self.width, self.height) = utils.get_libcell_size(self.name, GDS["unit"], layer["mem"], "s8sram_cell\x00") self.pin_map = utils.get_libcell_pins(self.pin_names, self.name, GDS["unit"])
def __init__(self, name, rows, rbl, replica_bit, column_offset=0): super().__init__(rows=sum(rbl) + rows + 2, cols=1, column_offset=column_offset, name=name) self.rows = rows self.left_rbl = rbl[0] self.right_rbl = rbl[1] self.replica_bit = replica_bit # left, right, regular rows plus top/bottom dummy cells if not cell_properties.compare_ports( cell_properties.bitcell_array.use_custom_cell_arrangement): self.total_size = self.left_rbl + rows + self.right_rbl + 2 else: 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 <= self.left_rbl or replica_bit >= self.total_size - self.right_rbl - 1, "Replica bit cannot be in the regular array.") if OPTS.tech_name == "sky130": debug.check( rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0, "sky130 currently requires rows to be even and to start with X mirroring" + " (left_rbl must be odd) for LVS.") self.create_netlist() if not OPTS.netlist_only: self.create_layout()
def place_array(self, name_template, row_offset=0): # We increase it by a well enclosure so the precharges don't overlap our wells if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.height = self.row_size * self.cell.height self.width = self.column_size * self.cell.width xoffset = 0.0 for col in range(self.column_size): yoffset = 0.0 tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset) for row in range(self.row_size): tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset) if dir_x and dir_y: dir_key = "XY" elif dir_x: dir_key = "MX" elif dir_y: dir_key = "MY" else: dir_key = "" self.cell_inst[row, col].place(offset=[tempx, tempy], mirror=dir_key) yoffset += self.cell.height xoffset += self.cell.width else: from tech import custom_cell_placement custom_cell_placement(self)
def create_all_wordline_names(self, num_remove_wordline=0): for row in range(self.row_size - num_remove_wordline): for port in self.all_ports: if not cell_properties.compare_ports(cell_properties.bitcell.split_wl): self.wordline_names[port].append("wl_{0}_{1}".format(port, row)) else: self.wordline_names[port].append("wl0_{0}_{1}".format(port, row)) self.wordline_names[port].append("wl1_{0}_{1}".format(port, row)) self.all_wordline_names = [x for sl in zip(*self.wordline_names) for x in sl]
def add_pins(self): for bl_name in self.get_bitline_names(): self.add_pin(bl_name, "INOUT") for wl_name in self.get_wordline_names(): self.add_pin(wl_name, "INPUT") if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") else: self.add_pin("vpwr", "POWER") self.add_pin("vgnd", "GROUND")
def add_modules(self): if not cell_properties.compare_ports( cell_properties.bitcell_array.use_custom_cell_arrangement): 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_{}".format(OPTS.bitcell)) self.add_mod(self.dummy_cell) try: edge_module_type = ( "col_cap" if cell_properties.bitcell.end_caps else "dummy") except AttributeError: 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") else: self.replica_cell = factory.create(module_type="s8_bitcell", version="opt1") self.add_mod(self.replica_cell) self.cell = self.replica_cell self.replica_cell2 = factory.create(module_type="s8_bitcell", version="opt1a") self.add_mod(self.replica_cell2) self.dummy_cell = factory.create(module_type="s8_bitcell", version="opt1") self.dummy_cell2 = factory.create(module_type="s8_bitcell", version="opt1") self.strap1 = factory.create(module_type="s8_internal", version="wlstrap") self.add_mod(self.strap1) self.strap2 = factory.create(module_type="s8_internal", version="wlstrap_p") self.add_mod(self.strap2) self.colend = factory.create(module_type="s8_col_end", version="colenda") self.edge_cell = self.colend self.add_mod(self.colend) self.colenda = factory.create(module_type="s8_col_end", version="colenda") self.add_mod(self.colenda) self.colend_p_cent = factory.create(module_type="s8_col_end", version="colend_p_cent") self.add_mod(self.colend_p_cent) self.colenda_p_cent = factory.create(module_type="s8_col_end", version="colenda_p_cent") self.add_mod(self.colenda_p_cent)
def create_instances(self): """ Create the module instances used in this design """ if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): self.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) self.connect_inst(self.all_bitline_names + self.all_wordline_names + self.supplies) # Replica columns self.replica_col_insts = [] for port in self.all_ports: if port in self.rbls: self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port), mod=self.replica_columns[port])) self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies) else: self.replica_col_insts.append(None) # Dummy rows under the bitcell array (connected with with the replica cell wl) self.dummy_row_replica_insts = [] # Note, this is the number of left and right even if we aren't adding the columns to this bitcell array! for port in self.all_ports: self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port), mod=self.dummy_row)) self.connect_inst([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[port]] + self.supplies) # Top/bottom dummy rows or col caps self.dummy_row_insts = [] self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot", mod=self.col_cap)) self.connect_inst(["gnd"] * len(self.col_cap.get_wordline_names()) + self.supplies) self.dummy_row_insts.append(self.add_inst(name="dummy_row_top", mod=self.col_cap)) self.connect_inst(["gnd"] * len(self.col_cap.get_wordline_names()) + self.supplies) # Left/right Dummy columns self.dummy_col_insts = [] self.dummy_col_insts.append(self.add_inst(name="dummy_col_left", mod=self.row_cap_left)) self.connect_inst(self.replica_array_wordline_names + self.supplies) self.dummy_col_insts.append(self.add_inst(name="dummy_col_right", mod=self.row_cap_right)) self.connect_inst(self.replica_array_wordline_names + self.supplies) else: from tech import custom_replica_bitcell_array_arrangement custom_replica_bitcell_array_arrangement(self)
def route_unused_wordlines(self): """ Connect the unused RBL and dummy wordlines to gnd """ if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): # This grounds all the dummy row word lines for inst in self.dummy_row_insts: for wl_name in self.col_cap.get_wordline_names(): self.ground_pin(inst, wl_name) # Ground the unused replica wordlines for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): if wl_name in self.gnd_wordline_names: self.ground_pin(inst, pin_name)
def create_instances(self): """ Create the module instances used in this design """ self.cell_inst = {} if not props.compare_ports( props.bitcell_array.use_custom_cell_arrangement): 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.cell) self.connect_inst(self.get_bitcell_pins(row, col)) else: from tech import custom_cell_arrangement custom_cell_arrangement(self)
def add_layout_pins(self): """ Add the layout pins """ bitline_names = self.cell.get_all_bitline_names() for col in range(self.column_size): for port in self.all_ports: bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port]) self.add_layout_pin(text="bl_{0}_{1}".format(port, col), layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1]) self.add_layout_pin(text="br_{0}_{1}".format(port, col), layer=br_pin.layer, offset=br_pin.ll().scale(1, 0), width=br_pin.width(), height=self.height) wl_names = self.cell.get_all_wl_names() for row in range(self.row_size): for port in self.all_ports: wl_pin = self.cell_inst[row, 0].get_pin(wl_names[port]) self.add_layout_pin(text="wl_{0}_{1}".format(port, row), layer=wl_pin.layer, offset=wl_pin.ll().scale(0, 1), width=self.width, height=wl_pin.height()) if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): # Copy a vdd/gnd layout pin from every cell 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"]: self.copy_layout_pin(inst, pin_name) else: # Copy a vdd/gnd layout pin from every cell for row in range(self.row_size): for col in range(self.column_size): inst = self.cell_inst[row, col] for pin_name in ["vpwr", "vgnd"]: self.copy_layout_pin(inst, pin_name)
def add_modules(self): """ Add the modules used in this design """ if not props.compare_ports( props.bitcell_array.use_custom_cell_arrangement): self.cell = factory.create(module_type="bitcell") self.add_mod(self.cell) else: self.cell = factory.create(module_type="s8_bitcell", version="opt1") self.add_mod(self.cell) self.add_mod( factory.create(module_type="s8_bitcell", version="opt1a")) self.add_mod( factory.create(module_type="s8_internal", version="wlstrap")) self.add_mod( factory.create(module_type="s8_internal", version="wlstrap_p"))
def create_instances(self): self.cell_inst = {} if not cell_properties.compare_ports( cell_properties.bitcell_array.use_custom_cell_arrangement): try: end_caps_enabled = cell_properties.bitcell.end_caps except AttributeError: end_caps_enabled = False 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 <rows-right_rbl) # Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell. if (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(row, 0)) elif row == self.replica_bit: self.cell_inst[row] = self.add_inst(name=name, mod=self.replica_cell) self.connect_inst(self.get_bitcell_pins(row, 0)) elif (row == 0 or row == self.total_size - 1): self.cell_inst[row] = self.add_inst(name=name, mod=self.edge_cell) if end_caps_enabled: self.connect_inst(self.get_bitcell_pins_col_cap( row, 0)) else: self.connect_inst(self.get_bitcell_pins(row, 0)) else: self.cell_inst[row] = self.add_inst(name=name, mod=self.dummy_cell) self.connect_inst(self.get_bitcell_pins(row, 0)) else: from tech import custom_replica_column_arrangement custom_replica_column_arrangement(self)
def place_instances(self): if not cell_properties.compare_ports( cell_properties.bitcell_array.use_custom_cell_arrangement): # 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 # if our bitcells are mirrored on the y axis, check if we are in global # column that needs to be flipped. dir_y = False xoffset = 0 if cell_properties.bitcell.mirror.y and self.column_offset % 2: dir_y = True xoffset = self.replica_cell.width for row in range(self.total_size): # 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)) if dir_x and dir_y: dir_key = "XY" elif dir_x: dir_key = "MX" elif dir_y: dir_key = "MY" else: dir_key = "" self.cell_inst[row].place(offset=offset, mirror=dir_key) else: from tech import custom_replica_cell_placement custom_replica_cell_placement(self)
class bitcell(bitcell_base.bitcell_base): """ A single bit cell (6T, 8T, etc.) 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. """ # If we have a split WL bitcell, if not be backwards # compatible in the tech file if props.compare_ports(props.bitcell.split_wl): pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] else: pin_names = [ props.bitcell.cell_6t.pin.bl, props.bitcell.cell_6t.pin.br, props.bitcell.cell_6t.pin.wl, props.bitcell.cell_6t.pin.vdd, props.bitcell.cell_6t.pin.gnd ] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] storage_nets = ['Q', 'Q_bar'] (width, height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) def __init__(self, name=""): # Ignore the name argument bitcell_base.bitcell_base.__init__(self, "cell_6t") debug.info(2, "Create bitcell") self.width = bitcell.width self.height = bitcell.height self.pin_map = bitcell.pin_map self.add_pin_types(self.type_list) self.nets_match = self.do_nets_exist(self.storage_nets) #debug.check(OPTS.tech_name != "sky130", "sky130 does not yet support single port cells") def get_all_wl_names(self): """ Creates a list of all wordline pin names """ if props.compare_ports(props.bitcell.split_wl): row_pins = ["wl0", "wl1"] else: row_pins = [props.bitcell.cell_6t.pin.wl] return row_pins def get_all_bitline_names(self): """ Creates a list of all bitline pin names (both bl and br) """ pin = props.bitcell.cell_6t.pin column_pins = [pin.bl, pin.br] return column_pins def get_all_bl_names(self): """ Creates a list of all bl pins names """ return [props.bitcell.cell_6t.pin.bl] def get_all_br_names(self): """ Creates a list of all br pins names """ return [props.bitcell.cell_6t.pin.br] def get_bl_name(self, port=0): """Get bl name""" debug.check(port == 0, "One port for bitcell only.") return props.bitcell.cell_6t.pin.bl def get_br_name(self, port=0): """Get bl name""" debug.check(port == 0, "One port for bitcell only.") return props.bitcell.cell_6t.pin.br def get_wl_name(self, port=0): """Get wl name""" if props.compare_ports(props.bitcell.split_wl): return "wl{}".format(port) else: debug.check(port == 0, "One port for bitcell only.") return props.bitcell.cell_6t.pin.wl 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)
def add_modules(self): """ 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 v v bdDDDDDDDDDDDDDDdb <- Dummy row bdDDDDDDDDDDDDDDrb <- Dummy row br--------------rb br| Array |rb br| row x col |rb br--------------rb brDDDDDDDDDDDDDDdb <- Dummy row bdDDDDDDDDDDDDDDdb <- Dummy row ^^^^^^^^^^^^^^^ dummy rows cols x 1 ^ dummy columns ^ 1 x (rows + 4) """ # Bitcell array self.bitcell_array = factory.create(module_type="bitcell_array", column_offset=1 + len(self.left_rbl), cols=self.column_size, rows=self.row_size) self.add_mod(self.bitcell_array) # Replica bitlines self.replica_columns = {} for port in self.all_ports: if port in self.left_rbl: # We will always have self.rbl[0] rows of replica wordlines below # the array. # These go from the top (where the bitcell array starts ) down replica_bit = self.rbl[0] - port elif port in self.right_rbl: # We will always have self.rbl[0] rows of replica wordlines below # the array. # These go from the bottom up replica_bit = self.rbl[0] + self.row_size + port else: continue # If we have an odd numer on the bottom column_offset = self.rbl[0] + 1 self.replica_columns[port] = factory.create(module_type="replica_column", rows=self.row_size, rbl=self.rbl, column_offset=column_offset, replica_bit=replica_bit) self.add_mod(self.replica_columns[port]) try: end_caps_enabled = cell_properties.bitcell.end_caps except AttributeError: end_caps_enabled = False # Dummy row self.dummy_row = factory.create(module_type="dummy_array", cols=self.column_size, rows=1, # dummy column + left replica column column_offset=1 + len(self.left_rbl), mirror=0) self.add_mod(self.dummy_row) if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): # Dummy Row or Col Cap, depending on bitcell array properties col_cap_module_type = ("col_cap_array" if end_caps_enabled else "dummy_array") self.col_cap = factory.create(module_type=col_cap_module_type, cols=self.column_size, rows=1, # dummy column + left replica column column_offset=1 + len(self.left_rbl), mirror=0) self.add_mod(self.col_cap) # Dummy Col or Row Cap, depending on bitcell array properties row_cap_module_type = ("row_cap_array" if end_caps_enabled else "dummy_array") self.row_cap_left = factory.create(module_type=row_cap_module_type, cols=1, column_offset=0, rows=self.row_size + self.extra_rows, mirror=(self.rbl[0] + 1) % 2) self.add_mod(self.row_cap_left) self.row_cap_right = factory.create(module_type=row_cap_module_type, cols=1, # dummy column # + left replica column(s) # + bitcell columns # + right replica column(s) column_offset = 1 + len(self.left_rbl) + self.column_size + self.rbl[0], rows=self.row_size + self.extra_rows, mirror=(self.rbl[0] + 1) %2) self.add_mod(self.row_cap_right) else: # Dummy Row or Col Cap, depending on bitcell array properties col_cap_module_type = ("s8_col_cap_array" if end_caps_enabled else "dummy_array") self.col_cap_top = factory.create(module_type=col_cap_module_type, cols=self.column_size, rows=1, # dummy column + left replica column(s) column_offset=1 + len(self.left_rbl), mirror=0, location="top") self.add_mod(self.col_cap_top) self.col_cap_bottom = factory.create(module_type=col_cap_module_type, cols=self.column_size, rows=1, # dummy column + left replica column(s) column_offset=1 + len(self.left_rbl), mirror=0, location="bottom") self.add_mod(self.col_cap_bottom) # Dummy Col or Row Cap, depending on bitcell array properties row_cap_module_type = ("s8_row_cap_array" if end_caps_enabled else "dummy_array") self.row_cap_left = factory.create(module_type=row_cap_module_type, cols=1, column_offset=0, rows=self.row_size + self.extra_rows, mirror=0) self.add_mod(self.row_cap_left) self.row_cap_right = factory.create(module_type=row_cap_module_type, cols=1, # dummy column # + left replica column(s) # + bitcell columns # + right replica column(s) column_offset = 1 + len(self.left_rbl) + self.column_size + self.rbl[0], rows=self.row_size + self.extra_rows, mirror=0) self.add_mod(self.row_cap_right)
def add_layout_pins(self): """ Add the layout pins """ #All wordlines #Main array wl and bl/br if not cell_properties.compare_ports(cell_properties.bitcell_array.use_custom_cell_arrangement): for pin_name in self.all_wordline_names: pin_list = self.bitcell_array_inst.get_pins(pin_name) for pin in pin_list: self.add_layout_pin(text=pin_name, layer=pin.layer, offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) # Replica wordlines (go by the row instead of replica column because we may have to add a pin # even though the column is in another local bitcell array) for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): if wl_name in self.gnd_wordline_names: continue pin = inst.get_pin(pin_name) self.add_layout_pin(text=wl_name, layer=pin.layer, offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) else: for pin_name in self.all_wordline_names: pin_list = self.dummy_col_insts[0].get_pins(pin_name) for pin in pin_list: self.add_layout_pin(text=pin_name, layer=pin.layer, offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) # Replica wordlines (go by the row instead of replica column because we may have to add a pin # even though the column is in another local bitcell array) for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): if wl_name in self.gnd_wordline_names: continue pin = inst.get_pin(pin_name) self.add_layout_pin(text=wl_name, layer=pin.layer, offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) for pin_name in self.all_bitline_names: pin_list = self.bitcell_array_inst.get_pins(pin_name) for pin in pin_list: self.add_layout_pin(text=pin_name, layer=pin.layer, offset=pin.ll().scale(1, 0), width=pin.width(), height=self.height) # Replica bitlines if len(self.rbls) > 0: for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts): pin_names = self.replica_columns[self.rbls[0]].all_bitline_names for (bl_name, pin_name) in zip(names, pin_names): pin = inst.get_pin(pin_name) self.add_layout_pin(text=bl_name, layer=pin.layer, offset=pin.ll().scale(1, 0), width=pin.width(), height=self.height) # vdd/gnd are only connected in the perimeter cells # replica column should only have a vdd/gnd in the dummy cell on top/bottom supply_insts = self.dummy_col_insts + self.dummy_row_insts for pin_name in self.supplies: for inst in supply_insts: pin_list = inst.get_pins(pin_name) for pin in pin_list: self.add_power_pin(name=pin_name, loc=pin.center(), start_layer=pin.layer) for inst in self.replica_col_insts: if inst: self.copy_layout_pin(inst, pin_name)
def add_layout_pins(self): """ Add the layout pins """ if not cell_properties.compare_ports( cell_properties.bitcell_array.use_custom_cell_arrangement): for port in self.all_ports: bl_pin = self.cell_inst[0].get_pin(self.cell.get_bl_name(port)) self.add_layout_pin(text="bl_{0}_{1}".format(port, 0), layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) bl_pin = self.cell_inst[0].get_pin(self.cell.get_br_name(port)) self.add_layout_pin(text="br_{0}_{1}".format(port, 0), layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) 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 port in self.all_ports: for row in range(row_range_min, row_range_max): wl_pin = self.cell_inst[row].get_pin( self.cell.get_wl_name(port)) self.add_layout_pin(text="wl_{0}_{1}".format(port, row), layer=wl_pin.layer, offset=wl_pin.ll().scale(0, 1), width=self.width, height=wl_pin.height()) # Supplies are only connected in the ends for (index, inst) in self.cell_inst.items(): for pin_name in ["vdd", "gnd"]: if inst in [ self.cell_inst[0], self.cell_inst[self.total_size - 1] ]: self.copy_power_pins(inst, pin_name) else: self.copy_layout_pin(inst, pin_name) else: for port in self.all_ports: bl_pin = self.cell_inst[2].get_pin(self.cell.get_bl_name(port)) self.add_layout_pin(text="bl_{0}_{1}".format(port, 0), layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) bl_pin = self.cell_inst[2].get_pin(self.cell.get_br_name(port)) self.add_layout_pin(text="br_{0}_{1}".format(port, 0), layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) row_range_max = self.total_size - 1 row_range_min = 1 for port in self.all_ports: for row in range(row_range_min, row_range_max): wl_pin = self.cell_inst[row].get_pin( self.cell.get_wl_name(port)) self.add_layout_pin(text="wl_{0}_{1}".format(port, row), layer=wl_pin.layer, offset=wl_pin.ll().scale(0, 1), width=self.width, height=wl_pin.height()) # Supplies are only connected in the ends for (index, inst) in self.cell_inst.items(): for pin_name in ["vpwr", "vgnd"]: if inst in [ self.cell_inst[0], self.cell_inst[self.total_size - 1] ]: self.copy_power_pins(inst, pin_name) else: self.copy_layout_pin(inst, pin_name)
class replica_bitcell(design.design): """ A single bit cell (6T, 8T, etc.) 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. """ if props.compare_ports(props.bitcell.split_wl): pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] else: pin_names = [ props.bitcell.cell_6t.pin.bl, props.bitcell.cell_6t.pin.br, props.bitcell.cell_6t.pin.wl, props.bitcell.cell_6t.pin.vdd, props.bitcell.cell_6t.pin.gnd ] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] def __init__(self, version, name=""): # Ignore the name argument if version == "opt1": self.name = "s8sram_cell_opt1" self.border_structure = "s8sram_cell" elif version == "opt1a": self.name = "s8sram_cell_opt1a" self.border_structure = "s8sram_cell" self.pin_map = utils.get_libcell_pins(self.pin_names, self.name, GDS["unit"]) design.design.__init__(self, "replica_cell_6t") debug.info(2, "Create replica bitcell object") self.pin_map = utils.get_libcell_pins(self.pin_names, self.name, GDS["unit"]) self.add_pin_types(self.type_list) (self.width, self.height) = utils.get_libcell_size(self.name, GDS["unit"], layer["mem"]) def get_stage_effort(self, load): parasitic_delay = 1 size = 0.5 #This accounts for bitline being drained thought the access TX and internal node cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. read_port_load = 0.5 #min size NMOS gate load return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False) def input_load(self): """Return the relative capacitance of the access transistor gates""" # FIXME: This applies to bitline capacitances as well. access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"] return 2 * access_tx_cin def analytical_power(self, corner, load): """Bitcell power in nW. Only characterizes leakage.""" from tech import spice leakage = spice["bitcell_leakage"] dynamic = 0 #temporary total_power = self.return_power(dynamic, leakage) return total_power 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)