Fix some pep8 errors/warnings in pgate and examples.

This commit is contained in:
Matt Guthaus 2019-10-06 17:30:16 +00:00
parent 76ad2e68c0
commit 84c7146792
21 changed files with 915 additions and 740 deletions

View File

@ -14,7 +14,9 @@ route_supplies = True
check_lvsdrc = True
output_path = "temp"
output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -14,7 +14,9 @@ route_supplies = True
check_lvsdrc = True
output_path = "temp"
output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -10,5 +10,7 @@ route_supplies = True
check_lvsdrc = True
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)

View File

@ -10,7 +10,9 @@ route_supplies = True
check_lvsdrc = True
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -7,7 +7,9 @@ supply_voltages = [ 5.0 ]
temperatures = [ 25 ]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -7,7 +7,9 @@ supply_voltages = [ 3.3 ]
temperatures = [25]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -6,13 +6,11 @@
# All rights reserved.
#
import debug
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import pgate
from sram_factory import factory
class pand2(pgate.pgate):
"""
This is a simple buffer used for driving loads.
@ -36,7 +34,10 @@ class pand2(pgate.pgate):
self.nand = factory.create(module_type="pnand2", height=self.height)
self.add_mod(self.nand)
self.inv = factory.create(module_type="pdriver", neg_polarity=True, fanout=3*self.size, height=self.height)
self.inv = factory.create(module_type="pdriver",
neg_polarity=True,
fanout=3*self.size,
height=self.height)
self.add_mod(self.inv)
def create_layout(self):
@ -75,8 +76,8 @@ class pand2(pgate.pgate):
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("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
self.add_path("metal1",
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.

View File

@ -6,13 +6,11 @@
# All rights reserved.
#
import debug
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import pgate
from sram_factory import factory
class pand3(pgate.pgate):
"""
This is a simple buffer used for driving loads.
@ -36,7 +34,9 @@ class pand3(pgate.pgate):
self.nand = factory.create(module_type="pnand3", height=self.height)
self.add_mod(self.nand)
self.inv = factory.create(module_type="pinv", size=self.size, height=self.height)
self.inv = factory.create(module_type="pinv",
size=self.size,
height=self.height)
self.add_mod(self.inv)
def create_layout(self):
@ -76,8 +76,8 @@ class pand3(pgate.pgate):
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("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
self.add_path("metal1",
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
@ -111,12 +111,14 @@ class pand3(pgate.pgate):
width=pin.width(),
height=pin.height())
def analytical_delay(self, corner, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load())
inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load)
nand_delay = self.nand.analytical_delay(corner,
slew=slew,
load=self.inv.input_load())
inv_delay = self.inv.analytical_delay(corner,
slew=nand_delay.slew,
load=load)
return nand_delay + inv_delay
def get_stage_efforts(self, external_cout, inp_is_rise=False):

View File

