Exemple #1
0
    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)
Exemple #2
0
    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)
Exemple #3
0
    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.")
Exemple #4
0
    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))
Exemple #5
0
    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)
Exemple #6
0
    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
Exemple #7
0
    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))