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

@ -3,11 +3,11 @@ num_words = 128
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [ 5.0 ] supply_voltages = [5.0]
temperatures = [ 25 ] temperatures = [25]
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -14,7 +14,9 @@ route_supplies = True
check_lvsdrc = True check_lvsdrc = True
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -14,7 +14,9 @@ route_supplies = True
check_lvsdrc = True check_lvsdrc = True
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -10,5 +10,7 @@ route_supplies = True
check_lvsdrc = True check_lvsdrc = True
output_path = "temp" 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 check_lvsdrc = True
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -7,7 +7,9 @@ supply_voltages = [ 5.0 ]
temperatures = [ 25 ] temperatures = [ 25 ]
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -3,11 +3,13 @@ num_words = 256
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [ 3.3 ] supply_voltages = [3.3]
temperatures = [ 25 ] temperatures = [25]
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -6,16 +6,14 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
from tech import drc
from math import log
from vector import vector from vector import vector
from globals import OPTS
import pgate import pgate
from sram_factory import factory from sram_factory import factory
class pand2(pgate.pgate): class pand2(pgate.pgate):
""" """
This is a simple buffer used for driving loads. 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):
debug.info(1, "Creating pnand2 {}".format(name)) debug.info(1, "Creating pnand2 {}".format(name))
@ -23,7 +21,7 @@ class pand2(pgate.pgate):
self.size = size self.size = size
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
def create_netlist(self): def create_netlist(self):
@ -33,10 +31,13 @@ class pand2(pgate.pgate):
def create_modules(self): def create_modules(self):
# Shield the cap, but have at least a stage effort of 4 # Shield the cap, but have at least a stage effort of 4
self.nand = factory.create(module_type="pnand2",height=self.height) self.nand = factory.create(module_type="pnand2", height=self.height)
self.add_mod(self.nand) 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) self.add_mod(self.inv)
def create_layout(self): def create_layout(self):
@ -54,44 +55,44 @@ class pand2(pgate.pgate):
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def create_insts(self): def create_insts(self):
self.nand_inst=self.add_inst(name="pand2_nand", self.nand_inst = self.add_inst(name="pand2_nand",
mod=self.nand) mod=self.nand)
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
self.inv_inst=self.add_inst(name="pand2_inv", self.inv_inst = self.add_inst(name="pand2_inv",
mod=self.inv) mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self): def place_insts(self):
# Add NAND to the right # Add NAND to the right
self.nand_inst.place(offset=vector(0,0)) self.nand_inst.place(offset=vector(0, 0))
# Add INV to the right # Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(),0)) self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
def add_wires(self): def add_wires(self):
# nand Z to inv A # nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z") z1_pin = self.nand_inst.get_pin("Z")
a2_pin = self.inv_inst.get_pin("A") a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_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): def add_layout_pins(self):
# Continous vdd rail along with label. # Continous vdd rail along with label.
vdd_pin=self.inv_inst.get_pin("vdd") vdd_pin = self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=vdd_pin.ll().scale(0,1), offset=vdd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
# Continous gnd rail along with label. # Continous gnd rail along with label.
gnd_pin=self.inv_inst.get_pin("gnd") gnd_pin = self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=gnd_pin.ll().scale(0,1), offset=gnd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
@ -102,7 +103,7 @@ class pand2(pgate.pgate):
width=pin.width(), width=pin.width(),
height=pin.height()) height=pin.height())
for pin_name in ["A","B"]: for pin_name in ["A", "B"]:
pin = self.nand_inst.get_pin(pin_name) pin = self.nand_inst.get_pin(pin_name)
self.add_layout_pin_rect_center(text=pin_name, self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer, layer=pin.layer,

View File

@ -6,16 +6,14 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
from tech import drc
from math import log
from vector import vector from vector import vector
from globals import OPTS
import pgate import pgate
from sram_factory import factory from sram_factory import factory
class pand3(pgate.pgate): class pand3(pgate.pgate):
""" """
This is a simple buffer used for driving loads. 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):
debug.info(1, "Creating pand3 {}".format(name)) debug.info(1, "Creating pand3 {}".format(name))
@ -23,7 +21,7 @@ class pand3(pgate.pgate):
self.size = size self.size = size
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
def create_netlist(self): def create_netlist(self):
@ -33,10 +31,12 @@ class pand3(pgate.pgate):
def create_modules(self): def create_modules(self):
# Shield the cap, but have at least a stage effort of 4 # Shield the cap, but have at least a stage effort of 4
self.nand = factory.create(module_type="pnand3",height=self.height) self.nand = factory.create(module_type="pnand3", height=self.height)
self.add_mod(self.nand) 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) self.add_mod(self.inv)
def create_layout(self): def create_layout(self):
@ -55,44 +55,44 @@ class pand3(pgate.pgate):
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def create_insts(self): def create_insts(self):
self.nand_inst=self.add_inst(name="pand3_nand", self.nand_inst = self.add_inst(name="pand3_nand",
mod=self.nand) mod=self.nand)
self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"]) self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"])
self.inv_inst=self.add_inst(name="pand3_inv", self.inv_inst = self.add_inst(name="pand3_inv",
mod=self.inv) mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self): def place_insts(self):
# Add NAND to the right # Add NAND to the right
self.nand_inst.place(offset=vector(0,0)) self.nand_inst.place(offset=vector(0, 0))
# Add INV to the right # Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(),0)) self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
def add_wires(self): def add_wires(self):
# nand Z to inv A # nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z") z1_pin = self.nand_inst.get_pin("Z")
a2_pin = self.inv_inst.get_pin("A") a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_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): def add_layout_pins(self):
# Continous vdd rail along with label. # Continous vdd rail along with label.
vdd_pin=self.inv_inst.get_pin("vdd") vdd_pin = self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=vdd_pin.ll().scale(0,1), offset=vdd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
# Continous gnd rail along with label. # Continous gnd rail along with label.
gnd_pin=self.inv_inst.get_pin("gnd") gnd_pin = self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=gnd_pin.ll().scale(0,1), offset=gnd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
@ -103,20 +103,22 @@ class pand3(pgate.pgate):
width=pin.width(), width=pin.width(),
height=pin.height()) height=pin.height())
for pin_name in ["A","B", "C"]: for pin_name in ["A", "B", "C"]:
pin = self.nand_inst.get_pin(pin_name) pin = self.nand_inst.get_pin(pin_name)
self.add_layout_pin_rect_center(text=pin_name, self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer, layer=pin.layer,
offset=pin.center(), offset=pin.center(),
width=pin.width(), width=pin.width(),
height=pin.height()) height=pin.height())
def analytical_delay(self, corner, slew, load=0.0): def analytical_delay(self, corner, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """ """ Calculate the analytical delay of DFF-> INV -> INV """
nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load()) nand_delay = self.nand.analytical_delay(corner,
inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load) 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 return nand_delay + inv_delay
def get_stage_efforts(self, external_cout, inp_is_rise=False): def get_stage_efforts(self, external_cout, inp_is_rise=False):

View File

@ -6,20 +6,18 @@
# All rights reserved. # All rights reserved.
# #
import debug import debug
from tech import drc
from math import log
from vector import vector from vector import vector
from globals import OPTS
import pgate import pgate
from sram_factory import factory from sram_factory import factory
class pbuf(pgate.pgate): class pbuf(pgate.pgate):
""" """
This is a simple buffer used for driving loads. This is a simple buffer used for driving loads.
""" """
def __init__(self, name, size=4, height=None): def __init__(self, name, size=4, height=None):
debug.info(1, "creating {0} with size of {1}".format(name,size)) debug.info(1, "creating {0} with size of {1}".format(name, size))
self.add_comment("size: {}".format(size)) self.add_comment("size: {}".format(size))
self.stage_effort = 4 self.stage_effort = 4
@ -29,7 +27,6 @@ class pbuf(pgate.pgate):
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.create_modules() self.create_modules()
@ -49,53 +46,54 @@ class pbuf(pgate.pgate):
def create_modules(self): def create_modules(self):
# Shield the cap, but have at least a stage effort of 4 # Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(self.size/self.stage_effort)) 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.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) self.add_mod(self.inv2)
def create_insts(self): def create_insts(self):
self.inv1_inst=self.add_inst(name="buf_inv1", self.inv1_inst = self.add_inst(name="buf_inv1",
mod=self.inv1) mod=self.inv1)
self.connect_inst(["A", "zb_int", "vdd", "gnd"]) self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv2_inst = self.add_inst(name="buf_inv2",
self.inv2_inst=self.add_inst(name="buf_inv2", mod=self.inv2)
mod=self.inv2) self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self): def place_insts(self):
# Add INV1 to the right # Add INV1 to the right
self.inv1_inst.place(vector(0,0)) self.inv1_inst.place(vector(0, 0))
# Add INV2 to the right # Add INV2 to the right
self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
def add_wires(self): def add_wires(self):
# inv1 Z to inv2 A # inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z") z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A") a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy()) mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
def add_layout_pins(self): def add_layout_pins(self):
# Continous vdd rail along with label. # Continous vdd rail along with label.
vdd_pin=self.inv1_inst.get_pin("vdd") vdd_pin = self.inv1_inst.get_pin("vdd")
self.add_layout_pin(text="vdd", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=vdd_pin.ll().scale(0,1), offset=vdd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
# Continous gnd rail along with label. # Continous gnd rail along with label.
gnd_pin=self.inv1_inst.get_pin("gnd") gnd_pin = self.inv1_inst.get_pin("gnd")
self.add_layout_pin(text="gnd", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=gnd_pin.ll().scale(0,1), offset=gnd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())

View File

@ -7,28 +7,27 @@
# #
import debug import debug
import pgate import pgate
import math
from tech import drc
from math import log
from vector import vector from vector import vector
from globals import OPTS
from sram_factory import factory from sram_factory import factory
class pdriver(pgate.pgate): 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): def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None):
debug.info(1, "creating pdriver {}".format(name)) debug.info(1, "creating pdriver {}".format(name))
self.stage_effort = 3 self.stage_effort = 3
self.height = height self.height = height
self.neg_polarity = neg_polarity self.neg_polarity = neg_polarity
self.size_list = size_list self.size_list = size_list
self.fanout = fanout 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) debug.error("Either fanout or size list must be specified.", -1)
if self.size_list and self.fanout != 0: if self.size_list and self.fanout != 0:
debug.error("Cannot specify both size_list and fanout.", -1) debug.error("Cannot specify both size_list and fanout.", -1)
@ -36,34 +35,33 @@ class pdriver(pgate.pgate):
debug.error("Cannot specify both size_list and neg_polarity.", -1) debug.error("Cannot specify both size_list and neg_polarity.", -1)
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
def compute_sizes(self): def compute_sizes(self):
# size_list specified # size_list specified
if self.size_list: if self.size_list:
self.num_stages = len(self.size_list) self.num_stages = len(self.size_list)
else: else:
# Find the optimal number of stages for the given effort # 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 # Increase the number of stages if we need to fix polarity
if self.neg_polarity and (self.num_stages%2==0): if self.neg_polarity and (self.num_stages % 2 == 0):
self.num_stages += 1 self.num_stages += 1
elif not self.neg_polarity and (self.num_stages%2): elif not self.neg_polarity and (self.num_stages % 2):
self.num_stages += 1 self.num_stages += 1
self.size_list = [] self.size_list = []
# compute sizes backwards from the fanout # compute sizes backwards from the fanout
fanout_prev = self.fanout fanout_prev = self.fanout
for x in range(self.num_stages): for x in range(self.num_stages):
fanout_prev = max(round(fanout_prev/self.stage_effort),1) fanout_prev = max(round(fanout_prev / self.stage_effort), 1)
self.size_list.append(fanout_prev) self.size_list.append(fanout_prev)
# reverse the sizes to be from input to output # reverse the sizes to be from input to output
self.size_list.reverse() self.size_list.reverse()
def create_netlist(self): def create_netlist(self):
self.compute_sizes() self.compute_sizes()
self.add_comment("sizes: {}".format(str(self.size_list))) self.add_comment("sizes: {}".format(str(self.size_list)))
@ -79,29 +77,30 @@ class pdriver(pgate.pgate):
self.width = self.inv_inst_list[-1].rx() self.width = self.inv_inst_list[-1].rx()
self.height = self.inv_inst_list[0].height self.height = self.inv_inst_list[0].height
def add_pins(self): def add_pins(self):
self.add_pin("A", "INPUT") self.add_pin("A", "INPUT")
self.add_pin("Z", "OUTPUT") self.add_pin("Z", "OUTPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_modules(self): def add_modules(self):
self.inv_list = [] self.inv_list = []
for size in self.size_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.inv_list.append(temp_inv)
self.add_mod(temp_inv) self.add_mod(temp_inv)
def create_insts(self): def create_insts(self):
self.inv_inst_list = [] self.inv_inst_list = []
for x in range(1,self.num_stages+1): for x in range(1, self.num_stages + 1):
# Create first inverter # Create first inverter
if x == 1: if x == 1:
zbx_int = "Zb{}_int".format(x); zbx_int = "Zb{}_int".format(x)
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), inst = self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x-1])) mod=self.inv_list[x - 1])
self.inv_inst_list.append(inst)
if self.num_stages == 1: if self.num_stages == 1:
self.connect_inst(["A", "Z", "vdd", "gnd"]) self.connect_inst(["A", "Z", "vdd", "gnd"])
else: else:
@ -109,70 +108,72 @@ class pdriver(pgate.pgate):
# Create last inverter # Create last inverter
elif x == self.num_stages: elif x == self.num_stages:
zbn_int = "Zb{}_int".format(x-1); zbn_int = "Zb{}_int".format(x - 1)
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), inst = self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x-1])) mod=self.inv_list[x - 1])
self.inv_inst_list.append(inst)
self.connect_inst([zbn_int, "Z", "vdd", "gnd"]) self.connect_inst([zbn_int, "Z", "vdd", "gnd"])
# Create middle inverters # Create middle inverters
else: else:
zbx_int = "Zb{}_int".format(x-1); zbx_int = "Zb{}_int".format(x - 1)
zbn_int = "Zb{}_int".format(x); zbn_int = "Zb{}_int".format(x)
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x), inst = self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x-1])) mod=self.inv_list[x - 1])
self.inv_inst_list.append(inst)
self.connect_inst([zbx_int, zbn_int, "vdd", "gnd"]) self.connect_inst([zbx_int, zbn_int, "vdd", "gnd"])
def place_modules(self): def place_modules(self):
# Add the first inverter at the origin # Add the first inverter at the origin
self.inv_inst_list[0].place(vector(0,0)) self.inv_inst_list[0].place(vector(0, 0))
# Add inverters to the right of the previous inverter # Add inverters to the right of the previous inverter
for x in range(1,len(self.inv_inst_list)): 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): def route_wires(self):
z_inst_list = [] z_inst_list = []
a_inst_list = [] a_inst_list = []
# inv_current Z to inv_next A # inv_current Z to inv_next A
for x in range(0,len(self.inv_inst_list)-1): for x in range(0, len(self.inv_inst_list) - 1):
z_inst_list.append(self.inv_inst_list[x].get_pin("Z")) 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")) 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()) 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): def add_layout_pins(self):
# Continous vdd rail along with label. # Continous vdd rail along with label.
vdd_pin=self.inv_inst_list[0].get_pin("vdd") vdd_pin = self.inv_inst_list[0].get_pin("vdd")
self.add_layout_pin(text="vdd", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=vdd_pin.ll().scale(0,1), offset=vdd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
# Continous gnd rail along with label. # Continous gnd rail along with label.
gnd_pin=self.inv_inst_list[0].get_pin("gnd") gnd_pin = self.inv_inst_list[0].get_pin("gnd")
self.add_layout_pin(text="gnd", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=gnd_pin.ll().scale(0,1), offset=gnd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
z_pin = self.inv_inst_list[len(self.inv_inst_list)-1].get_pin("Z") z_pin = self.inv_inst_list[len(self.inv_inst_list) - 1].get_pin("Z")
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer=z_pin.layer, layer=z_pin.layer,
offset=z_pin.center(), offset=z_pin.center(),
width = z_pin.width(), width=z_pin.width(),
height = z_pin.height()) height=z_pin.height())
a_pin = self.inv_inst_list[0].get_pin("A") a_pin = self.inv_inst_list[0].get_pin("A")
self.add_layout_pin_rect_center(text="A", self.add_layout_pin_rect_center(text="A",
layer=a_pin.layer, layer=a_pin.layer,
offset=a_pin.center(), offset=a_pin.center(),
width = a_pin.width(), width=a_pin.width(),
height = a_pin.height()) height=a_pin.height())
def get_sizes(self): def get_sizes(self):
""" Return the relative sizes of the buffers """ """ Return the relative sizes of the buffers """
@ -181,14 +182,14 @@ class pdriver(pgate.pgate):
def get_stage_efforts(self, external_cout, inp_is_rise=False): def get_stage_efforts(self, external_cout, inp_is_rise=False):
""" Get the stage efforts of the A -> Z path """ """ Get the stage efforts of the A -> Z path """
cout_list = [] cout_list = []
for prev_inv,inv in zip(self.inv_list, self.inv_list[1:]): for prev_inv, inv in zip(self.inv_list, self.inv_list[1:]):
cout_list.append(inv.get_cin()) cout_list.append(inv.get_cin())
cout_list.append(external_cout) cout_list.append(external_cout)
stage_effort_list = [] stage_effort_list = []
last_inp_is_rise = inp_is_rise last_inp_is_rise = inp_is_rise
for inv,cout in zip(self.inv_list,cout_list): for inv, cout in zip(self.inv_list, cout_list):
stage = inv.get_stage_effort(cout, last_inp_is_rise) stage = inv.get_stage_effort(cout, last_inp_is_rise)
stage_effort_list.append(stage) stage_effort_list.append(stage)
last_inp_is_rise = stage.is_rise last_inp_is_rise = stage.is_rise

View File

@ -8,14 +8,16 @@
import contact import contact
import design import design
import debug import debug
from tech import drc, parameter, spice from tech import drc
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
class pgate(design.design): 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): def __init__(self, name, height=None):
@ -29,78 +31,85 @@ class pgate(design.design):
self.height = b.height self.height = b.height
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def create_netlist(self): def create_netlist(self):
""" Pure virtual function """ """ Pure virtual function """
debug.error("Must over-ride create_netlist.",-1) debug.error("Must over-ride create_netlist.", -1)
def create_layout(self): def create_layout(self):
""" Pure virtual function """ """ Pure virtual function """
debug.error("Must over-ride create_layout.",-1) debug.error("Must over-ride create_layout.", -1)
def connect_pin_to_rail(self,inst,pin,supply): def connect_pin_to_rail(self, inst, pin, supply):
""" Connects a ptx pin to a supply rail. """ """ Connects a ptx pin to a supply rail. """
source_pin = inst.get_pin(pin) source_pin = inst.get_pin(pin)
supply_pin = self.get_pin(supply) supply_pin = self.get_pin(supply)
if supply_pin.overlaps(source_pin): if supply_pin.overlaps(source_pin):
return return
if supply=="gnd": if supply == "gnd":
height=supply_pin.by()-source_pin.by() height = supply_pin.by() - source_pin.by()
elif supply=="vdd": elif supply == "vdd":
height=supply_pin.uy()-source_pin.by() height = supply_pin.uy() - source_pin.by()
else: else:
debug.error("Invalid supply name.",-1) debug.error("Invalid supply name.", -1)
if abs(height)>0: if abs(height) > 0:
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=source_pin.ll(), offset=source_pin.ll(),
height=height, height=height,
width=source_pin.width()) width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=False): 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") nmos_gate_pin = nmos_inst.get_pin("G")
pmos_gate_pin = pmos_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! # 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 # 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) nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5 * self.poly_width, 0)
pmos_gate_pos = vector(nmos_gate_pos.x,pmos_gate_pin.bc().y) pmos_gate_pos = vector(nmos_gate_pos.x, pmos_gate_pin.bc().y)
self.add_path("poly",[nmos_gate_pos,pmos_gate_pos]) self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
# Add the via to the cell midpoint along the gate # Add the via to the cell midpoint along the gate
left_gate_offset = vector(nmos_gate_pin.lx(),ypos) left_gate_offset = vector(nmos_gate_pin.lx(), ypos)
# Center is completely symmetric. # Center is completely symmetric.
if rotate: if rotate:
contact_width = contact.poly.height contact_width = contact.poly.height
contact_m1_width = contact.poly.second_layer_height contact_m1_width = contact.poly.second_layer_height
contact_m1_height = contact.poly.second_layer_width contact_m1_height = contact.poly.second_layer_width
directions = ("H","V") directions = ("H", "V")
else: else:
contact_width = contact.poly.width contact_width = contact.poly.width
contact_m1_width = contact.poly.second_layer_width contact_m1_width = contact.poly.second_layer_width
contact_m1_height = contact.poly.second_layer_height contact_m1_height = contact.poly.second_layer_height
directions = ("V","H") directions = ("V", "H")
if position=="center": if position == "center":
contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0) contact_offset = left_gate_offset \
elif position=="farleft": + vector(0.5 * self.poly_width, 0)
contact_offset = left_gate_offset - vector(0.5*contact.poly.width, 0) elif position == "farleft":
elif position=="left": contact_offset = left_gate_offset \
contact_offset = left_gate_offset - vector(0.5*contact_width - 0.5*self.poly_width, 0) - vector(0.5 * contact.poly.width, 0)
elif position=="right": 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)
else: else:
debug.error("Invalid contact placement option.", -1) debug.error("Invalid contact placement option.", -1)
@ -110,29 +119,26 @@ class pgate(design.design):
offset=contact_offset, offset=contact_offset,
directions=directions) 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, self.add_layout_pin_rect_center(text=name,
layer="metal1", layer="metal1",
offset=contact_offset, offset=contact_offset,
width=contact_m1_width, width=contact_m1_width,
height=contact_m1_height) height=contact_m1_height)
# This is to ensure that the contact is
# This is to ensure that the contact is connected to the gate # connected to the gate
mid_point = contact_offset.scale(0.5,1)+left_gate_offset.scale(0.5,0) mid_point = contact_offset.scale(0.5, 1) \
+ left_gate_offset.scale(0.5, 0)
self.add_rect_center(layer="poly", self.add_rect_center(layer="poly",
offset=mid_point, offset=mid_point,
height=contact.poly.first_layer_width, height=contact.poly.first_layer_width,
width=left_gate_offset.x-contact_offset.x) width=left_gate_offset.x - contact_offset.x)
def extend_wells(self, middle_position): def extend_wells(self, middle_position):
""" Extend the n/p wells to cover whole cell """ """ Extend the n/p wells to cover whole cell """
# Add a rail width to extend the well to the top of the rail # Add a rail width to extend the well to the top of the rail
max_y_offset = self.height + 0.5*self.m1_width max_y_offset = self.height + 0.5 * self.m1_width
self.nwell_position = middle_position self.nwell_position = middle_position
nwell_height = max_y_offset - middle_position.y nwell_height = max_y_offset - middle_position.y
if drc("has_nwell"): if drc("has_nwell"):
@ -145,8 +151,8 @@ class pgate(design.design):
width=self.well_width, width=self.well_width,
height=nwell_height) height=nwell_height)
pwell_position = vector(0,-0.5*self.m1_width) pwell_position = vector(0, -0.5 * self.m1_width)
pwell_height = middle_position.y-pwell_position.y pwell_height = middle_position.y - pwell_position.y
if drc("has_pwell"): if drc("has_pwell"):
self.add_rect(layer="pwell", self.add_rect(layer="pwell",
offset=pwell_position, offset=pwell_position,
@ -163,38 +169,45 @@ class pgate(design.design):
layer_stack = ("active", "contact", "metal1") layer_stack = ("active", "contact", "metal1")
# To the right a spacing away from the pmos right active edge # 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") contact_xoffset = pmos_pos.x + pmos.active_width \
# Must be at least an well enclosure of active down from the top of the well + 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. # OR align the active with the top of PMOS active.
max_y_offset = self.height + 0.5*self.m1_width 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, contact_yoffset = min(pmos_pos.y + pmos.active_height - pmos.active_contact.first_layer_height,
max_y_offset - pmos.active_contact.first_layer_height/2 - self.well_enclose_active) max_y_offset - pmos.active_contact.first_layer_height / 2 - self.well_enclose_active)
contact_offset = vector(contact_xoffset, contact_yoffset) contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact in x and y # Offset by half a contact in x and y
contact_offset += vector(0.5*pmos.active_contact.first_layer_width, contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
0.5*pmos.active_contact.first_layer_height) 0.5 * pmos.active_contact.first_layer_height)
self.nwell_contact=self.add_via_center(layers=layer_stack, self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=contact_offset, offset=contact_offset,
directions=("H","V"), directions=("H", "V"),
implant_type="n", implant_type="n",
well_type="n") well_type="n")
self.add_rect_center(layer="metal1", self.add_rect_center(layer="metal1",
offset=contact_offset + vector(0,0.5*(self.height-contact_offset.y)), offset=contact_offset + vector(0, 0.5 * (self.height-contact_offset.y)),
width=self.nwell_contact.mod.second_layer_width, width=self.nwell_contact.mod.second_layer_width,
height=self.height - contact_offset.y) height=self.height - contact_offset.y)
# Now add the full active and implant for the PMOS # Now add the full active and implant for the PMOS
#active_offset = pmos_pos + vector(pmos.active_width,0) # 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", # self.add_rect(layer="active",
# offset=active_offset, # offset=active_offset,
# width=pmos.active_contact.width, # width=pmos.active_contact.width,
# height=pmos.active_height) # 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_spacing = self.implant_space+self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active) # implant_offset = active_offset + vector(implant_spacing,0) \
# implant_width = pmos.active_contact.width + 2*self.implant_enclose_active # - 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 # implant_height = pmos.active_height + 2*self.implant_enclose_active
# self.add_rect(layer="nimplant", # self.add_rect(layer="nimplant",
# offset=implant_offset, # offset=implant_offset,
@ -208,39 +221,45 @@ class pgate(design.design):
layer_stack = ("active", "contact", "metal1") layer_stack = ("active", "contact", "metal1")
pwell_position = vector(0,-0.5*self.m1_width) pwell_position = vector(0, -0.5 * self.m1_width)
# To the right a spacing away from the nmos right active edge # 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") contact_xoffset = nmos_pos.x + nmos.active_width \
# Must be at least an well enclosure of active up from the bottom of the well + 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, 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) contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact # Offset by half a contact
contact_offset += vector(0.5*nmos.active_contact.first_layer_width, contact_offset += vector(0.5 * nmos.active_contact.first_layer_width,
0.5*nmos.active_contact.first_layer_height) 0.5 * nmos.active_contact.first_layer_height)
self.pwell_contact=self.add_via_center(layers=layer_stack, self.pwell_contact= self.add_via_center(layers=layer_stack,
offset=contact_offset, offset=contact_offset,
directions=("H","V"), directions=("H", "V"),
implant_type="p", implant_type="p",
well_type="p") well_type="p")
self.add_rect_center(layer="metal1", self.add_rect_center(layer="metal1",
offset=contact_offset.scale(1,0.5), offset=contact_offset.scale(1,0.5),
width=self.pwell_contact.mod.second_layer_width, width=self.pwell_contact.mod.second_layer_width,
height=contact_offset.y) height=contact_offset.y)
# Now add the full active and implant for the NMOS # Now add the full active and implant for the NMOS
# active_offset = nmos_pos + vector(nmos.active_width,0) # 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", # self.add_rect(layer="active",
# offset=active_offset, # offset=active_offset,
# width=nmos.active_contact.width, # width=nmos.active_contact.width,
# height=nmos.active_height) # height=nmos.active_height)
# implant_spacing = self.implant_space+self.implant_enclose_active # implant_spacing = self.implant_space+self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active) # implant_offset = active_offset + vector(implant_spacing,0) \
# implant_width = nmos.active_contact.width + 2*self.implant_enclose_active # - 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 # implant_height = nmos.active_height + 2*self.implant_enclose_active
# self.add_rect(layer="pimplant", # self.add_rect(layer="pimplant",
# offset=implant_offset, # offset=implant_offset,

View File

@ -16,6 +16,7 @@ from utils import round_to_grid
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
class pinv(pgate.pgate): class pinv(pgate.pgate):
""" """
Pinv generates gds of a parametrically sized inverter. The Pinv generates gds of a parametrically sized inverter. The
@ -28,12 +29,14 @@ class pinv(pgate.pgate):
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, 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.add_comment("size: {}".format(size))
self.size = size self.size = size
self.nmos_size = size self.nmos_size = size
self.pmos_size = beta*size self.pmos_size = beta * size
self.beta = beta self.beta = beta
self.route_output = False self.route_output = False
@ -44,7 +47,7 @@ class pinv(pgate.pgate):
self.add_pins() self.add_pins()
self.determine_tx_mults() self.determine_tx_mults()
self.add_ptx() self.add_ptx()
self.create_ptx() self.create_ptx()
def create_layout(self): def create_layout(self):
""" Calls all functions related to the generation of the layout """ """ Calls all functions related to the generation of the layout """
@ -54,7 +57,11 @@ class pinv(pgate.pgate):
self.add_well_contacts() self.add_well_contacts()
self.extend_wells(self.well_pos) self.extend_wells(self.well_pos)
self.connect_rails() 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() self.route_outputs()
def add_pins(self): def add_pins(self):
@ -63,7 +70,6 @@ class pinv(pgate.pgate):
dir_list = ["INPUT", "OUTPUT", "POWER", "GROUND"] dir_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list) self.add_pin_list(pin_list, dir_list)
def determine_tx_mults(self): def determine_tx_mults(self):
""" """
Determines the number of fingers needed to achieve the size within Determines the number of fingers needed to achieve the size within
@ -73,58 +79,71 @@ class pinv(pgate.pgate):
# This may make the result differ when the layout is created... # This may make the result differ when the layout is created...
if OPTS.netlist_only: if OPTS.netlist_only:
self.tx_mults = 1 self.tx_mults = 1
self.nmos_width = self.nmos_size*drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
return return
# Do a quick sanity check and bail if unlikely feasible height # Do a quick sanity check and bail if unlikely feasible height
# Sanity check. can we make an inverter in the height with minimum tx sizes? # Sanity check. can we make an inverter in the height
# Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain) # 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 # plus the tx height
nmos = factory.create(module_type="ptx", tx_type="nmos") 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 tx_height = nmos.poly_height + pmos.poly_height
# rotated m1 pitch or poly to active spacing # rotated m1 pitch or poly to active spacing
min_channel = max(contact.poly.width + self.m1_space, min_channel = max(contact.poly.width + self.m1_space,
contact.poly.width + 2*drc("poly_to_active")) contact.poly.width + 2 * drc("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 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 # 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.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)
total_height = tx_height + min_channel + 2*self.top_bottom_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
tx_height_available = self.height - min_channel - 2*self.top_bottom_space # the number of fingers
# Divide the height in half. Could divide proportional to beta, but this makes tx_height_available = self.height - min_channel - 2 * self.top_bottom_space
# 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 # Subtract the poly space under the rail of the tx
nmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly") 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") 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,
nmos_height_available, "Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available,
pmos_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
self.nmos_width = self.nmos_size*drc("minwidth_tx") # into available space
self.pmos_width = self.pmos_size*drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
nmos_required_mults = max(int(ceil(self.nmos_width/nmos_height_available)),1) self.pmos_width = self.pmos_size * drc("minwidth_tx")
pmos_required_mults = max(int(ceil(self.pmos_width/pmos_height_available)),1) nmos_required_mults = max(int(ceil(self.nmos_width / nmos_height_available)), 1)
pmos_required_mults = max(int(ceil(self.pmos_width / pmos_height_available)), 1)
# The mults must be the same for easy connection of poly # The mults must be the same for easy connection of poly
self.tx_mults = max(nmos_required_mults, pmos_required_mults) self.tx_mults = max(nmos_required_mults, pmos_required_mults)
# Recompute each mult width and check it isn't too small # Recompute each mult width and check it isn't too small
# This could happen if the height is narrow and the size is small # This could happen if the height is narrow and the size is small
# User should pick a bigger size to fix it... # 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 # We also need to round the width to the grid or we will end up
# mismatch errors when fingers are not a grid length and get rounded in the offset geometry. # 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) 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) 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): def setup_layout_constants(self):
""" """
@ -136,9 +155,7 @@ class pinv(pgate.pgate):
self.well_width = self.pmos.active_width + self.pmos.active_contact.width \ self.well_width = self.pmos.active_width + self.pmos.active_contact.width \
+ drc("active_to_body_active") + 2*drc("well_enclosure_active") + drc("active_to_body_active") + 2*drc("well_enclosure_active")
self.width = self.well_width self.width = self.well_width
# Height is an input parameter, so it is not recomputed. # Height is an input parameter, so it is not recomputed.
def add_ptx(self): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """
@ -162,58 +179,57 @@ class pinv(pgate.pgate):
""" Add vdd/gnd rails to the top and bottom. """ """ Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd", self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,0), offset=vector(0.5 * self.width, 0),
width=self.width) width=self.width)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def create_ptx(self): def create_ptx(self):
""" """
Create the PMOS and NMOS netlist. Create the PMOS and NMOS netlist.
""" """
self.pmos_inst=self.add_inst(name="pinv_pmos", self.pmos_inst = self.add_inst(name="pinv_pmos",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["Z", "A", "vdd", "vdd"]) self.connect_inst(["Z", "A", "vdd", "vdd"])
self.nmos_inst=self.add_inst(name="pinv_nmos", self.nmos_inst = self.add_inst(name="pinv_nmos",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"]) self.connect_inst(["Z", "A", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
""" """
Place PMOS and NMOS to the layout at the upper-most and lowest position Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel to provide maximum routing in channel
""" """
# place PMOS so it is half a poly spacing down from the top # place PMOS so it is half a poly spacing down from the top
self.pmos_pos = self.pmos.active_offset.scale(1,0) \ 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) self.pmos_inst.place(self.pmos_pos)
# place NMOS so that it is half a poly spacing up from the bottom # 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) self.nmos_inst.place(self.nmos_pos)
# Output position will be in between the PMOS and NMOS drains # Output position will be in between the PMOS and NMOS drains
pmos_drain_pos = self.pmos_inst.get_pin("D").ll() pmos_drain_pos = self.pmos_inst.get_pin("D").ll()
nmos_drain_pos = self.nmos_inst.get_pin("D").ul() nmos_drain_pos = self.nmos_inst.get_pin("D").ul()
self.output_pos = vector(0,0.5*(pmos_drain_pos.y+nmos_drain_pos.y)) self.output_pos = vector(0, 0.5 * (pmos_drain_pos.y + nmos_drain_pos.y))
# This will help with the wells
self.well_pos = vector(0,self.nmos_inst.uy())
# This will help with the wells
self.well_pos = vector(0, self.nmos_inst.uy())
def route_outputs(self): 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 # Get the drain pins
nmos_drain_pin = self.nmos_inst.get_pin("D") nmos_drain_pin = self.nmos_inst.get_pin("D")
@ -222,14 +238,14 @@ class pinv(pgate.pgate):
# Pick point at right most of NMOS and connect down to PMOS # Pick point at right most of NMOS and connect down to PMOS
nmos_drain_pos = nmos_drain_pin.bc() nmos_drain_pos = nmos_drain_pin.bc()
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y) pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos]) self.add_path("metal1", [nmos_drain_pos, pmos_drain_pos])
# Remember the mid for the output # Remember the mid for the output
mid_drain_offset = vector(nmos_drain_pos.x,self.output_pos.y) 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 # This extends the output to the edge of the cell
output_offset = mid_drain_offset.scale(0,1) + vector(self.width,0) output_offset = mid_drain_offset.scale(0, 1) + vector(self.width, 0)
self.add_layout_pin_segment_center(text="Z", self.add_layout_pin_segment_center(text="Z",
layer="metal1", layer="metal1",
start=mid_drain_offset, start=mid_drain_offset,
@ -238,8 +254,8 @@ class pinv(pgate.pgate):
# This leaves the output as an internal pin (min sized) # This leaves the output as an internal pin (min sized)
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="metal1", 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): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """
@ -251,9 +267,9 @@ class pinv(pgate.pgate):
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos_inst,"S","gnd") self.connect_pin_to_rail(self.nmos_inst, "S", "gnd")
self.connect_pin_to_rail(self.pmos_inst,"S","vdd") self.connect_pin_to_rail(self.pmos_inst, "S", "vdd")
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""
@ -268,27 +284,35 @@ class pinv(pgate.pgate):
def calculate_effective_capacitance(self, load): def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load 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 transition_prob = 0.5
return transition_prob*(c_load + c_para) return transition_prob * (c_load + c_para)
def input_load(self): 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 return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True): 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.
""" """
parasitic_delay = 1 Returns an object representing the parameters for delay in tau units.
return logical_effort.logical_effort(self.name, Optional is_rise refers to the input direction rise/fall.
self.size, Input inverted by this stage.
self.input_load(), """
cout, parasitic_delay = 1
parasitic_delay, return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise) not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function.""" """
self.add_graph_edges(graph, port_nets) 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 debug
import pgate import pgate
from tech import drc
from math import log
from vector import vector from vector import vector
from globals import OPTS
from sram_factory import factory from sram_factory import factory
class pinvbuf(pgate.pgate): class pinvbuf(pgate.pgate):
""" """
This is a simple inverter/buffer used for driving loads. It is This is a simple inverter/buffer used for driving loads. It is
@ -31,11 +29,10 @@ class pinvbuf(pgate.pgate):
# The pinvbuf has a FO of 2 for the first stage, so the second stage # The pinvbuf has a FO of 2 for the first stage, so the second stage
# should be sized "half" to prevent loading of the first stage # should be sized "half" to prevent loading of the first stage
self.size = size self.size = size
self.predriver_size = max(int(self.size/(self.stage_effort/2)),1) self.predriver_size = max(int(self.size / (self.stage_effort / 2)), 1)
# Creates the netlist and layout
pgate.pgate.__init__(self, name)
# Creates the netlist and layout
pgate.pgate.__init__(self, name)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
@ -44,8 +41,8 @@ class pinvbuf(pgate.pgate):
def create_layout(self): def create_layout(self):
self.width = 2*self.inv1.width + self.inv2.width self.width = 2 * self.inv1.width + self.inv2.width
self.height = 2*self.inv1.height self.height = 2 * self.inv1.height
self.place_modules() self.place_modules()
self.route_wires() self.route_wires()
@ -53,7 +50,6 @@ class pinvbuf(pgate.pgate):
self.offset_all_coordinates() self.offset_all_coordinates()
def add_pins(self): def add_pins(self):
self.add_pin("A") self.add_pin("A")
self.add_pin("Zb") self.add_pin("Zb")
@ -64,96 +60,100 @@ class pinvbuf(pgate.pgate):
def add_modules(self): def add_modules(self):
# Shield the cap, but have at least a stage effort of 4 # Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(self.predriver_size/self.stage_effort)) 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.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.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) self.add_mod(self.inv2)
def create_insts(self): def create_insts(self):
# Create INV1 (capacitance shield) # Create INV1 (capacitance shield)
self.inv1_inst=self.add_inst(name="buf_inv1", self.inv1_inst = self.add_inst(name="buf_inv1",
mod=self.inv) mod=self.inv)
self.connect_inst(["A", "zb_int", "vdd", "gnd"]) self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv2_inst = self.add_inst(name="buf_inv2",
self.inv2_inst=self.add_inst(name="buf_inv2", mod=self.inv1)
mod=self.inv1) self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
self.inv3_inst=self.add_inst(name="buf_inv3", self.inv3_inst = self.add_inst(name="buf_inv3",
mod=self.inv2) mod=self.inv2)
self.connect_inst(["z_int", "Zb", "vdd", "gnd"]) self.connect_inst(["z_int", "Zb", "vdd", "gnd"])
self.inv4_inst=self.add_inst(name="buf_inv4", self.inv4_inst = self.add_inst(name="buf_inv4",
mod=self.inv2) mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_modules(self): def place_modules(self):
# Add INV1 to the left (capacitance shield) # Add INV1 to the left (capacitance shield)
self.inv1_inst.place(vector(0,0)) self.inv1_inst.place(vector(0, 0))
# Add INV2 to the right of INV1 # Add INV2 to the right of INV1
self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
# Add INV3 to the right of INV2 # Add INV3 to the right of INV2
self.inv3_inst.place(vector(self.inv2_inst.rx(),0)) self.inv3_inst.place(vector(self.inv2_inst.rx(), 0))
# Add INV4 flipped to the bottom aligned with INV2 # 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(),
mirror = "MX") 2 * self.inv2.height),
mirror="MX")
def route_wires(self): def route_wires(self):
# inv1 Z to inv2 A # inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z") z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A") a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy()) mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
# inv2 Z to inv3 A # inv2 Z to inv3 A
z2_pin = self.inv2_inst.get_pin("Z") z2_pin = self.inv2_inst.get_pin("Z")
a3_pin = self.inv3_inst.get_pin("A") a3_pin = self.inv3_inst.get_pin("A")
mid_point = vector(z2_pin.cx(), a3_pin.cy()) mid_point = vector(z2_pin.cx(), a3_pin.cy())
self.add_path("metal1", [z2_pin.center(), mid_point, a3_pin.center()]) self.add_path("metal1", [z2_pin.center(), mid_point, a3_pin.center()])
# inv1 Z to inv4 A (up and over) # inv1 Z to inv4 A (up and over)
z1_pin = self.inv1_inst.get_pin("Z") z1_pin = self.inv1_inst.get_pin("Z")
a4_pin = self.inv4_inst.get_pin("A") a4_pin = self.inv4_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a4_pin.cy()) 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"),
self.add_via_center(layers=("metal1","via1","metal2"), [z1_pin.center(), mid_point, a4_pin.center()])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=z1_pin.center()) offset=z1_pin.center())
def add_layout_pins(self): def add_layout_pins(self):
# Continous vdd rail along with label. # Continous vdd rail along with label.
vdd_pin=self.inv1_inst.get_pin("vdd") vdd_pin = self.inv1_inst.get_pin("vdd")
self.add_layout_pin(text="vdd", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=vdd_pin.ll().scale(0,1), offset=vdd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
# Continous vdd rail along with label. # Continous vdd rail along with label.
gnd_pin=self.inv4_inst.get_pin("gnd") gnd_pin = self.inv4_inst.get_pin("gnd")
self.add_layout_pin(text="gnd", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=gnd_pin.ll().scale(0,1), offset=gnd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=gnd_pin.height()) height=gnd_pin.height())
# Continous gnd rail along with label. # Continous gnd rail along with label.
gnd_pin=self.inv1_inst.get_pin("gnd") gnd_pin = self.inv1_inst.get_pin("gnd")
self.add_layout_pin(text="gnd", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=gnd_pin.ll().scale(0,1), offset=gnd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
@ -161,22 +161,21 @@ class pinvbuf(pgate.pgate):
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="metal2", layer="metal2",
offset=z_pin.center()) offset=z_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=z_pin.center()) offset=z_pin.center())
zb_pin = self.inv3_inst.get_pin("Z") zb_pin = self.inv3_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Zb", self.add_layout_pin_rect_center(text="Zb",
layer="metal2", layer="metal2",
offset=zb_pin.center()) offset=zb_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=zb_pin.center()) offset=zb_pin.center())
a_pin = self.inv1_inst.get_pin("A") a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_rect_center(text="A", self.add_layout_pin_rect_center(text="A",
layer="metal2", layer="metal2",
offset=a_pin.center()) offset=a_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=a_pin.center()) offset=a_pin.center())
def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False): def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False):
@ -194,7 +193,8 @@ class pinvbuf(pgate.pgate):
def determine_clk_buf_bar_stage_efforts(self, external_cout, inp_is_rise=False): def determine_clk_buf_bar_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the clk -> clk_buf path""" """Get the stage efforts of the clk -> clk_buf path"""
#After (almost) every stage, the direction of the signal inverts.
# After (almost) every stage, the direction of the signal inverts.
stage_effort_list = [] stage_effort_list = []
stage1_cout = self.inv1.get_cin() + self.inv2.get_cin() stage1_cout = self.inv1.get_cin() + self.inv2.get_cin()
stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise) stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise)

