def determine_tx_mults(self): """ Determines the number of fingers for the height constraint. """ min_tx = ptx(width=self.minwidth_tx, mults=1, tx_type="nmos") # This is a active-to-active of a flipped cell of active-conatct to power-rail inside cell top_bottom_space = max(self.active_to_active, 2 * self.m1_space + contact.m1m2.width) # Determine the height left to the transistors for number of fingers calculation tx_height_available = self.height - top_bottom_space #maximum possible num fingers max_mults = max(int(ceil(tx_height_available / min_tx.active_width)), 1) if self.nmos_size < max_mults: self.tx_mults = self.nmos_size else: self.tx_mults = max_mults # We need to round the width to the grid or we will end up with LVS property mismatch # errors when fingers are not a grid length and get rounded in the offset geometry. self.nmos_width = round_to_grid( (self.nmos_size * self.minwidth_tx) / self.tx_mults) self.pmos_width = round_to_grid( (self.pmos_size * self.minwidth_tx) / self.tx_mults)
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0): """Initializes an instance to represent a module""" geometry.__init__(self) debug.check( mirror not in ["R90", "R180", "R270"], "Please use rotation and not mirroring during instantiation.") self.name = name self.mod = mod self.gds = mod.gds self.rotate = rotate self.offset = vector(offset).snap_to_grid() self.mirror = mirror if OPTS.netlist_only: self.width = 0 self.height = 0 else: if mirror in ["R90", "R270"] or rotate in [90, 270]: self.width = round_to_grid(mod.height) self.height = round_to_grid(mod.width) else: self.width = round_to_grid(mod.width) self.height = round_to_grid(mod.height) self.compute_boundary(offset, mirror, rotate) debug.info(4, "creating instance: " + self.name)
def determine_tx_mults(self): """ Determines the number of fingers needed to achieve the size within the height constraint. This may fail if the user has a tight height. """ # This may make the result differ when the layout is created... if OPTS.netlist_only: self.tx_mults = 1 self.nmos_width = self.nmos_size*drc("minwidth_tx") self.pmos_width = self.pmos_size*drc("minwidth_tx") return # Do a quick sanity check and bail if unlikely feasible height # Sanity check. can we make an inverter in the height with minimum tx sizes? # Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain) # plus the tx height nmos = factory.create(module_type="ptx", tx_type="nmos") pmos = factory.create(module_type="ptx", width=drc("minwidth_tx"), tx_type="pmos") tx_height = nmos.poly_height + pmos.poly_height # rotated m1 pitch or poly to active spacing min_channel = max(contact.poly.width + self.m1_space, contact.poly.width + 2*drc("poly_to_active")) # This is the extra space needed to ensure DRC rules to the active contacts extra_contact_space = max(-nmos.get_pin("D").by(),0) # This is a poly-to-poly of a flipped cell self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, drc("poly_extend_active"), self.poly_space) total_height = tx_height + min_channel + 2*self.top_bottom_space debug.check(self.height> total_height,"Cell height {0} too small for simple min height {1}.".format(self.height,total_height)) # Determine the height left to the transistors to determine the number of fingers tx_height_available = self.height - min_channel - 2*self.top_bottom_space # Divide the height in half. Could divide proportional to beta, but this makes # connecting wells of multiple cells easier. # Subtract the poly space under the rail of the tx nmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly") pmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly") debug.info(2,"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available, nmos_height_available, pmos_height_available)) # Determine the number of mults for each to fit width into available space self.nmos_width = self.nmos_size*drc("minwidth_tx") self.pmos_width = self.pmos_size*drc("minwidth_tx") nmos_required_mults = max(int(ceil(self.nmos_width/nmos_height_available)),1) pmos_required_mults = max(int(ceil(self.pmos_width/pmos_height_available)),1) # The mults must be the same for easy connection of poly self.tx_mults = max(nmos_required_mults, pmos_required_mults) # Recompute each mult width and check it isn't too small # This could happen if the height is narrow and the size is small # User should pick a bigger size to fix it... # We also need to round the width to the grid or we will end up with LVS property # mismatch errors when fingers are not a grid length and get rounded in the offset geometry. self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) debug.check(self.nmos_width>=drc("minwidth_tx"),"Cannot finger NMOS transistors to fit cell height.") self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) debug.check(self.pmos_width>=drc("minwidth_tx"),"Cannot finger PMOS transistors to fit cell height.")
def __init__(self, lpp, offset, width, height): """Initializes a rectangular shape for specified layer""" super().__init__(lpp) self.name = "rect" self.offset = vector(offset).snap_to_grid() self.size = vector(width, height).snap_to_grid() self.width = round_to_grid(self.size.x) self.height = round_to_grid(self.size.y) self.compute_boundary(offset, "", 0) debug.info( 4, "creating rectangle (" + str(self.layerNumber) + "): " + str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
def compute_layer_pitch(self, layer_stack, preferred): (layer1, via, layer2) = layer_stack try: if layer1 == "poly" or layer1 == "active": contact1 = getattr(contact, layer1 + "_contact") else: contact1 = getattr(contact, layer1 + "_via") except AttributeError: contact1 = getattr(contact, layer2 + "_via") if preferred: if self.get_preferred_direction(layer1) == "V": contact_width = contact1.first_layer_width else: contact_width = contact1.first_layer_height else: if self.get_preferred_direction(layer1) == "V": contact_width = contact1.first_layer_height else: contact_width = contact1.first_layer_width layer_space = getattr(self, layer1 + "_space") #print(layer_stack) #print(contact1) pitch = contact_width + layer_space return round_to_grid(pitch)
def add_ptx(self): """ Create the two pass gate NMOS transistors to switch the bitlines. Width of column_mux cell is equal to bitcell' width for abutment. first NMOS at left and second NMOS at right""" # If NMOS size if bigger than bicell_width use multi finger NMOS n = int( math.floor((0.5 * (self.bitcell.width - 6 * self.m1_space)) / self.minwidth_tx)) m = (self.ptx_width / n) if m > self.minwidth_tx: num_fing = int(math.ceil(self.ptx_width / n)) else: num_fing = 1 self.nmos = ptx(width=round_to_grid(self.ptx_width / num_fing), mults=num_fing, tx_type="nmos", connect_active=True, connect_poly=True) self.add_mod(self.nmos) if info["has_nimplant"]: shift = self.implant_enclose_poly else: shift = 0.5 * self.poly_space # Add nmos1 and nmos2 with a pace it in the center self.nmos1_position = vector(self.nmos.height + shift, -self.nmos.active_offset.x) self.nmos1 = self.add_inst(name="mux_tx1", mod=self.nmos, offset=self.nmos1_position, rotate=90) self.connect_inst(["bl", "sel", "bl_out", "gnd"]) # nmos2 in same y_offset as nmos1 for gate abutting self.nmos2_position = vector(self.bitcell.width - shift, self.nmos1_position.y) self.nmos2 = self.add_inst(name="mux_tx2", mod=self.nmos, offset=self.nmos2_position, rotate=90) self.connect_inst(["br", "sel", "br_out", "gnd"]) self.top = self.nmos2.uy() + self.pin_height self.height = self.top + contact.well.height + self.pin_height
def determine_tx_mults(self): """ Determines the number of fingers needed to achieve the size within the height constraint. This may fail if the user has a tight height. """ # This may make the result differ when the layout is created... if OPTS.netlist_only: self.tx_mults = 1 self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") if OPTS.tech_name == "sky130": (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) return # Do a quick sanity check and bail if unlikely feasible height # Sanity check. can we make an inverter in the height # with minimum tx sizes? # Assume we need 3 metal 1 pitches (2 power rails, one # between the tx for the drain) # plus the tx height nmos = factory.create(module_type="ptx", tx_type="nmos") pmos = factory.create(module_type="ptx", width=drc("minwidth_tx"), tx_type="pmos") tx_height = nmos.poly_height + pmos.poly_height # rotated m1 pitch or poly to active spacing min_channel = max(contact.poly_contact.width + self.m1_space, contact.poly_contact.width + 2 * self.poly_to_active) total_height = tx_height + min_channel + 2 * self.top_bottom_space # debug.check(self.height > total_height, # "Cell height {0} too small for simple min height {1}.".format(self.height, # total_height)) if total_height > self.height: msg = "Cell height {0} too small for simple min height {1}.".format( self.height, total_height) raise drc_error(msg) # Determine the height left to the transistors to determine # the number of fingers tx_height_available = self.height - min_channel - 2 * self.top_bottom_space # Divide the height in half. Could divide proportional to beta, # but this makes connecting wells of multiple cells easier. # Subtract the poly space under the rail of the tx nmos_height_available = 0.5 * tx_height_available - 0.5 * self.poly_space pmos_height_available = 0.5 * tx_height_available - 0.5 * self.poly_space debug.info( 2, "Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format( tx_height_available, nmos_height_available, pmos_height_available)) # Determine the number of mults for each to fit width # into available space if OPTS.tech_name != "sky130": self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") nmos_required_mults = max( int(ceil(self.nmos_width / nmos_height_available)), 1) pmos_required_mults = max( int(ceil(self.pmos_width / pmos_height_available)), 1) # The mults must be the same for easy connection of poly self.tx_mults = max(nmos_required_mults, pmos_required_mults) # Recompute each mult width and check it isn't too small # This could happen if the height is narrow and the size is small # User should pick a bigger size to fix it... # We also need to round the width to the grid or we will end up # with LVS property mismatch errors when fingers are not a grid # length and get rounded in the offset geometry. self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) # debug.check(self.nmos_width >= drc("minwidth_tx"), # "Cannot finger NMOS transistors to fit cell height.") if self.nmos_width < drc("minwidth_tx"): raise drc_error( "Cannot finger NMOS transistors to fit cell height.") self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) #debug.check(self.pmos_width >= drc("minwidth_tx"), # "Cannot finger PMOS transistors to fit cell height.") if self.pmos_width < drc("minwidth_tx"): raise drc_error( "Cannot finger NMOS transistors to fit cell height.") else: self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") nmos_bins = self.permute_widths("nmos", self.nmos_width) pmos_bins = self.permute_widths("pmos", self.pmos_width) valid_pmos = [] for bin in pmos_bins: if abs(self.bin_accuracy( self.pmos_width, bin[0])) > OPTS.accuracy_requirement and abs( self.bin_accuracy(self.pmos_width, bin[0])) <= 1: valid_pmos.append(bin) valid_pmos.sort(key=operator.itemgetter(1)) valid_nmos = [] for bin in nmos_bins: if abs(self.bin_accuracy( self.nmos_width, bin[0])) > OPTS.accuracy_requirement and abs( self.bin_accuracy(self.nmos_width, bin[0])) <= 1: valid_nmos.append(bin) valid_nmos.sort(key=operator.itemgetter(1)) for bin in valid_pmos: if bin[0] / bin[1] < pmos_height_available: self.pmos_width = bin[0] / bin[1] pmos_mults = bin[1] break for bin in valid_nmos: if bin[0] / bin[1] < nmos_height_available: self.nmos_width = bin[0] / bin[1] nmos_mults = bin[1] break self.tx_mults = max(pmos_mults, nmos_mults) debug.info( 2, "prebinning {0} tx, target: {4}, found {1} x {2} = {3}".format( "pmos", self.pmos_width, pmos_mults, self.pmos_width * pmos_mults, self.pmos_size * drc("minwidth_tx"))) debug.info( 2, "prebinning {0} tx, target: {4}, found {1} x {2} = {3}".format( "nmos", self.nmos_width, nmos_mults, self.nmos_width * nmos_mults, self.nmos_size * drc("minwidth_tx"))) pinv.bin_count += 1 pinv.bin_error += abs(((self.pmos_width * pmos_mults) - (self.pmos_size * drc("minwidth_tx"))) / (self.pmos_size * drc("minwidth_tx"))) pinv.bin_count += 1 pinv.bin_error += abs(((self.nmos_width * nmos_mults) - (self.nmos_size * drc("minwidth_tx"))) / (self.nmos_size * drc("minwidth_tx"))) debug.info( 2, "pinv bin count: {0} pinv bin error: {1} percent error {2}". format(pinv.bin_count, pinv.bin_error, pinv.bin_error / pinv.bin_count))