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"
process_corners = ["TT"]
supply_voltages = [ 5.0 ]
temperatures = [ 25 ]
supply_voltages = [5.0]
temperatures = [25]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size, num_words, tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,11 +3,13 @@ num_words = 256
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [ 3.3 ]
temperatures = [ 25 ]
supply_voltages = [3.3]
temperatures = [25]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

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

View File

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

View File

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

View File

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

View File

@ -8,14 +8,16 @@
import contact
import design
import debug
from tech import drc, parameter, spice
from tech import drc
from vector import vector
from globals import OPTS
from sram_factory import factory
class pgate(design.design):
"""
This is a module that implements some shared functions for parameterized gates.
This is a module that implements some shared
functions for parameterized gates.
"""
def __init__(self, name, height=None):
@ -29,78 +31,85 @@ class pgate(design.design):
self.height = b.height
self.create_netlist()
if not OPTS.netlist_only:
if not OPTS.netlist_only:
self.create_layout()
self.add_boundary()
self.DRC_LVS()
def create_netlist(self):
""" Pure virtual function """
debug.error("Must over-ride create_netlist.",-1)
debug.error("Must over-ride create_netlist.", -1)
def create_layout(self):
""" 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. """
source_pin = inst.get_pin(pin)
supply_pin = self.get_pin(supply)
if supply_pin.overlaps(source_pin):
return
if supply=="gnd":
height=supply_pin.by()-source_pin.by()
elif supply=="vdd":
height=supply_pin.uy()-source_pin.by()
if supply == "gnd":
height = supply_pin.by() - source_pin.by()
elif supply == "vdd":
height = supply_pin.uy() - source_pin.by()
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",
offset=source_pin.ll(),
height=height,
width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=False):
""" Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or right of gate. """
"""
Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or
right of gate.
"""
nmos_gate_pin = nmos_inst.get_pin("G")
pmos_gate_pin = pmos_inst.get_pin("G")
# Check if the gates are aligned and give an error if they aren't!
debug.check(nmos_gate_pin.ll().x==pmos_gate_pin.ll().x, "Connecting unaligned gates not supported.")
debug.check(nmos_gate_pin.ll().x == pmos_gate_pin.ll().x,
"Connecting unaligned gates not supported.")
# Pick point on the left of NMOS and connect down to PMOS
nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5*self.poly_width,0)
pmos_gate_pos = vector(nmos_gate_pos.x,pmos_gate_pin.bc().y)
self.add_path("poly",[nmos_gate_pos,pmos_gate_pos])
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)
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
# 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:
contact_width = contact.poly.height
contact_m1_width = contact.poly.second_layer_height
contact_m1_height = contact.poly.second_layer_width
directions = ("H","V")
directions = ("H", "V")
else:
contact_width = contact.poly.width
contact_m1_width = contact.poly.second_layer_width
contact_m1_height = contact.poly.second_layer_height
directions = ("V","H")
directions = ("V", "H")
if position=="center":
contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0)
elif position=="farleft":
contact_offset = left_gate_offset - vector(0.5*contact.poly.width, 0)
elif position=="left":
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)
if position == "center":
contact_offset = left_gate_offset \
+ vector(0.5 * self.poly_width, 0)
elif position == "farleft":
contact_offset = left_gate_offset \
- vector(0.5 * contact.poly.width, 0)
elif position == "left":
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:
debug.error("Invalid contact placement option.", -1)
@ -110,29 +119,26 @@ class pgate(design.design):
offset=contact_offset,
directions=directions)
# self.add_layout_pin_segment_center(text=name,
# layer="metal1",
# start=left_gate_offset.scale(0,1),
# end=left_gate_offset)
self.add_layout_pin_rect_center(text=name,
layer="metal1",
offset=contact_offset,
width=contact_m1_width,
height=contact_m1_height)
# This is to ensure that the contact is connected to the gate
mid_point = contact_offset.scale(0.5,1)+left_gate_offset.scale(0.5,0)
# This is to ensure that the contact is
# connected to the gate
mid_point = contact_offset.scale(0.5, 1) \
+ left_gate_offset.scale(0.5, 0)
self.add_rect_center(layer="poly",
offset=mid_point,
height=contact.poly.first_layer_width,
width=left_gate_offset.x-contact_offset.x)
width=left_gate_offset.x - contact_offset.x)
def extend_wells(self, middle_position):
""" Extend the n/p wells to cover whole cell """
# 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
nwell_height = max_y_offset - middle_position.y
if drc("has_nwell"):
@ -145,8 +151,8 @@ class pgate(design.design):
width=self.well_width,
height=nwell_height)
pwell_position = vector(0,-0.5*self.m1_width)
pwell_height = middle_position.y-pwell_position.y
pwell_position = vector(0, -0.5 * self.m1_width)
pwell_height = middle_position.y - pwell_position.y
if drc("has_pwell"):
self.add_rect(layer="pwell",
offset=pwell_position,
@ -163,38 +169,45 @@ class pgate(design.design):
layer_stack = ("active", "contact", "metal1")
# To the right a spacing away from the pmos right active edge
contact_xoffset = pmos_pos.x + pmos.active_width + drc("active_to_body_active")
# Must be at least an well enclosure of active down from the top of the well
contact_xoffset = pmos_pos.x + pmos.active_width \
+ drc("active_to_body_active")
# Must be at least an well enclosure of active down
# from the top of the well
# OR align the active with the top of PMOS active.
max_y_offset = self.height + 0.5*self.m1_width
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,
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)
# Offset by half a contact in x and y
contact_offset += vector(0.5*pmos.active_contact.first_layer_width,
0.5*pmos.active_contact.first_layer_height)
self.nwell_contact=self.add_via_center(layers=layer_stack,
offset=contact_offset,
directions=("H","V"),
implant_type="n",
well_type="n")
contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
0.5 * pmos.active_contact.first_layer_height)
self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=contact_offset,
directions=("H", "V"),
implant_type="n",
well_type="n")
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,
height=self.height - contact_offset.y)
# Now add the full active and implant for the PMOS
#active_offset = pmos_pos + vector(pmos.active_width,0)
# This might be needed if the spacing between the actives is not satisifed
# active_offset = pmos_pos + vector(pmos.active_width,0)
# This might be needed if the spacing between the actives
# is not satisifed
# self.add_rect(layer="active",
# offset=active_offset,
# width=pmos.active_contact.width,
# height=pmos.active_height)
# we need to ensure implants don't overlap and are spaced far enough apart
# we need to ensure implants don't overlap and are
# spaced far enough apart
# implant_spacing = self.implant_space+self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active)
# implant_width = pmos.active_contact.width + 2*self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) \
# - vector(0,self.implant_enclose_active)
# implant_width = pmos.active_contact.width \
# + 2*self.implant_enclose_active
# implant_height = pmos.active_height + 2*self.implant_enclose_active
# self.add_rect(layer="nimplant",
# offset=implant_offset,
@ -208,39 +221,45 @@ class pgate(design.design):
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
contact_xoffset = nmos_pos.x + nmos.active_width + drc("active_to_body_active")
# Must be at least an well enclosure of active up from the bottom of the well
contact_xoffset = nmos_pos.x + nmos.active_width \
+ drc("active_to_body_active")
# Must be at least an well enclosure of active up
# from the bottom of the well
contact_yoffset = max(nmos_pos.y,
self.well_enclose_active - nmos.active_contact.first_layer_height/2)
self.well_enclose_active \
- nmos.active_contact.first_layer_height / 2)
contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact
contact_offset += vector(0.5*nmos.active_contact.first_layer_width,
0.5*nmos.active_contact.first_layer_height)
self.pwell_contact=self.add_via_center(layers=layer_stack,
offset=contact_offset,
directions=("H","V"),
implant_type="p",
well_type="p")
contact_offset += vector(0.5 * nmos.active_contact.first_layer_width,
0.5 * nmos.active_contact.first_layer_height)
self.pwell_contact= self.add_via_center(layers=layer_stack,
offset=contact_offset,
directions=("H", "V"),
implant_type="p",
well_type="p")
self.add_rect_center(layer="metal1",
offset=contact_offset.scale(1,0.5),
width=self.pwell_contact.mod.second_layer_width,
height=contact_offset.y)
# Now add the full active and implant for the NMOS
# active_offset = nmos_pos + vector(nmos.active_width,0)
# This might be needed if the spacing between the actives is not satisifed
# active_offset = nmos_pos + vector(nmos.active_width,0)
# This might be needed if the spacing between the actives
# is not satisifed
# self.add_rect(layer="active",
# offset=active_offset,
# width=nmos.active_contact.width,
# height=nmos.active_height)
# implant_spacing = self.implant_space+self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active)
# implant_width = nmos.active_contact.width + 2*self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) \
# - vector(0,self.implant_enclose_active)
# implant_width = nmos.active_contact.width \
# + 2*self.implant_enclose_active
# implant_height = nmos.active_height + 2*self.implant_enclose_active
# self.add_rect(layer="pimplant",
# offset=implant_offset,

