Move nmos gate to the top of the ptx.

This commit is contained in:
Matt Guthaus 2017-12-01 08:31:16 -08:00
parent 45ae8c7315
commit 8df46abb30
5 changed files with 89 additions and 70 deletions

View File

@ -22,7 +22,7 @@ class pinv(design.design):
unique_id = 1
def __init__(self, size=1, beta=parameter["pinv_beta"], height=bitcell.height, route_output=True):
def __init__(self, size=1, beta=parameter["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
@ -46,11 +46,11 @@ class pinv(design.design):
#self.DRC_LVS()
def add_pins(self):
"""Adds pins for spice netlist"""
""" 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"""
""" 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"))
@ -61,15 +61,7 @@ class pinv(design.design):
self.setup_layout_constants()
self.add_supply_rails()
self.add_ptx()
# These aren't for instantiating, but we use them to get the dimensions
# self.nwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"),
# dimensions=(1, self.pmos.num_contacts))
# self.pwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"),
# dimensions=(1, self.nmos.num_contacts))
self.extend_wells()
#self.extend_active()
self.add_well_contacts()
self.connect_rails()
self.route_input_gate()
@ -142,8 +134,6 @@ class pinv(design.design):
# This will help with the wells and the input/output placement
self.middle_position = vector(0,0.5*self.height)
# This will balance the PMOS and NMOS size, roughly.
#self.middle_position = vector(0,1.0/(self.beta+1)*self.height)
def create_ptx(self):
@ -181,18 +171,18 @@ class pinv(design.design):
"""
# place PMOS so it is half a poly spacing down from the top
self.pmos_pos = self.pmos.active_offset.scale(1,0) + vector(0, self.height-self.pmos.active_height-self.top_bottom_cell_space)
self.pmos_pos = self.pmos.active_offset.scale(1,0) \
+ vector(0, self.height-self.pmos.active_height-self.top_bottom_cell_space)
self.pmos_inst=self.add_inst(name="pinv_pmos",
mod=self.pmos,
offset=self.pmos_pos)
self.connect_inst(["Z", "A", "vdd", "vdd"])
# place NMOS so that it is half a poly spacing up from the bottom
self.nmos_pos = self.nmos.active_offset.scale(1,0) + vector(0,self.nmos.active_height+self.top_bottom_cell_space)
self.nmos_pos = self.nmos.active_offset.scale(1,0) + vector(0,self.top_bottom_cell_space)
self.nmos_inst=self.add_inst(name="pinv_nmos",
mod=self.nmos,
offset=self.nmos_pos,
mirror="MX")
offset=self.nmos_pos)
self.connect_inst(["Z", "A", "gnd", "gnd"])
@ -220,50 +210,9 @@ class pinv(design.design):
width=self.well_width,
height=self.middle_position.y)
# def extend_active(self):
# """Extends the active area for n/p mos for the addition of the n/p well taps"""
# # calculates the new active width that includes the well_taps
# self.active_width = self.pmos.active_width \
# + drc["active_to_body_active"] \
# + self.pmos.active_contact.width
# # Calculates the coordinates of the bottom left corner of active area
# # of the pmos
# offset = self.pmos_position + self.pmos.active_position
# self.add_rect(layer="active",
# offset=offset,
# width=self.active_width,
# height=self.pmos.active_height)
# # Determines where the active of the well portion starts to add the
# # implant
# offset = offset + vector(self.pmos.active_width,0)
# implant_width = self.active_width - self.pmos.active_width
# self.add_rect(layer="nimplant",
# offset=offset,
# width=implant_width,
# height=self.pmos.active_height)
# # Calculates the coordinates of the bottom left corner of active area
# # of the nmos
# offset = self.nmos_position + self.nmos.active_position
# self.add_rect(layer="active",
# offset=offset,
# width=self.active_width,
# height=self.nmos.active_height)
# # Determines where the active of the well portion starts to add the
# # implant
# offset = offset + vector(self.pmos.active_width,0)
# implant_width = self.active_width - self.nmos.active_width
# self.add_rect(layer="pimplant",
# offset=offset,
# width=implant_width,
# height=self.nmos.active_height)
def route_input_gate(self):
"""Routes 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 """
nmos_gate_pin = self.nmos_inst.get_pin("G")
pmos_gate_pin = self.pmos_inst.get_pin("G")
@ -322,13 +271,13 @@ class pinv(design.design):
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies"""
""" Add n/p well taps to the layout and connect to supplies """
layer_stack = ("active", "contact", "metal1")
# To the right a spacing away from the nmos right active edge
nwell_contact_xoffset = self.nmos_pos.x + self.nmos.active_width + drc["active_to_body_active"]
nwell_contact_yoffset = self.nmos_pos.y - self.nmos.active_height
nwell_contact_yoffset = self.nmos_pos.y
nwell_offset = vector(nwell_contact_xoffset, nwell_contact_yoffset)
# Offset by half a contact in x and y
nwell_offset += vector(0.5*self.nmos.active_contact.first_layer_width,
@ -337,7 +286,7 @@ class pinv(design.design):
offset=nwell_offset)
self.add_path("metal1",[nwell_offset,nwell_offset.scale(1,0)])
# Now add the full active and implant for the PMOS
nwell_offset = self.nmos_pos + vector(self.nmos.active_width,-self.nmos.active_height)
nwell_offset = self.nmos_pos + vector(self.nmos.active_width,0)
nwell_contact_width = drc["active_to_body_active"] + self.nmos.active_contact.width
self.add_rect(layer="active",
offset=nwell_offset,
@ -400,7 +349,6 @@ class pinv(design.design):
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
def analytical_delay(self, slew, load=0.0):
from tech import spice
r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"])
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
return self.cal_delay_with_rc(r = r, c = c_para+load, slew = slew)

View File

@ -164,10 +164,14 @@ class ptx(design.design):
# The width of the poly is from the left-most to right-most poly gate
poly_width = poly_positions[-1].x - poly_positions[0].x + self.poly_width
# This can be limited by poly to active spacing or the poly extension
distance_below_active = self.poly_width + max(self.poly_to_active,0.5*self.poly_height)
poly_offset = poly_positions[0] - vector(0.5*self.poly_width, distance_below_active)
if self.tx_type == "pmos":
# This can be limited by poly to active spacing or the poly extension
distance_below_active = self.poly_width + max(self.poly_to_active,0.5*self.poly_height)
poly_offset = poly_positions[0] - vector(0.5*self.poly_width, distance_below_active)
else:
# This can be limited by poly to active spacing or the poly extension
distance_above_active = max(self.poly_to_active,0.5*self.poly_height)
poly_offset = poly_positions[0] + vector(-0.5*self.poly_width, distance_above_active)
# Remove the old pin and add the new one
self.remove_layout_pin("G") # only keep the main pin
self.add_layout_pin(text="G",
@ -189,6 +193,15 @@ class ptx(design.design):
# This is the width of a contact to extend the ends of the pin
end_offset = vector(self.active_contact.second_layer_width/2,0)
# drains always go to the MIDDLE of the cell, so top of NMOS, bottom of PMOS
# so reverse the directions for NMOS
if self.tx_type == "pmos":
drain_dir = 1
source_dir = -1
else:
drain_dir = -1
source_dir = 1
if len(source_positions)>1:
self.remove_layout_pin("S") # remove the individual connections
# Add each vertical segment
@ -282,7 +295,6 @@ class ptx(design.design):
# The first one will always be a source
source_positions = [self.contact_offset]
drain_positions = []
for i in range(self.mults):
if i%2:
# It's a source... so offset from previous drain.

View File

@ -0,0 +1,59 @@
#!/usr/bin/env python2.7
"""
Run regression tests on a parameterized nand 2. This module doesn't
generate multi_finger 2_input nand gate. It generate only the minimum
size 2_input nand gate that is nmos_width=2*tech.drc[minwidth_tx].
"""
import unittest
from testutils import header
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
import verify
import sys
OPTS = globals.OPTS
#@unittest.skip("SKIPPING 04_nand_2_test")
class pnand2_test(unittest.TestCase):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
# we will manually run lvs/drc
OPTS.check_lvsdrc = False
import pnand2
import tech
debug.info(2, "Checking 2-input nand gate")
tx = pnand2.pnand2(size=1)
self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, tx):
tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds"
tx.sp_write(tempspice)
tx.gds_write(tempgds)
self.assertFalse(verify.run_drc(tx.name, tempgds))
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
os.remove(tempspice)
os.remove(tempgds)
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -67,7 +67,7 @@ layer["boundary"]= 239
#technology parameter
parameter={}
parameter["min_tx_size"] = 0.09
parameter["pinv_beta"] = 3
parameter["beta"] = 3
drclvs_home=os.environ.get("DRCLVS_HOME")
drc={}

View File

@ -54,7 +54,7 @@ layer["boundary"] = 83
#technology parameter
parameter={}
parameter["min_tx_size"] = 1.2
parameter["pinv_beta"] = 2 #for use in pinv
parameter["beta"] = 2
drclvs_home=os.environ.get("DRCLVS_HOME")