@ -6,13 +6,11 @@
# All rights reserved.
#
import debug
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import pgate
from sram_factory import factory
class pbuf(pgate.pgate):
"""
This is a simple buffer used for driving loads.
@ -29,7 +27,6 @@ class pbuf(pgate.pgate):
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def create_netlist(self):
self.add_pins()
self.create_modules()
@ -50,10 +47,14 @@ class pbuf(pgate.pgate):
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
input_size = max(1, int(self.size / self.stage_effort))
self.inv1 = factory.create(module_type="pinv", size=input_size, height=self.height)
self.inv1 = factory.create(module_type="pinv",
size=input_size,
height=self.height)
self.add_mod(self.inv1)
self.inv2 = factory.create(module_type="pinv", size=self.size, height=self.height)
self.inv2 = factory.create(module_type="pinv",
size=self.size,
height=self.height)
self.add_mod(self.inv2)
def create_insts(self):
@ -61,7 +62,6 @@ class pbuf(pgate.pgate):
mod=self.inv1)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv2_inst = self.add_inst(name="buf_inv2",
mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
@ -73,7 +73,6 @@ class pbuf(pgate.pgate):
# Add INV2 to the right
self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
def add_wires(self):
# inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z")
@ -81,7 +80,6 @@ class pbuf(pgate.pgate):
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv1_inst.get_pin("vdd")

View File

@ -7,17 +7,16 @@
#
import debug
import pgate
import math
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from sram_factory import factory
class pdriver(pgate.pgate):
"""
This instantiates an even or odd number of inverters sized for driving a load.
This instantiates an even or odd number of inverters
sized for driving a load.
"""
def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None):
debug.info(1, "creating pdriver {}".format(name))
@ -28,7 +27,7 @@ class pdriver(pgate.pgate):
self.size_list = size_list
self.fanout = fanout
if size_list == None and self.fanout == 0:
if not size_list and self.fanout == 0:
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)
@ -38,14 +37,14 @@ class pdriver(pgate.pgate):
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def compute_sizes(self):
# size_list specified
if self.size_list:
self.num_stages = len(self.size_list)
else:
# Find the optimal number of stages for the given effort
self.num_stages = max(1,int(round(self.fanout**(1/self.stage_effort))))
self.num_stages = max(1,
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):
@ -63,7 +62,6 @@ class pdriver(pgate.pgate):
# reverse the sizes to be from input to output
self.size_list.reverse()
def create_netlist(self):
self.compute_sizes()
self.add_comment("sizes: {}".format(str(self.size_list)))
@ -79,7 +77,6 @@ class pdriver(pgate.pgate):
self.width = self.inv_inst_list[-1].rx()
self.height = self.inv_inst_list[0].height
def add_pins(self):
self.add_pin("A", "INPUT")
self.add_pin("Z", "OUTPUT")
@ -89,19 +86,21 @@ class pdriver(pgate.pgate):
def add_modules(self):
self.inv_list = []
for size in self.size_list:
temp_inv = factory.create(module_type="pinv", size=size, height=self.height)
temp_inv = factory.create(module_type="pinv",
size=size,
height=self.height)
self.inv_list.append(temp_inv)
self.add_mod(temp_inv)
def create_insts(self):
self.inv_inst_list = []
for x in range(1, self.num_stages + 1):
# Create first inverter
if x == 1:
zbx_int = "Zb{}_int".format(x);
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x-1]))
zbx_int = "Zb{}_int".format(x)
inst = self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x - 1])
self.inv_inst_list.append(inst)
if self.num_stages == 1:
self.connect_inst(["A", "Z", "vdd", "gnd"])
else:
@ -109,28 +108,29 @@ class pdriver(pgate.pgate):
# Create last inverter
elif x == self.num_stages:
zbn_int = "Zb{}_int".format(x-1);
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x-1]))
zbn_int = "Zb{}_int".format(x - 1)
inst = self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x - 1])
self.inv_inst_list.append(inst)
self.connect_inst([zbn_int, "Z", "vdd", "gnd"])
# Create middle inverters
else:
zbx_int = "Zb{}_int".format(x-1);
zbn_int = "Zb{}_int".format(x);
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x-1]))
zbx_int = "Zb{}_int".format(x - 1)
zbn_int = "Zb{}_int".format(x)
inst = self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x - 1])
self.inv_inst_list.append(inst)
self.connect_inst([zbx_int, zbn_int, "vdd", "gnd"])
def place_modules(self):
# Add the first inverter at the origin
self.inv_inst_list[0].place(vector(0, 0))
# Add inverters to the right of the previous inverter
for x in range(1, len(self.inv_inst_list)):
self.inv_inst_list[x].place(vector(self.inv_inst_list[x-1].rx(),0))
loc = vector(self.inv_inst_list[x - 1].rx(), 0)
self.inv_inst_list[x].place(loc)
def route_wires(self):
z_inst_list = []
@ -140,8 +140,9 @@ 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("metal1", [z_inst_list[x].center(), mid_point, a_inst_list[x].center()])
self.add_path("metal1",
[z_inst_list[x].center(), mid_point,
a_inst_list[x].center()])
def add_layout_pins(self):
# Continous vdd rail along with label.

View File

@ -8,14 +8,16 @@
import contact
import design
import debug
from tech import drc, parameter, spice
from tech import drc
from vector import vector
from globals import OPTS
from sram_factory import factory
class pgate(design.design):
"""
This is a module that implements some shared functions for parameterized gates.
This is a module that implements some shared
functions for parameterized gates.
"""
def __init__(self, name, height=None):
@ -34,7 +36,6 @@ class pgate(design.design):
self.add_boundary()
self.DRC_LVS()
def create_netlist(self):
""" Pure virtual function """
debug.error("Must over-ride create_netlist.", -1)
@ -64,14 +65,18 @@ class pgate(design.design):
width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=False):
""" Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or right of gate. """
"""
Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or
right of gate.
"""
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!
debug.check(nmos_gate_pin.ll().x==pmos_gate_pin.ll().x, "Connecting unaligned gates not supported.")
debug.check(nmos_gate_pin.ll().x == pmos_gate_pin.ll().x,
"Connecting unaligned gates not supported.")
# Pick point on the left of NMOS and connect down to PMOS
nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5 * self.poly_width, 0)
@ -94,13 +99,17 @@ class pgate(design.design):
directions = ("V", "H")
if position == "center":
contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0)
contact_offset = left_gate_offset \
+ vector(0.5 * self.poly_width, 0)
elif position == "farleft":
contact_offset = left_gate_offset - vector(0.5*contact.poly.width, 0)
contact_offset = left_gate_offset \
- vector(0.5 * contact.poly.width, 0)
elif position == "left":
contact_offset = left_gate_offset - vector(0.5*contact_width - 0.5*self.poly_width, 0)
contact_offset = left_gate_offset \
- vector(0.5 * contact_width - 0.5 * self.poly_width, 0)
elif position == "right":
contact_offset = left_gate_offset + vector(0.5*contact.width + 0.5*self.poly_width, 0)
contact_offset = left_gate_offset \
+ vector(0.5 * contact.width + 0.5 * self.poly_width, 0)
else:
debug.error("Invalid contact placement option.", -1)
@ -110,19 +119,16 @@ class pgate(design.design):
offset=contact_offset,
directions=directions)
# self.add_layout_pin_segment_center(text=name,
# layer="metal1",
# start=left_gate_offset.scale(0,1),
# end=left_gate_offset)
self.add_layout_pin_rect_center(text=name,
layer="metal1",
offset=contact_offset,
width=contact_m1_width,
height=contact_m1_height)
# This is to ensure that the contact is connected to the gate
mid_point = contact_offset.scale(0.5,1)+left_gate_offset.scale(0.5,0)
# This is to ensure that the contact is
# connected to the gate
mid_point = contact_offset.scale(0.5, 1) \
+ left_gate_offset.scale(0.5, 0)
self.add_rect_center(layer="poly",
offset=mid_point,
height=contact.poly.first_layer_width,
@ -163,8 +169,11 @@ class pgate(design.design):
layer_stack = ("active", "contact", "metal1")
# To the right a spacing away from the pmos right active edge
contact_xoffset = pmos_pos.x + pmos.active_width + drc("active_to_body_active")
# Must be at least an well enclosure of active down from the top of the well
contact_xoffset = pmos_pos.x + pmos.active_width \
+ drc("active_to_body_active")
# Must be at least an well enclosure of active down
# from the top of the well
# OR align the active with the top of PMOS active.
max_y_offset = self.height + 0.5 * self.m1_width
contact_yoffset = min(pmos_pos.y + pmos.active_height - pmos.active_contact.first_layer_height,
@ -185,16 +194,20 @@ class pgate(design.design):
# Now add the full active and implant for the PMOS
# active_offset = pmos_pos + vector(pmos.active_width,0)
# This might be needed if the spacing between the actives is not satisifed
# This might be needed if the spacing between the actives
# is not satisifed
# self.add_rect(layer="active",
# offset=active_offset,
# width=pmos.active_contact.width,
# height=pmos.active_height)
# we need to ensure implants don't overlap and are spaced far enough apart
# we need to ensure implants don't overlap and are
# spaced far enough apart
# implant_spacing = self.implant_space+self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active)
# implant_width = pmos.active_contact.width + 2*self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) \
# - vector(0,self.implant_enclose_active)
# implant_width = pmos.active_contact.width \
# + 2*self.implant_enclose_active
# implant_height = pmos.active_height + 2*self.implant_enclose_active
# self.add_rect(layer="nimplant",
# offset=implant_offset,
@ -211,10 +224,13 @@ class pgate(design.design):
pwell_position = vector(0, -0.5 * self.m1_width)
# To the right a spacing away from the nmos right active edge
contact_xoffset = nmos_pos.x + nmos.active_width + drc("active_to_body_active")
# Must be at least an well enclosure of active up from the bottom of the well
contact_xoffset = nmos_pos.x + nmos.active_width \
+ drc("active_to_body_active")
# Must be at least an well enclosure of active up
# from the bottom of the well
contact_yoffset = max(nmos_pos.y,
self.well_enclose_active - nmos.active_contact.first_layer_height/2)
self.well_enclose_active \
- nmos.active_contact.first_layer_height / 2)
contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact
@ -232,15 +248,18 @@ class pgate(design.design):
# Now add the full active and implant for the NMOS
# active_offset = nmos_pos + vector(nmos.active_width,0)
# This might be needed if the spacing between the actives is not satisifed
# This might be needed if the spacing between the actives
# is not satisifed
# self.add_rect(layer="active",
# offset=active_offset,
# width=nmos.active_contact.width,
# height=nmos.active_height)
# implant_spacing = self.implant_space+self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active)
# implant_width = nmos.active_contact.width + 2*self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) \
# - vector(0,self.implant_enclose_active)
# implant_width = nmos.active_contact.width \
# + 2*self.implant_enclose_active
# implant_height = nmos.active_height + 2*self.implant_enclose_active
# self.add_rect(layer="pimplant",
# offset=implant_offset,

View File

@ -16,6 +16,7 @@ from utils import round_to_grid
import logical_effort
from sram_factory import factory
class pinv(pgate.pgate):
"""
Pinv generates gds of a parametrically sized inverter. The
@ -28,7 +29,9 @@ class pinv(pgate.pgate):
def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True):
debug.info(2, "creating pinv structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pinv structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
self.size = size
@ -54,7 +57,11 @@ class pinv(pgate.pgate):
self.add_well_contacts()
self.extend_wells(self.well_pos)
self.connect_rails()
self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", position="farleft")
self.route_input_gate(self.pmos_inst,
self.nmos_inst,
self.output_pos.y,
"A",
position="farleft")
self.route_outputs()
def add_pins(self):
@ -63,7 +70,6 @@ class pinv(pgate.pgate):
dir_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list)
def determine_tx_mults(self):
"""
Determines the number of fingers needed to achieve the size within
@ -78,36 +84,47 @@ class pinv(pgate.pgate):
return
# 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)
# 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 = factory.create(module_type="ptx", tx_type="nmos")
pmos = factory.create(module_type="ptx", width=drc("minwidth_tx"), tx_type="pmos")
pmos = factory.create(module_type="ptx",
width=drc("minwidth_tx"),
tx_type="pmos")
tx_height = nmos.poly_height + pmos.poly_height
# rotated m1 pitch or poly to active spacing
min_channel = max(contact.poly.width + self.m1_space,
contact.poly.width + 2 * drc("poly_to_active"))
# This is the extra space needed to ensure DRC rules to the active contacts
# 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,
drc("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,total_height))
debug.check(self.height > total_height,
"Cell height {0} too small for simple min height {1}.".format(self.height,
total_height))
# Determine the height left to the transistors to determine the number of fingers
# Determine the height left to the transistors to determine
# the number of fingers
tx_height_available = self.height - min_channel - 2 * self.top_bottom_space
# Divide the height in half. Could divide proportional to beta, but this makes
# connecting wells of multiple cells easier.
# Divide the height in half. Could divide proportional to beta,
# but this makes connecting wells of multiple cells easier.
# Subtract the poly space under the rail of the tx
nmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly")
pmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly")
debug.info(2,"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available,
debug.info(2,
"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available,
nmos_height_available,
pmos_height_available))
# Determine the number of mults for each to fit width into available space
# 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)
@ -118,13 +135,15 @@ class pinv(pgate.pgate):
# 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...
# We also need to round the width to the grid or we will end up with LVS property
# mismatch errors when fingers are not a grid length and get rounded in the offset geometry.
# We also need to round the width to the grid or we will end up
# with LVS property mismatch errors when fingers are not a grid
# length and get rounded in the offset geometry.
self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults)
debug.check(self.nmos_width>=drc("minwidth_tx"),"Cannot finger NMOS transistors to fit cell height.")
debug.check(self.nmos_width >= drc("minwidth_tx"),
"Cannot finger NMOS transistors to fit cell height.")
self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults)
debug.check(self.pmos_width>=drc("minwidth_tx"),"Cannot finger PMOS transistors to fit cell height.")
debug.check(self.pmos_width >= drc("minwidth_tx"),
"Cannot finger PMOS transistors to fit cell height.")
def setup_layout_constants(self):
"""
@ -138,8 +157,6 @@ class pinv(pgate.pgate):
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx",
@ -170,8 +187,6 @@ class pinv(pgate.pgate):
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
Create the PMOS and NMOS netlist.
@ -185,7 +200,6 @@ class pinv(pgate.pgate):
mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"])
def place_ptx(self):
"""
Place PMOS and NMOS to the layout at the upper-most and lowest position
@ -194,14 +208,15 @@ class pinv(pgate.pgate):
# 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_space)
+ vector(0,
self.height - self.pmos.active_height - self.top_bottom_space)
self.pmos_inst.place(self.pmos_pos)
# 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.top_bottom_space)
self.nmos_pos = self.nmos.active_offset.scale(1, 0) \
+ vector(0, self.top_bottom_space)
self.nmos_inst.place(self.nmos_pos)
# Output position will be in between the PMOS and NMOS drains
pmos_drain_pos = self.pmos_inst.get_pin("D").ll()
nmos_drain_pos = self.nmos_inst.get_pin("D").ul()
@ -210,10 +225,11 @@ class pinv(pgate.pgate):
# This will help with the wells
self.well_pos = vector(0, self.nmos_inst.uy())
def route_outputs(self):
""" Route the output (drains) together. Optionally, routes output to edge. """
"""
Route the output (drains) together.
Optionally, routes output to edge.
"""
# Get the drain pins
nmos_drain_pin = self.nmos_inst.get_pin("D")
@ -227,7 +243,7 @@ class pinv(pgate.pgate):
# Remember the mid for the output
mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y)
if self.route_output == True:
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",
@ -238,8 +254,8 @@ class pinv(pgate.pgate):
# This leaves the output as an internal pin (min sized)
self.add_layout_pin_rect_center(text="Z",
layer="metal1",
offset=mid_drain_offset + vector(0.5*self.m1_width,0))
offset=mid_drain_offset \
+ vector(0.5 * self.m1_width, 0))
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
@ -268,18 +284,23 @@ class pinv(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.5
return transition_prob * (c_load + c_para)
def input_load(self):
"""Return the capacitance of the gate connection in generic capacitive
units relative to the minimum width of a transistor"""
"""
Return the capacitance of the gate connection in generic capacitive
units relative to the minimum width of a transistor
"""
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall.
Input inverted by this stage.
"""
parasitic_delay = 1
return logical_effort.logical_effort(self.name,
@ -290,5 +311,8 @@ class pinv(pgate.pgate):
not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -7,12 +7,10 @@
#
import debug
import pgate
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from sram_factory import factory
class pinvbuf(pgate.pgate):
"""
This is a simple inverter/buffer used for driving loads. It is
@ -36,7 +34,6 @@ class pinvbuf(pgate.pgate):
# Creates the netlist and layout
pgate.pgate.__init__(self, name)
def create_netlist(self):
self.add_pins()
self.add_modules()
@ -53,7 +50,6 @@ class pinvbuf(pgate.pgate):
self.offset_all_coordinates()
def add_pins(self):
self.add_pin("A")
self.add_pin("Zb")
@ -65,13 +61,19 @@ class pinvbuf(pgate.pgate):
# Shield the cap, but have at least a stage effort of 4
input_size = max(1, int(self.predriver_size / self.stage_effort))
self.inv = factory.create(module_type="pinv", size=input_size, height=self.row_height)
self.inv = factory.create(module_type="pinv",
size=input_size,
height=self.row_height)
self.add_mod(self.inv)
self.inv1 = factory.create(module_type="pinv", size=self.predriver_size, height=self.row_height)
self.inv1 = factory.create(module_type="pinv",
size=self.predriver_size,
height=self.row_height)
self.add_mod(self.inv1)
self.inv2 = factory.create(module_type="pinv", size=self.size, height=self.row_height)
self.inv2 = factory.create(module_type="pinv",
size=self.size,
height=self.row_height)
self.add_mod(self.inv2)
def create_insts(self):
@ -80,7 +82,6 @@ class pinvbuf(pgate.pgate):
mod=self.inv)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv2_inst = self.add_inst(name="buf_inv2",
mod=self.inv1)
self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
@ -104,10 +105,10 @@ class pinvbuf(pgate.pgate):
self.inv3_inst.place(vector(self.inv2_inst.rx(), 0))
# Add INV4 flipped to the bottom aligned with INV2
self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),2*self.inv2.height),
self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),
2 * self.inv2.height),
mirror="MX")
def route_wires(self):
# inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z")
@ -125,12 +126,11 @@ class pinvbuf(pgate.pgate):
z1_pin = self.inv1_inst.get_pin("Z")
a4_pin = self.inv4_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a4_pin.cy())
self.add_wire(("metal1","via1","metal2"), [z1_pin.center(), mid_point, a4_pin.center()])
self.add_wire(("metal1", "via1", "metal2"),
[z1_pin.center(), mid_point, a4_pin.center()])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=z1_pin.center())
def add_layout_pins(self):
# Continous vdd rail along with label.
@ -171,7 +171,6 @@ class pinvbuf(pgate.pgate):
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=zb_pin.center())
a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_rect_center(text="A",
layer="metal2",
@ -194,6 +193,7 @@ class pinvbuf(pgate.pgate):
def determine_clk_buf_bar_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the clk -> clk_buf path"""
# After (almost) every stage, the direction of the signal inverts.
stage_effort_list = []
stage1_cout = self.inv1.get_cin() + self.inv2.get_cin()

View File

@ -10,10 +10,10 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory
class pnand2(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nand.
@ -22,7 +22,9 @@ class pnand2(pgate.pgate):
def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nand """
debug.info(2, "creating pnand2 structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pnand2 structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
self.size = size
@ -61,7 +63,6 @@ class pnand2(pgate.pgate):
dir_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list)
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx",
@ -90,19 +91,22 @@ class pnand2(pgate.pgate):
self.m3_space + contact.m2m3.second_layer_width)
# Compute the other pmos2 location, but determining offset to overlap the
# 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()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2 * self.pmos.active_width + contact.active.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active")
+ 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# This is the extra space needed to ensure DRC rules to the active contacts
# 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,
@ -141,7 +145,6 @@ class pnand2(pgate.pgate):
mod=self.nmos)
self.connect_inst(["net1", "A", "gnd", "gnd"])
def place_ptx(self):
"""
Place PMOS and NMOS to the layout at the upper-most and lowest position
@ -149,32 +152,36 @@ class pnand2(pgate.pgate):
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.height - self.pmos.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, self.top_bottom_space)
nmos1_pos = vector(self.pmos.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))
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies AFTER the wells are created """
"""
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)
self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
@ -186,13 +193,20 @@ class pnand2(pgate.pgate):
def route_inputs(self):
""" Route the A and B inputs """
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + 0.5*self.m2_width
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
+ self.m2_space + 0.5 * self.m2_width
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
"B",
position="center")
# 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")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A")
def route_output(self):
""" Route the Z output """
@ -204,7 +218,8 @@ class pnand2(pgate.pgate):
bottom_pin_offset = nmos_pin.center()
# Output pin
out_offset = vector(nmos_pin.center().x + self.m1_pitch,self.inputA_yoffset)
out_offset = vector(nmos_pin.center().x + self.m1_pitch,
self.inputA_yoffset)
# Midpoints of the L routes go horizontal first then vertical
mid1_offset = vector(out_offset.x, top_pin_offset.y)
@ -219,9 +234,10 @@ class pnand2(pgate.pgate):
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=out_offset)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset])
self.add_path("metal2",
[top_pin_offset, mid1_offset, out_offset,
mid2_offset, bottom_pin_offset])
# This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z",
@ -243,7 +259,8 @@ class pnand2(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1875
return transition_prob * (c_load + c_para)
@ -252,12 +269,22 @@ class pnand2(pgate.pgate):
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall.
Input inverted by this stage.
"""
parasitic_delay = 2
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise)
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -10,10 +10,10 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory
class pnand3(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nand.
@ -22,7 +22,9 @@ class pnand3(pgate.pgate):
def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 3 input nand """
debug.info(2, "creating pnand3 structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pnand3 structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
# We have trouble pitch matching a 3x sizes to the bitcell...
@ -40,7 +42,6 @@ class pnand3(pgate.pgate):
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def add_pins(self):
""" Adds pins for spice netlist """
pin_list = ["A", "B", "C", "Z", "vdd", "gnd"]
@ -99,12 +100,15 @@ class pnand3(pgate.pgate):
# This will help with the wells and the input/output placement
self.output_pos = vector(0, 0.5*self.height)
# This is the extra space needed to ensure DRC rules to the active contacts
# 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,
drc("poly_extend_active"), self.poly_space)
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space \
+ extra_contact_space,
drc("poly_extend_active"),
self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
@ -147,15 +151,15 @@ class pnand3(pgate.pgate):
mod=self.nmos)
self.connect_inst(["net2", "A", "gnd", "gnd"])
def place_ptx(self):
"""
Place the PMOS and NMOS in the layout at the upper-most and lowest position
to provide maximum routing in channel
Place the PMOS and NMOS in the layout at the upper-most
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)
self.height - self.pmos.active_height \
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
pmos2_pos = pmos1_pos + self.overlap_offset
@ -165,7 +169,8 @@ class pnand3(pgate.pgate):
self.pmos3_inst.place(self.pmos3_pos)
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
nmos1_pos = vector(self.pmos.active_offset.x,
self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos)
nmos2_pos = nmos1_pos + self.overlap_offset
@ -183,7 +188,6 @@ class pnand3(pgate.pgate):
self.add_nwell_contact(self.pmos, self.pmos3_pos)
self.add_pwell_contact(self.nmos, self.nmos3_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
@ -196,20 +200,32 @@ class pnand3(pgate.pgate):
def route_inputs(self):
""" Route the A and B inputs """
# wire space or wire and one contact space
metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width,
metal_spacing = max(self.m1_space + self.m1_width,
self.m2_space + self.m2_width,
self.m1_space + 0.5 *contact.poly.width + 0.5 * self.m1_width)
active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc("poly_to_active"))
active_spacing = max(self.m1_space,
0.5 * contact.poly.first_layer_width + drc("poly_to_active"))
inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing
self.route_input_gate(self.pmos3_inst, self.nmos3_inst, inputC_yoffset, "C", position="center")
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
inputC_yoffset,
"C",
position="center")
inputB_yoffset = inputC_yoffset + metal_spacing
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
"B",
position="center")
self.inputA_yoffset = inputB_yoffset + metal_spacing
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A", position="center")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A",
position="center")
def route_output(self):
""" Route the Z output """
@ -256,7 +272,8 @@ class pnand3(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1094
return transition_prob *(c_load + c_para)
@ -265,12 +282,22 @@ class pnand3(pgate.pgate):
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall.
Input inverted by this stage.
"""
parasitic_delay = 3
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise)
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -10,10 +10,9 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory
class pnor2(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nor.
@ -22,7 +21,9 @@ class pnor2(pgate.pgate):
def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nor """
debug.info(2, "creating pnor2 structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pnor2 structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
self.nmos_size = size
@ -38,7 +39,6 @@ class pnor2(pgate.pgate):
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def create_netlist(self):
self.add_pins()
self.add_ptx()
@ -89,23 +89,27 @@ class pnor2(pgate.pgate):
self.m2_space + contact.m2m3.first_layer_width,
self.m3_space + contact.m2m3.second_layer_width)
# Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins
# 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()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + self.pmos.active_contact.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active")
self.well_width = 2 * self.pmos.active_width \
+ self.pmos.active_contact.width \
+ 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# This is the extra space needed to ensure DRC rules to the active contacts
# 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,
drc("poly_extend_active"), self.poly_space)
drc("poly_extend_active"),
self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
@ -142,7 +146,6 @@ class pnor2(pgate.pgate):
mod=self.nmos)
self.connect_inst(["Z", "B", "gnd", "gnd"])
def place_ptx(self):
"""
Add PMOS and NMOS to the layout at the upper-most and lowest position
@ -150,7 +153,8 @@ class pnor2(pgate.pgate):
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.height - self.pmos.active_height \
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset
@ -163,7 +167,8 @@ class pnor2(pgate.pgate):
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))
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
@ -174,7 +179,6 @@ class pnor2(pgate.pgate):
self.add_nwell_contact(self.pmos, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
@ -184,16 +188,23 @@ class pnor2(pgate.pgate):
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
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_pos.y + self.nmos.active_height + self.m2_space + self.m2_width
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
+ self.m2_space + self.m2_width
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
"B",
position="center")
# 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")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A")
def route_output(self):
""" Route the Z output """
@ -210,14 +221,15 @@ class pnor2(pgate.pgate):
m1m2_contact = self.add_via_center(layers=("metal1", "via1", "metal2"),
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)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[pmos_pin.bc(), mid2_offset, mid3_offset])
self.add_path("metal2",[nmos_pin.rc(), mid1_offset, mid2_offset])
self.add_path("metal2",
[pmos_pin.bc(), mid2_offset, mid3_offset])
self.add_path("metal2",
[nmos_pin.rc(), mid1_offset, mid2_offset])
# This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=mid3_offset)
@ -240,7 +252,8 @@ class pnor2(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1875
return transition_prob * (c_load + c_para)

View File

@ -13,6 +13,7 @@ from vector import vector
from globals import OPTS
from sram_factory import factory
class precharge(design.design):
"""
Creates a single precharge cell
@ -30,7 +31,6 @@ class precharge(design.design):
self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br
# Creates the netlist and layout
# Since it has variable height, it is not a pgate.
self.create_netlist()
@ -53,7 +53,8 @@ class precharge(design.design):
self.connect_to_bitlines()
def add_pins(self):
self.add_pin_list(["bl", "br", "en_bar", "vdd"], ["OUTPUT", "OUTPUT", "INPUT", "POWER"])
self.add_pin_list(["bl", "br", "en_bar", "vdd"],
["OUTPUT", "OUTPUT", "INPUT", "POWER"])
def add_ptx(self):
"""
@ -64,9 +65,6 @@ class precharge(design.design):
tx_type="pmos")
self.add_mod(self.pmos)
def route_vdd_rail(self):
"""
Adds a vdd rail at the top of the cell
@ -87,7 +85,6 @@ class precharge(design.design):
# Add vdd pin above the transistor
self.add_power_pin("vdd", pmos_pin.center(), vertical=True)
def create_ptx(self):
"""
Create both the upper_pmos and lower_pmos to the module
@ -105,21 +102,21 @@ class precharge(design.design):
mod=self.pmos)
self.connect_inst(["br", "en_bar", "vdd", "vdd"])
def place_ptx(self):
"""
Place both the upper_pmos and lower_pmos to the module
"""
# Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins
# Compute the other pmos2 location,
# but determining offset to overlap the source and drain pins
overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# This is how much the contact is placed inside the ptx active
contact_xdiff = self.pmos.get_pin("S").lx()
# adds the lower pmos to layout
bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx()
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, self.well_enclose_active),
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff,
self.well_enclose_active),
self.pmos.active_offset.y)
self.lower_pmos_inst.place(self.lower_pmos_position)
@ -146,7 +143,9 @@ class precharge(design.design):
# connects the two poly for the two upper pmos(s)
offset = offset + vector(0, ylength - self.poly_width)
xlength = self.upper_pmos2_inst.get_pin("G").lx() - self.upper_pmos1_inst.get_pin("G").lx() + self.poly_width
xlength = self.upper_pmos2_inst.get_pin("G").lx() \
- self.upper_pmos1_inst.get_pin("G").lx() \
+ self.poly_width
self.add_rect(layer="poly",
offset=offset,
width=xlength,
@ -158,7 +157,8 @@ class precharge(design.design):
"""
# adds the en contact to connect the gates to the en rail on metal1
offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space)
offset = self.lower_pmos_inst.get_pin("G").ul() \
+ vector(0, 0.5 * self.poly_space)
self.add_via_center(layers=("poly", "contact", "metal1"),
offset=offset)
@ -168,7 +168,6 @@ class precharge(design.design):
start=offset.scale(0, 1),
end=offset.scale(0, 1) + vector(self.width, 0))
def place_nwell_and_contact(self):
"""
Adds a nwell tap to connect to the vdd rail
@ -176,7 +175,8 @@ class precharge(design.design):
# adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active"))
+ vector(0, self.upper_pmos1_inst.uy() + contact.well.height / 2 \
+ drc("well_extend_active"))
self.add_via_center(layers=("active", "contact", "metal1"),
offset=well_contact_pos,
implant_type="n",
@ -191,14 +191,14 @@ class precharge(design.design):
width=self.width,
height=self.height)
def route_bitlines(self):
"""
Adds both bit-line and bit-line-bar to the module
"""
# adds the BL on metal 2
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0)
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(), 0) \
- vector(0.5 * self.m2_width, 0)
self.bl_pin = self.add_layout_pin(text="bl",
layer="metal2",
offset=offset,
@ -206,7 +206,8 @@ class precharge(design.design):
height=self.height)
# adds the BR on metal 2
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0)
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(), 0) \
- vector(0.5 * self.m2_width, 0)
self.br_pin = self.add_layout_pin(text="br",
layer="metal2",
offset=offset,
@ -218,11 +219,14 @@ class precharge(design.design):
Connect the bitlines to the devices
"""
self.add_bitline_contacts()
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl"))
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl"))
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),self.get_pin("br"))
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br"))
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),
self.get_pin("bl"))
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),
self.get_pin("bl"))
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),
self.get_pin("br"))
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),
self.get_pin("br"))
def add_bitline_contacts(self):
"""
@ -271,7 +275,8 @@ class precharge(design.design):
def get_en_cin(self):
"""Get the relative capacitance of the enable in the precharge cell"""
#The enable connect to three pmos gates. They all use the same size pmos.
# The enable connect to three pmos gates
# They all use the same size pmos.
pmos_cin = self.pmos.get_cin()
return 3 * pmos_cin

View File

@ -10,16 +10,12 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from math import ceil
from globals import OPTS
from utils import round_to_grid
import logical_effort
from sram_factory import factory
class ptristate_inv(pgate.pgate):
"""
ptristate generates gds of a parametrically sized tristate inverter.
There is some flexibility in the size, but we do not allow multiple fingers
to fit in the cell height.
@ -27,7 +23,9 @@ class ptristate_inv(pgate.pgate):
def __init__(self, name, size=1, height=None):
debug.info(2, "creating ptristate inv {0} with size of {1}".format(name, size))
debug.info(2,
"creating ptristate inv {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
# We are 2x since there are two series devices
@ -112,34 +110,35 @@ class ptristate_inv(pgate.pgate):
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
Create the PMOS and NMOS netlist.
"""
# These are the inverter PMOS/NMOS
self.pmos1_inst=self.add_inst(name="ptri_pmos1", mod=self.pmos)
self.pmos1_inst = self.add_inst(name="ptri_pmos1",
mod=self.pmos)
self.connect_inst(["vdd", "in", "n1", "vdd"])
self.nmos1_inst=self.add_inst(name="ptri_nmos1", mod=self.nmos)
self.nmos1_inst = self.add_inst(name="ptri_nmos1",
mod=self.nmos)
self.connect_inst(["gnd", "in", "n2", "gnd"])
# These are the tristate PMOS/NMOS
self.pmos2_inst = self.add_inst(name="ptri_pmos2", mod=self.pmos)
self.connect_inst(["out", "en_bar", "n1", "vdd"])
self.nmos2_inst=self.add_inst(name="ptri_nmos2", mod=self.nmos)
self.nmos2_inst = self.add_inst(name="ptri_nmos2",
mod=self.nmos)
self.connect_inst(["out", "en", "n2", "gnd"])
def place_ptx(self):
"""
Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos_yoff = self.height - self.pmos.active_height - self.top_bottom_space - 0.5*contact.well.height
pmos_yoff = self.height - self.pmos.active_height \
- self.top_bottom_space - 0.5 * contact.well.height
nmos_yoff = self.top_bottom_space + 0.5 * contact.well.height
# Tristate transistors
@ -155,20 +154,23 @@ class ptristate_inv(pgate.pgate):
self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0, 0.5*(pmos_yoff + nmos_yoff + self.nmos.height))
self.output_pos = vector(0,
0.5 * (pmos_yoff + nmos_yoff + self.nmos.height))
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
def route_inputs(self):
""" Route the gates """
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.output_pos.y, "in", position="farleft")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.output_pos.y,
"in",
position="farleft")
self.route_single_gate(self.pmos2_inst, "en_bar", position="left")
self.route_single_gate(self.nmos2_inst, "en", position="left")
def route_outputs(self):
""" Route the output (drains) together. """
@ -183,9 +185,11 @@ class ptristate_inv(pgate.pgate):
offset=nmos_drain_pos,
height=pmos_drain_pos.y - nmos_drain_pos.y)
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies AFTER the wells are created """
"""
Add n/p well taps to the layout and connect to
supplies AFTER the wells are created
"""
layer_stack = ("active", "contact", "metal1")
@ -202,8 +206,6 @@ class ptristate_inv(pgate.pgate):
implant_type="p",
well_type="p")
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
@ -212,7 +214,8 @@ class ptristate_inv(pgate.pgate):
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
# Power in this module currently not defined.
# Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
return total_power