View File

@ -16,6 +16,7 @@ from utils import round_to_grid
import logical_effort
from sram_factory import factory
class pinv(pgate.pgate):
"""
Pinv generates gds of a parametrically sized inverter. The
@ -28,12 +29,14 @@ class pinv(pgate.pgate):
def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True):
debug.info(2, "creating pinv structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pinv structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
self.size = size
self.nmos_size = size
self.pmos_size = beta*size
self.pmos_size = beta * size
self.beta = beta
self.route_output = False
@ -44,7 +47,7 @@ class pinv(pgate.pgate):
self.add_pins()
self.determine_tx_mults()
self.add_ptx()
self.create_ptx()
self.create_ptx()
def create_layout(self):
""" Calls all functions related to the generation of the layout """
@ -54,7 +57,11 @@ class pinv(pgate.pgate):
self.add_well_contacts()
self.extend_wells(self.well_pos)
self.connect_rails()
self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", position="farleft")
self.route_input_gate(self.pmos_inst,
self.nmos_inst,
self.output_pos.y,
"A",
position="farleft")
self.route_outputs()
def add_pins(self):
@ -63,7 +70,6 @@ class pinv(pgate.pgate):
dir_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list)
def determine_tx_mults(self):
"""
Determines the number of fingers needed to achieve the size within
@ -73,58 +79,71 @@ class pinv(pgate.pgate):
# This may make the result differ when the layout is created...
if OPTS.netlist_only:
self.tx_mults = 1
self.nmos_width = self.nmos_size*drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx")
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
return
# Do a quick sanity check and bail if unlikely feasible height
# Sanity check. can we make an inverter in the height with minimum tx sizes?
# Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain)
# Sanity check. can we make an inverter in the height
# with minimum tx sizes?
# Assume we need 3 metal 1 pitches (2 power rails, one
# between the tx for the drain)
# plus the tx height
nmos = factory.create(module_type="ptx", tx_type="nmos")
pmos = factory.create(module_type="ptx", width=drc("minwidth_tx"), tx_type="pmos")
pmos = factory.create(module_type="ptx",
width=drc("minwidth_tx"),
tx_type="pmos")
tx_height = nmos.poly_height + pmos.poly_height
# rotated m1 pitch or poly to active spacing
min_channel = max(contact.poly.width + self.m1_space,
contact.poly.width + 2*drc("poly_to_active"))
# This is the extra space needed to ensure DRC rules to the active contacts
extra_contact_space = max(-nmos.get_pin("D").by(),0)
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 a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"), self.poly_space)
total_height = tx_height + min_channel + 2*self.top_bottom_space
debug.check(self.height> total_height,"Cell height {0} too small for simple min height {1}.".format(self.height,total_height))
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))
# Determine the height left to the transistors to determine the number of fingers
tx_height_available = self.height - min_channel - 2*self.top_bottom_space
# Divide the height in half. Could divide proportional to beta, but this makes
# connecting wells of multiple cells easier.
# Determine the height left to the transistors to determine
# the number of fingers
tx_height_available = self.height - min_channel - 2 * self.top_bottom_space
# Divide the height in half. Could divide proportional to beta,
# but this makes connecting wells of multiple cells easier.
# Subtract the poly space under the rail of the tx
nmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly")
pmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly")
nmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly")
pmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly")
debug.info(2,"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available,
nmos_height_available,
pmos_height_available))
debug.info(2,
"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available,
nmos_height_available,
pmos_height_available))
# Determine the number of mults for each to fit width into available space
self.nmos_width = self.nmos_size*drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx")
nmos_required_mults = max(int(ceil(self.nmos_width/nmos_height_available)),1)
pmos_required_mults = max(int(ceil(self.pmos_width/pmos_height_available)),1)
# Determine the number of mults for each to fit width
# into available space
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
nmos_required_mults = max(int(ceil(self.nmos_width / nmos_height_available)), 1)
pmos_required_mults = max(int(ceil(self.pmos_width / pmos_height_available)), 1)
# The mults must be the same for easy connection of poly
self.tx_mults = max(nmos_required_mults, pmos_required_mults)
# Recompute each mult width and check it isn't too small
# This could happen if the height is narrow and the size is small
# User should pick a bigger size to fix it...
# We also need to round the width to the grid or we will end up with LVS property
# mismatch errors when fingers are not a grid length and get rounded in the offset geometry.
# We also need to round the width to the grid or we will end up
# with LVS property mismatch errors when fingers are not a grid
# length and get rounded in the offset geometry.
self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults)
debug.check(self.nmos_width>=drc("minwidth_tx"),"Cannot finger NMOS transistors to fit cell height.")
debug.check(self.nmos_width >= drc("minwidth_tx"),
"Cannot finger NMOS transistors to fit cell height.")
self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults)
debug.check(self.pmos_width>=drc("minwidth_tx"),"Cannot finger PMOS transistors to fit cell height.")
debug.check(self.pmos_width >= drc("minwidth_tx"),
"Cannot finger PMOS transistors to fit cell height.")
def setup_layout_constants(self):
"""
@ -136,9 +155,7 @@ class pinv(pgate.pgate):
self.well_width = self.pmos.active_width + self.pmos.active_contact.width \
+ drc("active_to_body_active") + 2*drc("well_enclosure_active")
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# Height is an input parameter, so it is not recomputed.
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
@ -162,58 +179,57 @@ class pinv(pgate.pgate):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
"""
Create the PMOS and NMOS netlist.
"""
self.pmos_inst=self.add_inst(name="pinv_pmos",
mod=self.pmos)
self.pmos_inst = self.add_inst(name="pinv_pmos",
mod=self.pmos)
self.connect_inst(["Z", "A", "vdd", "vdd"])
self.nmos_inst=self.add_inst(name="pinv_nmos",
mod=self.nmos)
self.nmos_inst = self.add_inst(name="pinv_nmos",
mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"])
def place_ptx(self):
"""
"""
Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
# place PMOS so it is half a poly spacing down from the top
self.pmos_pos = self.pmos.active_offset.scale(1,0) \
+ vector(0, self.height-self.pmos.active_height-self.top_bottom_space)
self.pmos_pos = self.pmos.active_offset.scale(1, 0) \
+ vector(0,
self.height - self.pmos.active_height - self.top_bottom_space)
self.pmos_inst.place(self.pmos_pos)
# place NMOS so that it is half a poly spacing up from the bottom
self.nmos_pos = self.nmos.active_offset.scale(1,0) + vector(0,self.top_bottom_space)
self.nmos_pos = self.nmos.active_offset.scale(1, 0) \
+ vector(0, self.top_bottom_space)
self.nmos_inst.place(self.nmos_pos)
# Output position will be in between the PMOS and NMOS drains
pmos_drain_pos = self.pmos_inst.get_pin("D").ll()
nmos_drain_pos = self.nmos_inst.get_pin("D").ul()
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())
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())
def route_outputs(self):
""" Route the output (drains) together. Optionally, routes output to edge. """
"""
Route the output (drains) together.
Optionally, routes output to edge.
"""
# Get the drain pins
nmos_drain_pin = self.nmos_inst.get_pin("D")
@ -222,14 +238,14 @@ class pinv(pgate.pgate):
# Pick point at right most of NMOS and connect down to PMOS
nmos_drain_pos = nmos_drain_pin.bc()
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos])
self.add_path("metal1", [nmos_drain_pos, pmos_drain_pos])
# 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
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",
layer="metal1",
start=mid_drain_offset,
@ -238,8 +254,8 @@ class pinv(pgate.pgate):
# This leaves the output as an internal pin (min sized)
self.add_layout_pin_rect_center(text="Z",
layer="metal1",
offset=mid_drain_offset + vector(0.5*self.m1_width,0))
offset=mid_drain_offset \
+ vector(0.5 * self.m1_width, 0))
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
@ -251,9 +267,9 @@ class pinv(pgate.pgate):
def connect_rails(self):
""" 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):
"""Returns dynamic and leakage power. Results in nW"""
@ -268,27 +284,35 @@ class pinv(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.5
return transition_prob*(c_load + c_para)
return transition_prob * (c_load + c_para)
def input_load(self):
"""Return the capacitance of the gate connection in generic capacitive
units relative to the minimum width of a transistor"""
"""
Return the capacitance of the gate connection in generic capacitive
units relative to the minimum width of a transistor
"""
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
parasitic_delay = 1
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall.
Input inverted by this stage.
"""
parasitic_delay = 1
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, 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)

View File

@ -7,12 +7,10 @@
#
import debug
import pgate
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from sram_factory import factory
class pinvbuf(pgate.pgate):
"""
This is a simple inverter/buffer used for driving loads. It is
@ -31,11 +29,10 @@ class pinvbuf(pgate.pgate):
# 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
self.size = size
self.predriver_size = max(int(self.size/(self.stage_effort/2)),1)
# Creates the netlist and layout
pgate.pgate.__init__(self, name)
self.predriver_size = max(int(self.size / (self.stage_effort / 2)), 1)
# Creates the netlist and layout
pgate.pgate.__init__(self, name)
def create_netlist(self):
self.add_pins()
@ -44,8 +41,8 @@ class pinvbuf(pgate.pgate):
def create_layout(self):
self.width = 2*self.inv1.width + self.inv2.width
self.height = 2*self.inv1.height
self.width = 2 * self.inv1.width + self.inv2.width
self.height = 2 * self.inv1.height
self.place_modules()
self.route_wires()
@ -53,7 +50,6 @@ class pinvbuf(pgate.pgate):
self.offset_all_coordinates()
def add_pins(self):
self.add_pin("A")
self.add_pin("Zb")
@ -64,96 +60,100 @@ class pinvbuf(pgate.pgate):
def add_modules(self):
# Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(self.predriver_size/self.stage_effort))
self.inv = factory.create(module_type="pinv", size=input_size, height=self.row_height)
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.add_mod(self.inv)
self.inv1 = factory.create(module_type="pinv", size=self.predriver_size, height=self.row_height)
self.inv1 = factory.create(module_type="pinv",
size=self.predriver_size,
height=self.row_height)
self.add_mod(self.inv1)
self.inv2 = factory.create(module_type="pinv", size=self.size, height=self.row_height)
self.inv2 = factory.create(module_type="pinv",
size=self.size,
height=self.row_height)
self.add_mod(self.inv2)
def create_insts(self):
# Create INV1 (capacitance shield)
self.inv1_inst=self.add_inst(name="buf_inv1",
mod=self.inv)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv1_inst = self.add_inst(name="buf_inv1",
mod=self.inv)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv2_inst=self.add_inst(name="buf_inv2",
mod=self.inv1)
self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
self.inv2_inst = self.add_inst(name="buf_inv2",
mod=self.inv1)
self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
self.inv3_inst=self.add_inst(name="buf_inv3",
mod=self.inv2)
self.connect_inst(["z_int", "Zb", "vdd", "gnd"])
self.inv3_inst = self.add_inst(name="buf_inv3",
mod=self.inv2)
self.connect_inst(["z_int", "Zb", "vdd", "gnd"])
self.inv4_inst=self.add_inst(name="buf_inv4",
mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
self.inv4_inst = self.add_inst(name="buf_inv4",
mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_modules(self):
# 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
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
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
self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),2*self.inv2.height),
mirror = "MX")
self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),
2 * self.inv2.height),
mirror="MX")
def route_wires(self):
# inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z")
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()])
# inv2 Z to inv3 A
z2_pin = self.inv2_inst.get_pin("Z")
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()])
# inv1 Z to inv4 A (up and over)
z1_pin = self.inv1_inst.get_pin("Z")
a4_pin = self.inv4_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a4_pin.cy())
self.add_wire(("metal1","via1","metal2"), [z1_pin.center(), mid_point, a4_pin.center()])
self.add_via_center(layers=("metal1","via1","metal2"),
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_via_center(layers=("metal1", "via1", "metal2"),
offset=z1_pin.center())
def add_layout_pins(self):
# 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",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# 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",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=gnd_pin.height())
# 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",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
@ -161,22 +161,21 @@ class pinvbuf(pgate.pgate):
self.add_layout_pin_rect_center(text="Z",
layer="metal2",
offset=z_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=z_pin.center())
zb_pin = self.inv3_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Zb",
layer="metal2",
offset=zb_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=zb_pin.center())
a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_rect_center(text="A",
layer="metal2",
offset=a_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=a_pin.center())
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):
"""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 = []
stage1_cout = self.inv1.get_cin() + self.inv2.get_cin()
stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise)

View File

@ -10,10 +10,10 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory
class pnand2(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nand.
@ -22,17 +22,19 @@ class pnand2(pgate.pgate):
def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nand """
debug.info(2, "creating pnand2 structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pnand2 structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
self.size = size
self.nmos_size = 2*size
self.pmos_size = parameter["beta"]*size
self.nmos_width = self.nmos_size*drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx")
self.nmos_size = 2 * size
self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
# 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
# Creates the netlist and layout
@ -61,7 +63,6 @@ class pnand2(pgate.pgate):
dir_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list)
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx",
@ -90,113 +91,126 @@ class pnand2(pgate.pgate):
self.m3_space + contact.m2m3.second_layer_width)
# Compute the other pmos2 location, but determining offset to overlap the
# Compute the other pmos2 location,
# but determining offset to overlap the
# source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + contact.active.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active")
self.well_width = 2 * self.pmos.active_width + contact.active.width \
+ 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# This is the extra space needed to ensure DRC rules to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(),0)
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"), self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
offset=vector(0.5*self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
"""
Add PMOS and NMOS to the netlist.
"""
self.pmos1_inst=self.add_inst(name="pnand2_pmos1",
mod=self.pmos)
self.pmos1_inst = self.add_inst(name="pnand2_pmos1",
mod=self.pmos)
self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
mod=self.pmos)
self.connect_inst(["Z", "B", "vdd", "vdd"])
self.nmos1_inst=self.add_inst(name="pnand2_nmos1",
mod=self.nmos)
self.nmos1_inst = self.add_inst(name="pnand2_nmos1",
mod=self.nmos)
self.connect_inst(["Z", "B", "net1", "gnd"])
self.nmos2_inst=self.add_inst(name="pnand2_nmos2",
mod=self.nmos)
self.nmos2_inst = self.add_inst(name="pnand2_nmos2",
mod=self.nmos)
self.connect_inst(["net1", "A", "gnd", "gnd"])
def place_ptx(self):
"""
"""
Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.height - self.pmos.active_height \
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst.place(self.pmos2_pos)
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
nmos1_pos = vector(self.pmos.active_offset.x,
self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos)
self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
# 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))
# This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy())
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies AFTER the wells are created """
"""
Add n/p well taps to the layout and connect to supplies
AFTER the wells are created
"""
self.add_nwell_contact(self.pmos, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
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):
""" Route the A and B inputs """
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + 0.5*self.m2_width
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
+ self.m2_space + 0.5 * self.m2_width
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
"B",
position="center")
# This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A")
def route_output(self):
""" Route the Z output """
# PMOS1 drain
# PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center()
# NMOS2 drain
@ -204,24 +218,26 @@ class pnand2(pgate.pgate):
bottom_pin_offset = nmos_pin.center()
# Output pin
out_offset = vector(nmos_pin.center().x + self.m1_pitch,self.inputA_yoffset)
out_offset = vector(nmos_pin.center().x + self.m1_pitch,
self.inputA_yoffset)
# Midpoints of the L routes go horizontal first then vertical
mid1_offset = vector(out_offset.x, top_pin_offset.y)
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"),
offset=pmos_pin.center(),
directions=("V","H"))
directions=("V", "H"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center(),
directions=("V","H"))
directions=("V", "H"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=out_offset)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset])
self.add_path("metal2",
[top_pin_offset, mid1_offset, out_offset,
mid2_offset, bottom_pin_offset])
# This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z",
@ -243,21 +259,32 @@ class pnand2(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1875
return transition_prob*(c_load + c_para)
return transition_prob * (c_load + c_para)
def input_load(self):
"""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):
"""Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
parasitic_delay = 2
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise)
Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall.
Input inverted by this stage.
"""
parasitic_delay = 2
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise)
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)
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)

