mirror of https://github.com/VLSIDA/OpenRAM.git
Prune ptx code. Change sizes to be relative to min size.
This commit is contained in:
parent
d4f8d63442
commit
1bcef7e3ee
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue