diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index 985c8a86..1e55d5fb 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -372,66 +372,78 @@ class pgate(design.design): self.width = width @staticmethod - def bin_width(tx_type, target_width): + def best_bin(tx_type, target_width): + """ + Determine the width transistor that meets the accuracy requirement and is larger than target_width. + """ + + # Find all of the relavent scaled bins and multiples + scaled_bins = pgate.scaled_bins(tx_type, target_width) + for (scaled_width, multiple) in scaled_bins: + if abs(target_width - scaled_width) / target_width <= 1 - OPTS.accuracy_requirement: + break + else: + debug.error("failed to bin tx size {}, try reducing accuracy requirement".format(target_width), 1) + + debug.info(2, "binning {0} tx, target: {4}, found {1} x {2} = {3}".format(tx_type, + multiple, + scaled_width / multiple, + scaled_width, + target_width)) + + return(scaled_width / multiple, multiple) + + @staticmethod + def scaled_bins(tx_type, target_width): + """ + Determine a set of widths and multiples that could be close to the right size + sorted by the fewest number of fingers. + """ if tx_type == "nmos": bins = nmos_bins[drc("minwidth_poly")] elif tx_type == "pmos": bins = pmos_bins[drc("minwidth_poly")] else: debug.error("invalid tx type") - + + # Prune out bins that are too big, except for one bigger bins = bins[0:bisect_left(bins, target_width) + 1] + + # Determine multiple of target width for each bin if len(bins) == 1: - selected_bin = bins[0] - scaling_factor = math.ceil(target_width / selected_bin) - scaled_bin = bins[0] * scaling_factor - + scaled_bins = [(bins[0], math.ceil(target_width / bins[0]))] else: - base_bins = [] scaled_bins = [] - scaling_factors = [] + # Add the biggest size as 1x multiple + scaled_bins.append((bins[-1], 1)) + # Compute discrete multiple of other sizes + for width in reversed(bins[:-1]): + multiple = math.ceil(target_width / width) + scaled_bins.append((multiple * width, multiple)) - for width in bins: - m = math.ceil(target_width / width) - base_bins.append(width) - scaling_factors.append(m) - scaled_bins.append(m * width) - - select = -1 - for i in reversed(range(0, len(scaled_bins))): - if abs(target_width - scaled_bins[i])/target_width <= 1-OPTS.accuracy_requirement: - select = i - break - if select == -1: - debug.error("failed to bin tx size {}, try reducing accuracy requirement".format(target_width), 1) - scaling_factor = scaling_factors[select] - scaled_bin = scaled_bins[select] - selected_bin = base_bins[select] - - debug.info(2, "binning {0} tx, target: {4}, found {1} x {2} = {3}".format(tx_type, selected_bin, scaling_factor, selected_bin * scaling_factor, target_width)) - - return(selected_bin, scaling_factor) - - def permute_widths(self, tx_type, target_width): + return(scaled_bins) + @staticmethod + def nearest_bin(tx_type, target_width): + """ + Determine the nearest width to the given target_width + while assuming a single multiple. + """ if tx_type == "nmos": bins = nmos_bins[drc("minwidth_poly")] elif tx_type == "pmos": bins = pmos_bins[drc("minwidth_poly")] else: - debug.error("invalid tx type") - bins = bins[0:bisect_left(bins, target_width) + 1] - if len(bins) == 1: - scaled_bins = [(bins[0], math.ceil(target_width / bins[0]))] - else: - scaled_bins = [] - scaled_bins.append((bins[-1], 1)) - for width in bins[:-1]: - m = math.ceil(target_width / width) - scaled_bins.append((m * width, m)) + debug.error("invalid tx type") - return(scaled_bins) - - def bin_accuracy(self, ideal_width, width): - return 1-abs((ideal_width - width)/ideal_width) + # Find the next larger bin + bin_loc = bisect_left(bins, target_width) + if bin_loc < len(bins): + return bins[bin_loc] + else: + return bins[-1] + + @staticmethod + def bin_accuracy(ideal_width, width): + return 1 - abs((ideal_width - width) / ideal_width) diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index aa0f7d9a..4caf2a18 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -14,15 +14,11 @@ from vector import vector from math import ceil from globals import OPTS from utils import round_to_grid -from bisect import bisect_left import logical_effort from sram_factory import factory from errors import drc_error -if(OPTS.tech_name == "sky130"): - from tech import nmos_bins, pmos_bins - class pinv(pgate.pgate): """ Pinv generates gds of a parametrically sized inverter. The @@ -164,8 +160,8 @@ class pinv(pgate.pgate): 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) + nmos_bins = self.scaled_bins("nmos", self.nmos_width) + pmos_bins = self.scaled_bins("pmos", self.pmos_width) valid_pmos = [] for bin in pmos_bins: diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py index 4d4f2229..672bde2d 100644 --- a/compiler/pgates/pinv_dec.py +++ b/compiler/pgates/pinv_dec.py @@ -13,6 +13,7 @@ from vector import vector from globals import OPTS from sram_factory import factory + class pinv_dec(pinv.pinv): """ This is another version of pinv but with layout for the decoder. @@ -50,9 +51,8 @@ class pinv_dec(pinv.pinv): 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 + self.nmos_width = self.nearest_bin("nmos", self.nmos_width) + self.pmos_width = self.nearest_bin("pmos", self.pmos_width) # Over-ride the route input gate to call the horizontal version. # Other top-level netlist and layout functions are not changed. diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index 3974d95b..fb6bb210 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -39,8 +39,8 @@ class pnand2(pgate.pgate): self.tx_mults = 1 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) + self.nmos_width = self.nearest_bin("nmos", self.nmos_width) + self.pmos_width = self.nearest_bin("pmos", self.pmos_width) # Creates the netlist and layout pgate.pgate.__init__(self, name, height, add_wells) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index ff988d88..e4e71e61 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -42,8 +42,8 @@ class pnand3(pgate.pgate): self.tx_mults = 1 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) + self.nmos_width = self.nearest_bin("nmos", self.nmos_width) + self.pmos_width = self.nearest_bin("pmos", self.pmos_width) # Creates the netlist and layout pgate.pgate.__init__(self, name, height, add_wells) diff --git a/compiler/pgates/pnor2.py b/compiler/pgates/pnor2.py index 908bba82..aad405e8 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/pgates/pnor2.py @@ -38,8 +38,8 @@ class pnor2(pgate.pgate): self.tx_mults = 1 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) + self.nmos_width = self.nearest_bin("nmos", self.nmos_width) + self.pmos_width = self.nearest_bin("pmos", self.pmos_width) # Creates the netlist and layout pgate.pgate.__init__(self, name, height, add_wells) diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index b3d25865..52d24390 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -9,11 +9,10 @@ import contact import design import debug from pgate import pgate -from tech import parameter +from tech import parameter, drc from vector import vector from globals import OPTS from sram_factory import factory -from tech import drc, layer class precharge(design.design): @@ -81,7 +80,7 @@ class precharge(design.design): Initializes the upper and lower pmos """ if(OPTS.tech_name == "sky130"): - (self.ptx_width, self.ptx_mults) = pgate.bin_width("pmos", self.ptx_width) + self.ptx_width = pgate.nearest_bin("pmos", self.ptx_width) self.pmos = factory.create(module_type="ptx", width=self.ptx_width, mults=self.ptx_mults, diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index ce8591fd..b6f839f3 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -131,7 +131,7 @@ class ptx(design.design): perimeter_sd = 2 * self.poly_width + 2 * self.tx_width if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre": # sky130 simulation cannot use the mult parameter in simulation - (self.tx_width, self.mults) = pgate.bin_width(self.tx_type, self.tx_width) + (self.tx_width, self.mults) = pgate.best_bin(self.tx_type, self.tx_width) main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], self.mults, self.tx_width,