Prune ptx code. Change sizes to be relative to min size.

This commit is contained in:
Matt Guthaus 2017-11-29 12:31:00 -08:00
parent d4f8d63442
commit 1bcef7e3ee
8 changed files with 121 additions and 76 deletions

View File

@ -470,7 +470,7 @@ class bank(design.design):
""" """
# 4x Inverter # 4x Inverter
self.inv4x = pinv(nmos_width=4*drc["minwidth_tx"]) self.inv4x = pinv(4)
self.add_mod(self.inv4x) self.add_mod(self.inv4x)
self.nor2 = nor_2() self.nor2 = nor_2()

View File

@ -50,13 +50,13 @@ class control_logic(design.design):
# Special gates: inverters for buffering # Special gates: inverters for buffering
self.inv = self.inv1 = pinv() self.inv = self.inv1 = pinv()
self.add_mod(self.inv1) self.add_mod(self.inv1)
self.inv2 = pinv(nmos_width=2*drc["minwidth_tx"]) self.inv2 = pinv(2)
self.add_mod(self.inv2) self.add_mod(self.inv2)
self.inv4 = pinv(nmos_width=4*drc["minwidth_tx"]) self.inv4 = pinv(4)
self.add_mod(self.inv4) self.add_mod(self.inv4)
self.inv8 = pinv(nmos_width=8*drc["minwidth_tx"]) self.inv8 = pinv(8)
self.add_mod(self.inv8) self.add_mod(self.inv8)
self.inv16 = pinv(nmos_width=16*drc["minwidth_tx"]) self.inv16 = pinv(16)
self.add_mod(self.inv16) self.add_mod(self.inv16)
c = reload(__import__(OPTS.config.ms_flop_array)) c = reload(__import__(OPTS.config.ms_flop_array))

View File

@ -21,16 +21,16 @@ class nand_2(design.design):
unique_id = 1 unique_id = 1
def __init__(self, nmos_width=2*drc["minwidth_tx"], height=bitcell.height): def __init__(self, size=2, height=bitcell.height):
"""Constructor : Creates a cell for a simple 2 input nand""" """Constructor : Creates a cell for a simple 2 input nand"""
name = "nand2_{0}".format(nand_2.unique_id) name = "nand2_{0}".format(nand_2.unique_id)
nand_2.unique_id += 1 nand_2.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(2, "create nand_2 structure {0} with size of {1}".format(name, nmos_width)) debug.info(2, "create nand_2 structure {0} with size of {1}".format(name, nmos_width))
self.nmos_size = nmos_width self.nmos_size = 2*size
# FIXME why is this? # FIXME why is this?
self.pmos_size = nmos_width self.pmos_size = size
self.tx_mults = 1 self.tx_mults = 1
self.height = height self.height = height

View File

@ -21,16 +21,16 @@ class nand_3(design.design):
unique_id = 1 unique_id = 1
def __init__(self, nmos_width=3*drc["minwidth_tx"], height=bitcell.height): def __init__(self, size=1, height=bitcell.height):
"""Constructor : Creates a cell for a simple 3 input nand""" """Constructor : Creates a cell for a simple 3 input nand"""
name = "nand3_{0}".format(nand_3.unique_id) name = "nand3_{0}".format(nand_3.unique_id)
nand_3.unique_id += 1 nand_3.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(2, "create nand_3 structure {0} with size of {1}".format(name, nmos_width)) debug.info(2, "create nand_3 structure {0} with size of {1}".format(name, nmos_width))
self.nmos_size = nmos_width self.nmos_size = 3*size
# FIXME: Why is this?? # FIXME: Why is this??
self.pmos_size = 2 * nmos_width / 3 self.pmos_size = 2*size
self.tx_mults = 1 self.tx_mults = 1
self.height = height self.height = height

View File

