# 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 design from globals import OPTS import logical_effort from tech import parameter, drc, layer class bitcell_base(design.design): """ Base bitcell parameters to be over-riden. """ def __init__(self, name, cell_name=None, prop=None): design.design.__init__(self, name, cell_name, prop) # Set the bitcell specific properties if prop: self.storage_nets = prop.storage_nets self.nets_match = self.do_nets_exist(prop.storage_nets) self.mirror = prop.mirror self.end_caps = prop.end_caps self.supplies = ["vdd", "gnd"] def get_stage_effort(self, load): parasitic_delay = 1 # This accounts for bitline being drained # thought the access TX and internal node size = 0.5 # Assumes always a minimum sizes inverter. # Could be specified in the tech.py file. cin = 3 # min size NMOS gate load read_port_load = 0.5 return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False) def analytical_power(self, corner, load): """Bitcell power in nW. Only characterizes leakage.""" from tech import spice leakage = spice["bitcell_leakage"] # FIXME dynamic = 0 total_power = self.return_power(dynamic, leakage) return total_power def input_load(self): """ Return the relative capacitance of the access transistor gates """ # FIXME: This applies to bitline capacitances as well. # FIXME: sizing is not accurate with the handmade cell. # Change once cell widths are fixed. access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"] return 2 * access_tx_cin def get_wl_cin(self): """Return the relative capacitance of the access transistor gates""" # This is a handmade cell so the value must be entered # in the tech.py file or estimated. # Calculated in the tech file by summing the widths of all # the related gates and dividing by the minimum width. # FIXME: sizing is not accurate with the handmade cell. # Change once cell widths are fixed. access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"] return 2 * access_tx_cin def get_storage_net_names(self): """ Returns names of storage nodes in bitcell in [non-inverting, inverting] format. """ # Checks that they do exist if self.nets_match: return self.storage_nets else: fmt_str = "Storage nodes={} not found in spice file." debug.info(1, fmt_str.format(self.storage_nets)) return None def get_storage_net_offset(self): """ Gets the location of the storage net labels to add top level labels for pex simulation. """ # If we generated the bitcell, we already know where Q and Q_bar are if OPTS.bitcell != "pbitcell": self.storage_net_offsets = [] for i in range(len(self.get_storage_net_names())): for text in self.gds.getTexts(layer["m1"]): if self.storage_nets[i] == text.textString.rstrip('\x00'): self.storage_net_offsets.append(text.coordinates[0]) for i in range(len(self.storage_net_offsets)): self.storage_net_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.storage_net_offsets[i]]) return(self.storage_net_offsets) def get_bitline_offset(self): bl_names = self.get_all_bl_names() br_names = self.get_all_br_names() found_bl = [] found_br = [] self.bl_offsets = [] self.br_offsets = [] for i in range(len(bl_names)): for text in self.gds.getTexts(layer["m2"]): if not bl_names[i] in found_bl: if bl_names[i] == text.textString.rstrip('\x00'): self.bl_offsets.append(text.coordinates[0]) found_bl.append(bl_names[i]) continue for i in range(len(br_names)): for text in self.gds.getTexts(layer["m2"]): if not br_names[i] in found_br: if br_names[i] == text.textString.rstrip('\x00'): self.br_offsets.append(text.coordinates[0]) found_br.append(br_names[i]) continue for i in range(len(self.bl_offsets)): self.bl_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.bl_offsets[i]]) for i in range(len(self.br_offsets)): self.br_offsets[i] = tuple([self.gds.info["units"][0] * x for x in self.br_offsets[i]]) return(self.bl_offsets, self.br_offsets, found_bl, found_br) def get_normalized_storage_nets_offset(self): """ Convert storage net offset to be relative to the bottom left corner of the bitcell. This is useful for making sense of offsets outside of the bitcell. """ if OPTS.bitcell != "pbitcell": normalized_storage_net_offset = self.get_storage_net_offset() else: net_offset = self.get_storage_net_offset() Q_x = net_offset[0][0] - self.leftmost_xpos Q_y = net_offset[0][1] - self.botmost_ypos Q_bar_x = net_offset[1][0] - self.leftmost_xpos Q_bar_y = net_offset[1][1] - self.botmost_ypos normalized_storage_net_offset = [[Q_x, Q_y], [Q_bar_x, Q_bar_y]] return normalized_storage_net_offset def get_normalized_bitline_offset(self): return self.get_bitline_offset() def build_graph(self, graph, inst_name, port_nets): """ By default, bitcells won't be part of the graph. """ return