View File

@ -10,10 +10,10 @@ import pgate
import debug import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
from vector import vector from vector import vector
from globals import OPTS
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
class pnand2(pgate.pgate): class pnand2(pgate.pgate):
""" """
This module generates gds of a parametrically sized 2-input nand. This module generates gds of a parametrically sized 2-input nand.
@ -22,17 +22,19 @@ class pnand2(pgate.pgate):
def __init__(self, name, size=1, height=None): def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nand """ """ 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.add_comment("size: {}".format(size))
self.size = size self.size = size
self.nmos_size = 2*size self.nmos_size = 2 * size
self.pmos_size = parameter["beta"]*size self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size*drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized # FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnand2 is only supported now.") debug.check(size == 1, "Size 1 pnand2 is only supported now.")
self.tx_mults = 1 self.tx_mults = 1
# Creates the netlist and layout # Creates the netlist and layout
@ -61,7 +63,6 @@ class pnand2(pgate.pgate):
dir_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] dir_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list) self.add_pin_list(pin_list, dir_list)
def add_ptx(self): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx", self.nmos = factory.create(module_type="ptx",
@ -90,113 +91,126 @@ class pnand2(pgate.pgate):
self.m3_space + contact.m2m3.second_layer_width) 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 # source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() 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. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + contact.active.width \ 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 self.width = self.well_width
# Height is an input parameter, so it is not recomputed. # 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
extra_contact_space = max(-self.nmos.get_pin("D").by(),0) # 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 # 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.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): def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """ """ Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd", self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,0), offset=vector(0.5*self.width, 0),
width=self.width) width=self.width)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def create_ptx(self): def create_ptx(self):
""" """
Add PMOS and NMOS to the netlist. Add PMOS and NMOS to the netlist.
""" """
self.pmos1_inst=self.add_inst(name="pnand2_pmos1", self.pmos1_inst = self.add_inst(name="pnand2_pmos1",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["vdd", "A", "Z", "vdd"]) self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand2_pmos2", self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["Z", "B", "vdd", "vdd"]) self.connect_inst(["Z", "B", "vdd", "vdd"])
self.nmos1_inst=self.add_inst(name="pnand2_nmos1", self.nmos1_inst = self.add_inst(name="pnand2_nmos1",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "B", "net1", "gnd"]) self.connect_inst(["Z", "B", "net1", "gnd"])
self.nmos2_inst=self.add_inst(name="pnand2_nmos2", self.nmos2_inst = self.add_inst(name="pnand2_nmos2",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["net1", "A", "gnd", "gnd"]) self.connect_inst(["net1", "A", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
""" """
Place PMOS and NMOS to the layout at the upper-most and lowest position Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel to provide maximum routing in channel
""" """
pmos1_pos = vector(self.pmos.active_offset.x, 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.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset self.pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst.place(self.pmos2_pos) self.pmos2_inst.place(self.pmos2_pos)
nmos1_pos = vector(self.pmos.active_offset.x,
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space) self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos) self.nmos1_inst.place(nmos1_pos)
self.nmos2_pos = nmos1_pos + self.overlap_offset self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos) self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS # 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 # This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy()) self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self): 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_nwell_contact(self.pmos, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos) self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd") self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd") self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
self.connect_pin_to_rail(self.pmos2_inst,"D","vdd") self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd")
def route_inputs(self): def route_inputs(self):
""" Route the A and B inputs """ """ Route the A and B inputs """
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + 0.5*self.m2_width inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center") + 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 # This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing 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): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS1 drain # PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D") pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center() top_pin_offset = pmos_pin.center()
# NMOS2 drain # NMOS2 drain
@ -204,24 +218,26 @@ class pnand2(pgate.pgate):
bottom_pin_offset = nmos_pin.center() bottom_pin_offset = nmos_pin.center()
# Output pin # 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 # Midpoints of the L routes go horizontal first then vertical
mid1_offset = vector(out_offset.x, top_pin_offset.y) mid1_offset = vector(out_offset.x, top_pin_offset.y)
mid2_offset = vector(out_offset.x, bottom_pin_offset.y) mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center(), offset=pmos_pin.center(),
directions=("V","H")) directions=("V", "H"))
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center(), offset=nmos_pin.center(),
directions=("V","H")) directions=("V", "H"))
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=out_offset) offset=out_offset)
# PMOS1 to mid-drain to NMOS2 drain # 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 # This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
@ -243,21 +259,32 @@ class pnand2(pgate.pgate):
def calculate_effective_capacitance(self, load): def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load 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 transition_prob = 0.1875
return transition_prob*(c_load + c_para) return transition_prob * (c_load + c_para)
def input_load(self): def input_load(self):
"""Return the relative input capacitance of a single input""" """Return the relative input capacitance of a single input"""
return self.nmos_size+self.pmos_size return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True): 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.
""" """
parasitic_delay = 2 Returns an object representing the parameters for delay in tau units.
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise) 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)
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function.""" """
self.add_graph_edges(graph, port_nets) 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 import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
from vector import vector from vector import vector
from globals import OPTS
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
class pnand3(pgate.pgate): class pnand3(pgate.pgate):
""" """
This module generates gds of a parametrically sized 2-input nand. This module generates gds of a parametrically sized 2-input nand.
@ -22,24 +22,25 @@ class pnand3(pgate.pgate):
def __init__(self, name, size=1, height=None): def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 3 input nand """ """ 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)) self.add_comment("size: {}".format(size))
# We have trouble pitch matching a 3x sizes to the bitcell... # We have trouble pitch matching a 3x sizes to the bitcell...
# If we relax this, we could size this better. # If we relax this, we could size this better.
self.size = size self.size = size
self.nmos_size = 2*size self.nmos_size = 2 * size
self.pmos_size = parameter["beta"]*size self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size*drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized # FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnand3 is only supported now.") debug.check(size == 1,"Size 1 pnand3 is only supported now.")
self.tx_mults = 1 self.tx_mults = 1
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
def add_pins(self): def add_pins(self):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """
@ -90,41 +91,44 @@ class pnand3(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # Enclosure space on the sides.
self.well_width = 3*self.pmos.active_width + self.pmos.active_contact.width \ self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") \ + 2 * drc("active_to_body_active") + 2 * drc("well_enclosure_active") \
- self.overlap_offset.x - self.overlap_offset.x
self.width = self.well_width self.width = self.well_width
# Height is an input parameter, so it is not recomputed. # Height is an input parameter, so it is not recomputed.
# This will help with the wells and the input/output placement # This will help with the wells and the input/output placement
self.output_pos = vector(0,0.5*self.height) 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") nmos = factory.create(module_type="ptx", tx_type="nmos")
extra_contact_space = max(-nmos.get_pin("D").by(),0) extra_contact_space = max(-nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell # 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.top_bottom_space = max(0.5 * self.m1_width + self.m1_space \
drc("poly_extend_active"), self.poly_space) + extra_contact_space,
drc("poly_extend_active"),
self.poly_space)
def route_supply_rails(self): def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """ """ Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd", self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,0), offset=vector(0.5 * self.width, 0),
width=self.width) width=self.width)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def create_ptx(self): def create_ptx(self):
""" """
Create the PMOS and NMOS in the netlist. Create the PMOS and NMOS in the netlist.
""" """
self.pmos1_inst=self.add_inst(name="pnand3_pmos1", self.pmos1_inst = self.add_inst(name="pnand3_pmos1",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["vdd", "A", "Z", "vdd"]) self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand3_pmos2", self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
@ -135,27 +139,27 @@ class pnand3(pgate.pgate):
mod=self.pmos) mod=self.pmos)
self.connect_inst(["Z", "C", "vdd", "vdd"]) self.connect_inst(["Z", "C", "vdd", "vdd"])
self.nmos1_inst=self.add_inst(name="pnand3_nmos1", self.nmos1_inst = self.add_inst(name="pnand3_nmos1",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "C", "net1", "gnd"]) self.connect_inst(["Z", "C", "net1", "gnd"])
self.nmos2_inst=self.add_inst(name="pnand3_nmos2", self.nmos2_inst = self.add_inst(name="pnand3_nmos2",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["net1", "B", "net2", "gnd"]) self.connect_inst(["net1", "B", "net2", "gnd"])
self.nmos3_inst=self.add_inst(name="pnand3_nmos3", self.nmos3_inst = self.add_inst(name="pnand3_nmos3",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["net2", "A", "gnd", "gnd"]) self.connect_inst(["net2", "A", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
""" """
Place the PMOS and NMOS in the layout at the upper-most and lowest position Place the PMOS and NMOS in the layout at the upper-most
to provide maximum routing in channel and lowest position to provide maximum routing in channel
""" """
pmos1_pos = vector(self.pmos.active_offset.x, 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.pmos1_inst.place(pmos1_pos)
pmos2_pos = pmos1_pos + self.overlap_offset pmos2_pos = pmos1_pos + self.overlap_offset
@ -165,7 +169,8 @@ class pnand3(pgate.pgate):
self.pmos3_inst.place(self.pmos3_pos) 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) self.nmos1_inst.place(nmos1_pos)
nmos2_pos = nmos1_pos + self.overlap_offset nmos2_pos = nmos1_pos + self.overlap_offset
@ -175,7 +180,7 @@ class pnand3(pgate.pgate):
self.nmos3_inst.place(self.nmos3_pos) self.nmos3_inst.place(self.nmos3_pos)
# This should be placed at the top of the NMOS well # This should be placed at the top of the NMOS well
self.well_pos = vector(0,self.nmos1_inst.uy()) self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """
@ -183,42 +188,53 @@ class pnand3(pgate.pgate):
self.add_nwell_contact(self.pmos, self.pmos3_pos) self.add_nwell_contact(self.pmos, self.pmos3_pos)
self.add_pwell_contact(self.nmos, self.nmos3_pos) self.add_pwell_contact(self.nmos, self.nmos3_pos)
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd") self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd") self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
self.connect_pin_to_rail(self.pmos2_inst,"D","vdd") self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd")
def route_inputs(self): def route_inputs(self):
""" Route the A and B inputs """ """ Route the A and B inputs """
# wire space or wire and one contact space # 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.m1_space + 0.5*contact.poly.width + 0.5*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 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 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.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): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS1 drain # PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D") pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain # PMOS3 drain
pmos3_pin = self.pmos3_inst.get_pin("D") pmos3_pin = self.pmos3_inst.get_pin("D")
# NMOS3 drain # NMOS3 drain
nmos3_pin = self.nmos3_inst.get_pin("D") nmos3_pin = self.nmos3_inst.get_pin("D")
# Go up to metal2 for ease on all output pins # Go up to metal2 for ease on all output pins
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
@ -229,10 +245,10 @@ class pnand3(pgate.pgate):
offset=nmos3_pin.center()) offset=nmos3_pin.center())
# PMOS3 and NMOS3 are drain aligned # PMOS3 and NMOS3 are drain aligned
self.add_path("metal2",[pmos3_pin.bc(), nmos3_pin.uc()]) self.add_path("metal2", [pmos3_pin.bc(), nmos3_pin.uc()])
# Route in the A input track (top track) # Route in the A input track (top track)
mid_offset = vector(nmos3_pin.center().x,self.inputA_yoffset) mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
self.add_path("metal2",[pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) self.add_path("metal2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell # This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
@ -256,21 +272,32 @@ class pnand3(pgate.pgate):
def calculate_effective_capacitance(self, load): def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load 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 transition_prob = 0.1094
return transition_prob*(c_load + c_para) return transition_prob *(c_load + c_para)
def input_load(self): def input_load(self):
"""Return the relative input capacitance of a single input""" """Return the relative input capacitance of a single input"""
return self.nmos_size+self.pmos_size return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True): 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.
""" """
parasitic_delay = 3 Returns an object representing the parameters for delay in tau units.
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise) 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)
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function.""" """
self.add_graph_edges(graph, port_nets) 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 import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
from vector import vector from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory from sram_factory import factory
class pnor2(pgate.pgate): class pnor2(pgate.pgate):
""" """
This module generates gds of a parametrically sized 2-input nor. This module generates gds of a parametrically sized 2-input nor.
@ -22,22 +21,23 @@ class pnor2(pgate.pgate):
def __init__(self, name, size=1, height=None): def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nor """ """ 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.add_comment("size: {}".format(size))
self.nmos_size = size self.nmos_size = size
# We will just make this 1.5 times for now. NORs are not ideal anyhow. # We will just make this 1.5 times for now. NORs are not ideal anyhow.
self.pmos_size = 1.5*parameter["beta"]*size self.pmos_size = 1.5 * parameter["beta"] * size
self.nmos_width = self.nmos_size*drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized # FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnor2 is only supported now.") debug.check(size==1, "Size 1 pnor2 is only supported now.")
self.tx_mults = 1 self.tx_mults = 1
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
@ -89,44 +89,48 @@ class pnor2(pgate.pgate):
self.m2_space + contact.m2m3.first_layer_width, self.m2_space + contact.m2m3.first_layer_width,
self.m3_space + contact.m2m3.second_layer_width) 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
# source and drain pins # offset to overlap the source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() 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. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + self.pmos.active_contact.width \ self.well_width = 2 * self.pmos.active_width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") + self.pmos.active_contact.width \
+ 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = self.well_width self.width = self.well_width
# Height is an input parameter, so it is not recomputed. # 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
extra_contact_space = max(-self.nmos.get_pin("D").by(),0) # 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 # 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.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): def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """ """ Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd", self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,0), offset=vector(0.5 * self.width, 0),
width=self.width) width=self.width)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def create_ptx(self): def create_ptx(self):
""" """
Add PMOS and NMOS to the layout at the upper-most and lowest position Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel to provide maximum routing in channel
""" """
self.pmos1_inst=self.add_inst(name="pnor2_pmos1", self.pmos1_inst = self.add_inst(name="pnor2_pmos1",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["vdd", "A", "net1", "vdd"]) self.connect_inst(["vdd", "A", "net1", "vdd"])
self.pmos2_inst = self.add_inst(name="pnor2_pmos2", self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
@ -134,23 +138,23 @@ class pnor2(pgate.pgate):
self.connect_inst(["net1", "B", "Z", "vdd"]) self.connect_inst(["net1", "B", "Z", "vdd"])
self.nmos1_inst=self.add_inst(name="pnor2_nmos1", self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"]) self.connect_inst(["Z", "A", "gnd", "gnd"])
self.nmos2_inst=self.add_inst(name="pnor2_nmos2", self.nmos2_inst = self.add_inst(name="pnor2_nmos2",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "B", "gnd", "gnd"]) self.connect_inst(["Z", "B", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
""" """
Add PMOS and NMOS to the layout at the upper-most and lowest position Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel to provide maximum routing in channel
""" """
pmos1_pos = vector(self.pmos.active_offset.x, 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.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset self.pmos2_pos = pmos1_pos + self.overlap_offset
@ -162,42 +166,49 @@ class pnor2(pgate.pgate):
self.nmos2_pos = nmos1_pos + self.overlap_offset self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos) self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS # 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 # This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy()) self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.pmos, self.pmos2_pos) self.add_nwell_contact(self.pmos, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos) self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd") self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
self.connect_pin_to_rail(self.nmos2_inst,"D","gnd") self.connect_pin_to_rail(self.nmos2_inst, "D", "gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
def route_inputs(self): def route_inputs(self):
""" Route the A and B inputs """ """ Route the A and B inputs """
# Use M2 spaces so we can drop vias on the pins later! # 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 inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center") + 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 # This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing 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): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS2 drain # PMOS2 drain
pmos_pin = self.pmos2_inst.get_pin("D") pmos_pin = self.pmos2_inst.get_pin("D")
# NMOS1 drain # NMOS1 drain
nmos_pin = self.nmos1_inst.get_pin("D") nmos_pin = self.nmos1_inst.get_pin("D")
@ -207,17 +218,18 @@ class pnor2(pgate.pgate):
# Go up to metal2 for ease on all output pins # Go up to metal2 for ease on all output pins
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center()) offset=pmos_pin.center())
m1m2_contact=self.add_via_center(layers=("metal1", "via1", "metal2"), m1m2_contact = self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center()) offset=nmos_pin.center())
mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y) mid1_offset = vector(pmos_pin.center().x, nmos2_pin.center().y)
mid2_offset = vector(pmos_pin.center().x,self.inputA_yoffset) mid2_offset = vector(pmos_pin.center().x, self.inputA_yoffset)
mid3_offset = mid2_offset + vector(self.m2_width,0) mid3_offset = mid2_offset + vector(self.m2_width, 0)
# PMOS1 to mid-drain to NMOS2 drain # PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[pmos_pin.bc(), mid2_offset, mid3_offset]) self.add_path("metal2",
self.add_path("metal2",[nmos_pin.rc(), mid1_offset, mid2_offset]) [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 # This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=mid3_offset) offset=mid3_offset)
@ -240,10 +252,11 @@ class pnor2(pgate.pgate):
def calculate_effective_capacitance(self, load): def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load 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 transition_prob = 0.1875
return transition_prob*(c_load + c_para) return transition_prob * (c_load + c_para)
def build_graph(self, graph, inst_name, port_nets): 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) self.add_graph_edges(graph, port_nets)

View File

@ -13,6 +13,7 @@ from vector import vector
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
class precharge(design.design): class precharge(design.design):
""" """
Creates a single precharge cell Creates a single precharge cell
@ -25,16 +26,15 @@ class precharge(design.design):
self.bitcell = factory.create(module_type="bitcell") self.bitcell = factory.create(module_type="bitcell")
self.beta = parameter["beta"] self.beta = parameter["beta"]
self.ptx_width = self.beta*parameter["min_tx_size"] self.ptx_width = self.beta * parameter["min_tx_size"]
self.width = self.bitcell.width self.width = self.bitcell.width
self.bitcell_bl = bitcell_bl self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br self.bitcell_br = bitcell_br
# Creates the netlist and layout # Creates the netlist and layout
# Since it has variable height, it is not a pgate. # Since it has variable height, it is not a pgate.
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
self.DRC_LVS() self.DRC_LVS()
@ -53,7 +53,8 @@ class precharge(design.design):
self.connect_to_bitlines() self.connect_to_bitlines()
def add_pins(self): 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): def add_ptx(self):
""" """
@ -64,16 +65,13 @@ class precharge(design.design):
tx_type="pmos") tx_type="pmos")
self.add_mod(self.pmos) self.add_mod(self.pmos)
def route_vdd_rail(self): def route_vdd_rail(self):
""" """
Adds a vdd rail at the top of the cell Adds a vdd rail at the top of the cell
""" """
# Adds the rail across the width of the cell # Adds the rail across the width of the cell
vdd_position = vector(0.5*self.width, self.height) vdd_position = vector(0.5 * self.width, self.height)
self.add_rect_center(layer="metal1", self.add_rect_center(layer="metal1",
offset=vdd_position, offset=vdd_position,
width=self.width, width=self.width,
@ -87,44 +85,43 @@ class precharge(design.design):
# Add vdd pin above the transistor # Add vdd pin above the transistor
self.add_power_pin("vdd", pmos_pin.center(), vertical=True) self.add_power_pin("vdd", pmos_pin.center(), vertical=True)
def create_ptx(self): def create_ptx(self):
""" """
Create both the upper_pmos and lower_pmos to the module Create both the upper_pmos and lower_pmos to the module
""" """
self.lower_pmos_inst=self.add_inst(name="lower_pmos", self.lower_pmos_inst = self.add_inst(name="lower_pmos",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["bl", "en_bar", "br", "vdd"]) self.connect_inst(["bl", "en_bar", "br", "vdd"])
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1", self.upper_pmos1_inst = self.add_inst(name="upper_pmos1",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["bl", "en_bar", "vdd", "vdd"]) self.connect_inst(["bl", "en_bar", "vdd", "vdd"])
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2", self.upper_pmos2_inst = self.add_inst(name="upper_pmos2",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["br", "en_bar", "vdd", "vdd"]) self.connect_inst(["br", "en_bar", "vdd", "vdd"])
def place_ptx(self): def place_ptx(self):
""" """
Place both the upper_pmos and lower_pmos to the module Place both the upper_pmos and lower_pmos to the module
""" """
# Compute the other pmos2 location, but determining offset to overlap the # Compute the other pmos2 location,
# source and drain pins # but determining offset to overlap the source and drain pins
overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() 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 # This is how much the contact is placed inside the ptx active
contact_xdiff = self.pmos.get_pin("S").lx() contact_xdiff = self.pmos.get_pin("S").lx()
# adds the lower pmos to layout # adds the lower pmos to layout
bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx() 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.pmos.active_offset.y)
self.lower_pmos_inst.place(self.lower_pmos_position) self.lower_pmos_inst.place(self.lower_pmos_position)
# adds the upper pmos(s) to layout # adds the upper pmos(s) to layout
ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width ydiff = self.pmos.height + 2 * self.m1_space + contact.poly.width
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff) self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
self.upper_pmos1_inst.place(self.upper_pmos1_pos) self.upper_pmos1_inst.place(self.upper_pmos1_pos)
@ -146,7 +143,9 @@ class precharge(design.design):
# connects the two poly for the two upper pmos(s) # connects the two poly for the two upper pmos(s)
offset = offset + vector(0, ylength - self.poly_width) 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", self.add_rect(layer="poly",
offset=offset, offset=offset,
width=xlength, width=xlength,
@ -158,16 +157,16 @@ class precharge(design.design):
""" """
# adds the en contact to connect the gates to the en rail on metal1 # 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"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=offset) offset=offset)
# adds the en rail on metal1 # adds the en rail on metal1
self.add_layout_pin_segment_center(text="en_bar", self.add_layout_pin_segment_center(text="en_bar",
layer="metal1", layer="metal1",
start=offset.scale(0,1), start=offset.scale(0, 1),
end=offset.scale(0,1)+vector(self.width,0)) end=offset.scale(0, 1) + vector(self.width, 0))
def place_nwell_and_contact(self): def place_nwell_and_contact(self):
""" """
@ -175,8 +174,9 @@ class precharge(design.design):
""" """
# adds the contact from active to metal1 # adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \ 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"), self.add_via_center(layers=("active", "contact", "metal1"),
offset=well_contact_pos, offset=well_contact_pos,
implant_type="n", implant_type="n",
@ -187,18 +187,18 @@ class precharge(design.design):
# nwell should span the whole design since it is pmos only # nwell should span the whole design since it is pmos only
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
offset=vector(0,0), offset=vector(0, 0),
width=self.width, width=self.width,
height=self.height) height=self.height)
def route_bitlines(self): def route_bitlines(self):
""" """
Adds both bit-line and bit-line-bar to the module Adds both bit-line and bit-line-bar to the module
""" """
# adds the BL on metal 2 # 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", self.bl_pin = self.add_layout_pin(text="bl",
layer="metal2", layer="metal2",
offset=offset, offset=offset,
@ -206,7 +206,8 @@ class precharge(design.design):
height=self.height) height=self.height)
# adds the BR on metal 2 # 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", self.br_pin = self.add_layout_pin(text="br",
layer="metal2", layer="metal2",
offset=offset, offset=offset,
@ -218,60 +219,64 @@ class precharge(design.design):
Connect the bitlines to the devices Connect the bitlines to the devices
""" """
self.add_bitline_contacts() self.add_bitline_contacts()
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl")) self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl")) self.get_pin("bl"))
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),self.get_pin("br")) self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br")) 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): def add_bitline_contacts(self):
""" """
Adds contacts/via from metal1 to metal2 for bit-lines Adds contacts/via from metal1 to metal2 for bit-lines
""" """
stack=("metal1", "via1", "metal2") stack = ("metal1", "via1", "metal2")
upper_pin = self.upper_pmos1_inst.get_pin("S") upper_pin = self.upper_pmos1_inst.get_pin("S")
lower_pin = self.lower_pmos_inst.get_pin("S") lower_pin = self.lower_pmos_inst.get_pin("S")
# BL goes up to M2 at the transistor # BL goes up to M2 at the transistor
self.bl_contact=self.add_via_center(layers=stack, self.bl_contact =self.add_via_center(layers=stack,
offset=upper_pin.center(), offset=upper_pin.center(),
directions=("V","V")) directions=("V", "V"))
self.add_via_center(layers=stack, self.add_via_center(layers=stack,
offset=lower_pin.center(), offset=lower_pin.center(),
directions=("V","V")) directions=("V", "V"))
# BR routes over on M1 first # BR routes over on M1 first
self.add_via_center(layers=stack, self.add_via_center(layers=stack,
offset = vector(self.br_pin.cx(), upper_pin.cy()), offset=vector(self.br_pin.cx(), upper_pin.cy()),
directions=("V","V")) directions=("V", "V"))
self.add_via_center(layers=stack, self.add_via_center(layers=stack,
offset = vector(self.br_pin.cx(), lower_pin.cy()), offset=vector(self.br_pin.cx(), lower_pin.cy()),
directions=("V","V")) directions=("V", "V"))
def connect_pmos_m1(self, pmos_pin, bit_pin): def connect_pmos_m1(self, pmos_pin, bit_pin):
""" """
Connect a pmos pin to bitline pin Connect a pmos pin to bitline pin
""" """
left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
self.add_path("metal1", [ left_pos, right_pos] ) self.add_path("metal1", [left_pos, right_pos] )
def connect_pmos_m2(self, pmos_pin, bit_pin): def connect_pmos_m2(self, pmos_pin, bit_pin):
""" """
Connect a pmos pin to bitline pin Connect a pmos pin to bitline pin
""" """
left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy()) right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
self.add_path("metal2", [ left_pos, right_pos], self.bl_contact.height) self.add_path("metal2", [left_pos, right_pos], self.bl_contact.height)
def get_en_cin(self): def get_en_cin(self):
"""Get the relative capacitance of the enable in the precharge cell""" """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() pmos_cin = self.pmos.get_cin()
return 3*pmos_cin return 3 * pmos_cin

View File

@ -10,16 +10,12 @@ import pgate
import debug import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
from vector import vector 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 from sram_factory import factory
class ptristate_inv(pgate.pgate): class ptristate_inv(pgate.pgate):
""" """
ptristate generates gds of a parametrically sized tristate inverter. ptristate generates gds of a parametrically sized tristate inverter.
There is some flexibility in the size, but we do not allow multiple fingers There is some flexibility in the size, but we do not allow multiple fingers
to fit in the cell height. to fit in the cell height.
@ -27,14 +23,16 @@ class ptristate_inv(pgate.pgate):
def __init__(self, name, size=1, height=None): 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)) self.add_comment("size: {}".format(size))
# We are 2x since there are two series devices # We are 2x since there are two series devices
self.size = 2*size self.size = 2 * size
self.nmos_size = size self.nmos_size = size
self.beta = parameter["beta"] self.beta = parameter["beta"]
self.pmos_size = self.beta*size self.pmos_size = self.beta * size
self.nmos_width = self.nmos_size * drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
@ -75,10 +73,10 @@ class ptristate_inv(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + drc("well_enclosure_active") self.well_width = 2 * self.pmos.active_width + drc("well_enclosure_active")
# Add an extra space because we route the output on the right of the S/D # Add an extra space because we route the output on the right of the S/D
self.width = self.well_width + 0.5*self.m1_space self.width = self.well_width + 0.5 * self.m1_space
# Height is an input parameter, so it is not recomputed. # Height is an input parameter, so it is not recomputed.
# Make sure we can put a well above and below # Make sure we can put a well above and below
@ -104,43 +102,44 @@ class ptristate_inv(pgate.pgate):
""" Add vdd/gnd rails to the top and bottom. """ """ Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd", self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,0), offset=vector(0.5 * self.width, 0),
width=self.width) width=self.width)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def create_ptx(self): def create_ptx(self):
""" """
Create the PMOS and NMOS netlist. Create the PMOS and NMOS netlist.
""" """
# These are the inverter PMOS/NMOS # 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.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"]) self.connect_inst(["gnd", "in", "n2", "gnd"])
# These are the tristate PMOS/NMOS # These are the tristate PMOS/NMOS
self.pmos2_inst = self.add_inst(name="ptri_pmos2", mod=self.pmos) self.pmos2_inst = self.add_inst(name="ptri_pmos2", mod=self.pmos)
self.connect_inst(["out", "en_bar", "n1", "vdd"]) 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"]) self.connect_inst(["out", "en", "n2", "gnd"])
def place_ptx(self): def place_ptx(self):
""" """
Place PMOS and NMOS to the layout at the upper-most and lowest position Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel 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 \
nmos_yoff = self.top_bottom_space + 0.5*contact.well.height - self.top_bottom_space - 0.5 * contact.well.height
nmos_yoff = self.top_bottom_space + 0.5 * contact.well.height
# Tristate transistors # Tristate transistors
pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff) pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff)
@ -154,21 +153,24 @@ class ptristate_inv(pgate.pgate):
self.nmos2_pos = nmos1_pos + self.overlap_offset self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos) self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS # 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 # This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy()) self.well_pos = vector(0, self.nmos1_inst.uy())
def route_inputs(self): def route_inputs(self):
""" Route the gates """ """ 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.pmos2_inst, "en_bar", position="left")
self.route_single_gate(self.nmos2_inst, "en", position="left") self.route_single_gate(self.nmos2_inst, "en", position="left")
def route_outputs(self): def route_outputs(self):
""" Route the output (drains) together. """ """ Route the output (drains) together. """
@ -181,40 +183,41 @@ class ptristate_inv(pgate.pgate):
self.add_layout_pin(text="out", self.add_layout_pin(text="out",
layer="metal1", layer="metal1",
offset=nmos_drain_pos, offset=nmos_drain_pos,
height=pmos_drain_pos.y-nmos_drain_pos.y) height=pmos_drain_pos.y - nmos_drain_pos.y)
def add_well_contacts(self): 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") layer_stack = ("active", "contact", "metal1")
drain_pos = self.nmos1_inst.get_pin("S").center() drain_pos = self.nmos1_inst.get_pin("S").center()
vdd_pos = self.get_pin("vdd").center() vdd_pos = self.get_pin("vdd").center()
self.nwell_contact=self.add_via_center(layers=layer_stack, self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x,vdd_pos.y), offset=vector(drain_pos.x, vdd_pos.y),
implant_type="n", implant_type="n",
well_type="n") well_type="n")
gnd_pos = self.get_pin("gnd").center() gnd_pos = self.get_pin("gnd").center()
self.pwell_contact=self.add_via_center(layers=layer_stack, self.pwell_contact = self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x,gnd_pos.y), offset=vector(drain_pos.x, gnd_pos.y),
implant_type="p", implant_type="p",
well_type="p") well_type="p")
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd") self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd") self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """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.
total_power = self.return_power() # Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
return total_power return total_power
def get_cin(self): def get_cin(self):
return 9*spice["min_tx_gate_c"] return 9 * spice["min_tx_gate_c"]

View File

@ -9,9 +9,9 @@ import design
import debug import debug
from tech import drc, spice from tech import drc, spice
from vector import vector from vector import vector
from globals import OPTS
from sram_factory import factory from sram_factory import factory
class ptx(design.design): class ptx(design.design):
""" """
This module generates gds and spice of a parametrically NMOS or 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. 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 # We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will # 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 # over-write a design in GDS if one has and the other doesn't
@ -34,7 +41,7 @@ class ptx(design.design):
if num_contacts: if num_contacts:
name += "_c{}".format(num_contacts) name += "_c{}".format(num_contacts)
# replace periods with underscore for newer spice compatibility # replace periods with underscore for newer spice compatibility
name=name.replace('.','_') name = name.replace('.', '_')
debug.info(3, "creating ptx {0}".format(name)) debug.info(3, "creating ptx {0}".format(name))
design.design.__init__(self, name) design.design.__init__(self, name)
@ -51,42 +58,43 @@ class ptx(design.design):
# We must always create ptx layout for pbitcell # We must always create ptx layout for pbitcell
# some transistor sizes in other netlist depend on pbitcell # some transistor sizes in other netlist depend on pbitcell
self.create_layout() self.create_layout()
def create_layout(self): def create_layout(self):
"""Calls all functions related to the generation of the layout""" """Calls all functions related to the generation of the layout"""
self.setup_layout_constants() self.setup_layout_constants()
self.add_active() self.add_active()
self.add_well_implant() self.add_well_implant()
self.add_poly() self.add_poly()
self.add_active_contacts() self.add_active_contacts()
self.translate_all(self.active_offset) self.translate_all(self.active_offset)
# for run-time, we won't check every transitor DRC independently # for run-time, we won't check every transitor DRC independently
# but this may be uncommented for debug purposes # but this may be uncommented for debug purposes
#self.DRC() # self.DRC()
def create_netlist(self): def create_netlist(self):
pin_list = ["D", "G", "S", "B"] pin_list = ["D", "G", "S", "B"]
if self.tx_type=="nmos": if self.tx_type == "nmos":
body_dir = 'GROUND' 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' body_dir = 'POWER'
dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir] dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir]
self.add_pin_list(pin_list, dir_list) self.add_pin_list(pin_list, dir_list)
# self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
# " ".join(self.pins))) # " ".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
area_sd = 2.5*drc("minwidth_poly")*self.tx_width # be decided in the layout later.
perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width area_sd = 2.5 * drc("minwidth_poly") * 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], perimeter_sd = 2 * drc("minwidth_poly") + 2 * self.tx_width
self.mults, main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.tx_width, self.mults,
drc("minwidth_poly"), self.tx_width,
perimeter_sd, drc("minwidth_poly"))
area_sd) 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("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name)) # self.spice.append(".ENDS {0}".format(self.name))
@ -116,38 +124,39 @@ class ptx(design.design):
# The contacted poly pitch (or uncontacted in an odd technology) # The contacted poly pitch (or uncontacted in an odd technology)
self.poly_pitch = max(2*self.contact_to_gate + self.contact_width + self.poly_width, self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width,
self.poly_space) self.poly_space)
# The contacted poly pitch (or uncontacted in an odd technology) # The contacted poly pitch (or uncontacted in an odd technology)
self.contact_pitch = 2*self.contact_to_gate + self.contact_width + self.poly_width self.contact_pitch = 2 * self.contact_to_gate + self.contact_width + self.poly_width
# The enclosure of an active contact. Not sure about second term. # The enclosure of an active contact. Not sure about second term.
active_enclose_contact = max(drc("active_enclosure_contact"), active_enclose_contact = max(drc("active_enclosure_contact"),
(self.active_width - self.contact_width)/2) (self.active_width - self.contact_width) / 2)
# This is the distance from the edge of poly to the contacted end of active # 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 self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate
# Active width is determined by enclosure on both ends and contacted pitch, # Active width is determined by enclosure on both ends and contacted pitch,
# at least one poly and n-1 poly pitches # at least one poly and n-1 poly pitches
self.active_width = 2*self.end_to_poly + self.poly_width + (self.mults - 1)*self.poly_pitch self.active_width = 2 * self.end_to_poly + self.poly_width + (self.mults - 1) * self.poly_pitch
# Active height is just the transistor width # Active height is just the transistor width
self.active_height = self.tx_width self.active_height = self.tx_width
# Poly height must include poly extension over active # Poly height must include poly extension over active
self.poly_height = self.tx_width + 2*self.poly_extend_active self.poly_height = self.tx_width + 2 * self.poly_extend_active
# The active offset is due to the well extension # The active offset is due to the well extension
self.active_offset = vector([self.well_enclose_active]*2) self.active_offset = vector([self.well_enclose_active] * 2)
# Well enclosure of active, ensure minwidth as well # Well enclosure of active, ensure minwidth as well
if drc("has_{}well".format(self.well_type)): if drc("has_{}well".format(self.well_type)):
self.cell_well_width = max(self.active_width + 2*self.well_enclose_active, self.cell_well_width = max(self.active_width + 2 * self.well_enclose_active,
self.well_width) self.well_width)
self.cell_well_height = max(self.tx_width + 2*self.well_enclose_active, self.cell_well_height = max(self.tx_width + 2 * self.well_enclose_active,
self.well_width) self.well_width)
# We are going to shift the 0,0, so include that in the width and height # We are going to shift the 0,0, so include that in the width and height
self.height = self.cell_well_height - self.active_offset.y self.height = self.cell_well_height - self.active_offset.y
self.width = self.cell_well_width - self.active_offset.x self.width = self.cell_well_width - self.active_offset.x
@ -157,17 +166,20 @@ class ptx(design.design):
self.width = self.active_width self.width = self.active_width
# The active offset is due to the well extension # The active offset is due to the well extension
self.active_offset = vector([self.well_enclose_active]*2) self.active_offset = vector([self.well_enclose_active] * 2)
# This is the center of the first active contact offset (centered vertically) # This is the center of the first active contact offset (centered vertically)
self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5*self.contact_width, self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5 * self.contact_width,
0.5*self.active_height) 0.5 * self.active_height)
# Min area results are just flagged for now. # Min area results are just flagged for now.
debug.check(self.active_width*self.active_height>=drc("minarea_active"),"Minimum active area violated.") debug.check(self.active_width * self.active_height >= drc("minarea_active"),
# We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue. "Minimum active area violated.")
debug.check(self.poly_width*self.poly_height>=drc("minarea_poly"),"Minimum poly 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): 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 # 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 poly_width = poly_positions[-1].x - poly_positions[0].x + self.poly_width
if self.tx_type == "pmos": if self.tx_type == "pmos":
# This can be limited by poly to active spacing or the poly extension # This can be limited by poly to active spacing
distance_below_active = self.poly_width + max(self.poly_to_active,0.5*self.poly_height) # or the poly extension
poly_offset = poly_positions[0] - vector(0.5*self.poly_width, distance_below_active) 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: else:
# This can be limited by poly to active spacing or the poly extension # This can be limited by poly to active spacing
distance_above_active = max(self.poly_to_active,0.5*self.poly_height) # or the poly extension
poly_offset = poly_positions[0] + vector(-0.5*self.poly_width, distance_above_active) 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 # Remove the old pin and add the new one
self.remove_layout_pin("G") # only keep the main pin self.remove_layout_pin("G") # only keep the main pin
self.add_layout_pin(text="G", self.add_layout_pin(text="G",
@ -205,12 +223,13 @@ class ptx(design.design):
# This is the distance that we must route up or down from the center # 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 # of the contacts to avoid DRC violations to the other contacts
pin_offset = vector(0, 0.5*self.active_contact.second_layer_height \ pin_offset = vector(0, 0.5 * self.active_contact.second_layer_height \
+ self.m1_space + 0.5*self.m1_width) + self.m1_space + 0.5 * self.m1_width)
# This is the width of a m1 extend the ends of the pin # This is the width of a m1 extend the ends of the pin
end_offset = vector(self.m1_width/2,0) 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. # so reverse the directions for NMOS compared to PMOS.
if self.tx_type == "pmos": if self.tx_type == "pmos":
drain_dir = -1 drain_dir = -1
@ -219,17 +238,19 @@ class ptx(design.design):
drain_dir = 1 drain_dir = 1
source_dir = -1 source_dir = -1
if len(source_positions)>1: if len(source_positions) > 1:
source_offset = pin_offset.scale(source_dir,source_dir) source_offset = pin_offset.scale(source_dir,source_dir)
self.remove_layout_pin("S") # remove the individual connections self.remove_layout_pin("S") # remove the individual connections
# Add each vertical segment # Add each vertical segment
for a in source_positions: 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 # Add a single horizontal pin
self.add_layout_pin_segment_center(text="S", self.add_layout_pin_segment_center(text="S",
layer="metal1", layer="metal1",
start=source_positions[0]+source_offset-end_offset, start=source_positions[0] + source_offset - end_offset,
end=source_positions[-1]+source_offset+end_offset) end=source_positions[-1] + source_offset + end_offset)
if len(drain_positions)>1: if len(drain_positions)>1:
drain_offset = pin_offset.scale(drain_dir,drain_dir) drain_offset = pin_offset.scale(drain_dir,drain_dir)
@ -240,24 +261,27 @@ class ptx(design.design):
# Add a single horizontal pin # Add a single horizontal pin
self.add_layout_pin_segment_center(text="D", self.add_layout_pin_segment_center(text="D",
layer="metal1", layer="metal1",
start=drain_positions[0]+drain_offset-end_offset, start=drain_positions[0] + drain_offset - end_offset,
end=drain_positions[-1]+drain_offset+end_offset) end=drain_positions[-1] + drain_offset + end_offset)
def add_poly(self): def add_poly(self):
""" """
Add the poly gates(s) and (optionally) connect them. Add the poly gates(s) and (optionally) connect them.
""" """
# poly is one contacted spacing from the end and down an extension # 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) + vector(self.end_to_poly, -self.poly_extend_active)
# poly_positions are the bottom center of the poly gates # poly_positions are the bottom center of the poly gates
poly_positions = [] 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 # order for the accessors
for i in range(0, self.mults): 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", self.add_rect_center(layer="poly",
offset=poly_offset, offset=poly_offset,
height=self.poly_height, height=self.poly_height,
@ -274,8 +298,8 @@ class ptx(design.design):
self.connect_fingered_poly(poly_positions) self.connect_fingered_poly(poly_positions)
def add_active(self): def add_active(self):
""" """
Adding the diffusion (active region = diffusion region) Adding the diffusion (active region = diffusion region)
""" """
self.add_rect(layer="active", self.add_rect(layer="active",
offset=self.active_offset, offset=self.active_offset,
@ -284,11 +308,11 @@ class ptx(design.design):
# If the implant must enclose the active, shift offset # If the implant must enclose the active, shift offset
# and increase width/height # and increase width/height
enclose_width = drc("implant_enclosure_active") enclose_width = drc("implant_enclosure_active")
enclose_offset = [enclose_width]*2 enclose_offset = [enclose_width] * 2
self.add_rect(layer="{}implant".format(self.implant_type), self.add_rect(layer="{}implant".format(self.implant_type),
offset=self.active_offset - enclose_offset, offset=self.active_offset - enclose_offset,
width=self.active_width + 2*enclose_width, width=self.active_width + 2 * enclose_width,
height=self.active_height + 2*enclose_width) height=self.active_height + 2 * enclose_width)
def add_well_implant(self): def add_well_implant(self):
""" """
@ -320,15 +344,16 @@ class ptx(design.design):
# The first one will always be a source # The first one will always be a source
source_positions = [self.contact_offset] source_positions = [self.contact_offset]
drain_positions = [] 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. # order for the accessors.
for i in range(self.mults): for i in range(self.mults):
if i%2: if i%2:
# It's a source... so offset from previous drain. # It's a source... so offset from previous drain.
source_positions.append(drain_positions[-1] + vector(self.contact_pitch,0)) source_positions.append(drain_positions[-1] + vector(self.contact_pitch, 0))
else: else:
# It's a drain... so offset from previous source. # It's a drain... so offset from previous source.
drain_positions.append(source_positions[-1] + vector(self.contact_pitch,0)) drain_positions.append(source_positions[-1] + vector(self.contact_pitch, 0))
return [source_positions,drain_positions] return [source_positions,drain_positions]
@ -371,9 +396,12 @@ class ptx(design.design):
def get_cin(self): def get_cin(self):
"""Returns the relative gate cin of the tx""" """Returns the relative gate cin of the tx"""
return self.tx_width/drc("minwidth_tx") return self.tx_width / drc("minwidth_tx")
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function.""" """
self.add_graph_edges(graph, port_nets) 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 import debug
from tech import drc from tech import drc
from vector import vector from vector import vector
import contact
from globals import OPTS
from sram_factory import factory from sram_factory import factory
import logical_effort import logical_effort
class single_level_column_mux(pgate.pgate): class single_level_column_mux(pgate.pgate):
""" """
This module implements the columnmux bitline cell used in the design. This module implements the columnmux bitline cell used in the design.
Creates a single columnmux cell with the given integer size relative Creates a single columnmux cell with the given integer size relative
to minimum size. Default is 8x. Per Samira and Hodges-Jackson book: 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"): def __init__(self, name, tx_size=8, bitcell_bl="bl", bitcell_br="br"):
@ -38,7 +38,7 @@ class single_level_column_mux(pgate.pgate):
def create_layout(self): def create_layout(self):
self.pin_height = 2*self.m2_width self.pin_height = 2 * self.m2_width
self.width = self.bitcell.width self.width = self.bitcell.width
self.height = self.nmos_upper.uy() + self.pin_height self.height = self.nmos_upper.uy() + self.pin_height
self.connect_poly() self.connect_poly()
@ -50,11 +50,9 @@ class single_level_column_mux(pgate.pgate):
self.bitcell = factory.create(module_type="bitcell") self.bitcell = factory.create(module_type="bitcell")
# Adds nmos_lower,nmos_upper to the module # Adds nmos_lower,nmos_upper to the module
self.ptx_width = self.tx_size*drc("minwidth_tx") self.ptx_width = self.tx_size * drc("minwidth_tx")
self.nmos = factory.create(module_type="ptx", width=self.ptx_width) self.nmos = factory.create(module_type="ptx", width=self.ptx_width)
self.add_mod(self.nmos) self.add_mod(self.nmos)
def add_pins(self): def add_pins(self):
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"]) self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
@ -68,11 +66,11 @@ class single_level_column_mux(pgate.pgate):
# bl and br # bl and br
self.add_layout_pin(text="bl", self.add_layout_pin(text="bl",
layer="metal2", layer="metal2",
offset=bl_pos + vector(0,self.height - self.pin_height), offset=bl_pos + vector(0, self.height - self.pin_height),
height=self.pin_height) height=self.pin_height)
self.add_layout_pin(text="br", self.add_layout_pin(text="br",
layer="metal2", layer="metal2",
offset=br_pos + vector(0,self.height - self.pin_height), offset=br_pos + vector(0, self.height - self.pin_height),
height=self.pin_height) height=self.pin_height)
# bl_out and br_out # bl_out and br_out
@ -85,35 +83,34 @@ class single_level_column_mux(pgate.pgate):
offset=br_pos, offset=br_pos,
height=self.pin_height) height=self.pin_height)
def add_ptx(self): def add_ptx(self):
""" Create the two pass gate NMOS transistors to switch the bitlines""" """ Create the two pass gate NMOS transistors to switch the bitlines"""
# Space it in the center # 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) \
self.nmos_lower=self.add_inst(name="mux_tx1", + vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0)
mod=self.nmos, self.nmos_lower = self.add_inst(name="mux_tx1",
offset=nmos_lower_position) mod=self.nmos,
offset=nmos_lower_position)
self.connect_inst(["bl", "sel", "bl_out", "gnd"]) self.connect_inst(["bl", "sel", "bl_out", "gnd"])
# This aligns it directly above the other tx with gates abutting # 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 \
self.nmos_upper=self.add_inst(name="mux_tx2", + vector(0, self.nmos.active_height + self.poly_space)
mod=self.nmos, self.nmos_upper = self.add_inst(name="mux_tx2",
offset=nmos_upper_position) mod=self.nmos,
offset=nmos_upper_position)
self.connect_inst(["br", "sel", "br_out", "gnd"]) self.connect_inst(["br", "sel", "br_out", "gnd"])
def connect_poly(self): def connect_poly(self):
""" Connect the poly gate of the two pass transistors """ """ Connect the poly gate of the two pass transistors """
height=self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by() height = self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by()
self.add_layout_pin(text="sel", self.add_layout_pin(text="sel",
layer="poly", layer="poly",
offset=self.nmos_lower.get_pin("G").ll(), offset=self.nmos_lower.get_pin("G").ll(),
height=height) height=height)
def connect_bitlines(self): def connect_bitlines(self):
""" Connect the bitlines to the mux transistors """ """ Connect the bitlines to the mux transistors """
# These are on metal2 # These are on metal2
@ -129,52 +126,61 @@ class single_level_column_mux(pgate.pgate):
nmos_upper_d_pin = self.nmos_upper.get_pin("D") nmos_upper_d_pin = self.nmos_upper.get_pin("D")
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D # Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=bl_pin.bc(), offset=bl_pin.bc(),
directions=("V","V")) directions=("V", "V"))
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=br_out_pin.uc(), offset=br_out_pin.uc(),
directions=("V","V")) directions=("V", "V"))
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_upper_s_pin.center(), offset=nmos_upper_s_pin.center(),
directions=("V","V")) directions=("V", "V"))
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_lower_d_pin.center(), offset=nmos_lower_d_pin.center(),
directions=("V","V")) directions=("V", "V"))
# bl -> nmos_upper/D on metal1 # bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2 # 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 # halfway up, move over
mid1 = bl_out_pin.uc().scale(1,0.4)+nmos_upper_s_pin.bc().scale(0,0.4) mid1 = bl_out_pin.uc().scale(1, 0.4) \
mid2 = bl_out_pin.uc().scale(0,0.4)+nmos_upper_s_pin.bc().scale(1,0.4) + nmos_upper_s_pin.bc().scale(0, 0.4)
self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()]) 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 -> nmos_lower/D on metal2
# br_out -> nmos_lower/S on metal1 # 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 # halfway up, move over
mid1 = br_pin.bc().scale(1,0.5)+nmos_lower_d_pin.uc().scale(0,0.5) mid1 = br_pin.bc().scale(1,0.5) \
mid2 = br_pin.bc().scale(0,0.5)+nmos_lower_d_pin.uc().scale(1,0.5) + nmos_lower_d_pin.uc().scale(0,0.5)
self.add_path("metal2",[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()]) 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): def add_wells(self):
""" """
Add a well and implant over the whole cell. Also, add the Add a well and implant over the whole cell. Also, add the
pwell contact (if it exists) pwell contact (if it exists)
""" """
# Add it to the right, aligned in between the two tx # 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_pos = vector(self.bitcell.width,
active_via = self.add_via_center(layers=("active", "contact", "metal1"), self.nmos_upper.by() - 0.5 * self.poly_space)
offset=active_pos, self.add_via_center(layers=("active", "contact", "metal1"),
implant_type="p", offset=active_pos,
well_type="p") implant_type="p",
well_type="p")
# Add the M1->M2->M3 stack
# Add the M1->M2->M3 stack
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=active_pos) offset=active_pos)
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
@ -185,13 +191,22 @@ class single_level_column_mux(pgate.pgate):
# Add well enclosure over all the tx and contact # Add well enclosure over all the tx and contact
self.add_rect(layer="pwell", self.add_rect(layer="pwell",
offset=vector(0,0), offset=vector(0, 0),
width=self.bitcell.width, width=self.bitcell.width,
height=self.height) height=self.height)
def get_stage_effort(self, corner, slew, load): 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 parasitic_delay = 1
cin = 2*self.tx_size #This is not CMOS, so using this may be incorrect. # 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) cin = 2 * self.tx_size
return logical_effort.logical_effort("column_mux",
self.tx_size,
cin,
load,
parasitic_delay,
False)