@ -20,7 +20,7 @@ class nor_2(design.design):
unique_id = 1 unique_id = 1
def __init__(self, nmos_width=drc["minwidth_tx"], height=bitcell.height): def __init__(self, size=1, height=bitcell.height):
"""Constructor : Creates a cell for a simple 2 input nor""" """Constructor : Creates a cell for a simple 2 input nor"""
name = "nor2_{0}".format(nor_2.unique_id) name = "nor2_{0}".format(nor_2.unique_id)
nor_2.unique_id += 1 nor_2.unique_id += 1
@ -29,7 +29,7 @@ class nor_2(design.design):
debug.check(nmos_width==drc["minwidth_tx"], "Need to rewrite nor2 for sizing.") debug.check(nmos_width==drc["minwidth_tx"], "Need to rewrite nor2 for sizing.")
self.nmos_width = nmos_width self.nmos_size =
self.height = height self.height = height
self.add_pins() self.add_pins()

View File

@ -9,37 +9,47 @@ from globals import OPTS
class pinv(design.design): class pinv(design.design):
""" """
This module generates gds of a parametrically sized inverter. This module generates gds of a parametrically sized inverter. The
This model use ptx to generate a inverter within a cetrain height. size is specified as the nmos size (relative to minimum) and a
The inverter's cell_height should be the same as the 6t library cell. beta value for choosing the pmos size. The inverter's cell_height
is usually the same as the 6t library cell. The route_output will
route the output to the right side of the cell for easier access.
""" """
c = reload(__import__(OPTS.config.bitcell)) c = reload(__import__(OPTS.config.bitcell))
bitcell = getattr(c, OPTS.config.bitcell) bitcell = getattr(c, OPTS.config.bitcell)
unique_id = 1 unique_id = 1
def __init__(self, nmos_width=drc["minwidth_tx"], beta=parameter["pinv_beta"], height=bitcell.height, route_output=True): def __init__(self, size=1, beta=parameter["pinv_beta"], height=bitcell.height, route_output=True):
"""Constructor : Creates a cell for a simple inverter""" # We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will
# over-write a design in GDS if one has and the other doesn't
# have poly connected, for example.
name = "pinv{0}".format(pinv.unique_id) name = "pinv{0}".format(pinv.unique_id)
pinv.unique_id += 1 pinv.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(2, "create pinv structure {0} with size of {1}".format(name, nmos_width)) debug.info(2, "create pinv structure {0} with size of {1}".format(name, size))
self.nmos_width = nmos_width self.nmos_size = size
self.pmos_size = beta*size
self.beta = beta self.beta = beta
self.height = height self.height = height
self.route_output = route_output self.route_output = route_output
self.add_pins() self.add_pins()
self.create_layout() self.create_layout()
# for run-time, we won't check every transitor DRC/LVS independently
# but this may be uncommented for debug purposes
#self.DRC_LVS() #self.DRC_LVS()
def add_pins(self): def add_pins(self):
"""Adds pins for spice netlist processing""" """Adds pins for spice netlist"""
self.add_pin_list(["A", "Z", "vdd", "gnd"]) self.add_pin_list(["A", "Z", "vdd", "gnd"])
def create_layout(self): def create_layout(self):
"""Calls all functions related to the generation of the layout(gds)""" """Calls all functions related to the generation of the layout"""
# These aren't for instantiating, but we use them to get the dimensions # These aren't for instantiating, but we use them to get the dimensions
self.poly_contact = contact.contact(("poly", "contact", "metal1")) self.poly_contact = contact.contact(("poly", "contact", "metal1"))
@ -66,45 +76,54 @@ class pinv(design.design):
self.route_pins() self.route_pins()
def determine_tx_mults(self): def determine_tx_mults(self):
"""Determines the number of fingers needed to achieve same size with a height constraint""" """
# check minimum distance between well Determines the number of fingers needed to achieve the size within
minwidth_poly_contact = drc["minwidth_contact"] \ the height constraint. This may fail if the user has a tight height.
+ 2 * drc["poly_enclosure_contact"] """
# 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 = ptx(tx_type="nmos")
pmos = ptx(width=self.beta*drc["minwidth_tx"], tx_type="pmos")
tx_height = nmos.height + pmos.height
# rotated m1 pitch
m1_pitch = self.poly_contact.width + drc["metal1_to_metal1"]
metal_height = 4 * m1_pitch # This could be computed more accurately
debug.check(self.height>tx_height + metal_height,"Cell height too small for our simple design rules.")
# this should be 2*poly extension beyond active? # Determine the height left to the transistors to determine the number of fingers
minwidth_box_poly = 2 * drc["minwidth_poly"] \ tx_height_available = self.height - metal_height
+ drc["poly_to_poly"] # Divide the height according to beta
well_to_well = max(drc["pwell_to_nwell"], nmos_height_available = 1.0/(self.beta+1) * tx_height_available
minwidth_poly_contact, pmos_height_available = tx_height_available - nmos_height_available
minwidth_box_poly)
# determine both mos enclosure sizes # Determine the number of mults for each to fit width into available space
bot_mos_enclosure = 2 * (drc["well_enclosure_active"]) self.nmos_width = self.nmos_size*drc["minwidth_tx"]
top_mos_enclosure = 2 * max(drc["well_enclosure_active"], self.pmos_width = self.pmos_size*drc["minwidth_tx"]
drc["metal1_to_metal1"] + 0.5 * drc["minwidth_metal1"]) 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)
self.nmos_size = parameter["min_tx_size"] # The mults must be the same for easy connection of poly
self.pmos_size = parameter["min_tx_size"] * self.beta self.tx_mults = max(nmos_required_mults, pmos_required_mults)
# use multi finger if the cell is not big enough
if self.nmos_size <= self.nmos_width:
self.tx_mults = int(ceil(self.nmos_width / self.nmos_size))
else:
self.tx_mults = 1
# 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...
self.nmos_width = 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 = self.pmos_width / self.tx_mults
debug.check(self.pmos_width>=drc["minwidth_tx"],"Cannot finger PMOS transistors to fit cell height.")
def create_ptx(self): def create_ptx(self):
"""Intiializes a ptx object""" """Intiializes a ptx object"""
self.nmos = ptx(width=self.nmos_size, self.nmos = ptx(width=self.nmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos", tx_type="nmos")
connect_active=True,
connect_poly=True)
self.add_mod(self.nmos) self.add_mod(self.nmos)
self.pmos = ptx(width=self.pmos_size, self.pmos = ptx(width=self.pmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="pmos", tx_type="pmos")
connect_active=True,
connect_poly=True)
self.add_mod(self.pmos) self.add_mod(self.pmos)
def setup_layout_constants(self): def setup_layout_constants(self):

View File

@ -40,17 +40,19 @@ class ptx(design.design):
self.connect_poly = connect_poly self.connect_poly = connect_poly
self.num_contacts = num_contacts self.num_contacts = num_contacts
self.add_pins()
self.create_spice() self.create_spice()
self.create_layout() self.create_layout()
# for run-time, we won't check every transitor DRC independently # for run-time, we won't check every transitor DRC independently
# but this may be uncommented for debug purposes # but this may be uncommented for debug purposes
#self.DRC() #self.DRC()
def add_pins(self): def add_pins(self):
"""Adds pins for spice netlist"""
self.add_pin_list(["D", "G", "S", "B"]) self.add_pin_list(["D", "G", "S", "B"])
def create_layout(self): def create_layout(self):
"""Calls all functions related to the generation of the layout"""
self.setup_layout_constants() self.setup_layout_constants()
self.add_active() self.add_active()
self.add_well_implant() self.add_well_implant()
@ -58,6 +60,8 @@ class ptx(design.design):
self.add_active_contacts() self.add_active_contacts()
def create_spice(self): def create_spice(self):
self.add_pins()
self.spice.append("\n.SUBCKT {0} {1}".format(self.name, self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
" ".join(self.pins))) " ".join(self.pins)))
self.spice.append("M{0} {1} {2} m={3} w={4}u l={5}u".format(self.tx_type, self.spice.append("M{0} {1} {2} m={3} w={4}u l={5}u".format(self.tx_type,
@ -75,7 +79,18 @@ class ptx(design.design):
if self.num_contacts==None: if self.num_contacts==None:
self.num_contacts=self.calculate_num_contacts() self.num_contacts=self.calculate_num_contacts()
# Determine layer types needed
if self.tx_type == "nmos":
self.implant_type = "n"
self.well_type = "p"
elif self.tx_type == "pmos":
self.implant_type = "p"
self.well_type = "n"
else:
self.error("Invalid transitor type.",-1)
# This is not actually instantiated but used for calculations # This is not actually instantiated but used for calculations
self.active_contact = contact(layer_stack=("active", "contact", "metal1"), self.active_contact = contact(layer_stack=("active", "contact", "metal1"),
dimensions=(1, self.num_contacts)) dimensions=(1, self.num_contacts))
@ -112,11 +127,19 @@ class ptx(design.design):
self.poly_height = self.tx_width + 2*self.poly_extend_active self.poly_height = self.tx_width + 2*self.poly_extend_active
# Well enclosure of active, ensure minwidth as well # Well enclosure of active, ensure minwidth as well
self.well_width = max(self.active_width + 2*drc["well_enclosure_active"], if info["has_{}well".format(self.well_type)]:
drc["minwidth_well"]) self.well_width = max(self.active_width + 2*drc["well_enclosure_active"],
self.well_height = max(self.tx_width + 2*drc["well_enclosure_active"], drc["minwidth_well"])
drc["minwidth_well"]) self.well_height = max(self.tx_width + 2*drc["well_enclosure_active"],
drc["minwidth_well"])
self.height = self.well_height
self.width = self.well_width
else:
# If no well, use the boundary of the active and poly
self.height = self.poly_height
self.width = self.active_width
# The active offset is due to the well extension # The active offset is due to the well extension
self.active_offset = vector([drc["well_enclosure_active"]]*2) self.active_offset = vector([drc["well_enclosure_active"]]*2)
@ -231,17 +254,8 @@ class ptx(design.design):
""" """
Add an (optional) well and implant for the type of transistor. Add an (optional) well and implant for the type of transistor.
""" """
if self.tx_type == "nmos": if info["has_{}well".format(self.well_type)]:
implant_type = "n" self.add_rect(layer="{}well".format(self.well_type),
well_type = "p"
elif self.tx_type == "pmos":
implant_type = "p"
well_type = "n"
else:
self.error("Invalid transitor type.",-1)
if info["has_{}well".format(well_type)]:
self.add_rect(layer="{}well".format(well_type),
offset=(0,0), offset=(0,0),
width=self.well_width, width=self.well_width,
height=self.well_height) height=self.well_height)
@ -249,7 +263,7 @@ class ptx(design.design):
offset=(0,0), offset=(0,0),
width=self.well_width, width=self.well_width,
height=self.well_height) height=self.well_height)
self.add_rect(layer="{}implant".format(implant_type), self.add_rect(layer="{}implant".format(self.implant_type),
offset=self.active_offset, offset=self.active_offset,
width=self.active_width, width=self.active_width,
height=self.active_height) height=self.active_height)

View File

@ -25,18 +25,30 @@ class pinv_test(unittest.TestCase):
import pinv import pinv
import tech import tech
# debug.info(2, "Checking min size inverter") debug.info(2, "Checking min size inverter")
# tx = pinv.pinv(nmos_width=tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"]) tx = pinv.pinv(size=1, beta=tech.parameter["pinv_beta"])
# self.local_check(tx) self.local_check(tx)
debug.info(2, "Checking 2x min size inverter") debug.info(2, "Checking 2x min size inverter")
tx = pinv.pinv(nmos_width=2 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"]) tx = pinv.pinv(size=2, beta=tech.parameter["pinv_beta"])
self.local_check(tx) self.local_check(tx)
debug.info(2, "Checking 5x min size inverter") debug.info(2, "Checking 5x min size inverter")
tx = pinv.pinv(nmos_width=5 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"]) tx = pinv.pinv(size=5, beta=tech.parameter["pinv_beta"])
self.local_check(tx) self.local_check(tx)
debug.info(2, "Checking min size inverter with new beta")
tx = pinv.pinv(size=1, beta=3)
self.local_check(tx)
debug.info(2, "Checking 2x size inverter with new beta")
tx = pinv.pinv(size=2, beta=3)
self.local_check(tx)
debug.info(2, "Checking 5x size inverter with new beta")
tx = pinv.pinv(size=4, beta=3)
self.local_check(tx)
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()