View File

@ -10,10 +10,10 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory
class pnand3(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nand.
@ -22,24 +22,25 @@ class pnand3(pgate.pgate):
def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 3 input nand """
debug.info(2, "creating pnand3 structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pnand3 structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
# We have trouble pitch matching a 3x sizes to the bitcell...
# If we relax this, we could size this better.
self.size = size
self.nmos_size = 2*size
self.pmos_size = parameter["beta"]*size
self.nmos_width = self.nmos_size*drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx")
self.nmos_size = 2 * size
self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
# 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
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def add_pins(self):
""" Adds pins for spice netlist """
@ -90,41 +91,44 @@ class pnand3(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 3*self.pmos.active_width + self.pmos.active_contact.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") \
self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
+ 2 * drc("active_to_body_active") + 2 * drc("well_enclosure_active") \
- self.overlap_offset.x
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# 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")
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
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"), self.poly_space)
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space \
+ extra_contact_space,
drc("poly_extend_active"),
self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
"""
Create the PMOS and NMOS in the netlist.
"""
self.pmos1_inst=self.add_inst(name="pnand3_pmos1",
mod=self.pmos)
self.pmos1_inst = self.add_inst(name="pnand3_pmos1",
mod=self.pmos)
self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
@ -135,27 +139,27 @@ class pnand3(pgate.pgate):
mod=self.pmos)
self.connect_inst(["Z", "C", "vdd", "vdd"])
self.nmos1_inst=self.add_inst(name="pnand3_nmos1",
mod=self.nmos)
self.nmos1_inst = self.add_inst(name="pnand3_nmos1",
mod=self.nmos)
self.connect_inst(["Z", "C", "net1", "gnd"])
self.nmos2_inst=self.add_inst(name="pnand3_nmos2",
mod=self.nmos)
self.nmos2_inst = self.add_inst(name="pnand3_nmos2",
mod=self.nmos)
self.connect_inst(["net1", "B", "net2", "gnd"])
self.nmos3_inst=self.add_inst(name="pnand3_nmos3",
mod=self.nmos)
self.nmos3_inst = self.add_inst(name="pnand3_nmos3",
mod=self.nmos)
self.connect_inst(["net2", "A", "gnd", "gnd"])
def place_ptx(self):
"""
Place the PMOS and NMOS in the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
Place the PMOS and NMOS in the layout at the upper-most
and lowest position to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.height - self.pmos.active_height \
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
pmos2_pos = pmos1_pos + self.overlap_offset
@ -165,7 +169,8 @@ class pnand3(pgate.pgate):
self.pmos3_inst.place(self.pmos3_pos)
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
nmos1_pos = vector(self.pmos.active_offset.x,
self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos)
nmos2_pos = nmos1_pos + self.overlap_offset
@ -175,7 +180,7 @@ class pnand3(pgate.pgate):
self.nmos3_inst.place(self.nmos3_pos)
# 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):
""" 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_pwell_contact(self.nmos, self.nmos3_pos)
def connect_rails(self):
""" 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):
""" Route the A and B inputs """
# wire space or wire and one contact space
metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width,
self.m1_space + 0.5*contact.poly.width + 0.5*self.m1_width)
metal_spacing = max(self.m1_space + self.m1_width,
self.m2_space + self.m2_width,
self.m1_space + 0.5 *contact.poly.width + 0.5 * self.m1_width)
active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc("poly_to_active"))
active_spacing = max(self.m1_space,
0.5 * contact.poly.first_layer_width + drc("poly_to_active"))
inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing
self.route_input_gate(self.pmos3_inst, self.nmos3_inst, inputC_yoffset, "C", position="center")
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
inputC_yoffset,
"C",
position="center")
inputB_yoffset = inputC_yoffset + metal_spacing
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
"B",
position="center")
self.inputA_yoffset = inputB_yoffset + metal_spacing
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A", position="center")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A",
position="center")
def route_output(self):
""" Route the Z output """
# PMOS1 drain
# PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain
# PMOS3 drain
pmos3_pin = self.pmos3_inst.get_pin("D")
# 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
self.add_via_center(layers=("metal1", "via1", "metal2"),
@ -229,10 +245,10 @@ class pnand3(pgate.pgate):
offset=nmos3_pin.center())
# 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)
mid_offset = vector(nmos3_pin.center().x,self.inputA_yoffset)
self.add_path("metal2",[pmos1_pin.bc(), mid_offset, nmos3_pin.uc()])
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
self.add_path("metal2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"),
@ -256,21 +272,32 @@ class pnand3(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1094
return transition_prob*(c_load + c_para)
return transition_prob *(c_load + c_para)
def input_load(self):
"""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):
"""Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
parasitic_delay = 3
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise)
Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall.
Input inverted by this stage.
"""
parasitic_delay = 3
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise)
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)
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)

View File

@ -10,10 +10,9 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory
class pnor2(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nor.
@ -22,22 +21,23 @@ class pnor2(pgate.pgate):
def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nor """
debug.info(2, "creating pnor2 structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pnor2 structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
self.nmos_size = size
# We will just make this 1.5 times for now. NORs are not ideal anyhow.
self.pmos_size = 1.5*parameter["beta"]*size
self.nmos_width = self.nmos_size*drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx")
self.pmos_size = 1.5 * parameter["beta"] * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
# 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
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def create_netlist(self):
self.add_pins()
@ -89,44 +89,48 @@ class pnor2(pgate.pgate):
self.m2_space + contact.m2m3.first_layer_width,
self.m3_space + contact.m2m3.second_layer_width)
# Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins
# Compute the other pmos2 location, but determining
# offset to overlap the source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + self.pmos.active_contact.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active")
self.well_width = 2 * self.pmos.active_width \
+ self.pmos.active_contact.width \
+ 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# This is the extra space needed to ensure DRC rules to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(),0)
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"), self.poly_space)
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"),
self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
"""
Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
self.pmos1_inst=self.add_inst(name="pnor2_pmos1",
mod=self.pmos)
self.pmos1_inst = self.add_inst(name="pnor2_pmos1",
mod=self.pmos)
self.connect_inst(["vdd", "A", "net1", "vdd"])
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.nmos1_inst=self.add_inst(name="pnor2_nmos1",
mod=self.nmos)
self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"])
self.nmos2_inst=self.add_inst(name="pnor2_nmos2",
mod=self.nmos)
self.nmos2_inst = self.add_inst(name="pnor2_nmos2",
mod=self.nmos)
self.connect_inst(["Z", "B", "gnd", "gnd"])
def place_ptx(self):
"""
"""
Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.height - self.pmos.active_height \
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
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_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
# 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))
# This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy())
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.pmos, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
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.pmos1_inst,"S","vdd")
self.connect_pin_to_rail(self.nmos2_inst, "D", "gnd")
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
def route_inputs(self):
""" Route the A and B inputs """
# Use M2 spaces so we can drop vias on the pins later!
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + self.m2_width
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
+ self.m2_space + self.m2_width
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
"B",
position="center")
# This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A")
def route_output(self):
""" Route the Z output """
# PMOS2 drain
# PMOS2 drain
pmos_pin = self.pmos2_inst.get_pin("D")
# NMOS1 drain
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
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center())
m1m2_contact=self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center())
m1m2_contact = self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center())
mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y)
mid2_offset = vector(pmos_pin.center().x,self.inputA_yoffset)
mid3_offset = mid2_offset + vector(self.m2_width,0)
mid1_offset = vector(pmos_pin.center().x, nmos2_pin.center().y)
mid2_offset = vector(pmos_pin.center().x, self.inputA_yoffset)
mid3_offset = mid2_offset + vector(self.m2_width, 0)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[pmos_pin.bc(), mid2_offset, mid3_offset])
self.add_path("metal2",[nmos_pin.rc(), mid1_offset, mid2_offset])
self.add_path("metal2",
[pmos_pin.bc(), mid2_offset, mid3_offset])
self.add_path("metal2",
[nmos_pin.rc(), mid1_offset, mid2_offset])
# This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=mid3_offset)
@ -240,10 +252,11 @@ class pnor2(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1875
return transition_prob*(c_load + c_para)
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."""
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 sram_factory import factory
class precharge(design.design):
"""
Creates a single precharge cell
@ -25,16 +26,15 @@ class precharge(design.design):
self.bitcell = factory.create(module_type="bitcell")
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.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br
# Creates the netlist and layout
# Since it has variable height, it is not a pgate.
self.create_netlist()
if not OPTS.netlist_only:
if not OPTS.netlist_only:
self.create_layout()
self.DRC_LVS()
@ -53,7 +53,8 @@ class precharge(design.design):
self.connect_to_bitlines()
def add_pins(self):
self.add_pin_list(["bl", "br", "en_bar", "vdd"], ["OUTPUT", "OUTPUT", "INPUT", "POWER"])
self.add_pin_list(["bl", "br", "en_bar", "vdd"],
["OUTPUT", "OUTPUT", "INPUT", "POWER"])
def add_ptx(self):
"""
@ -64,16 +65,13 @@ class precharge(design.design):
tx_type="pmos")
self.add_mod(self.pmos)
def route_vdd_rail(self):
"""
Adds a vdd rail at the top of the cell
"""
# 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",
offset=vdd_position,
width=self.width,
@ -87,44 +85,43 @@ class precharge(design.design):
# Add vdd pin above the transistor
self.add_power_pin("vdd", pmos_pin.center(), vertical=True)
def create_ptx(self):
"""
Create both the upper_pmos and lower_pmos to the module
"""
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
mod=self.pmos)
self.lower_pmos_inst = self.add_inst(name="lower_pmos",
mod=self.pmos)
self.connect_inst(["bl", "en_bar", "br", "vdd"])
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
mod=self.pmos)
self.upper_pmos1_inst = self.add_inst(name="upper_pmos1",
mod=self.pmos)
self.connect_inst(["bl", "en_bar", "vdd", "vdd"])
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
mod=self.pmos)
self.upper_pmos2_inst = self.add_inst(name="upper_pmos2",
mod=self.pmos)
self.connect_inst(["br", "en_bar", "vdd", "vdd"])
def place_ptx(self):
"""
Place both the upper_pmos and lower_pmos to the module
"""
# Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins
# Compute the other pmos2 location,
# but determining offset to overlap the source and drain pins
overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# This is how much the contact is placed inside the ptx active
contact_xdiff = self.pmos.get_pin("S").lx()
# adds the lower pmos to layout
bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx()
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, self.well_enclose_active),
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff,
self.well_enclose_active),
self.pmos.active_offset.y)
self.lower_pmos_inst.place(self.lower_pmos_position)
# 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_inst.place(self.upper_pmos1_pos)
@ -146,7 +143,9 @@ class precharge(design.design):
# connects the two poly for the two upper pmos(s)
offset = offset + vector(0, ylength - self.poly_width)
xlength = self.upper_pmos2_inst.get_pin("G").lx() - self.upper_pmos1_inst.get_pin("G").lx() + self.poly_width
xlength = self.upper_pmos2_inst.get_pin("G").lx() \
- self.upper_pmos1_inst.get_pin("G").lx() \
+ self.poly_width
self.add_rect(layer="poly",
offset=offset,
width=xlength,
@ -158,16 +157,16 @@ class precharge(design.design):
"""
# adds the en contact to connect the gates to the en rail on metal1
offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space)
offset = self.lower_pmos_inst.get_pin("G").ul() \
+ vector(0, 0.5 * self.poly_space)
self.add_via_center(layers=("poly", "contact", "metal1"),
offset=offset)
# adds the en rail on metal1
self.add_layout_pin_segment_center(text="en_bar",
layer="metal1",
start=offset.scale(0,1),
end=offset.scale(0,1)+vector(self.width,0))
start=offset.scale(0, 1),
end=offset.scale(0, 1) + vector(self.width, 0))
def place_nwell_and_contact(self):
"""
@ -175,8 +174,9 @@ class precharge(design.design):
"""
# adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active"))
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"))
self.add_via_center(layers=("active", "contact", "metal1"),
offset=well_contact_pos,
implant_type="n",
@ -187,18 +187,18 @@ class precharge(design.design):
# nwell should span the whole design since it is pmos only
self.add_rect(layer="nwell",
offset=vector(0,0),
offset=vector(0, 0),
width=self.width,
height=self.height)
def route_bitlines(self):
"""
Adds both bit-line and bit-line-bar to the module
"""
# adds the BL on metal 2
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0)
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(), 0) \
- vector(0.5 * self.m2_width, 0)
self.bl_pin = self.add_layout_pin(text="bl",
layer="metal2",
offset=offset,
@ -206,7 +206,8 @@ class precharge(design.design):
height=self.height)
# adds the BR on metal 2
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0)
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(), 0) \
- vector(0.5 * self.m2_width, 0)
self.br_pin = self.add_layout_pin(text="br",
layer="metal2",
offset=offset,
@ -218,60 +219,64 @@ class precharge(design.design):
Connect the bitlines to the devices
"""
self.add_bitline_contacts()
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl"))
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl"))
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),self.get_pin("br"))
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br"))
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),
self.get_pin("bl"))
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),
self.get_pin("bl"))
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),
self.get_pin("br"))
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),
self.get_pin("br"))
def add_bitline_contacts(self):
"""
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")
lower_pin = self.lower_pmos_inst.get_pin("S")
# BL goes up to M2 at the transistor
self.bl_contact=self.add_via_center(layers=stack,
offset=upper_pin.center(),
directions=("V","V"))
self.bl_contact =self.add_via_center(layers=stack,
offset=upper_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=stack,
offset=lower_pin.center(),
directions=("V","V"))
directions=("V", "V"))
# BR routes over on M1 first
self.add_via_center(layers=stack,
offset = vector(self.br_pin.cx(), upper_pin.cy()),
directions=("V","V"))
offset=vector(self.br_pin.cx(), upper_pin.cy()),
directions=("V", "V"))
self.add_via_center(layers=stack,
offset = vector(self.br_pin.cx(), lower_pin.cy()),
directions=("V","V"))
offset=vector(self.br_pin.cx(), lower_pin.cy()),
directions=("V", "V"))
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())
right_pos = vector(max(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())
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):
"""
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())
right_pos = vector(max(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())
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):
"""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.
"""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.
pmos_cin = self.pmos.get_cin()
return 3*pmos_cin
return 3 * pmos_cin