View File

@ -9,9 +9,9 @@ import design
import debug
from tech import drc, spice
from vector import vector
from globals import OPTS
from sram_factory import factory
class ptx(design.design):
"""
This module generates gds and spice of a parametrically NMOS or
@ -21,7 +21,14 @@ class ptx(design.design):
you to connect the fingered gates and active for parallel devices.
"""
def __init__(self, name="", width=drc("minwidth_tx"), mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None):
def __init__(self,
name="",
width=drc("minwidth_tx"),
mults=1,
tx_type="nmos",
connect_active=False,
connect_poly=False,
num_contacts=None):
# 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
@ -52,8 +59,6 @@ class ptx(design.design):
# some transistor sizes in other netlist depend on pbitcell
self.create_layout()
def create_layout(self):
"""Calls all functions related to the generation of the layout"""
self.setup_layout_constants()
@ -71,22 +76,25 @@ class ptx(design.design):
pin_list = ["D", "G", "S", "B"]
if self.tx_type == "nmos":
body_dir = 'GROUND'
else: #Assumed that the check for either pmos or nmos is done elsewhere.
else:
# Assumed that the check for either pmos or nmos is done elsewhere.
body_dir = 'POWER'
dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir]
self.add_pin_list(pin_list, dir_list)
# self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
# " ".join(self.pins)))
# Just make a guess since these will actually be decided in the layout later.
# Just make a guess since these will actually
# be decided in the layout later.
area_sd = 2.5 * drc("minwidth_poly") * self.tx_width
perimeter_sd = 2 * drc("minwidth_poly") + 2 * self.tx_width
self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4:.2f}u ps={4:.2f}u as={5:.2f}p ad={5:.2f}p".format(spice[self.tx_type],
main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"),
perimeter_sd,
drc("minwidth_poly"))
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd,
area_sd)
self.spice_device= main_str + area_str
self.spice.append("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name))
@ -125,6 +133,7 @@ class ptx(design.design):
# The enclosure of an active contact. Not sure about second term.
active_enclose_contact = max(drc("active_enclosure_contact"),
(self.active_width - self.contact_width) / 2)
# This is the distance from the edge of poly to the contacted end of active
self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate
@ -165,9 +174,12 @@ class ptx(design.design):
# Min area results are just flagged for now.
debug.check(self.active_width*self.active_height>=drc("minarea_active"),"Minimum active area violated.")
# We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue.
debug.check(self.poly_width*self.poly_height>=drc("minarea_poly"),"Minimum poly area violated.")
debug.check(self.active_width * self.active_height >= drc("minarea_active"),
"Minimum active area violated.")
# We do not want to increase the poly dimensions to fix
# an area problem as it would cause an LVS issue.
debug.check(self.poly_width * self.poly_height >= drc("minarea_poly"),
"Minimum poly area violated.")
def connect_fingered_poly(self, poly_positions):
"""
@ -182,13 +194,19 @@ 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
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)
# 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)
# 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",
@ -210,7 +228,8 @@ class ptx(design.design):
# This is the width of a m1 extend the ends of the pin
end_offset = vector(self.m1_width/2,0)
# drains always go to the MIDDLE of the cell, so top of NMOS, bottom of PMOS
# 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
@ -224,7 +243,9 @@ class ptx(design.design):
self.remove_layout_pin("S") # remove the individual connections
# Add each vertical segment
for a in source_positions:
self.add_path(("metal1"), [a,a+pin_offset.scale(source_dir,source_dir)])
self.add_path(("metal1"),
[a, a + pin_offset.scale(source_dir,
source_dir)])
# Add a single horizontal pin
self.add_layout_pin_segment_center(text="S",
layer="metal1",
@ -248,16 +269,19 @@ class ptx(design.design):
Add the poly gates(s) and (optionally) connect them.
"""
# poly is one contacted spacing from the end and down an extension
poly_offset = self.active_offset + vector(self.poly_width,self.poly_height).scale(0.5,0.5) \
poly_offset = self.active_offset \
+ vector(self.poly_width, self.poly_height).scale(0.5, 0.5) \
+ vector(self.end_to_poly, -self.poly_extend_active)
# poly_positions are the bottom center of the poly gates
poly_positions = []
# It is important that these are from left to right, so that the pins are in the right
# It is important that these are from left to right,
# so that the pins are in the right
# order for the accessors
for i in range(0, self.mults):
# Add this duplicate rectangle in case we remove the pin when joining fingers
# Add this duplicate rectangle in case we remove
# the pin when joining fingers
self.add_rect_center(layer="poly",
offset=poly_offset,
height=self.poly_height,
@ -320,7 +344,8 @@ class ptx(design.design):
# The first one will always be a source
source_positions = [self.contact_offset]
drain_positions = []
# It is important that these are from left to right, so that the pins are in the right
# It is important that these are from left to right,
# so that the pins are in the right
# order for the accessors.
for i in range(self.mults):
if i%2:
@ -374,6 +399,9 @@ class ptx(design.design):
return self.tx_width / drc("minwidth_tx")
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -9,17 +9,17 @@ import pgate
import debug
from tech import drc
from vector import vector
import contact
from globals import OPTS
from sram_factory import factory
import logical_effort
class single_level_column_mux(pgate.pgate):
"""
This module implements the columnmux bitline cell used in the design.
Creates a single columnmux cell with the given integer size relative
to minimum size. Default is 8x. Per Samira and Hodges-Jackson book:
Column-mux transistors driven by the decoder must be sized for optimal speed
Column-mux transistors driven by the decoder must be sized
for optimal speed
"""
def __init__(self, name, tx_size=8, bitcell_bl="bl", bitcell_br="br"):
@ -54,8 +54,6 @@ class single_level_column_mux(pgate.pgate):
self.nmos = factory.create(module_type="ptx", width=self.ptx_width)
self.add_mod(self.nmos)
def add_pins(self):
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
@ -85,25 +83,25 @@ class single_level_column_mux(pgate.pgate):
offset=br_pos,
height=self.pin_height)
def add_ptx(self):
""" 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) + vector(0.5*self.bitcell.width-0.5*self.nmos.active_width,0)
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,
offset=nmos_lower_position)
self.connect_inst(["bl", "sel", "bl_out", "gnd"])
# This aligns it directly above the other tx with gates abutting
nmos_upper_position = nmos_lower_position + vector(0,self.nmos.active_height + self.poly_space)
nmos_upper_position = nmos_lower_position \
+ vector(0, self.nmos.active_height + self.poly_space)
self.nmos_upper = self.add_inst(name="mux_tx2",
mod=self.nmos,
offset=nmos_upper_position)
self.connect_inst(["br", "sel", "br_out", "gnd"])
def connect_poly(self):
""" Connect the poly gate of the two pass transistors """
@ -113,7 +111,6 @@ class single_level_column_mux(pgate.pgate):
offset=self.nmos_lower.get_pin("G").ll(),
height=height)
def connect_bitlines(self):
""" Connect the bitlines to the mux transistors """
# These are on metal2
@ -142,23 +139,32 @@ class single_level_column_mux(pgate.pgate):
offset=nmos_lower_d_pin.center(),
directions=("V", "V"))
# bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2
self.add_path("metal1",[bl_pin.ll(), vector(nmos_upper_d_pin.cx(),bl_pin.by()), nmos_upper_d_pin.center()])
self.add_path("metal1",
[bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()),
nmos_upper_d_pin.center()])
# halfway up, move over
mid1 = bl_out_pin.uc().scale(1,0.4)+nmos_upper_s_pin.bc().scale(0,0.4)
mid2 = bl_out_pin.uc().scale(0,0.4)+nmos_upper_s_pin.bc().scale(1,0.4)
self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()])
mid1 = bl_out_pin.uc().scale(1, 0.4) \
+ nmos_upper_s_pin.bc().scale(0, 0.4)
mid2 = bl_out_pin.uc().scale(0, 0.4) \
+ nmos_upper_s_pin.bc().scale(1, 0.4)
self.add_path("metal2",
[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()])
# br -> nmos_lower/D on metal2
# br_out -> nmos_lower/S on metal1
self.add_path("metal1",[br_out_pin.uc(), vector(nmos_lower_s_pin.cx(),br_out_pin.uy()), nmos_lower_s_pin.center()])
self.add_path("metal1",
[br_out_pin.uc(),
vector(nmos_lower_s_pin.cx(), br_out_pin.uy()),
nmos_lower_s_pin.center()])
# halfway up, move over
mid1 = br_pin.bc().scale(1,0.5)+nmos_lower_d_pin.uc().scale(0,0.5)
mid2 = br_pin.bc().scale(0,0.5)+nmos_lower_d_pin.uc().scale(1,0.5)
self.add_path("metal2",[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()])
mid1 = br_pin.bc().scale(1,0.5) \
+ nmos_lower_d_pin.uc().scale(0,0.5)
mid2 = br_pin.bc().scale(0,0.5) \
+ nmos_lower_d_pin.uc().scale(1,0.5)
self.add_path("metal2",
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()])
def add_wells(self):
"""
@ -167,13 +173,13 @@ class single_level_column_mux(pgate.pgate):
"""
# Add it to the right, aligned in between the two tx
active_pos = vector(self.bitcell.width,self.nmos_upper.by() - 0.5*self.poly_space)
active_via = self.add_via_center(layers=("active", "contact", "metal1"),
active_pos = vector(self.bitcell.width,
self.nmos_upper.by() - 0.5 * self.poly_space)
self.add_via_center(layers=("active", "contact", "metal1"),
offset=active_pos,
implant_type="p",
well_type="p")
# Add the M1->M2->M3 stack
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=active_pos)
@ -190,8 +196,17 @@ class single_level_column_mux(pgate.pgate):
height=self.height)
def get_stage_effort(self, corner, slew, load):
"""Returns relative delay that the column mux. Difficult to convert to LE model."""
"""
Returns relative delay that the column mux.
Difficult to convert to LE model.
"""
parasitic_delay = 1
cin = 2*self.tx_size #This is not CMOS, so using this may be incorrect.
return logical_effort.logical_effort('column_mux', self.tx_size, cin, load, parasitic_delay, False)
# This is not CMOS, so using this may be incorrect.
cin = 2 * self.tx_size
return logical_effort.logical_effort("column_mux",
self.tx_size,
cin,
load,
parasitic_delay,
False)