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
self.inv4x = pinv(nmos_width=4*drc["minwidth_tx"])
self.inv4x = pinv(4)
self.add_mod(self.inv4x)
self.nor2 = nor_2()

View File

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

View File

@ -21,16 +21,16 @@ class nand_2(design.design):
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"""
name = "nand2_{0}".format(nand_2.unique_id)
nand_2.unique_id += 1
design.design.__init__(self, name)
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?
self.pmos_size = nmos_width
self.pmos_size = size
self.tx_mults = 1
self.height = height

View File

@ -21,16 +21,16 @@ class nand_3(design.design):
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"""
name = "nand3_{0}".format(nand_3.unique_id)
nand_3.unique_id += 1
design.design.__init__(self, name)
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??
self.pmos_size = 2 * nmos_width / 3
self.pmos_size = 2*size
self.tx_mults = 1
self.height = height

View File

@ -20,7 +20,7 @@ class nor_2(design.design):
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"""
name = "nor2_{0}".format(nor_2.unique_id)
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.")
self.nmos_width = nmos_width
self.nmos_size =
self.height = height
self.add_pins()

View File

@ -9,37 +9,47 @@ from globals import OPTS
class pinv(design.design):
"""
This module generates gds of a parametrically sized inverter.
This model use ptx to generate a inverter within a cetrain height.
The inverter's cell_height should be the same as the 6t library cell.
This module generates gds of a parametrically sized inverter. The
size is specified as the nmos size (relative to minimum) and a
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))
bitcell = getattr(c, OPTS.config.bitcell)
unique_id = 1
def __init__(self, nmos_width=drc["minwidth_tx"], beta=parameter["pinv_beta"], height=bitcell.height, route_output=True):
"""Constructor : Creates a cell for a simple inverter"""
def __init__(self, size=1, beta=parameter["pinv_beta"], height=bitcell.height, route_output=True):
# 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)
pinv.unique_id += 1
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.height = height
self.route_output = route_output
self.add_pins()
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()
def add_pins(self):
"""Adds pins for spice netlist processing"""
"""Adds pins for spice netlist"""
self.add_pin_list(["A", "Z", "vdd", "gnd"])
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
self.poly_contact = contact.contact(("poly", "contact", "metal1"))
@ -66,45 +76,54 @@ class pinv(design.design):
self.route_pins()
def determine_tx_mults(self):
"""Determines the number of fingers needed to achieve same size with a height constraint"""
# check minimum distance between well
minwidth_poly_contact = drc["minwidth_contact"] \
+ 2 * drc["poly_enclosure_contact"]
"""
Determines the number of fingers needed to achieve the size within
the height constraint. This may fail if the user has a tight height.
"""
# 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?
minwidth_box_poly = 2 * drc["minwidth_poly"] \
+ drc["poly_to_poly"]
well_to_well = max(drc["pwell_to_nwell"],
minwidth_poly_contact,
minwidth_box_poly)
# Determine the height left to the transistors to determine the number of fingers
tx_height_available = self.height - metal_height
# Divide the height according to beta
nmos_height_available = 1.0/(self.beta+1) * tx_height_available
pmos_height_available = tx_height_available - nmos_height_available
# determine both mos enclosure sizes
bot_mos_enclosure = 2 * (drc["well_enclosure_active"])
top_mos_enclosure = 2 * max(drc["well_enclosure_active"],
drc["metal1_to_metal1"] + 0.5 * drc["minwidth_metal1"])
self.nmos_size = parameter["min_tx_size"]
self.pmos_size = parameter["min_tx_size"] * self.beta
# 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
# 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...
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):
"""Intiializes a ptx object"""
self.nmos = ptx(width=self.nmos_size,
self.nmos = ptx(width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
connect_active=True,
connect_poly=True)
tx_type="nmos")
self.add_mod(self.nmos)
self.pmos = ptx(width=self.pmos_size,
self.pmos = ptx(width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
connect_active=True,
connect_poly=True)
tx_type="pmos")
self.add_mod(self.pmos)
def setup_layout_constants(self):

View File

@ -40,17 +40,19 @@ class ptx(design.design):
self.connect_poly = connect_poly
self.num_contacts = num_contacts
self.add_pins()
self.create_spice()
self.create_layout()
# for run-time, we won't check every transitor DRC independently
# but this may be uncommented for debug purposes
#self.DRC()
def add_pins(self):
"""Adds pins for spice netlist"""
self.add_pin_list(["D", "G", "S", "B"])
def create_layout(self):
"""Calls all functions related to the generation of the layout"""
self.setup_layout_constants()
self.add_active()
self.add_well_implant()
@ -58,6 +60,8 @@ class ptx(design.design):
self.add_active_contacts()
def create_spice(self):
self.add_pins()
self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
" ".join(self.pins)))
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:
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
self.active_contact = contact(layer_stack=("active", "contact", "metal1"),
dimensions=(1, self.num_contacts))
@ -112,11 +127,19 @@ class ptx(design.design):
self.poly_height = self.tx_width + 2*self.poly_extend_active
# Well enclosure of active, ensure minwidth as well
self.well_width = max(self.active_width + 2*drc["well_enclosure_active"],
drc["minwidth_well"])
self.well_height = max(self.tx_width + 2*drc["well_enclosure_active"],
drc["minwidth_well"])
if info["has_{}well".format(self.well_type)]:
self.well_width = max(self.active_width + 2*drc["well_enclosure_active"],
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
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.
"""
if self.tx_type == "nmos":
implant_type = "n"
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),
if info["has_{}well".format(self.well_type)]:
self.add_rect(layer="{}well".format(self.well_type),
offset=(0,0),
width=self.well_width,
height=self.well_height)
@ -249,7 +263,7 @@ class ptx(design.design):
offset=(0,0),
width=self.well_width,
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,
width=self.active_width,
height=self.active_height)

View File

@ -25,18 +25,30 @@ class pinv_test(unittest.TestCase):
import pinv
import tech
# debug.info(2, "Checking min size inverter")
# tx = pinv.pinv(nmos_width=tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"])
# self.local_check(tx)
debug.info(2, "Checking min size inverter")
tx = pinv.pinv(size=1, beta=tech.parameter["pinv_beta"])
self.local_check(tx)
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)
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)
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
globals.end_openram()