optimize tx binning for area

This commit is contained in:
jcirimel 2020-06-05 02:53:03 -07:00
parent 5666e79287
commit 08f6bd8d24
1 changed files with 89 additions and 57 deletions

View File

@ -17,22 +17,39 @@ from globals import OPTS
if(OPTS.tech_name == "s8"): if(OPTS.tech_name == "s8"):
from tech import nmos_bins, pmos_bins, accuracy_requirement from tech import nmos_bins, pmos_bins, accuracy_requirement
class pgate(design.design): class pgate(design.design):
""" """
This is a module that implements some shared This is a module that implements some shared
functions for parameterized gates. functions for parameterized gates.
""" """
def __init__(self, name, height=None): def __init__(self, name, height=None, add_wells=True):
""" Creates a generic cell """ """ Creates a generic cell """
design.design.__init__(self, name) design.design.__init__(self, name)
if height: if height:
self.height = height self.height = height
elif not height: elif not height:
# By default, we make it 10 M1 pitch tall # By default, something simple
self.height = 10*self.m1_pitch self.height = 14 * self.m1_pitch
self.add_wells = add_wells
if "li" in layer:
self.route_layer = "li"
else:
self.route_layer = "m1"
self.route_layer_width = getattr(self, "{}_width".format(self.route_layer))
self.route_layer_space = getattr(self, "{}_space".format(self.route_layer))
self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer))
# This is the space from a S/D contact to the supply rail
contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space
# This is a poly-to-poly of a flipped cell
poly_to_poly_gate_space = self.poly_extend_active + self.poly_space
self.top_bottom_space = max(contact_to_vdd_rail_space,
poly_to_poly_gate_space)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -47,27 +64,27 @@ class pgate(design.design):
""" Pure virtual function """ """ Pure virtual function """
debug.error("Must over-ride create_layout.", -1) debug.error("Must over-ride create_layout.", -1)
def connect_pin_to_rail(self, inst, pin, supply): def connect_pin_to_rail(self, inst, pin_name, supply_name):
""" Connects a ptx pin to a supply rail. """ """ Connects a ptx pin to a supply rail. """
source_pin = inst.get_pin(pin) supply_pin = self.get_pin(supply_name)
supply_pin = self.get_pin(supply)
if supply_pin.overlaps(source_pin):
return
if supply == "gnd":
height = supply_pin.by() - source_pin.by()
elif supply == "vdd":
height = supply_pin.uy() - source_pin.by()
else:
debug.error("Invalid supply name.", -1)
if abs(height) > 0: source_pins = inst.get_pins(pin_name)
self.add_rect(layer="m1", for source_pin in source_pins:
if supply_name == "gnd":
height = supply_pin.by() - source_pin.by()
elif supply_name == "vdd":
height = supply_pin.uy() - source_pin.by()
else:
debug.error("Invalid supply name.", -1)
debug.check(supply_pin.layer == source_pin.layer, "Supply pin is not on correct layer.")
self.add_rect(layer=source_pin.layer,
offset=source_pin.ll(), offset=source_pin.ll(),
height=height, height=height,
width=source_pin.width()) width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left"): def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None):
""" """
Route the input gate to the left side of the cell for access. Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or Position specifies to place the contact the left, center, or
@ -93,8 +110,6 @@ class pgate(design.design):
# Center is completely symmetric. # Center is completely symmetric.
contact_width = contact.poly_contact.width contact_width = contact.poly_contact.width
contact_m1_width = contact.poly_contact.second_layer_width
contact_m1_height = contact.poly_contact.second_layer_height
if position == "center": if position == "center":
contact_offset = left_gate_offset \ contact_offset = left_gate_offset \
@ -111,18 +126,16 @@ class pgate(design.design):
else: else:
debug.error("Invalid contact placement option.", -1) debug.error("Invalid contact placement option.", -1)
if hasattr(self, "li_stack"): via = self.add_via_stack_center(from_layer="poly",
self.add_via_center(layers=self.li_stack, to_layer=self.route_layer,
offset=contact_offset) offset=contact_offset,
directions=directions)
self.add_via_center(layers=self.poly_stack,
offset=contact_offset)
self.add_layout_pin_rect_center(text=name, self.add_layout_pin_rect_center(text=name,
layer="m1", layer=self.route_layer,
offset=contact_offset, offset=contact_offset,
width=contact_m1_width, width=via.mod.second_layer_width,
height=contact_m1_height) height=via.mod.second_layer_height)
# This is to ensure that the contact is # This is to ensure that the contact is
# connected to the gate # connected to the gate
mid_point = contact_offset.scale(0.5, 1) \ mid_point = contact_offset.scale(0.5, 1) \
@ -137,7 +150,7 @@ class pgate(design.design):
# This should match the cells in the cell library # This should match the cells in the cell library
self.nwell_y_offset = 0.48 * self.height self.nwell_y_offset = 0.48 * self.height
full_height = self.height + 0.5* self.m1_width full_height = self.height + 0.5 * self.m1_width
# FIXME: float rounding problem # FIXME: float rounding problem
if "nwell" in layer: if "nwell" in layer:
@ -148,12 +161,12 @@ class pgate(design.design):
nwell_height = nwell_max_offset - self.nwell_y_offset nwell_height = nwell_max_offset - self.nwell_y_offset
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
offset=nwell_position, offset=nwell_position,
width=self.well_width, width=self.width + 2 * self.well_extend_active,
height=nwell_height) height=nwell_height)
if "vtg" in layer: if "vtg" in layer:
self.add_rect(layer="vtg", self.add_rect(layer="vtg",
offset=nwell_position, offset=nwell_position,
width=self.well_width, width=self.width + 2 * self.well_extend_active,
height=nwell_height) height=nwell_height)
# Start this half a rail width below the cell # Start this half a rail width below the cell
@ -164,12 +177,12 @@ class pgate(design.design):
pwell_height = self.nwell_y_offset - pwell_position.y pwell_height = self.nwell_y_offset - pwell_position.y
self.add_rect(layer="pwell", self.add_rect(layer="pwell",
offset=pwell_position, offset=pwell_position,
width=self.well_width, width=self.width + 2 * self.well_extend_active,
height=pwell_height) height=pwell_height)
if "vtg" in layer: if "vtg" in layer:
self.add_rect(layer="vtg", self.add_rect(layer="vtg",
offset=pwell_position, offset=pwell_position,
width=self.well_width, width=self.width + 2 * self.well_extend_active,
height=pwell_height) height=pwell_height)
def add_nwell_contact(self, pmos, pmos_pos): def add_nwell_contact(self, pmos, pmos_pos):
@ -194,12 +207,10 @@ class pgate(design.design):
self.nwell_contact = self.add_via_center(layers=layer_stack, self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=contact_offset, offset=contact_offset,
implant_type="n", implant_type="n",
well_type="n") well_type="n",
if hasattr(self, "li_stack"): directions=("V", "V"))
self.add_via_center(layers=self.li_stack,
offset=contact_offset)
self.add_rect_center(layer="m1", self.add_rect_center(layer=self.route_layer,
offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)), offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)),
width=self.nwell_contact.mod.second_layer_width, width=self.nwell_contact.mod.second_layer_width,
height=self.height - contact_offset.y) height=self.height - contact_offset.y)
@ -249,13 +260,10 @@ class pgate(design.design):
self.pwell_contact= self.add_via_center(layers=layer_stack, self.pwell_contact= self.add_via_center(layers=layer_stack,
offset=contact_offset, offset=contact_offset,
implant_type="p", implant_type="p",
well_type="p") well_type="p",
directions=("V", "V"))
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack, self.add_rect_center(layer=self.route_layer,
offset=contact_offset)
self.add_rect_center(layer="m1",
offset=contact_offset.scale(1, 0.5), offset=contact_offset.scale(1, 0.5),
width=self.pwell_contact.mod.second_layer_width, width=self.pwell_contact.mod.second_layer_width,
height=contact_offset.y) height=contact_offset.y)
@ -279,13 +287,33 @@ class pgate(design.design):
# offset=implant_offset, # offset=implant_offset,
# width=implant_width, # width=implant_width,
# height=implant_height) # height=implant_height)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer=self.route_layer,
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer=self.route_layer,
offset=vector(0.5 * self.width, self.height),
width=self.width)
def determine_width(self): def determine_width(self):
""" Determine the width based on the well contacts (assumed to be on the right side) """ """ Determine the width based on the well contacts (assumed to be on the right side) """
# It was already set or is left as default (minimum)
# Width is determined by well contact and spacing and allowing a supply via between each cell # Width is determined by well contact and spacing and allowing a supply via between each cell
self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width if self.add_wells:
self.well_width = self.width + 2 * self.nwell_enclose_active width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width
# Height is an input parameter, so it is not recomputed. # Height is an input parameter, so it is not recomputed.
else:
max_active_xoffset = self.find_highest_layer_coords("active").x
max_route_xoffset = self.find_highest_layer_coords(self.route_layer).x + 0.5 * self.m1_space
width = max(max_active_xoffset, max_route_xoffset)
self.width = width
@staticmethod @staticmethod
def bin_width(tx_type, target_width): def bin_width(tx_type, target_width):
@ -307,16 +335,20 @@ class pgate(design.design):
base_bins = [] base_bins = []
scaled_bins = [] scaled_bins = []
scaling_factors = [] scaling_factors = []
scaled_bins.append(bins[-1])
base_bins.append(bins[-1]) for width in bins:
scaling_factors.append(1)
for width in bins[0:-1]:
m = math.ceil(target_width / width) m = math.ceil(target_width / width)
base_bins.append(width) base_bins.append(width)
scaling_factors.append(m) scaling_factors.append(m)
scaled_bins.append(m * width) scaled_bins.append(m * width)
select = bisect_left(scaled_bins, target_width) select = -1
for i in reversed(range(0, len(scaled_bins))):
if abs(target_width - scaled_bins[i])/target_width <= 1-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] scaling_factor = scaling_factors[select]
scaled_bin = scaled_bins[select] scaled_bin = scaled_bins[select]
selected_bin = base_bins[select] selected_bin = base_bins[select]
@ -346,4 +378,4 @@ class pgate(design.design):
return(scaled_bins) return(scaled_bins)
def bin_accuracy(self, ideal_width, width): def bin_accuracy(self, ideal_width, width):
return abs(1-(ideal_width - width)/ideal_width) return abs(1-(ideal_width - width)/ideal_width)