View File

@ -10,16 +10,12 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from math import ceil
from globals import OPTS
from utils import round_to_grid
import logical_effort
from sram_factory import factory
class ptristate_inv(pgate.pgate):
"""
ptristate generates gds of a parametrically sized tristate inverter.
ptristate generates gds of a parametrically sized tristate inverter.
There is some flexibility in the size, but we do not allow multiple fingers
to fit in the cell height.
@ -27,14 +23,16 @@ class ptristate_inv(pgate.pgate):
def __init__(self, name, size=1, height=None):
debug.info(2, "creating ptristate inv {0} with size of {1}".format(name, size))
debug.info(2,
"creating ptristate inv {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
# We are 2x since there are two series devices
self.size = 2*size
self.size = 2 * size
self.nmos_size = size
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.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.
# 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
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.
# 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. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
"""
Create the PMOS and NMOS netlist.
"""
# These are the inverter PMOS/NMOS
self.pmos1_inst=self.add_inst(name="ptri_pmos1", mod=self.pmos)
self.pmos1_inst = self.add_inst(name="ptri_pmos1",
mod=self.pmos)
self.connect_inst(["vdd", "in", "n1", "vdd"])
self.nmos1_inst=self.add_inst(name="ptri_nmos1", mod=self.nmos)
self.nmos1_inst = self.add_inst(name="ptri_nmos1",
mod=self.nmos)
self.connect_inst(["gnd", "in", "n2", "gnd"])
# These are the tristate PMOS/NMOS
self.pmos2_inst = self.add_inst(name="ptri_pmos2", mod=self.pmos)
self.connect_inst(["out", "en_bar", "n1", "vdd"])
self.nmos2_inst=self.add_inst(name="ptri_nmos2", mod=self.nmos)
self.nmos2_inst = self.add_inst(name="ptri_nmos2",
mod=self.nmos)
self.connect_inst(["out", "en", "n2", "gnd"])
def place_ptx(self):
"""
"""
Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos_yoff = self.height - self.pmos.active_height - self.top_bottom_space - 0.5*contact.well.height
nmos_yoff = self.top_bottom_space + 0.5*contact.well.height
pmos_yoff = self.height - self.pmos.active_height \
- self.top_bottom_space - 0.5 * contact.well.height
nmos_yoff = self.top_bottom_space + 0.5 * contact.well.height
# Tristate transistors
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_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0, 0.5*(pmos_yoff + nmos_yoff + self.nmos.height))
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,
0.5 * (pmos_yoff + nmos_yoff + self.nmos.height))
# This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy())
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
def route_inputs(self):
""" Route the gates """
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.output_pos.y, "in", position="farleft")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.output_pos.y,
"in",
position="farleft")
self.route_single_gate(self.pmos2_inst, "en_bar", position="left")
self.route_single_gate(self.nmos2_inst, "en", position="left")
def route_outputs(self):
""" Route the output (drains) together. """
@ -181,40 +183,41 @@ class ptristate_inv(pgate.pgate):
self.add_layout_pin(text="out",
layer="metal1",
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):
""" 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")
drain_pos = self.nmos1_inst.get_pin("S").center()
vdd_pos = self.get_pin("vdd").center()
self.nwell_contact=self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x,vdd_pos.y),
implant_type="n",
well_type="n")
self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x, vdd_pos.y),
implant_type="n",
well_type="n")
gnd_pos = self.get_pin("gnd").center()
self.pwell_contact=self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x,gnd_pos.y),
implant_type="p",
well_type="p")
self.pwell_contact = self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x, gnd_pos.y),
implant_type="p",
well_type="p")
def connect_rails(self):
""" 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.pmos1_inst,"S","vdd")
self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
# Power in this module currently not defined.
# Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
return total_power
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
from tech import drc, spice
from vector import vector
from globals import OPTS
from sram_factory import factory
class ptx(design.design):
"""
This module generates gds and spice of a parametrically NMOS or
@ -21,7 +21,14 @@ class ptx(design.design):
you to connect the fingered gates and active for parallel devices.
"""
def __init__(self, name="", width=drc("minwidth_tx"), mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None):
def __init__(self,
name="",
width=drc("minwidth_tx"),
mults=1,
tx_type="nmos",
connect_active=False,
connect_poly=False,
num_contacts=None):
# We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will
# over-write a design in GDS if one has and the other doesn't
@ -34,7 +41,7 @@ class ptx(design.design):
if num_contacts:
name += "_c{}".format(num_contacts)
# replace periods with underscore for newer spice compatibility
name=name.replace('.','_')
name = name.replace('.', '_')
debug.info(3, "creating ptx {0}".format(name))
design.design.__init__(self, name)
@ -51,42 +58,43 @@ class ptx(design.design):
# We must always create ptx layout for pbitcell
# some transistor sizes in other netlist depend on pbitcell
self.create_layout()
def create_layout(self):
"""Calls all functions related to the generation of the layout"""
self.setup_layout_constants()
self.add_active()
self.add_well_implant()
self.add_well_implant()
self.add_poly()
self.add_active_contacts()
self.translate_all(self.active_offset)
# for run-time, we won't check every transitor DRC independently
# but this may be uncommented for debug purposes
#self.DRC()
# self.DRC()
def create_netlist(self):
pin_list = ["D", "G", "S", "B"]
if self.tx_type=="nmos":
if self.tx_type == "nmos":
body_dir = 'GROUND'
else: #Assumed that the check for either pmos or nmos is done elsewhere.
else:
# Assumed that the check for either pmos or nmos is done elsewhere.
body_dir = 'POWER'
dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir]
self.add_pin_list(pin_list, dir_list)
# self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
# " ".join(self.pins)))
# Just make a guess since these will actually be decided in the layout later.
area_sd = 2.5*drc("minwidth_poly")*self.tx_width
perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width
self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4:.2f}u ps={4:.2f}u as={5:.2f}p ad={5:.2f}p".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"),
perimeter_sd,
area_sd)
# Just make a guess since these will actually
# be decided in the layout later.
area_sd = 2.5 * drc("minwidth_poly") * self.tx_width
perimeter_sd = 2 * drc("minwidth_poly") + 2 * self.tx_width
main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"))
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd,
area_sd)
self.spice_device= main_str + area_str
self.spice.append("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name))
@ -116,38 +124,39 @@ class ptx(design.design):
# 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)
# 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.
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
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,
# 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
self.active_height = self.tx_width
# 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
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
if drc("has_{}well".format(self.well_type)):
self.cell_well_width = max(self.active_width + 2*self.well_enclose_active,
self.well_width)
self.cell_well_height = max(self.tx_width + 2*self.well_enclose_active,
self.well_width)
self.cell_well_width = max(self.active_width + 2 * self.well_enclose_active,
self.well_width)
self.cell_well_height = max(self.tx_width + 2 * self.well_enclose_active,
self.well_width)
# 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.width = self.cell_well_width - self.active_offset.x
@ -157,17 +166,20 @@ class ptx(design.design):
self.width = self.active_width
# 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)
self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5*self.contact_width,
0.5*self.active_height)
self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5 * self.contact_width,
0.5 * self.active_height)
# Min area results are just flagged for now.
debug.check(self.active_width*self.active_height>=drc("minarea_active"),"Minimum active area violated.")
# We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue.
debug.check(self.poly_width*self.poly_height>=drc("minarea_poly"),"Minimum poly area violated.")
debug.check(self.active_width * self.active_height >= drc("minarea_active"),
"Minimum active area violated.")
# We do not want to increase the poly dimensions to fix
# an area problem as it would cause an LVS issue.
debug.check(self.poly_width * self.poly_height >= drc("minarea_poly"),
"Minimum poly area violated.")
def connect_fingered_poly(self, poly_positions):
"""
@ -182,13 +194,19 @@ class ptx(design.design):
# The width of the poly is from the left-most to right-most poly gate
poly_width = poly_positions[-1].x - poly_positions[0].x + self.poly_width
if self.tx_type == "pmos":
# This can be limited by poly to active spacing or the poly extension
distance_below_active = self.poly_width + max(self.poly_to_active,0.5*self.poly_height)
poly_offset = poly_positions[0] - vector(0.5*self.poly_width, distance_below_active)
# This can be limited by poly to active spacing
# or the poly extension
distance_below_active = self.poly_width + max(self.poly_to_active,
0.5 * self.poly_height)
poly_offset = poly_positions[0] - vector(0.5 * self.poly_width,
distance_below_active)
else:
# This can be limited by poly to active spacing or the poly extension
distance_above_active = max(self.poly_to_active,0.5*self.poly_height)
poly_offset = poly_positions[0] + vector(-0.5*self.poly_width, distance_above_active)
# This can be limited by poly to active spacing
# or the poly extension
distance_above_active = max(self.poly_to_active,
0.5 * self.poly_height)
poly_offset = poly_positions[0] + vector(-0.5 * self.poly_width,
distance_above_active)
# Remove the old pin and add the new one
self.remove_layout_pin("G") # only keep the main pin
self.add_layout_pin(text="G",
@ -205,12 +223,13 @@ class ptx(design.design):
# This is the distance that we must route up or down from the center
# of the contacts to avoid DRC violations to the other contacts
pin_offset = vector(0, 0.5*self.active_contact.second_layer_height \
+ self.m1_space + 0.5*self.m1_width)
pin_offset = vector(0, 0.5 * self.active_contact.second_layer_height \
+ self.m1_space + 0.5 * self.m1_width)
# This is the width of a m1 extend the ends of the pin
end_offset = vector(self.m1_width/2,0)
# drains always go to the MIDDLE of the cell, so top of NMOS, bottom of PMOS
# drains always go to the MIDDLE of the cell,
# so top of NMOS, bottom of PMOS
# so reverse the directions for NMOS compared to PMOS.
if self.tx_type == "pmos":
drain_dir = -1
@ -219,17 +238,19 @@ class ptx(design.design):
drain_dir = 1
source_dir = -1
if len(source_positions)>1:
if len(source_positions) > 1:
source_offset = pin_offset.scale(source_dir,source_dir)
self.remove_layout_pin("S") # remove the individual connections
# Add each vertical segment
for a in source_positions:
self.add_path(("metal1"), [a,a+pin_offset.scale(source_dir,source_dir)])
self.add_path(("metal1"),
[a, a + pin_offset.scale(source_dir,
source_dir)])
# Add a single horizontal pin
self.add_layout_pin_segment_center(text="S",
layer="metal1",
start=source_positions[0]+source_offset-end_offset,
end=source_positions[-1]+source_offset+end_offset)
start=source_positions[0] + source_offset - end_offset,
end=source_positions[-1] + source_offset + end_offset)
if len(drain_positions)>1:
drain_offset = pin_offset.scale(drain_dir,drain_dir)
@ -240,24 +261,27 @@ class ptx(design.design):
# Add a single horizontal pin
self.add_layout_pin_segment_center(text="D",
layer="metal1",
start=drain_positions[0]+drain_offset-end_offset,
end=drain_positions[-1]+drain_offset+end_offset)
start=drain_positions[0] + drain_offset - end_offset,
end=drain_positions[-1] + drain_offset + end_offset)
def add_poly(self):
"""
Add the poly gates(s) and (optionally) connect them.
"""
# poly is one contacted spacing from the end and down an extension
poly_offset = self.active_offset + vector(self.poly_width,self.poly_height).scale(0.5,0.5) \
poly_offset = self.active_offset \
+ vector(self.poly_width, self.poly_height).scale(0.5, 0.5) \
+ vector(self.end_to_poly, -self.poly_extend_active)
# poly_positions are the bottom center of the poly gates
poly_positions = []
# It is important that these are from left to right, so that the pins are in the right
# It is important that these are from left to right,
# so that the pins are in the right
# order for the accessors
for i in range(0, self.mults):
# Add this duplicate rectangle in case we remove the pin when joining fingers
# Add this duplicate rectangle in case we remove
# the pin when joining fingers
self.add_rect_center(layer="poly",
offset=poly_offset,
height=self.poly_height,
@ -274,8 +298,8 @@ class ptx(design.design):
self.connect_fingered_poly(poly_positions)
def add_active(self):
"""
Adding the diffusion (active region = diffusion region)
"""
Adding the diffusion (active region = diffusion region)
"""
self.add_rect(layer="active",
offset=self.active_offset,
@ -284,11 +308,11 @@ class ptx(design.design):
# If the implant must enclose the active, shift offset
# and increase width/height
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),
offset=self.active_offset - enclose_offset,
width=self.active_width + 2*enclose_width,
height=self.active_height + 2*enclose_width)
width=self.active_width + 2 * enclose_width,
height=self.active_height + 2 * enclose_width)
def add_well_implant(self):
"""
@ -320,15 +344,16 @@ class ptx(design.design):
# The first one will always be a source
source_positions = [self.contact_offset]
drain_positions = []
# It is important that these are from left to right, so that the pins are in the right
# It is important that these are from left to right,
# so that the pins are in the right
# order for the accessors.
for i in range(self.mults):
if i%2:
# 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:
# 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]
@ -371,9 +396,12 @@ class ptx(design.design):
def get_cin(self):
"""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):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, 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)

View File

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