Merge branch 'dev' into discrete_models

This commit is contained in:
jcirimel
2020-06-05 16:47:01 -07:00
121 changed files with 4635 additions and 2093 deletions
+71 -35
View File
@@ -13,16 +13,16 @@ from sram_factory import factory
class pand2(pgate.pgate):
"""
This is a simple buffer used for driving loads.
This is an AND (or NAND) with configurable drive strength.
"""
def __init__(self, name, size=1, height=None):
debug.info(1, "Creating pnand2 {}".format(name))
def __init__(self, name, size=1, height=None, vertical=False, add_wells=True):
debug.info(1, "Creating pand2 {}".format(name))
self.add_comment("size: {}".format(size))
self.vertical = vertical
self.size = size
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
pgate.pgate.__init__(self, name, height, add_wells)
def create_netlist(self):
self.add_pins()
@@ -30,20 +30,29 @@ class pand2(pgate.pgate):
self.create_insts()
def create_modules(self):
self.nand = factory.create(module_type="pnand2", height=self.height)
self.add_mod(self.nand)
self.nand = factory.create(module_type="pnand2",
height=self.height,
add_wells=self.vertical)
self.inv = factory.create(module_type="pdriver",
neg_polarity=True,
fanout=self.size,
height=self.height)
size_list=[self.size],
height=self.height,
add_wells=self.add_wells)
self.add_mod(self.nand)
self.add_mod(self.inv)
def create_layout(self):
self.width = self.nand.width + self.inv.width
if self.vertical:
self.height = 2 * self.nand.height
self.width = max(self.nand.width, self.inv.width)
else:
self.width = self.nand.width + self.inv.width
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
self.DRC_LVS()
@@ -67,35 +76,62 @@ class pand2(pgate.pgate):
# Add NAND to the right
self.nand_inst.place(offset=vector(0, 0))
# Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
if self.vertical:
# Add INV above
self.inv_inst.place(offset=vector(self.inv.width,
2 * self.nand.height),
mirror="XY")
else:
# Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
def route_supply_rails(self):
""" Add vdd/gnd rails to the top, (middle), and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer=self.route_layer,
offset=vector(0.5 * self.width, 0),
width=self.width)
# Second gnd of the inverter gate
if self.vertical:
self.add_layout_pin_rect_center(text="gnd",
layer=self.route_layer,
offset=vector(0.5 * self.width, self.height),
width=self.width)
if self.vertical:
# Shared between two gates
y_offset = 0.5 * self.height
else:
y_offset = self.height
self.add_layout_pin_rect_center(text="vdd",
layer=self.route_layer,
offset=vector(0.5 * self.width, y_offset),
width=self.width)
def add_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_pin.cy())
self.add_path("m1",
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
if self.vertical:
route_layer = "m2"
self.add_via_stack_center(offset=z1_pin.center(),
from_layer=z1_pin.layer,
to_layer=route_layer)
self.add_zjog(route_layer,
z1_pin.uc(),
a2_pin.bc(),
"V")
self.add_via_stack_center(offset=a2_pin.center(),
from_layer=a2_pin.layer,
to_layer=route_layer)
else:
route_layer = self.route_layer
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path(route_layer,
[z1_pin.center(), mid1_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin = self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
pin = self.inv_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=pin.layer,
+72 -32
View File
@@ -15,14 +15,15 @@ class pand3(pgate.pgate):
"""
This is a simple buffer used for driving loads.
"""
def __init__(self, name, size=1, height=None):
def __init__(self, name, size=1, height=None, vertical=False, add_wells=True):
debug.info(1, "Creating pand3 {}".format(name))
self.add_comment("size: {}".format(size))
self.vertical = vertical
self.size = size
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
pgate.pgate.__init__(self, name, height, add_wells)
def create_netlist(self):
self.add_pins()
@@ -31,19 +32,31 @@ class pand3(pgate.pgate):
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
self.nand = factory.create(module_type="pnand3", height=self.height)
self.add_mod(self.nand)
self.nand = factory.create(module_type="pnand3",
height=self.height,
add_wells=self.vertical)
self.inv = factory.create(module_type="pinv",
size=self.size,
height=self.height)
# Add the well tap to the inverter because when stacked
# vertically it is sometimes narrower
self.inv = factory.create(module_type="pdriver",
size_list=[self.size],
height=self.height,
add_wells=self.add_wells)
self.add_mod(self.nand)
self.add_mod(self.inv)
def create_layout(self):
self.width = self.nand.width + self.inv.width
if self.vertical:
self.height = 2 * self.nand.height
self.width = max(self.nand.width, self.inv.width)
else:
self.width = self.nand.width + self.inv.width
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
self.DRC_LVS()
@@ -68,35 +81,62 @@ class pand3(pgate.pgate):
# Add NAND to the right
self.nand_inst.place(offset=vector(0, 0))
# Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
if self.vertical:
# Add INV above
self.inv_inst.place(offset=vector(self.inv.width,
2 * self.nand.height),
mirror="XY")
else:
# Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
def route_supply_rails(self):
""" Add vdd/gnd rails to the top, (middle), and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer=self.route_layer,
offset=vector(0.5 * self.width, 0),
width=self.width)
# Second gnd of the inverter gate
if self.vertical:
self.add_layout_pin_rect_center(text="gnd",
layer=self.route_layer,
offset=vector(0.5 * self.width, self.height),
width=self.width)
if self.vertical:
# Shared between two gates
y_offset = 0.5 * self.height
else:
y_offset = self.height
self.add_layout_pin_rect_center(text="vdd",
layer=self.route_layer,
offset=vector(0.5 * self.width, y_offset),
width=self.width)
def add_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_pin.cy())
self.add_path("m1",
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
if self.vertical:
route_layer = "m2"
self.add_via_stack_center(offset=z1_pin.center(),
from_layer=z1_pin.layer,
to_layer=route_layer)
self.add_zjog(route_layer,
z1_pin.uc(),
a2_pin.bc(),
"V")
self.add_via_stack_center(offset=a2_pin.center(),
from_layer=a2_pin.layer,
to_layer=route_layer)
else:
route_layer = self.route_layer
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path(route_layer,
[z1_pin.center(), mid1_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin = self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
pin = self.inv_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=pin.layer,
+4 -19
View File
@@ -37,6 +37,7 @@ class pbuf(pgate.pgate):
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
def add_pins(self):
@@ -55,7 +56,8 @@ class pbuf(pgate.pgate):
self.inv2 = factory.create(module_type="pinv",
size=self.size,
height=self.height)
height=self.height,
add_wells=False)
self.add_mod(self.inv2)
def create_insts(self):
@@ -78,26 +80,9 @@ class pbuf(pgate.pgate):
# inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("m1", [z1_pin.center(), mid_point, a2_pin.center()])
self.add_zjog(self.route_layer, z1_pin.center(), a2_pin.center())
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv1_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin = self.inv1_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
z_pin = self.inv2_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=z_pin.layer,
+14 -25
View File
@@ -17,13 +17,13 @@ class pdriver(pgate.pgate):
sized for driving a load.
"""
def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None):
def __init__(self, name, inverting=False, fanout=0, size_list=None, height=None, add_wells=True):
debug.info(1, "creating pdriver {}".format(name))
self.stage_effort = 3
self.height = height
self.neg_polarity = neg_polarity
self.inverting = inverting
self.size_list = size_list
self.fanout = fanout
@@ -31,11 +31,11 @@ class pdriver(pgate.pgate):
debug.error("Either fanout or size list must be specified.", -1)
if self.size_list and self.fanout != 0:
debug.error("Cannot specify both size_list and fanout.", -1)
if self.size_list and self.neg_polarity:
debug.error("Cannot specify both size_list and neg_polarity.", -1)
if self.size_list and self.inverting:
debug.error("Cannot specify both size_list and inverting.", -1)
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
pgate.pgate.__init__(self, name, height, add_wells)
def compute_sizes(self):
# size_list specified
@@ -47,9 +47,9 @@ class pdriver(pgate.pgate):
int(round(self.fanout ** (1 / self.stage_effort))))
# Increase the number of stages if we need to fix polarity
if self.neg_polarity and (self.num_stages % 2 == 0):
if self.inverting and (self.num_stages % 2 == 0):
self.num_stages += 1
elif not self.neg_polarity and (self.num_stages % 2):
elif not self.inverting and (self.num_stages % 2):
self.num_stages += 1
self.size_list = []
@@ -73,9 +73,10 @@ class pdriver(pgate.pgate):
self.place_modules()
self.route_wires()
self.add_layout_pins()
self.width = self.inv_inst_list[-1].rx()
self.height = self.inv_inst_list[0].height
self.extend_wells()
self.route_supply_rails()
self.add_boundary()
def add_pins(self):
@@ -86,10 +87,13 @@ class pdriver(pgate.pgate):
def add_modules(self):
self.inv_list = []
add_well = self.add_wells
for size in self.size_list:
temp_inv = factory.create(module_type="pinv",
size=size,
height=self.height)
height=self.height,
add_wells=add_well)
add_well=False
self.inv_list.append(temp_inv)
self.add_mod(temp_inv)
@@ -141,26 +145,11 @@ class pdriver(pgate.pgate):
z_inst_list.append(self.inv_inst_list[x].get_pin("Z"))
a_inst_list.append(self.inv_inst_list[x + 1].get_pin("A"))
mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy())
self.add_path("m1",
self.add_path(self.route_layer,
[z_inst_list[x].center(), mid_point,
a_inst_list[x].center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv_inst_list[0].get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin = self.inv_inst_list[0].get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
z_pin = self.inv_inst_list[len(self.inv_inst_list) - 1].get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
+22 -44
View File
@@ -22,17 +22,17 @@ from errors import drc_error
if(OPTS.tech_name == "s8"):
from tech import nmos_bins, pmos_bins, accuracy_requirement
class pinv(pgate.pgate):
"""
Pinv generates gds of a parametrically sized inverter. The
size is specified as the drive size (relative to minimum NMOS) and
a beta value for choosing the pmos size. The inverter's cell
height is usually the same as the 6t library cell and is measured
from center of rail to rail.. The route_output will route the
output to the right side of the cell for easier access.
from center of rail to rail.
"""
def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True):
def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True):
debug.info(2,
"creating pinv structure {0} with size of {1}".format(name,
@@ -40,12 +40,12 @@ class pinv(pgate.pgate):
self.add_comment("size: {}".format(size))
self.size = size
debug.check(self.size >= 1, "Must have a size greater than or equal to 1.")
self.nmos_size = size
self.pmos_size = beta * size
self.beta = beta
self.route_output = False
pgate.pgate.__init__(self, name, height)
pgate.pgate.__init__(self, name, height, add_wells)
def create_netlist(self):
""" Calls all functions related to the generation of the netlist """
@@ -57,17 +57,18 @@ class pinv(pgate.pgate):
def create_layout(self):
""" Calls all functions related to the generation of the layout """
self.place_ptx()
self.add_well_contacts()
if self.add_wells:
self.add_well_contacts()
self.determine_width()
self.extend_wells()
self.route_supply_rails()
self.connect_rails()
self.route_input_gate(self.pmos_inst,
self.nmos_inst,
self.output_pos.y,
"A",
position="farleft")
self.route_outputs()
self.route_supply_rails()
self.connect_rails()
self.add_boundary()
def add_pins(self):
@@ -108,13 +109,6 @@ class pinv(pgate.pgate):
min_channel = max(contact.poly_contact.width + self.m1_space,
contact.poly_contact.width + 2 * self.poly_to_active)
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space)
total_height = tx_height + min_channel + 2 * self.top_bottom_space
# debug.check(self.height > total_height,
# "Cell height {0} too small for simple min height {1}.".format(self.height,
@@ -202,30 +196,22 @@ class pinv(pgate.pgate):
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer,
connect_poly=True,
connect_active=True)
connect_drain_active=True)
self.add_mod(self.nmos)
self.pmos = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer,
connect_poly=True,
connect_active=True)
connect_drain_active=True)
self.add_mod(self.pmos)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="m1",
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
Create the PMOS and NMOS netlist.
@@ -266,7 +252,7 @@ class pinv(pgate.pgate):
Route the output (drains) together.
Optionally, routes output to edge.
"""
# Get the drain pins
nmos_drain_pin = self.nmos_inst.get_pin("D")
pmos_drain_pin = self.pmos_inst.get_pin("D")
@@ -274,24 +260,16 @@ class pinv(pgate.pgate):
# Pick point at right most of NMOS and connect down to PMOS
nmos_drain_pos = nmos_drain_pin.bc()
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
self.add_path("m1", [nmos_drain_pos, pmos_drain_pos])
self.add_path(self.route_layer, [nmos_drain_pos, pmos_drain_pos])
# Remember the mid for the output
mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y)
if self.route_output:
# This extends the output to the edge of the cell
output_offset = mid_drain_offset.scale(0, 1) + vector(self.width, 0)
self.add_layout_pin_segment_center(text="Z",
layer="m1",
start=mid_drain_offset,
end=output_offset)
else:
# This leaves the output as an internal pin (min sized)
self.add_layout_pin_rect_center(text="Z",
layer="m1",
offset=mid_drain_offset \
+ vector(0.5 * self.m1_width, 0))
# This leaves the output as an internal pin (min sized)
output_offset = mid_drain_offset + vector(0.5 * self.route_layer_width, 0)
self.add_layout_pin_rect_center(text="Z",
layer=self.route_layer,
offset=output_offset)
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
+217
View File
@@ -0,0 +1,217 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import contact
import pinv
import debug
from tech import drc, parameter
from vector import vector
from globals import OPTS
from sram_factory import factory
if(OPTS.tech_name == "s8"):
from tech import nmos_bins, pmos_bins, accuracy_requirement
class pinv_dec(pinv.pinv):
"""
This is another version of pinv but with layout for the decoder.
Other stuff is the same (netlist, sizes, etc.)
"""
def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True):
debug.info(2,
"creating pinv_dec structure {0} with size of {1}".format(name,
size))
if not height:
b = factory.create(module_type="bitcell")
self.cell_height = b.height
else:
self.cell_height = height
# Inputs to cells are on input layer
# Outputs from cells are on output layer
if OPTS.tech_name == "s8":
self.supply_layer = "m1"
else:
self.supply_layer = "m2"
pinv.pinv.__init__(self, name, size, beta, self.cell_height, add_wells)
def determine_tx_mults(self):
"""
Determines the number of fingers needed to achieve the size within
the height constraint. This may fail if the user has a tight height.
"""
# This is always 1 tx, because we have horizontal transistors.
self.tx_mults = 1
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
if OPTS.tech_name == "s8":
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width)
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
return
# Over-ride the route input gate to call the horizontal version.
# Other top-level netlist and layout functions are not changed.
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.
Position is actually ignored and is left to be compatible with the pinv.
"""
nmos_gate_pin = nmos_inst.get_pin("G")
pmos_gate_pin = pmos_inst.get_pin("G")
# Check if the gates are aligned and give an error if they aren't!
if nmos_gate_pin.ll().y != pmos_gate_pin.ll().y:
self.gds_write("unaliged_gates.gds")
debug.check(nmos_gate_pin.ll().y == pmos_gate_pin.ll().y,
"Connecting unaligned gates not supported. See unaligned_gates.gds.")
# Pick point on the left of NMOS and up to PMOS
nmos_gate_pos = nmos_gate_pin.rc()
pmos_gate_pos = pmos_gate_pin.lc()
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
# Center is completely symmetric.
contact_width = contact.poly_contact.width
contact_offset = nmos_gate_pin.lc() \
- vector(self.poly_extend_active + 0.5 * contact_width, 0)
via = self.add_via_stack_center(from_layer="poly",
to_layer=self.route_layer,
offset=contact_offset,
directions=directions)
self.add_path("poly", [contact_offset, nmos_gate_pin.lc()])
self.add_layout_pin_rect_center(text=name,
layer=self.route_layer,
offset=contact_offset,
width=via.mod.second_layer_width,
height=via.mod.second_layer_height)
def determine_width(self):
self.width = self.pmos_inst.rx() + self.well_extend_active
def extend_wells(self):
""" Extend bottom to top for each well. """
from tech import layer
if "pwell" in layer:
ll = self.nmos_inst.ll() - self.nmos_inst.mod.active_offset
ur = self.nmos_inst.ur() + self.nmos_inst.mod.active_offset
self.add_rect(layer="pwell",
offset=ll,
width=ur.x - ll.x,
height=self.height - ll.y)
if "nwell" in layer:
ll = self.pmos_inst.ll() - self.pmos_inst.mod.active_offset
ur = self.pmos_inst.ur() + self.pmos_inst.mod.active_offset
self.add_rect(layer="nwell",
offset=ll - vector(self.nwell_enclose_active, 0),
width=ur.x - ll.x + self.nwell_enclose_active,
height=self.height - ll.y + 2 * self.nwell_enclose_active)
def place_ptx(self):
"""
"""
# offset so that the input contact is over from the left edge by poly spacing
x_offset = self.nmos.active_offset.y + contact.poly_contact.width + self.poly_space
# center the transistor in the y-dimension
y_offset = self.nmos.width + self.active_space
self.nmos_pos = vector(x_offset, y_offset)
self.nmos_inst.place(self.nmos_pos)
self.nmos_inst.place(self.nmos_pos,
rotate=270)
# place PMOS so it is half a poly spacing down from the top
xoffset = self.nmos_inst.height + 2 * self.poly_extend_active + 2 * self.well_extend_active + drc("pwell_to_nwell")
self.pmos_pos = self.nmos_pos + vector(xoffset, 0)
self.pmos_inst.place(self.pmos_pos,
rotate=270)
# Output position will be in between the PMOS and NMOS drains
pmos_drain_pos = self.pmos_inst.get_pin("D").center()
nmos_drain_pos = self.nmos_inst.get_pin("D").center()
self.output_pos = vector(0.5 * (pmos_drain_pos.x + nmos_drain_pos.x), nmos_drain_pos.y)
def route_outputs(self):
"""
Route the output (drains) together.
Optionally, routes output to edge.
"""
# Get the drain pin
nmos_drain_pin = self.nmos_inst.get_pin("D")
# Pick point at right most of NMOS and connect over to PMOS
nmos_drain_pos = nmos_drain_pin.lc()
right_side = vector(self.width, nmos_drain_pos.y)
self.add_layout_pin_segment_center("Z",
self.route_layer,
nmos_drain_pos,
right_side)
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
source_pos = self.pmos_inst.get_pin("S").center()
contact_pos = vector(source_pos.x, self.height)
self.nwell_contact = self.add_via_center(layers=self.active_stack,
offset=contact_pos,
implant_type="n",
well_type="n")
self.add_via_stack_center(offset=contact_pos,
from_layer=self.active_stack[2],
to_layer=self.supply_layer)
source_pos = self.nmos_inst.get_pin("S").center()
contact_pos = vector(source_pos.x, self.height)
self.pwell_contact= self.add_via_center(layers=self.active_stack,
offset=contact_pos,
implant_type="p",
well_type="p")
self.add_via_stack_center(offset=contact_pos,
from_layer=self.active_stack[2],
to_layer=self.supply_layer)
def route_supply_rails(self):
pin = self.nmos_inst.get_pin("S")
source_pos = pin.center()
bottom_pos = source_pos.scale(1, 0)
top_pos = bottom_pos + vector(0, self.height)
self.add_layout_pin_segment_center("gnd",
self.supply_layer,
start=bottom_pos,
end=top_pos)
pin = self.pmos_inst.get_pin("S")
source_pos = pin.center()
bottom_pos = source_pos.scale(1, 0)
top_pos = bottom_pos + vector(0, self.height)
self.add_layout_pin_segment_center("vdd",
self.supply_layer,
start=bottom_pos,
end=top_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
source_pos = self.nmos_inst.get_pin("S").center()
self.add_via_stack_center(offset=source_pos,
from_layer=self.route_layer,
to_layer=self.supply_layer)
source_pos = self.pmos_inst.get_pin("S").center()
self.add_via_stack_center(offset=source_pos,
from_layer=self.route_layer,
to_layer=self.supply_layer)
+77 -97
View File
@@ -5,7 +5,6 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import contact
import pgate
import debug
from tech import drc, parameter, spice
@@ -20,7 +19,7 @@ class pnand2(pgate.pgate):
This module generates gds of a parametrically sized 2-input nand.
This model use ptx to generate a 2-input nand within a cetrain height.
"""
def __init__(self, name, size=1, height=None):
def __init__(self, name, size=1, height=None, add_wells=True):
""" Creates a cell for a simple 2 input nand """
debug.info(2,
@@ -43,7 +42,7 @@ class pnand2(pgate.pgate):
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
pgate.pgate.__init__(self, name, height, add_wells)
def create_netlist(self):
self.add_pins()
@@ -55,13 +54,14 @@ class pnand2(pgate.pgate):
self.setup_layout_constants()
self.place_ptx()
self.add_well_contacts()
if self.add_wells:
self.add_well_contacts()
self.route_output()
self.determine_width()
self.route_supply_rails()
self.connect_rails()
self.extend_wells()
self.route_inputs()
self.route_output()
self.add_boundary()
def add_pins(self):
@@ -72,65 +72,45 @@ class pnand2(pgate.pgate):
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos_nd = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_drain_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos_nd)
self.nmos_left = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=self.route_layer,
add_drain_contact="active")
self.add_mod(self.nmos_left)
self.nmos_ns = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos_ns)
self.nmos_right = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="active",
add_drain_contact=self.route_layer)
self.add_mod(self.nmos_right)
self.pmos = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.pmos)
self.pmos_left = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_left)
self.pmos_right = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_right)
def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """
# metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
self.m1_space + contact.m1_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width,
self.m3_space + contact.m2_via.second_layer_width)
# Compute the other pmos2 location,
# but determining offset to overlap the
# source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-self.nmos_nd.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="m1",
offset=vector(0.5*self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
self.overlap_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center()
def create_ptx(self):
"""
@@ -138,19 +118,19 @@ class pnand2(pgate.pgate):
"""
self.pmos1_inst = self.add_inst(name="pnand2_pmos1",
mod=self.pmos)
mod=self.pmos_left)
self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
mod=self.pmos)
mod=self.pmos_right)
self.connect_inst(["Z", "B", "vdd", "vdd"])
self.nmos1_inst = self.add_inst(name="pnand2_nmos1",
mod=self.nmos_nd)
mod=self.nmos_left)
self.connect_inst(["Z", "B", "net1", "gnd"])
self.nmos2_inst = self.add_inst(name="pnand2_nmos2",
mod=self.nmos_ns)
mod=self.nmos_right)
self.connect_inst(["net1", "A", "gnd", "gnd"])
def place_ptx(self):
@@ -159,35 +139,29 @@ class pnand2(pgate.pgate):
to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height \
pmos1_pos = vector(self.pmos_left.active_offset.x,
self.height - self.pmos_left.active_height \
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst.place(self.pmos2_pos)
nmos1_pos = vector(self.pmos.active_offset.x,
nmos1_pos = vector(self.pmos_left.active_offset.x,
self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos)
self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos_nd.active_height))
def add_well_contacts(self):
"""
Add n/p well taps to the layout and connect to supplies
AFTER the wells are created
"""
self.add_nwell_contact(self.pmos,
self.pmos2_pos + vector(self.m1_pitch, 0))
self.add_pwell_contact(self.nmos_nd,
self.nmos2_pos + vector(self.m1_pitch, 0))
self.add_nwell_contact(self.pmos_right, self.pmos2_pos)
self.add_pwell_contact(self.nmos_left, self.nmos2_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
@@ -200,35 +174,46 @@ class pnand2(pgate.pgate):
def route_inputs(self):
""" Route the A and B inputs """
inputB_yoffset = self.nmos2_inst.uy() + 0.5 * contact.poly_contact.height
# Top of NMOS drain
nmos_pin = self.nmos2_inst.get_pin("D")
bottom_pin_offset = nmos_pin.uy()
self.inputA_yoffset = bottom_pin_offset + self.m1_pitch
self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch
# This will help with the wells and the input/output placement
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
self.inputB_yoffset,
"B",
position="center")
# This will help with the wells and the input/output placement
self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \
- contact.poly_contact.height
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A")
"A",
position="center")
def route_output(self):
""" Route the Z output """
# One routing track layer below the PMOS contacts
route_layer_offset = 0.5 * self.route_layer_width + self.route_layer_space
output_yoffset = self.pmos1_inst.get_pin("D").by() - route_layer_offset
# PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center()
top_pin_offset = pmos_pin.bc()
# NMOS2 drain
nmos_pin = self.nmos2_inst.get_pin("D")
bottom_pin_offset = nmos_pin.center()
bottom_pin_offset = nmos_pin.uc()
# Output pin
c_pin = self.get_pin("B")
out_offset = vector(c_pin.cx() + self.m1_pitch,
self.inputA_yoffset)
out_offset = vector(nmos_pin.cx() + self.route_layer_pitch,
output_yoffset)
# This routes on M2
# # Midpoints of the L routes go horizontal first then vertical
@@ -251,27 +236,22 @@ class pnand2(pgate.pgate):
# [top_pin_offset, mid1_offset, out_offset,
# mid2_offset, bottom_pin_offset])
# This routes on M1
# This routes on route_layer
# Midpoints of the L routes goes vertical first then horizontal
mid1_offset = vector(top_pin_offset.x, out_offset.y)
# Midpoints of the L routes goes horizontal first then vertical
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
top_mid_offset = vector(top_pin_offset.x, out_offset.y)
# Top transistors
self.add_path(self.route_layer,
[top_pin_offset, top_mid_offset, out_offset])
self.add_path("m1",
[top_pin_offset, mid1_offset, out_offset])
# Route in two segments to have the width rule
self.add_path("m1",
[bottom_pin_offset, mid2_offset + vector(0.5 * self.m1_width, 0)],
width=nmos_pin.height())
self.add_path("m1",
[mid2_offset, out_offset])
bottom_mid_offset = bottom_pin_offset + vector(0, self.route_layer_pitch)
# Bottom transistors
self.add_path(self.route_layer,
[out_offset, bottom_mid_offset, bottom_pin_offset])
# This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z",
layer="m1",
offset=out_offset,
width=contact.m1_via.first_layer_width,
height=contact.m1_via.first_layer_height)
layer=self.route_layer,
offset=out_offset)
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
+98 -108
View File
@@ -5,7 +5,6 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import contact
import pgate
import debug
from tech import drc, parameter, spice
@@ -14,12 +13,13 @@ import logical_effort
from sram_factory import factory
from globals import OPTS
class pnand3(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nand.
This model use ptx to generate a 2-input nand within a cetrain height.
"""
def __init__(self, name, size=1, height=None):
def __init__(self, name, size=1, height=None, add_wells=True):
""" Creates a cell for a simple 3 input nand """
debug.info(2,
@@ -45,7 +45,7 @@ class pnand3(pgate.pgate):
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
pgate.pgate.__init__(self, name, height, add_wells)
def add_pins(self):
""" Adds pins for spice netlist """
@@ -63,79 +63,76 @@ class pnand3(pgate.pgate):
self.setup_layout_constants()
self.place_ptx()
self.add_well_contacts()
if self.add_wells:
self.add_well_contacts()
self.route_inputs()
self.route_output()
self.determine_width()
self.route_supply_rails()
self.connect_rails()
self.extend_wells()
self.route_inputs()
self.route_output()
self.add_boundary()
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos_nsnd = factory.create(module_type="ptx",
self.nmos_center = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="active",
add_drain_contact="active")
self.add_mod(self.nmos_center)
self.nmos_right = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="active",
add_drain_contact=self.route_layer)
self.add_mod(self.nmos_right)
self.nmos_left = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=False,
add_drain_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos_nsnd)
self.nmos_ns = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos_ns)
self.nmos_nd = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_drain_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos_nd)
add_source_contact=self.route_layer,
add_drain_contact="active")
self.add_mod(self.nmos_left)
self.pmos = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.pmos)
self.pmos_left = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_left)
self.pmos_center = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_center)
self.pmos_right = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_right)
def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """
# Compute the overlap of the source and drain pins
overlap_xoffset = self.pmos.get_pin("D").ll().x - self.pmos.get_pin("S").ll().x
self.ptx_offset = vector(overlap_xoffset, 0)
self.ptx_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center()
# This is the extra space needed to ensure DRC rules
# to the active contacts
nmos = factory.create(module_type="ptx", tx_type="nmos")
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="m1",
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
@@ -143,27 +140,27 @@ class pnand3(pgate.pgate):
"""
self.pmos1_inst = self.add_inst(name="pnand3_pmos1",
mod=self.pmos)
mod=self.pmos_left)
self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
mod=self.pmos)
mod=self.pmos_center)
self.connect_inst(["Z", "B", "vdd", "vdd"])
self.pmos3_inst = self.add_inst(name="pnand3_pmos3",
mod=self.pmos)
mod=self.pmos_right)
self.connect_inst(["Z", "C", "vdd", "vdd"])
self.nmos1_inst = self.add_inst(name="pnand3_nmos1",
mod=self.nmos_nd)
mod=self.nmos_left)
self.connect_inst(["Z", "C", "net1", "gnd"])
self.nmos2_inst = self.add_inst(name="pnand3_nmos2",
mod=self.nmos_nsnd)
mod=self.nmos_center)
self.connect_inst(["net1", "B", "net2", "gnd"])
self.nmos3_inst = self.add_inst(name="pnand3_nmos3",
mod=self.nmos_ns)
mod=self.nmos_right)
self.connect_inst(["net2", "A", "gnd", "gnd"])
def place_ptx(self):
@@ -172,8 +169,8 @@ class pnand3(pgate.pgate):
and lowest position to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
pmos1_pos = vector(self.pmos_left.active_offset.x,
self.height - self.pmos_left.active_height - self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
pmos2_pos = pmos1_pos + self.ptx_offset
@@ -182,7 +179,7 @@ class pnand3(pgate.pgate):
self.pmos3_pos = pmos2_pos + self.ptx_offset
self.pmos3_inst.place(self.pmos3_pos)
nmos1_pos = vector(self.pmos.active_offset.x,
nmos1_pos = vector(self.pmos_left.active_offset.x,
self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos)
@@ -195,9 +192,9 @@ class pnand3(pgate.pgate):
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.pmos,
self.add_nwell_contact(self.pmos_right,
self.pmos3_pos + vector(self.m1_pitch, 0))
self.add_pwell_contact(self.nmos_ns,
self.add_pwell_contact(self.nmos_right,
self.nmos3_pos + vector(self.m1_pitch, 0))
def connect_rails(self):
@@ -212,37 +209,37 @@ class pnand3(pgate.pgate):
def route_inputs(self):
""" Route the A and B and C inputs """
m1_pitch = self.m1_space + contact.m1_via.first_layer_height
# Put B right on the well line
self.inputB_yoffset = self.nwell_y_offset
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
self.inputB_yoffset,
"B",
position="center")
# FIXME: constant hack
self.inputC_yoffset = self.inputB_yoffset - 1.15 * m1_pitch
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
self.inputC_yoffset,
"C",
position="right")
pmos_drain_bottom = self.pmos1_inst.get_pin("D").by()
self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
# FIXME: constant hack
if OPTS.tech_name == "s8":
self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch
else:
self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch
# This is a more compact offset, but the bottom one works better in the decoders to "center" the pins
# in the height of the gates
self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space
# self.inputA_yoffset = self.output_yoffset - self.m1_pitch
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A",
position="left")
# Put B right on the well line
self.inputB_yoffset = self.inputA_yoffset - self.m1_pitch
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
self.inputB_yoffset,
"B",
position="center")
self.inputC_yoffset = self.inputB_yoffset - self.m1_pitch
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
self.inputC_yoffset,
"C",
position="right")
def route_output(self):
""" Route the Z output """
# PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain
@@ -250,14 +247,9 @@ class pnand3(pgate.pgate):
# NMOS3 drain
nmos3_pin = self.nmos3_inst.get_pin("D")
# midpoint for routing
mid_offset = vector(nmos3_pin.cx() + self.m1_pitch,
self.inputA_yoffset)
out_offset = vector(nmos3_pin.cx() + self.route_layer_pitch,
self.output_yoffset)
# Aligned with the well taps
out_offset = vector(self.nwell_contact.cx(),
self.inputA_yoffset)
# Go up to metal2 for ease on all output pins
# self.add_via_center(layers=self.m1_stack,
# offset=pmos1_pin.center(),
@@ -282,26 +274,24 @@ class pnand3(pgate.pgate):
bottom_pin_offset = nmos3_pin.center()
# PMOS1 to output
self.add_path("m1", [top_left_pin_offset,
vector(top_left_pin_offset.x, out_offset.y),
out_offset])
self.add_path(self.route_layer, [top_left_pin_offset,
vector(top_left_pin_offset.x, out_offset.y),
out_offset])
# PMOS3 to output
self.add_path("m1", [top_right_pin_offset,
vector(top_right_pin_offset.x, mid_offset.y),
mid_offset])
self.add_path(self.route_layer, [top_right_pin_offset,
vector(top_right_pin_offset.x, out_offset.y),
out_offset])
# NMOS3 to output
mid2_offset = vector(mid_offset.x, bottom_pin_offset.y)
self.add_path("m1",
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
self.add_path(self.route_layer,
[bottom_pin_offset, mid2_offset],
width=nmos3_pin.height())
mid3_offset = vector(mid_offset.x, nmos3_pin.by())
self.add_path("m1", [mid3_offset, mid_offset])
mid3_offset = vector(out_offset.x, nmos3_pin.by())
self.add_path(self.route_layer, [mid3_offset, out_offset])
self.add_layout_pin_rect_center(text="Z",
layer="m1",
offset=out_offset,
width=contact.m1_via.first_layer_width,
height=contact.m1_via.first_layer_height)
layer=self.route_layer,
offset=out_offset)
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
+85 -99
View File
@@ -19,7 +19,7 @@ class pnor2(pgate.pgate):
This module generates gds of a parametrically sized 2-input nor.
This model use ptx to generate a 2-input nor within a cetrain height.
"""
def __init__(self, name, size=1, height=None):
def __init__(self, name, size=1, height=None, add_wells=True):
""" Creates a cell for a simple 2 input nor """
debug.info(2,
@@ -42,7 +42,7 @@ class pnor2(pgate.pgate):
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
pgate.pgate.__init__(self, name, height, add_wells)
def create_netlist(self):
self.add_pins()
@@ -54,13 +54,14 @@ class pnor2(pgate.pgate):
self.setup_layout_constants()
self.place_ptx()
self.add_well_contacts()
if self.add_wells:
self.add_well_contacts()
self.route_inputs()
self.route_output()
self.determine_width()
self.route_supply_rails()
self.connect_rails()
self.extend_wells()
self.route_inputs()
self.route_output()
self.add_boundary()
def add_pins(self):
@@ -71,74 +72,54 @@ class pnor2(pgate.pgate):
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
connect_poly=True,
connect_active=True)
self.add_mod(self.nmos)
self.nmos_left = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.nmos_left)
self.pmos_nd = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_drain_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.pmos_nd)
self.nmos_right = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.nmos_right)
self.pmos_left = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact="active")
self.add_mod(self.pmos_left)
self.pmos_ns = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=False,
connect_poly=True,
connect_active=True)
self.add_mod(self.pmos_ns)
self.pmos_right = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact="active",
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_right)
def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """
# metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
self.m1_space + contact.m1_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width,
self.m3_space + contact.m2_via.second_layer_width)
# Compute the other pmos2 location, but determining
# offset to overlap the source and drain pins
self.overlap_offset = self.pmos_ns.get_pin("D").ll() - self.pmos_nd.get_pin("S").ll()
self.overlap_offset = self.pmos_right.get_pin("D").center() - self.pmos_left.get_pin("S").center()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.width = 2 * self.pmos_ns.active_width \
+ self.pmos_ns.active_contact.width \
self.width = 2 * self.pmos_right.active_width \
+ self.pmos_right.active_contact.width \
+ 2 * self.active_space \
+ 0.5 * self.nwell_enclose_active
self.well_width = self.width + 2 * self.nwell_enclose_active
# Height is an input parameter, so it is not recomputed.
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active,
self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="m1",
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
Add PMOS and NMOS to the layout at the upper-most and lowest position
@@ -146,19 +127,19 @@ class pnor2(pgate.pgate):
"""
self.pmos1_inst = self.add_inst(name="pnor2_pmos1",
mod=self.pmos_nd)
mod=self.pmos_left)
self.connect_inst(["vdd", "A", "net1", "vdd"])
self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
mod=self.pmos_ns)
mod=self.pmos_right)
self.connect_inst(["net1", "B", "Z", "vdd"])
self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
mod=self.nmos)
mod=self.nmos_left)
self.connect_inst(["Z", "A", "gnd", "gnd"])
self.nmos2_inst = self.add_inst(name="pnor2_nmos2",
mod=self.nmos)
mod=self.nmos_right)
self.connect_inst(["Z", "B", "gnd", "gnd"])
def place_ptx(self):
@@ -166,30 +147,35 @@ class pnor2(pgate.pgate):
Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
# Some of the S/D contacts may extend beyond the active,
# but this needs to be done in the gate itself
contact_extend_active_space = max(-self.nmos_right.get_pin("D").by(), 0)
# Assume the contact starts at the active edge
contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space + contact_extend_active_space
# This is a poly-to-poly of a flipped cell
poly_to_poly_gate_space = self.poly_extend_active + self.poly_space
# Recompute this since it has a small txwith the added contact extend active spacing
self.top_bottom_space = max(contact_to_vdd_rail_space,
poly_to_poly_gate_space)
pmos1_pos = vector(self.pmos_ns.active_offset.x,
self.height - self.pmos_ns.active_height \
- self.top_bottom_space)
pmos1_pos = vector(self.pmos_right.active_offset.x,
self.height - self.pmos_right.active_height - self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst.place(self.pmos2_pos)
nmos1_pos = vector(self.pmos_ns.active_offset.x, self.top_bottom_space)
nmos1_pos = vector(self.pmos_right.active_offset.x, self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos)
self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.pmos_ns, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos)
self.add_nwell_contact(self.pmos_right, self.pmos2_pos)
self.add_pwell_contact(self.nmos_right, self.nmos2_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
@@ -202,53 +188,53 @@ class pnor2(pgate.pgate):
def route_inputs(self):
""" Route the A and B inputs """
# Use M2 spaces so we can drop vias on the pins later!
inputB_yoffset = self.nmos2_inst.uy() + contact.poly_contact.height
# Top of NMOS drain
nmos_pin = self.nmos2_inst.get_pin("D")
bottom_pin_offset = nmos_pin.uy()
self.inputB_yoffset = bottom_pin_offset + self.m1_nonpref_pitch
self.inputA_yoffset = self.inputB_yoffset + self.m1_nonpref_pitch
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
self.inputB_yoffset,
"B",
position="center")
position="right",
directions=("V", "V"))
# This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A")
"A",
directions=("V", "V"))
self.output_yoffset = self.inputA_yoffset + self.m1_nonpref_pitch
def route_output(self):
""" Route the Z output """
# PMOS2 drain
# PMOS2 (right) drain
pmos_pin = self.pmos2_inst.get_pin("D")
# NMOS1 drain
# NMOS1 (left) drain
nmos_pin = self.nmos1_inst.get_pin("D")
# NMOS2 drain (for output via placement)
# NMOS2 (right) drain (for output via placement)
nmos2_pin = self.nmos2_inst.get_pin("D")
# Go up to metal2 for ease on all output pins
self.add_via_center(layers=self.m1_stack,
offset=pmos_pin.center())
m1m2_contact = self.add_via_center(layers=self.m1_stack,
offset=nmos_pin.center())
mid1_offset = vector(pmos_pin.center().x, nmos2_pin.center().y)
mid2_offset = vector(pmos_pin.center().x, self.inputA_yoffset)
mid3_offset = mid2_offset + vector(self.m2_width, 0)
# self.add_via_center(layers=self.m1_stack,
# offset=pmos_pin.center())
# m1m2_contact = self.add_via_center(layers=self.m1_stack,
# offset=nmos_pin.center())
mid1_offset = vector(nmos_pin.center().x, self.output_yoffset)
mid2_offset = vector(pmos_pin.center().x, self.output_yoffset)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("m2",
[pmos_pin.center(), mid2_offset, mid3_offset])
self.add_path("m2",
[nmos_pin.rc(), mid1_offset, mid2_offset])
# This extends the output to the edge of the cell
self.add_via_center(layers=self.m1_stack,
offset=mid3_offset)
self.add_path(self.route_layer,
[nmos_pin.center(), mid1_offset, mid2_offset, pmos_pin.center()])
self.add_layout_pin_rect_center(text="Z",
layer="m1",
offset=mid3_offset,
width=contact.m1_via.first_layer_height,
height=contact.m1_via.first_layer_width)
layer=self.route_layer,
offset=mid2_offset)
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
+37 -59
View File
@@ -105,21 +105,16 @@ class precharge(design.design):
# center of vdd rail
pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos])
self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos])
# if enable is not on M1, the supply can be
if self.en_layer != "m1":
self.add_via_center(layers=self.m1_stack,
offset=pmos_vdd_pos)
self.add_power_pin("vdd",
self.well_contact_pos,
vertical=True)
# Hack for li layers
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=self.well_contact_pos)
directions=("V", "V"))
self.add_via_stack_center(from_layer=pmos_pin.layer,
to_layer=self.en_layer,
offset=pmos_pin.center(),
directions=("V", "V"))
def create_ptx(self):
"""
@@ -159,7 +154,7 @@ class precharge(design.design):
self.lower_pmos_inst.place(self.lower_pmos_position)
# adds the upper pmos(s) to layout with 2 M2 tracks
ydiff = self.pmos.height + self.m2_pitch
ydiff = self.pmos.height + 2 * self.m2_pitch
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
self.upper_pmos1_inst.place(self.upper_pmos1_pos)
@@ -196,19 +191,15 @@ class precharge(design.design):
"""
# adds the en contact to connect the gates to the en rail
# midway in the 4 M2 tracks
offset = self.lower_pmos_inst.get_pin("G").ul() \
+ vector(0, 0.5 * self.m2_pitch)
self.add_via_center(layers=self.poly_stack,
offset=offset)
if self.en_layer == "m2":
self.add_via_center(layers=self.m1_stack,
offset=offset)
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=offset)
# adds the en rail on metal1
pin_offset = self.lower_pmos_inst.get_pin("G").lr()
# This is an extra space down for some techs with contact to active spacing
offset = pin_offset - vector(0, self.poly_space)
self.add_via_stack_center(from_layer="poly",
to_layer=self.en_layer,
offset=offset)
self.add_path("poly",
[self.lower_pmos_inst.get_pin("G").bc(), offset])
# adds the en rail
self.add_layout_pin_segment_center(text="en_bar",
layer=self.en_layer,
start=offset.scale(0, 1),
@@ -225,13 +216,13 @@ class precharge(design.design):
self.nwell_extend_active
self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \
vector(0, offset_height)
self.add_via_center(layers=self.active_stack,
offset=self.well_contact_pos,
implant_type="n",
well_type="n")
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=self.well_contact_pos)
self.well_contact = self.add_via_center(layers=self.active_stack,
offset=self.well_contact_pos,
implant_type="n",
well_type="n")
self.add_via_stack_center(from_layer=self.active_stack[2],
to_layer=self.bitline_layer,
offset=self.well_contact_pos)
self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space
@@ -245,11 +236,10 @@ class precharge(design.design):
"""
Adds both bit-line and bit-line-bar to the module
"""
layer_width = drc("minwidth_" + self.bitline_layer)
layer_space = drc("{0}_to_{0}".format(self.bitline_layer))
layer_pitch = getattr(self, "{}_pitch".format(self.bitline_layer))
# adds the BL
self.bl_xoffset = layer_space + 0.5 * layer_width
self.bl_xoffset = layer_pitch
top_pos = vector(self.bl_xoffset, self.height)
pin_pos = vector(self.bl_xoffset, 0)
self.add_path(self.bitline_layer, [top_pos, pin_pos])
@@ -259,7 +249,7 @@ class precharge(design.design):
end=top_pos)
# adds the BR
self.br_xoffset = self.width - layer_space - 0.5 * layer_width
self.br_xoffset = self.width - layer_pitch
top_pos = vector(self.br_xoffset, self.height)
pin_pos = vector(self.br_xoffset, 0)
self.add_path(self.bitline_layer, [top_pos, pin_pos])
@@ -288,31 +278,19 @@ class precharge(design.design):
Adds contacts/via from metal1 to metal2 for bit-lines
"""
# No contacts needed if M1
if self.bitline_layer == "m1":
return
# BL
lower_pin = self.lower_pmos_inst.get_pin("S")
self.lower_via = self.add_via_center(layers=self.m1_stack,
offset=lower_pin.center(),
directions=("V", "V"))
for lower_pin in [self.lower_pmos_inst.get_pin("S"), self.lower_pmos_inst.get_pin("D")]:
self.add_via_stack_center(from_layer=lower_pin.layer,
to_layer=self.bitline_layer,
offset=lower_pin.center(),
directions=("V", "V"))
lower_pin = self.lower_pmos_inst.get_pin("D")
self.lower_via = self.add_via_center(layers=self.m1_stack,
offset=lower_pin.center(),
directions=("V", "V"))
# BR
upper_pin = self.upper_pmos1_inst.get_pin("S")
self.upper_via2 = self.add_via_center(layers=self.m1_stack,
offset=upper_pin.center(),
directions=("V", "V"))
upper_pin = self.upper_pmos2_inst.get_pin("D")
self.upper_via2 = self.add_via_center(layers=self.m1_stack,
offset=upper_pin.center(),
directions=("V", "V"))
for upper_pin in [self.upper_pmos1_inst.get_pin("S"), self.upper_pmos2_inst.get_pin("D")]:
self.add_via_stack_center(from_layer=upper_pin.layer,
to_layer=self.bitline_layer,
offset=upper_pin.center(),
directions=("V", "V"))
def connect_pmos(self, pmos_pin, bit_xoffset):
"""
-3
View File
@@ -79,9 +79,6 @@ class ptristate_inv(pgate.pgate):
self.width = self.well_width + 0.5 * self.m1_space
# Height is an input parameter, so it is not recomputed.
# Make sure we can put a well above and below
self.top_bottom_space = max(contact.active_contact.width, contact.active_contact.height)
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx",
+106 -90
View File
@@ -23,32 +23,47 @@ class ptx(design.design):
the transistor width. Mults is the number of transistors of the
given width. Total width is therefore mults*width. Options allow
you to connect the fingered gates and active for parallel devices.
The add_*_contact option tells which layer to bring source/drain up to.
"""
def __init__(self,
name="",
width=drc("minwidth_tx"),
mults=1,
tx_type="nmos",
add_source_contact=True,
add_drain_contact=True,
add_source_contact=None,
add_drain_contact=None,
series_devices=False,
connect_active=False,
connect_drain_active=False,
connect_source_active=False,
connect_poly=False,
num_contacts=None):
if "li" in layer:
self.route_layer = "li"
else:
self.route_layer = "m1"
# Default contacts are the lowest layer
if not add_source_contact:
add_source_contact = self.route_layer
# Default contacts are the lowest layer
if not add_drain_contact:
add_drain_contact = self.route_layer
# 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 = "{0}_m{1}_w{2:.3f}".format(tx_type, mults, width)
if not add_source_contact:
name += "_ns"
if not add_drain_contact:
name += "_nd"
name += "_s{}".format(add_source_contact)
name += "_d{}".format(add_drain_contact)
if series_devices:
name += "_sd"
if connect_active:
name += "_a"
if connect_drain_active:
name += "_da"
if connect_source_active:
name += "_sa"
if connect_poly:
name += "_p"
if num_contacts:
@@ -61,13 +76,17 @@ class ptx(design.design):
self.tx_type = tx_type
self.mults = mults
self.tx_width = width
self.connect_active = connect_active
self.connect_drain_active = connect_drain_active
self.connect_source_active = connect_source_active
self.connect_poly = connect_poly
self.add_source_contact = add_source_contact
self.add_drain_contact = add_drain_contact
self.series_devices = series_devices
self.num_contacts = num_contacts
self.route_layer_width = drc("minwidth_{}".format(self.route_layer))
self.route_layer_space = drc("{0}_to_{0}".format(self.route_layer))
# Since it has variable height, it is not a pgate.
self.create_netlist()
# We must always create ptx layout for pbitcell
@@ -266,55 +285,42 @@ class ptx(design.design):
width=poly_width,
height=self.poly_width)
def connect_fingered_active(self, drain_positions, source_positions):
def connect_fingered_active(self, positions, pin_name, top):
"""
Connect each contact up/down to a source or drain pin
"""
if len(positions) <= 1:
return
layer_space = getattr(self, "{}_space".format(self.route_layer))
layer_width = getattr(self, "{}_width".format(self.route_layer))
# This is the distance that we must route up or down from the center
# of the contacts to avoid DRC violations to the other contacts
pin_offset = vector(0,
0.5 * self.active_contact.second_layer_height + self.m1_space + 0.5 * self.m1_width)
0.5 * self.active_contact.second_layer_height + layer_space + 0.5 * layer_width)
# This is the width of a m1 extend the ends of the pin
end_offset = vector(self.m1_width / 2.0, 0)
end_offset = vector(layer_width / 2.0, 0)
# drains always go to the MIDDLE of the cell,
# so top of NMOS, bottom of PMOS
# so reverse the directions for NMOS compared to PMOS.
if self.tx_type == "pmos":
drain_dir = -1
source_dir = 1
# We move the opposite direction from the bottom
if not top:
offset = pin_offset.scale(-1, -1)
else:
drain_dir = 1
source_dir = -1
if len(source_positions) > 1:
source_offset = pin_offset.scale(source_dir, source_dir)
# remove the individual connections
self.remove_layout_pin("S")
# Add each vertical segment
for a in source_positions:
self.add_path(("m1"),
[a, a + pin_offset.scale(source_dir,
source_dir)])
# Add a single horizontal pin
self.add_layout_pin_segment_center(text="S",
layer="m1",
start=source_positions[0] + source_offset - end_offset,
end=source_positions[-1] + source_offset + end_offset)
offset = pin_offset
# remove the individual connections
self.remove_layout_pin(pin_name)
# Add each vertical segment
for a in positions:
self.add_path(self.route_layer,
[a, a + offset])
# Add a single horizontal pin
self.add_layout_pin_segment_center(text=pin_name,
layer=self.route_layer,
start=positions[0] + offset - end_offset,
end=positions[-1] + offset + end_offset)
if len(drain_positions)>1:
drain_offset = pin_offset.scale(drain_dir,drain_dir)
self.remove_layout_pin("D") # remove the individual connections
# Add each vertical segment
for a in drain_positions:
self.add_path(("m1"), [a,a+drain_offset])
# Add a single horizontal pin
self.add_layout_pin_segment_center(text="D",
layer="m1",
start=drain_positions[0] + drain_offset - end_offset,
end=drain_positions[-1] + drain_offset + end_offset)
def add_poly(self):
"""
Add the poly gates(s) and (optionally) connect them.
@@ -380,10 +386,12 @@ class ptx(design.design):
well_ll = center_pos - vector(0.5 * self.well_width,
0.5 * self.well_height)
if well_name in layer:
self.add_rect(layer=well_name,
offset=well_ll,
width=self.well_width,
height=self.well_height)
well = self.add_rect(layer=well_name,
offset=well_ll,
width=self.well_width,
height=self.well_height)
setattr(self, well_name, well)
if "vtg" in layer:
self.add_rect(layer="vtg",
offset=well_ll,
@@ -433,12 +441,12 @@ class ptx(design.design):
label = "S"
source_positions.append(pos)
if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact):
if (label=="S" and self.add_source_contact):
contact = self.add_diff_contact(label, pos)
if label == "S":
self.source_contacts.append(contact)
else:
self.drain_contacts.append(contact)
self.source_contacts.append(contact)
elif (label=="D" and self.add_drain_contact):
contact = self.add_diff_contact(label, pos)
self.drain_contacts.append(contact)
else:
self.add_layout_pin_rect_center(text=label,
layer="active",
@@ -454,19 +462,22 @@ class ptx(design.design):
label = "S"
source_positions.append(pos)
if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact):
if (label=="S" and self.add_source_contact):
contact = self.add_diff_contact(label, pos)
if label == "S":
self.source_contacts.append(contact)
else:
self.drain_contacts.append(contact)
self.source_contacts.append(contact)
elif (label=="D" and self.add_drain_contact):
contact = self.add_diff_contact(label, pos)
self.drain_contacts.append(contact)
else:
self.add_layout_pin_rect_center(text=label,
layer="active",
offset=pos)
if self.connect_active:
self.connect_fingered_active(drain_positions, source_positions)
if self.connect_source_active:
self.connect_fingered_active(source_positions, "S", top=(self.tx_type=="pmos"))
if self.connect_drain_active:
self.connect_fingered_active(drain_positions, "D", top=(self.tx_type=="nmos"))
def get_stage_effort(self, cout):
"""Returns an object representing the parameters for delay in tau units."""
@@ -489,34 +500,39 @@ class ptx(design.design):
return self.mults * self.tx_width / drc("minwidth_tx")
def add_diff_contact(self, label, pos):
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("V", "V"),
implant_type=self.implant_type,
well_type=self.well_type)
if hasattr(self, "li_stack"):
contact=self.add_via_center(layers=self.li_stack,
offset=pos,
directions=("V", "V"))
# contact_area = contact.mod.second_layer_width * contact.mod.second_layer_height
# min_area = drc("minarea_m1")
# width = contact.mod.second_layer_width
# if contact_area < min_area:
# height = min_area / width
# else:
# height = contact.mod.second_layer_height
width = contact.mod.second_layer_width
height = contact.mod.second_layer_height
if label == "S":
layer = self.add_source_contact
elif label == "D":
layer = self.add_drain_contact
else:
debug.error("Invalid source drain name.")
if layer != "active":
via=self.add_via_stack_center(offset=pos,
from_layer="active",
to_layer=layer,
size=(1, self.num_contacts),
directions=("V", "V"),
implant_type=self.implant_type,
well_type=self.well_type)
pin_height = via.mod.second_layer_height
pin_width = via.mod.second_layer_width
else:
via = None
pin_height = None
pin_width = None
# Source drain vias are all vertical
self.add_layout_pin_rect_center(text=label,
layer="m1",
layer=layer,
offset=pos,
width=width,
height=height)
width=pin_width,
height=pin_height)
return(contact)
return(via)
def get_cin(self):
"""Returns the relative gate cin of the tx"""
+33 -61
View File
@@ -11,6 +11,7 @@ from tech import drc, layer
from vector import vector
from sram_factory import factory
import logical_effort
from utils import round_to_grid
class single_level_column_mux(pgate.pgate):
@@ -44,13 +45,22 @@ class single_level_column_mux(pgate.pgate):
def create_layout(self):
self.pin_height = 2 * self.m2_width
# If li exists, use li and m1 for the mux, otherwise use m1 and m2
if "li" in layer:
self.col_mux_stack = self.li_stack
else:
self.col_mux_stack = self.m1_stack
self.pin_layer = self.bitcell.get_pin(self.bitcell_bl).layer
self.pin_pitch = getattr(self, "{}_pitch".format(self.pin_layer))
self.pin_width = getattr(self, "{}_width".format(self.pin_layer))
self.pin_height = 2 * self.pin_width
self.width = self.bitcell.width
self.height = self.nmos_upper.uy() + self.pin_height
self.connect_poly()
self.add_bitline_pins()
self.connect_bitlines()
self.add_wells()
self.add_pn_wells()
def add_modules(self):
self.bitcell = factory.create(module_type="bitcell")
@@ -58,9 +68,7 @@ class single_level_column_mux(pgate.pgate):
# Adds nmos_lower,nmos_upper to the module
self.ptx_width = self.tx_size * drc("minwidth_tx")
self.nmos = factory.create(module_type="ptx",
width=self.ptx_width,
add_source_contact=False,
add_drain_contact=False)
width=self.ptx_width)
self.add_mod(self.nmos)
def add_pins(self):
@@ -69,29 +77,26 @@ class single_level_column_mux(pgate.pgate):
def add_bitline_pins(self):
""" Add the top and bottom pins to this cell """
bl_pin=self.bitcell.get_pin(self.bitcell_bl)
br_pin=self.bitcell.get_pin(self.bitcell_br)
bl_pos = vector(bl_pin.lx(), 0)
br_pos = vector(br_pin.lx(), 0)
bl_pos = vector(self.pin_pitch, 0)
br_pos = vector(self.width - self.pin_pitch, 0)
# bl and br
self.add_layout_pin(text="bl",
layer=bl_pin.layer,
layer=self.pin_layer,
offset=bl_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
self.add_layout_pin(text="br",
layer=br_pin.layer,
layer=self.pin_layer,
offset=br_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
# bl_out and br_out
self.add_layout_pin(text="bl_out",
layer=bl_pin.layer,
layer=self.pin_layer,
offset=bl_pos,
height=self.pin_height)
self.add_layout_pin(text="br_out",
layer=br_pin.layer,
layer=self.pin_layer,
offset=br_pos,
height=self.pin_height)
@@ -99,7 +104,7 @@ class single_level_column_mux(pgate.pgate):
""" Create the two pass gate NMOS transistors to switch the bitlines"""
# Space it in the center
nmos_lower_position = self.nmos.active_offset.scale(0,1) \
nmos_lower_position = self.nmos.active_offset.scale(0, 1) \
+ vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0)
self.nmos_lower = self.add_inst(name="mux_tx1",
mod=self.nmos,
@@ -133,63 +138,30 @@ class single_level_column_mux(pgate.pgate):
def connect_bitlines(self):
""" Connect the bitlines to the mux transistors """
# If li exists, use li and m1 for the mux, otherwise use m1 and m2
if "li" in layer:
self.col_mux_stack = self.li_stack
else:
self.col_mux_stack = self.m1_stack
# These are on metal2
bl_pin = self.get_pin("bl")
br_pin = self.get_pin("br")
bl_out_pin = self.get_pin("bl_out")
br_out_pin = self.get_pin("br_out")
# These are on metal1
nmos_lower_s_pin = self.nmos_lower.get_pin("S")
nmos_lower_d_pin = self.nmos_lower.get_pin("D")
nmos_upper_s_pin = self.nmos_upper.get_pin("S")
nmos_upper_d_pin = self.nmos_upper.get_pin("D")
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
self.add_via_center(layers=self.col_mux_stack,
offset=bl_pin.bc(),
directions=("V", "V"))
self.add_via_center(layers=self.col_mux_stack,
offset=br_out_pin.uc(),
directions=("V", "V"))
self.add_via_center(layers=self.col_mux_stack,
offset=nmos_upper_s_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=self.col_mux_stack,
offset=nmos_lower_d_pin.center(),
directions=("V", "V"))
# Add diffusion contacts
# These were previously omitted with the options: add_source_contact=False, add_drain_contact=False
# They are added now and not previously so that they do not include m1 (which is usually included by default)
# This is only a concern when the local interconnect (li) layer is being used
self.add_via_center(layers=self.active_stack,
offset=nmos_upper_d_pin.center(),
directions=("V", "V"),
implant_type="n",
well_type="nwell")
self.add_via_center(layers=self.active_stack,
offset=nmos_lower_s_pin.center(),
directions=("V", "V"),
implant_type="n",
well_type="nwell")
self.add_via_center(layers=self.active_stack,
offset=nmos_upper_s_pin.center(),
directions=("V", "V"),
implant_type="n",
well_type="nwell")
self.add_via_center(layers=self.active_stack,
offset=nmos_lower_d_pin.center(),
directions=("V", "V"),
implant_type="n",
well_type="nwell")
self.add_via_stack_center(from_layer=bl_pin.layer,
to_layer=self.col_mux_stack[0],
offset=bl_pin.bc())
self.add_via_stack_center(from_layer=br_out_pin.layer,
to_layer=self.col_mux_stack[0],
offset=br_out_pin.uc())
self.add_via_stack_center(from_layer=nmos_upper_s_pin.layer,
to_layer=self.col_mux_stack[2],
offset=nmos_upper_s_pin.center())
self.add_via_stack_center(from_layer=nmos_lower_d_pin.layer,
to_layer=self.col_mux_stack[2],
offset=nmos_lower_d_pin.center())
# bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2
@@ -218,7 +190,7 @@ class single_level_column_mux(pgate.pgate):
self.add_path(self.col_mux_stack[2],
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()])
def add_wells(self):
def add_pn_wells(self):
"""
Add a well and implant over the whole cell. Also, add the
pwell contact (if it exists)
+151
View File
@@ -0,0 +1,151 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
from vector import vector
import design
from sram_factory import factory
from globals import OPTS
from tech import layer
class wordline_driver(design.design):
"""
This is an AND (or NAND) with configurable drive strength to drive the wordlines.
It is matched to the bitcell height.
"""
def __init__(self, name, size=1, height=None):
debug.info(1, "Creating wordline_driver {}".format(name))
self.add_comment("size: {}".format(size))
design.design.__init__(self, name)
if height is None:
b = factory.create(module_type="bitcell")
self.height = b.height
else:
self.height = height
self.size = size
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_pins()
self.create_modules()
self.create_insts()
def create_modules(self):
self.nand = factory.create(module_type="nand2_dec",
height=self.height)
self.driver = factory.create(module_type="inv_dec",
size=self.size,
height=self.nand.height)
self.add_mod(self.nand)
self.add_mod(self.driver)
def create_layout(self):
self.width = self.nand.width + self.driver.width
if "li" in layer:
self.route_layer = "li"
else:
self.route_layer = "m1"
self.place_insts()
self.route_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
self.add_pin("A", "INPUT")
self.add_pin("B", "INPUT")
self.add_pin("Z", "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_insts(self):
self.nand_inst = self.add_inst(name="wld_nand",
mod=self.nand)
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
self.driver_inst = self.add_inst(name="wl_driver",
mod=self.driver)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self):
# Add NAND to the right
self.nand_inst.place(offset=vector(0, 0))
# Add INV to the right
self.driver_inst.place(offset=vector(self.nand_inst.rx(), 0))
def route_supply_rails(self):
""" Add vdd/gnd rails to the top, (middle), and bottom. """
if OPTS.tech_name == "s8":
for name in ["vdd", "gnd"]:
for inst in [self.nand_inst, self.driver_inst]:
self.copy_layout_pin(inst, name)
else:
self.add_layout_pin_rect_center(text="gnd",
layer=self.route_layer,
offset=vector(0.5 * self.width, 0),
width=self.width)
y_offset = self.height
self.add_layout_pin_rect_center(text="vdd",
layer=self.route_layer,
offset=vector(0.5 * self.width, y_offset),
width=self.width)
def route_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
a2_pin = self.driver_inst.get_pin("A")
if OPTS.tech_name == "s8":
mid1_point = vector(a2_pin.cx(), z1_pin.cy())
else:
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path(self.route_layer,
[z1_pin.center(), mid1_point, a2_pin.center()])
def add_layout_pins(self):
pin = self.driver_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=pin.height())
for pin_name in ["A", "B"]:
pin = self.nand_inst.get_pin(pin_name)
self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=pin.height())
def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A or B -> Z path"""
stage_effort_list = []
stage1_cout = self.driver.get_cin()
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
stage_effort_list.append(stage1)
stage2 = self.driver.get_stage_effort(external_cout, stage1.is_rise)
stage_effort_list.append(stage2)
return stage_effort_list
def get_cin(self):
"""Return the relative input capacitance of a single input"""
return self.nand.get_cin()