pbitcell is now a multiport cell with a read transistor that connects to RBL and RROW and a read access transistor that connects to Q and gnd

current commit works without drc errors on freepdk45 but has drc rules not included in scn3me_subm. Does have lvs errors
adding several unit tests: the basic one that tests the full functionality of the pbitcell, one with no write ports, and one with no read ports
This commit is contained in:
Michael Timothy Grimes 2018-02-08 14:21:15 -08:00
parent fb2572bd71
commit b90f5c9a59
4 changed files with 486 additions and 323 deletions

View File

@ -9,26 +9,29 @@ from globals import OPTS
class pbitcell(design.design):
"""
This module implements a parametrically sized multi-port bitcell.
This module implements a parametrically sized multi-port bitcell
"""
def __init__(self):
def __init__(self, num_write=1, num_read=1):
name = "pbitcell"
design.design.__init__(self, name)
debug.info(2, "create a multi-port bitcell")
debug.info(2, "create a multi-port bitcell with {0} write ports and {1} read ports".format(num_write, num_read))
self.num_write = 2
self.num_read = 2
self.num_write = num_write
self.num_read = num_read
self.create_layout()
self.DRC_LVS()
def add_pins(self):
self.add_pin("WROW")
self.add_pin("WBL")
self.add_pin("WBL_bar")
for k in range(0,self.num_write):
self.add_pin("WROW{}".format(k))
for k in range(0,self.num_write):
self.add_pin("WBL{}".format(k))
self.add_pin("WBL_bar{}".format(k))
for k in range(0,self.num_read):
self.add_pin("RROW{}".format(k))
for k in range(0,self.num_read):
self.add_pin("RBL{}".format(k))
self.add_pin("RBL_bar{}".format(k))
self.add_pin("vdd")
@ -37,67 +40,127 @@ class pbitcell(design.design):
def create_layout(self):
self.create_ptx()
self.add_storage()
self.add_rails()
self.add_write_transistors()
self.add_read_transistors()
self.add_rails()
if(self.num_write > 0):
self.add_write_transistors()
if(self.num_read > 0):
self.add_read_transistors()
self.extend_well()
#self.add_fail()
def create_ptx(self):
"""
Create ptx all transistors. Also define measurements to be used throughout bitcell.
"""
self.i_nmos = ptx(width=2.3*parameter["min_tx_size"],
tx_type="nmos",
connect_active=True,
connect_poly=True)
""" Create ptx for all transistors. Also define measurements to be used throughout bitcell """
# create ptx for inverter transistors
self.i_nmos = ptx(width=2*parameter["min_tx_size"],
tx_type="nmos",
connect_active=True,
connect_poly=True)
self.add_mod(self.i_nmos)
self.i_pmos = ptx(width=parameter["min_tx_size"],
tx_type="pmos",
connect_active=True,
connect_poly=True)
tx_type="pmos",
connect_active=True,
connect_poly=True)
self.add_mod(self.i_pmos)
# create ptx for write transitors
self.w_nmos = ptx(width=1.5*parameter["min_tx_size"],
tx_type="nmos",
connect_active=True,
connect_poly=True)
self.w_nmos = ptx(width=parameter["min_tx_size"],
tx_type="nmos",
connect_active=True,
connect_poly=True)
self.add_mod(self.w_nmos)
# create ptx for read transistors
self.r_nmos = ptx(width=parameter["min_tx_size"],
tx_type="nmos",
connect_active=True,
connect_poly=True)
self.r_nmos = ptx(width=2*parameter["min_tx_size"],
mults=2,
tx_type="nmos",
connect_active=False,
connect_poly=False)
self.add_mod(self.r_nmos)
# determine metal contact extentions
self.in_ex = 0.5*(self.i_nmos.active_contact.height - self.i_nmos.active_height)
# determine metal contact extensions
self.ip_ex = 0.5*(self.i_pmos.active_contact.height - self.i_pmos.active_height)
self.w_ex = 0.5*(self.w_nmos.active_contact.height - self.w_nmos.active_height)
self.r_ex = 0.5*(self.r_nmos.active_contact.height - self.r_nmos.active_height)
self.w_ex = 0.5*(self.w_nmos.active_contact.height - self.w_nmos.active_height)
# determine global measurements
spacer = 5*drc["minwidth_metal2"] + self.w_ex + self.r_ex
w_spacer = drc["minwidth_metal2"] + self.w_ex + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"]
r_spacer = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width
rw_spacer = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.w_ex
self.w_tile_width = 3*parameter["min_tx_size"] + self.w_nmos.active_height
self.r_tile_width = 3*parameter["min_tx_size"] + self.r_nmos.active_height
self.inv_gap = 5*contact.poly.width
self.leftmost_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"] + drc["well_enclosure_active"]) \
- self.num_write*(3*parameter["min_tx_size"] + self.w_nmos.active_height) \
- (self.num_read-1)*(3*parameter["min_tx_size"] + self.r_nmos.active_height) - spacer - self.r_nmos.active_height
self.w_tile_width = w_spacer + self.w_nmos.active_height
self.r_tile_width = r_spacer + self.r_nmos.active_height
self.inv_gap = drc["poly_to_active"] + drc["poly_to_field_poly"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.ip_ex
self.cross_heightL = self.i_nmos.active_height + drc["poly_to_active"] + 0.5*contact.poly.width
self.cross_heightU = self.i_nmos.active_height + drc["poly_to_active"] + drc["poly_to_field_poly"] + 1.5*contact.poly.width
self.leftmost_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"]) \
- self.num_write*(w_spacer + self.w_nmos.active_height) \
- rw_spacer - self.r_nmos.active_height - (self.num_read-1)*self.r_tile_width \
- 3*drc["minwidth_metal1"]
self.botmost_ypos = -2*drc["minwidth_metal1"] - self.num_write*2*drc["minwidth_metal2"] - self.num_read*2*drc["minwidth_metal2"]
self.topmost_ypos = self.inv_gap + self.i_nmos.active_height + self.i_pmos.active_height + drc["poly_extend_active"] + 2*drc["minwidth_metal1"]
self.cell_width = -2*self.leftmost_xpos
self.cell_height = self.topmost_ypos - self.botmost_ypos
def add_storage(self):
"""
Creates the crossed coupled inverters that act as storage for the bitcell.
"""
lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"])
rit_xpos = 1.5*parameter["min_tx_size"]
it_ypos = self.inv_gap + self.i_nmos.active_height
# create active
self.i_nmosL = self.add_inst(name="i_nmosL",
mod=self.i_nmos,
offset=[lit_xpos,0])
self.connect_inst(["Q_bar", "Q", "gnd", "gnd"])
self.i_nmosR = self.add_inst(name="i_nmosR",
mod=self.i_nmos,
offset=[rit_xpos,0])
self.connect_inst(["gnd", "Q_bar", "Q", "gnd"])
self.i_pmosL = self.add_inst(name="i_pmosL",
mod=self.i_pmos,
offset=[lit_xpos, it_ypos])
self.connect_inst(["Q_bar", "Q", "vdd", "vdd"])
self.i_pmosR = self.add_inst(name="i_pmosR",
mod=self.i_pmos,
offset=[rit_xpos, it_ypos])
self.connect_inst(["vdd", "Q_bar", "Q", "vdd"])
# connect poly for inverters
self.add_path("poly", [self.i_nmosL.get_pin("G").uc(), self.i_pmosL.get_pin("G").bc()])
self.add_path("poly", [self.i_nmosR.get_pin("G").uc(), self.i_pmosR.get_pin("G").bc()])
# connect drains for inverters
self.add_path("metal1", [self.i_nmosL.get_pin("D").uc(), self.i_pmosL.get_pin("D").bc()])
self.add_path("metal1", [self.i_nmosR.get_pin("S").uc(), self.i_pmosR.get_pin("S").bc()])
# add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar)
offsetL = vector(self.i_nmosL.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_heightU)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetL,
rotate=90)
offsetR = vector(self.i_nmosR.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_heightL)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetR,
rotate=90)
# connect contacts to gate poly (cross couple)
gate_offsetR = vector(self.i_nmosR.get_pin("G").lc().x, offsetL.y)
self.add_path("poly", [offsetL, gate_offsetR])
gate_offsetL = vector(self.i_nmosL.get_pin("G").rc().x, offsetR.y)
self.add_path("poly", [offsetR, gate_offsetL])
def add_rails(self):
"""
Adds rails for vdd and gnd.
"""
""" Add rails for vdd and gnd """
self.gnd_position = vector(self.leftmost_xpos, -2*drc["minwidth_metal1"])
self.gnd = self.add_layout_pin(text="gnd",
layer="metal1",
@ -111,112 +174,27 @@ class pbitcell(design.design):
offset=self.vdd_position,
width=self.cell_width,
height=drc["minwidth_metal1"])
# connect sources to rails
# connect nmos to gnd
i_nmosL_source = self.i_nmosL.get_pin("S").bc()
gnd_posL = vector(self.i_nmosL.get_pin("S").bc().x, self.gnd_position.y + drc["minwidth_metal1"])
self.add_path("metal1", [i_nmosL_source, gnd_posL])
i_nmosR_drain = self.i_nmosR.get_pin("D").bc()
""" Connect inverters to rails """
# connect nmos to gnd
gnd_posL = vector(self.i_nmosL.get_pin("S").bc().x, self.gnd_position.y + drc["minwidth_metal1"])
self.add_path("metal1", [self.i_nmosL.get_pin("S").bc(), gnd_posL])
gnd_posR = vector(self.i_nmosR.get_pin("D").bc().x, self.gnd_position.y + drc["minwidth_metal1"])
self.add_path("metal1", [i_nmosR_drain, gnd_posR])
self.add_path("metal1", [self.i_nmosR.get_pin("D").bc(), gnd_posR])
# connect pmos to vdd
i_pmosL_source = self.i_pmosL.get_pin("S").uc()
vdd_posL = vector(self.i_nmosL.get_pin("S").uc().x, self.vdd_position.y)
self.add_path("metal1", [i_pmosL_source, vdd_posL])
self.add_path("metal1", [self.i_pmosL.get_pin("S").uc(), vdd_posL])
i_pmosR_drain = self.i_pmosR.get_pin("D").uc()
vdd_posR = vector(self.i_nmosR.get_pin("D").uc().x, self.vdd_position.y)
self.add_path("metal1", [i_pmosR_drain, vdd_posR])
self.add_path("metal1", [self.i_pmosR.get_pin("D").uc(), vdd_posR])
def add_storage(self):
"""
Creates the crossed coupled inverters that act as storage for the bitcell.
"""
# calculate offsets
lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"])
rit_xpos = 1.5*parameter["min_tx_size"]
it_ypos = self.inv_gap + self.i_nmos.active_height
# create active
self.i_nmosL = self.add_inst(name="i_nmosL",
mod=self.i_nmos,
offset=[lit_xpos,0])
self.connect_inst(["IR", "IL", "gnd", "gnd"])
self.i_nmosR = self.add_inst(name="i_nmosR",
mod=self.i_nmos,
offset=[rit_xpos,0])
self.connect_inst(["IL", "IR", "gnd", "gnd"])
self.i_pmosL = self.add_inst(name="i_pmosL",
mod=self.i_pmos,
offset=[lit_xpos, it_ypos])
self.connect_inst(["IL", "IR", "vdd", "vdd"])
self.i_pmosR = self.add_inst(name="i_pmosR",
mod=self.i_pmos,
offset=[rit_xpos, it_ypos])
self.connect_inst(["IL", "IR", "vdd", "vdd"])
# connect poly for inverters
i_nmosL_poly = self.i_nmosL.get_pin("G").uc()
i_pmosL_poly = self.i_pmosL.get_pin("G").bc()
self.add_path("poly", [i_nmosL_poly, i_pmosL_poly])
i_nmosR_poly = self.i_nmosR.get_pin("G").uc()
i_pmosR_poly = self.i_pmosR.get_pin("G").bc()
self.add_path("poly", [i_nmosR_poly, i_pmosR_poly])
# connect drains for inverters
i_nmosL_drain = self.i_nmosL.get_pin("D").uc()
i_pmosL_drain = self.i_pmosL.get_pin("D").bc()
self.add_path("metal1", [i_nmosL_drain, i_pmosL_drain])
i_nmosR_source = self.i_nmosR.get_pin("S").uc()
i_pmosR_source = self.i_pmosR.get_pin("S").bc()
self.add_path("metal1", [i_nmosR_source, i_pmosR_source])
# add cross couple vias
offset = vector(self.i_nmosL.get_pin("D").ur().x + contact.poly.height, self.i_nmos.active_height + 0.6*self.inv_gap)
self.icl = self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
rotate=90)
offset = vector(self.i_nmosR.get_pin("S").ul().x, self.i_nmos.active_height + 0.2*self.inv_gap)
self.icr = self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
rotate=90)
# connect vias to poly (cross couple)
correct = 0.5*(self.i_nmos.active_contact.width - drc["minwidth_metal1"])
cross_width = 0.5*self.i_nmos.active_width - 0.5*drc["minwidth_poly"] + 3*parameter["min_tx_size"] + correct
offset = vector(self.i_nmosL.get_pin("G").ur().x, self.i_nmos.active_height + 0.2*self.inv_gap)
self.add_rect(layer="poly",
offset=offset,
width=cross_width,
height=contact.poly.width)
offset = vector(self.i_nmosL.get_pin("D").ur().x, self.i_nmos.active_height + 0.6*self.inv_gap)
self.add_rect(layer="poly",
offset=offset,
width=cross_width,
height=contact.poly.width)
def add_write_transistors(self):
"""
Define variables relevant to write transistors
"""
""" Define variables relevant to write transistors """
lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"])
rit_xpos = 1.5*parameter["min_tx_size"]
rit_xpos = 1.5*parameter["min_tx_size"] + self.i_nmos.active_width
rot_correct = self.w_nmos.active_height
self.w_nmosL = [None] * self.num_write
@ -225,26 +203,27 @@ class pbitcell(design.design):
self.wbl_positions = [None] * self.num_write
self.wbl_bar_positions = [None] * self.num_write
for k in range(0,self.num_write):
"""
Add transistors and WROW lines
"""
wrow_ypos = -2*drc["minwidth_metal1"] - (k+1)*2*drc["minwidth_metal2"]
lwt_xpos = lit_xpos + rot_correct - (k+1)*self.w_tile_width
rwt_xpos = rit_xpos + self.i_nmos.active_width + 3*parameter["min_tx_size"] + rot_correct + k*self.w_tile_width
""" Add transistors and WROW lines """
# calculate transistor offsets
w_spacer = drc["minwidth_metal2"] + self.w_ex + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"]
wrow_ypos = self.gnd_position.y - (k+1)*2*drc["minwidth_metal2"]
lwt_xpos = lit_xpos - (k+1)*self.w_tile_width + rot_correct
rwt_xpos = rit_xpos + w_spacer + k*self.w_tile_width + rot_correct
# add write transistors
self.w_nmosL[k] = self.add_inst(name="w_nmosL{}".format(k),
mod=self.w_nmos,
offset=[lwt_xpos,0],
rotate=90)
self.connect_inst(["IR", "WROW{}".format(k), "WBL{}".format(k), "gnd"])
self.connect_inst(["Q", "WROW{}".format(k), "WBL{}".format(k), "gnd"])
self.w_nmosR[k] = self.add_inst(name="w_nmosR{}".format(k),
mod=self.w_nmos,
offset=[rwt_xpos,0],
rotate=90)
self.connect_inst(["IL", "WROW{}".format(k), "WBL_bar{}".format(k), "gnd"])
self.connect_inst(["Q_bar", "WROW{}".format(k), "WBL_bar{}".format(k), "gnd"])
# add WROW lines
self.wrow_positions[k] = vector(self.leftmost_xpos, wrow_ypos)
@ -254,130 +233,128 @@ class pbitcell(design.design):
width=self.cell_width,
height=drc["minwidth_metal1"])
"""
Source/WBL/WBL_bar connections
"""
# connect sources to WBL and WBL_bar
self.wbl_positions[k] = vector(lwt_xpos - drc["minwidth_metal2"],self.botmost_ypos)
""" Source/WBL/WBL_bar connections """
# add contacts to connect source of wt to WBL or WBL_bar
offsetL = self.w_nmosL[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetL,
rotate=90)
offsetR = self.w_nmosR[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetR,
rotate=90)
# add WBL and WBL_bar (simultaneously connects to source of wt)
self.wbl_positions[k] = vector(self.w_nmosL[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos)
self.add_layout_pin(text="WBL{}".format(k),
layer="metal2",
offset=self.wbl_positions[k],
width=drc["minwidth_metal2"],
height=self.cell_height)
self.wbl_bar_positions[k] = vector(rwt_xpos - rot_correct,self.botmost_ypos)
self.wbl_bar_positions[k] = vector(self.w_nmosR[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos)
self.add_layout_pin(text="WBL_bar{}".format(k),
layer="metal2",
offset=self.wbl_bar_positions[k],
width=drc["minwidth_metal2"],
height=self.cell_height)
# add vias to connect source of wt to WBL and WBL_bar
offset = self.w_nmosL[k].get_pin("S").ll() + vector(self.w_nmos.active_height, 0)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
offset = self.w_nmosR[k].get_pin("S").ll() + vector(self.w_nmos.active_height, 0)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
""" Gate/WROW connections """
# add contacts to connect gate of wt to WROW (poly to metal2)
offsetL = vector(self.w_nmosL[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width, self.w_nmosL[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetL)
"""
Gate/WROW connections
"""
# add vias to connect wt gate to WROW
correct = vector(0.5*(drc["minwidth_metal2"] - drc["minwidth_metal1"]),0)
offset = self.w_nmosL[k].get_pin("S").ll() - vector(drc["minwidth_metal2"] + contact.poly.width, 0)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetL)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset+correct)
self.add_rect_center(layer="poly",
offset=offsetL,
width=contact.poly.width,
height=contact.poly.height)
via_offsetL = offset + vector(0.5*contact.poly.width, 0)
gate_offsetL = vector(offset.x + 0.5*contact.poly.width, self.w_nmosL[k].get_pin("G").ul().y)
self.add_path("poly", [via_offsetL, gate_offsetL], width=contact.poly.width)
self.add_path("poly", [gate_offsetL, self.w_nmosL[k].get_pin("G").lc()])
offsetR = vector(self.w_nmosR[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width, self.w_nmosR[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetR)
offset = self.w_nmosR[k].get_pin("S").lr() + vector(drc["minwidth_metal2"], 0)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetR)
self.add_rect_center(layer="poly",
offset=offsetR,
width=contact.poly.width,
height=contact.poly.height)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset+correct)
# connect gate of wt to contact
midL = vector(offsetL.x, self.w_nmosL[k].get_pin("G").lc().y)
self.add_path("poly", [self.w_nmosL[k].get_pin("G").lc(), midL, offsetL])
via_offsetR = offset + vector(0.5*contact.poly.width, 0)
gate_offsetR = vector(offset.x + 0.5*contact.poly.width, self.w_nmosR[k].get_pin("G").ur().y)
self.add_path("poly", [via_offsetR, gate_offsetR], width=contact.poly.width)
self.add_path("poly", [gate_offsetR, self.w_nmosR[k].get_pin("G").rc()])
midR = vector(offsetR.x, self.w_nmosR[k].get_pin("G").rc().y)
self.add_path("poly", [self.w_nmosR[k].get_pin("G").rc(), midR, offsetR])
# add vias to WROW lines
offset = vector(via_offsetL.x + contact.m1m2.height - 0.5*drc["minwidth_metal2"],self.wrow_positions[k].y)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
wrow_offset = vector(via_offsetL.x, self.wrow_positions[k].y)
self.add_path("metal2", [via_offsetL, wrow_offset])
# add contacts to WROW lines
offset = vector(offsetL.x, self.wrow_positions[k].y + 0.5*drc["minwidth_metal1"])
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
offset = vector(via_offsetR.x + 0.5*drc["minwidth_metal2"], self.wrow_positions[k].y)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
offset = vector(offsetR.x, self.wrow_positions[k].y + 0.5*drc["minwidth_metal1"])
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
wrow_offset = vector(via_offsetR.x, self.wrow_positions[k].y)
self.add_path("metal2", [via_offsetR, wrow_offset])
# connect wt gate contacts to WROW contacts
wrow_offset = vector(offsetL.x, self.wrow_positions[k].y)
self.add_path("metal2", [offsetL, wrow_offset])
wrow_offset = vector(offsetR.x, self.wrow_positions[k].y)
self.add_path("metal2", [offsetR, wrow_offset])
"""
Drain/Storage connections
"""
""" Drain/Storage connections """
if(k == self.num_write-1):
# add vias to connect input of inverters to drain of wt
offset = vector(lit_xpos - drc["minwidth_metal1"], self.i_nmos.active_height + 0.2*self.inv_gap)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
rotate=90)
# add contacts to connect gate of inverters to drain of wt
offsetL = vector(self.i_nmosL.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_heightL)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetL,
rotate=90)
offset = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
rotate=90)
offsetR = vector(self.i_nmosR.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_heightL)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetR,
rotate=90)
# connect drains of wt to input of inverters
correct = 0.5*(contact.poly.width - drc["minwidth_metal1"])
w_nmosL_drain = self.w_nmosL[k].get_pin("D").rc()
via_offsetLB = vector(lit_xpos - drc["minwidth_metal1"] - contact.poly.height, self.w_nmosL[k].get_pin("D").rc().y)
via_offsetLT = vector(lit_xpos - drc["minwidth_metal1"] - contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + contact.poly.width - correct)
self.add_path("metal1", [w_nmosL_drain, via_offsetLB, via_offsetLT])
# connect gate of inverters to contacts
gate_offsetL = vector(self.i_nmosL.get_pin("G").lc().x, offsetL.y)
self.add_path("poly", [offsetL, gate_offsetL])
w_nmosR_drain = self.w_nmosR[k].get_pin("D").lc()
via_offsetRB = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.w_nmosR[k].get_pin("D").lc().y)
via_offsetRT = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + contact.poly.width - correct)
self.add_path("metal1", [w_nmosR_drain, via_offsetRB, via_offsetRT])
gate_offsetR = vector(self.i_nmosR.get_pin("G").rc().x, offsetR.y)
self.add_path("poly", [offsetR, gate_offsetR])
via_offsetL = vector(lit_xpos - drc["minwidth_metal1"] - contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width)
gate_offsetL = vector(self.i_nmosL.get_pin("G").ll().x, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width)
self.add_path("poly", [via_offsetL, gate_offsetL], width=contact.poly.width)
# connect contacts to drains of wt
midL0 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], offsetL.y)
midL1 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.w_nmosL[k].get_pin("D").lc().y)
self.add_path("metal1", [offsetL, midL0, midL1, self.w_nmosL[k].get_pin("D").lc()])
via_offsetR = vector(rit_xpos + self.i_nmos.active_width + drc["minwidth_metal1"] + contact.poly.height, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width)
gate_offsetR = vector(self.i_nmosR.get_pin("G").lr().x, self.i_nmos.active_height + 0.2*self.inv_gap + 0.5*contact.poly.width)
self.add_path("poly", [via_offsetR, gate_offsetR], width=contact.poly.width)
midR0 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], offsetR.y)
midR1 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.w_nmosR[k].get_pin("D").rc().y)
self.add_path("metal1", [offsetR, midR0, midR1, self.w_nmosR[k].get_pin("D").rc()])
def add_read_transistors(self):
"""
Define variables relevant to read transistors
"""
""" Define variables relevant to read transistors """
lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"])
rit_xpos = 1.5*parameter["min_tx_size"]
wrow_ypos = -2*drc["minwidth_metal1"] - self.num_write*2*drc["minwidth_metal2"]
rit_xpos = 1.5*parameter["min_tx_size"] + self.i_nmos.active_width
lwt_xpos = lit_xpos - self.num_write*self.w_tile_width
rwt_xpos = rit_xpos + self.i_nmos.active_width + self.num_write*self.w_tile_width
rwt_xpos = rit_xpos + self.num_write*self.w_tile_width
wrow_ypos = self.gnd_position.y - self.num_write*2*drc["minwidth_metal2"]
rot_correct = self.r_nmos.active_height
spacer = 5*drc["minwidth_metal2"]
r_spacer = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width
rw_spacer = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.w_ex
self.r_nmosL = [None] * self.num_read
self.r_nmosR = [None] * self.num_read
@ -387,24 +364,23 @@ class pbitcell(design.design):
for k in range(0,self.num_read):
"""
Add transistors and RROW lines
"""
lrt_xpos = lwt_xpos - spacer - self.r_nmos.active_height + rot_correct - k*self.r_tile_width
rrt_xpos = rwt_xpos + spacer + rot_correct + k*self.r_tile_width
""" Add transistors and RROW lines """
# calculate transistor offsets
lrt_xpos = lwt_xpos - rw_spacer - self.r_nmos.active_height - k*self.r_tile_width + rot_correct
rrt_xpos = rwt_xpos + rw_spacer + k*self.r_tile_width + rot_correct
# add read transistors
self.r_nmosL[k] = self.add_inst(name="r_nmosL",
mod=self.r_nmos,
offset=[lrt_xpos,0],
rotate=90)
self.connect_inst(["RROW{}".format(k), "IL", "RBL{}".format(k), "gnd"])
self.connect_inst(["RROW{}".format(k), "Q", "RBL{}".format(k), "gnd"])
self.r_nmosR[k] = self.add_inst(name="r_nmosR",
mod=self.r_nmos,
offset=[rrt_xpos,0],
rotate=90)
self.connect_inst(["RROW{}".format(k), "IR", "RBL_bar{}".format(k), "gnd"])
self.connect_inst(["RROW{}".format(k), "Q_bar", "RBL_bar{}".format(k), "gnd"])
# add RROW lines
rrow_ypos = wrow_ypos - (k+1)*2*drc["minwidth_metal2"]
@ -414,133 +390,200 @@ class pbitcell(design.design):
offset=self.rrow_positions[k],
width=self.cell_width,
height=drc["minwidth_metal1"])
"""
Drain/RBL/RBL_bar connections
"""
# connect drains to RBL and RBL_bar
self.rbl_positions[k] = vector(self.r_nmosL[k].get_pin("D").rc().x + drc["minwidth_metal2"],self.botmost_ypos)
""" Source of RA transistor / GND connection """
offset = vector(self.r_nmosL[k].get_pins("S")[0].bc().x, self.gnd_position.y)
self.add_path("metal1", [self.r_nmosL[k].get_pins("S")[0].bc(), offset])
offset = vector(self.r_nmosR[k].get_pins("S")[0].bc().x, self.gnd_position.y)
self.add_path("metal1", [self.r_nmosR[k].get_pins("S")[0].bc(), offset])
""" Source of R transistor / RBL & RBL_bar connection """
# add contacts to connect source of rt to RBL or RBL_bar
offsetL = self.r_nmosL[k].get_pins("S")[1].center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetL,
rotate=90)
offsetR = self.r_nmosR[k].get_pins("S")[1].center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetR,
rotate=90)
# add RBL and RBL_bar (simultaneously connects to source of rt)
self.rbl_positions[k] = vector(self.r_nmosL[k].get_pins("S")[1].center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos)
self.add_layout_pin(text="RBL{}".format(k),
layer="metal2",
offset=self.rbl_positions[k],
width=drc["minwidth_metal2"],
height=self.cell_height)
self.rbl_bar_positions[k] = vector(self.r_nmosR[k].get_pin("D").lc().x - 2*drc["minwidth_metal2"],self.botmost_ypos)
self.rbl_bar_positions[k] = vector(self.r_nmosR[k].get_pins("S")[1].center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos)
self.add_layout_pin(text="RBL_bar{}".format(k),
layer="metal2",
offset=self.rbl_bar_positions[k],
width=drc["minwidth_metal2"],
height=self.cell_height)
# add vias to connect drain of rt to RBL and RBL_bar
offset = self.r_nmosL[k].get_pin("D").ll() + vector(contact.m1m2.height, 0)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
offset = self.r_nmosR[k].get_pin("D").ll() + vector(contact.m1m2.height, 0)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
# connect drain of rt to RBL and RBL_bar
rbl_offset = vector(self.rbl_positions[k].x, self.r_nmosL[k].get_pin("D").lc().y)
self.add_path("metal2", [self.r_nmosL[k].get_pin("D").lc(), rbl_offset])
""" Gate of R transistor / RROW connection """
# add contact to connect gate of rt to RROW (poly to metal2)
offsetL = vector(lrt_xpos - rot_correct - drc["minwidth_poly"] - 0.5*contact.poly.width, self.r_nmosL[k].get_pins("G")[1].lc().y)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetL)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetL)
offsetR = vector(rrt_xpos + drc["minwidth_poly"] + 0.5*contact.poly.width, self.r_nmosR[k].get_pins("G")[1].rc().y)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetR)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetR)
rbl_bar_offset = vector(self.rbl_bar_positions[k].x, self.r_nmosR[k].get_pin("D").rc().y)
self.add_path("metal2", [self.r_nmosR[k].get_pin("D").rc(), rbl_bar_offset])
"""
Source/RROW connections
"""
# add vias on rt sources
offset = self.r_nmosL[k].get_pin("S").ll() + vector(contact.m1m2.height, 0)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
# connect gate of rt to contact
self.add_path("poly", [self.r_nmosL[k].get_pins("G")[1].lc(), offsetL])
self.add_path("poly", [self.r_nmosR[k].get_pins("G")[1].rc(), offsetR])
offset = self.r_nmosR[k].get_pin("S").ll() + vector(contact.m1m2.height, 0)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
# add contacts to RROW lines
row_offsetL = vector(self.r_nmosL[k].get_pins("S")[1].lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width, self.rrow_positions[k].y + 0.5*drc["minwidth_metal1"])
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=row_offsetL,
rotate=90)
row_offsetR = vector(self.r_nmosR[k].get_pins("S")[1].rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width, self.rrow_positions[k].y + 0.5*drc["minwidth_metal1"])
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=row_offsetR,
rotate=90)
# add vias on rrows
offset = vector(self.r_nmosL[k].get_pin("S").ll().x + contact.m1m2.height, self.rrow_positions[k].y)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
# connect rt gate contacts to RROW contacts
self.add_path("metal2", [offsetL, row_offsetL])
self.add_path("metal2", [offsetR, row_offsetR])
offset = vector(self.r_nmosR[k].get_pin("S").ll().x + contact.m1m2.height, self.rrow_positions[k].y)
self.add_contact(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=90)
# connect sources of rt to rrows
rrow_offset = vector(self.r_nmosL[k].get_pin("S").bc().x, self.rrow_positions[k].y)
self.add_path("metal2", [self.r_nmosL[k].get_pin("S").bc(), rrow_offset])
""" Gate of RA transistor / storage connection """
# add contact to connect gate of rat to output of inverters
offsetL = vector(lrt_xpos + drc["minwidth_poly"] + 0.5*contact.poly.width, self.r_nmosL[k].get_pins("G")[0].rc().y)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetL)
offsetR = vector(rrt_xpos - rot_correct - drc["minwidth_poly"] - 0.5*contact.poly.width, self.r_nmosR[k].get_pins("G")[0].lc().y)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetR)
rrow_offset = vector(self.r_nmosR[k].get_pin("S").bc().x, self.rrow_positions[k].y)
self.add_path("metal2", [self.r_nmosR[k].get_pin("S").bc(), rrow_offset])
# connect gate of rat to contact
self.add_path("poly", [self.r_nmosL[k].get_pins("G")[0].rc(), offsetL])
self.add_path("poly", [self.r_nmosR[k].get_pins("G")[0].lc(), offsetR])
"""
Gate/inverter connections
"""
# add vias to connect to gate of rt
offsetL = vector(self.r_nmosL[k].get_pin("D").lr().x + drc["minwidth_metal1"], self.r_nmosL[k].get_pin("G").lr().y)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offsetL)
offsetR = vector(self.r_nmosR[k].get_pin("D").ll().x - drc["minwidth_metal1"] - contact.poly.width, self.r_nmosR[k].get_pin("G").ll().y)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offsetR)
# add metal1 path between gate vias and inverter inputs
midL = vector(offsetL.x + 0.5*contact.poly.width, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"])
gate_offsetL = vector(self.i_nmosL.get_pin("D").ll().x, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"])
self.add_path("metal1", [offsetL+vector(0.5*contact.poly.width,0), midL, gate_offsetL])
midR = vector(offsetR.x + 0.5*contact.poly.width, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"])
gate_offsetR = vector(self.i_nmosR.get_pin("S").lr().x, self.i_nmos.active_height + 0.6*self.inv_gap + 0.5*drc["minwidth_metal1"])
self.add_path("metal1", [offsetR+vector(0.5*contact.poly.width,0), midR, gate_offsetR])
# connect contact to output of inverters
midL0 = vector(offsetL.x, self.r_nmosL[k].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"])
midL1 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.r_nmosL[0].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"])
midL2 = vector(self.i_nmosL.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.cross_heightU)
gate_offsetL = vector(self.i_nmosL.get_pin("D").center().x, self.cross_heightU)
self.add_path("metal1", [offsetL, midL0, midL1, midL2, gate_offsetL])
midR0 = vector(offsetR.x, self.r_nmosR[k].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"])
midR1 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.r_nmosR[k].get_pins("S")[1].uc().y + 1.5*drc["minwidth_metal1"])
midR2 = vector(self.i_nmosR.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.cross_heightU)
gate_offsetR = vector(self.i_nmosR.get_pin("S").center().x, self.cross_heightU)
self.add_path("metal1", [offsetR, midR0, midR1, midR2, gate_offsetR])
def extend_well(self):
offset = vector(self.leftmost_xpos, -2*drc["minwidth_metal1"])
well_height = self.w_nmos.well_width + 2*drc["minwidth_metal1"]
""" extend nwell and pwell """
# extend pwell to encompass i_nmos
offset = vector(self.leftmost_xpos, self.botmost_ypos)
well_height = -self.botmost_ypos + self.i_nmos.well_height - drc["well_enclosure_active"]
self.add_rect(layer="pwell",
offset=offset,
width=self.cell_width,
height=well_height)
# extend pwell to encompass r_nmos
r_well_width = self.num_read*self.r_tile_width
r_well_height = self.r_nmos.well_width - drc["well_enclosure_active"]
offset = vector(self.leftmost_xpos, 0)
self.add_rect(layer="pwell",
offset=offset,
width=r_well_width,
height=r_well_height)
offset = vector(-self.leftmost_xpos - r_well_width, 0)
self.add_rect(layer="pwell",
offset=offset,
width=r_well_width,
height=r_well_height)
# extend pwell to encompass w_nmos
lwt_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"] + self.w_tile_width - self.w_nmos.active_height - drc["well_enclosure_active"])
rwt_xpos = 1.5*parameter["min_tx_size"] + self.i_nmos.active_width + self.w_tile_width - self.w_nmos.active_height - drc["well_enclosure_active"]
w_well_width = -(self.leftmost_xpos - lwt_xpos)
w_well_height = self.w_nmos.well_width - drc["well_enclosure_active"]
offset = vector(lwt_xpos - w_well_width, 0)
self.add_rect(layer="pwell",
offset=offset,
width=w_well_width,
height=w_well_height)
offset = vector(rwt_xpos, 0)
self.add_rect(layer="pwell",
offset=offset,
width=w_well_width,
height=w_well_height)
# extend nwell to encompass i_pmos
lit_xpos = -(self.i_nmos.active_width + 1.5*parameter["min_tx_size"] + drc["well_enclosure_active"])
it_ypos = self.inv_gap + self.i_nmos.active_height - drc["well_enclosure_active"]
offset = [lit_xpos,it_ypos]
well_width = 2*self.i_pmos.active_width + 3*parameter["min_tx_size"] + 2*drc["well_enclosure_active"]
well_height = self.i_pmos.well_height + 2*drc["minwidth_metal1"]
well_height = self.vdd_position.y - it_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"]
self.add_rect(layer="nwell",
offset=offset,
width=well_width,
height=well_height)
""" add well contacts """
# connect pimplants to gnd
offset = vector(0, self.gnd_position.y + 0.5*drc["minwidth_metal1"])
self.add_contact_center(layers=("active", "contact", "metal1"),
offset=offset,
rotate=90)
self.add_rect_center(layer="pimplant",
offset=offset,
width=drc["minwidth_tx"],
height=drc["minwidth_tx"])
# connect nimplants to vdd
offset = vector(0, self.vdd_position.y + 0.5*drc["minwidth_metal1"])
self.add_contact_center(layers=("active", "contact", "metal1"),
offset=offset,
rotate=90)
self.add_rect_center(layer="nimplant",
offset=offset,
width=drc["minwidth_tx"],
height=drc["minwidth_tx"])
def add_fail(self):
# for failing drc
frail_width = self.well_width = 10*drc["minwidth_tx"]
frail_width = self.well_width = 2*drc["minwidth_metal1"]
frail_height = self.rail_height = drc["minwidth_metal1"]
self.gnd_position = vector(-25*drc["minwidth_tx"], - 1.5 * drc["minwidth_metal1"] - 0.5 * frail_height) # for tiling purposes
fail_position = vector(-25*drc["minwidth_tx"], - 1.5 * drc["minwidth_metal1"] - 0.5 * frail_height) # for tiling purposes
self.add_layout_pin(text="gnd",
layer="metal1",
offset=self.gnd_position,
offset=fail_position,
width=frail_width,
height=frail_height)
self.gnd_position2 = vector(-25*drc["minwidth_tx"], - 0.5 * drc["minwidth_metal1"])
fail_position2 = vector(-25*drc["minwidth_tx"], - 0.5 * drc["minwidth_metal1"])
self.add_layout_pin(text="gnd2",
layer="metal1",
offset=self.gnd_position2,
offset=fail_position2,
width=frail_width,
height=frail_height)

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python2.7
"""
Run regresion tests on a parameterized inverter
"""
import unittest
from testutils import header
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
import verify
OPTS = globals.OPTS
#@unittest.skip("SKIPPING 04_pbitcell_test")
class pbitcell_test(unittest.TestCase):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import pbitcell
import tech
debug.info(2, "Test for pbitcell with 2 write ports and 0 read ports")
tx = pbitcell.pbitcell(num_write=2,num_read=0)
self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, tx):
tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds"
tx.sp_write(tempspice)
tx.gds_write(tempgds)
self.assertFalse(verify.run_drc(tx.name, tempgds))
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
os.remove(tempspice)
os.remove(tempgds)
# reset the static duplicate name checker for unit tests
import design
design.design.name_map=[]
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python2.7
"""
Run regresion tests on a parameterized inverter
"""
import unittest
from testutils import header
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
import verify
OPTS = globals.OPTS
#@unittest.skip("SKIPPING 04_pbitcell_test")
class pbitcell_test(unittest.TestCase):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import pbitcell
import tech
debug.info(2, "Test for pbitcell with 0 write ports and 2 read ports")
tx = pbitcell.pbitcell(num_write=0,num_read=2)
self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram()
def local_check(self, tx):
tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds"
tx.sp_write(tempspice)
tx.gds_write(tempgds)
self.assertFalse(verify.run_drc(tx.name, tempgds))
self.assertFalse(verify.run_lvs(tx.name, tempgds, tempspice))
os.remove(tempspice)
os.remove(tempgds)
# reset the static duplicate name checker for unit tests
import design
design.design.name_map=[]
# instantiate a copy of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()

View File

@ -25,8 +25,8 @@ class pbitcell_test(unittest.TestCase):
import pbitcell
import tech
debug.info(2, "Test for pbitcell")
tx = pbitcell.pbitcell()
debug.info(2, "Test for pbitcell with 2 write ports and 2 read ports")
tx = pbitcell.pbitcell(num_write=2,num_read=2)
self.local_check(tx)
OPTS.check_lvsdrc = True