removed pbitcell for compiler folder

This commit is contained in:
Michael Timothy Grimes 2018-02-28 11:28:04 -08:00
parent d41abb3074
commit 1ba626fce1
1 changed files with 0 additions and 688 deletions

View File

@ -1,688 +0,0 @@
import contact
import pgate
import design
import debug
from tech import drc, parameter, spice
from vector import vector
from ptx import ptx
from globals import OPTS
class pbitcell(pgate.pgate):
"""
This module implements a parametrically sized multi-port bitcell
"""
def __init__(self, num_write=1, num_read=1):
name = "pbitcell_{0}W_{1}R".format(num_write, num_read)
pgate.pgate.__init__(self, name)
debug.info(2, "create a multi-port bitcell with {0} write ports and {1} read ports".format(num_write, num_read))
self.num_write = num_write
self.num_read = num_read
self.add_pins()
self.create_layout()
self.DRC_LVS()
def add_pins(self):
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")
self.add_pin("gnd")
def create_layout(self):
self.add_globals()
self.add_storage()
self.add_rails()
self.add_write_ports()
if(self.num_read > 0):
self.add_read_ports()
self.extend_well()
#self.add_fail()
def add_globals(self):
""" Calculate transistor sizes """
# if there are no read ports then write transistors are being used as read/write ports like in a 6T bitcell
if(self.num_read == 0):
inverter_nmos_width = 3*parameter["min_tx_size"]
inverter_pmos_width = parameter["min_tx_size"]
write_nmos_width = 1.5*parameter["min_tx_size"]
read_nmos_width = parameter["min_tx_size"]
# used for the dual port case where there are separate write and read ports
else:
inverter_nmos_width = 2*parameter["min_tx_size"]
inverter_pmos_width = parameter["min_tx_size"]
write_nmos_width = parameter["min_tx_size"]
read_nmos_width = 2*parameter["min_tx_size"]
""" Create ptx for all transistors """
# create ptx for inverter transistors
self.inverter_nmos = ptx(width=inverter_nmos_width,
tx_type="nmos")
self.add_mod(self.inverter_nmos)
self.inverter_pmos = ptx(width=inverter_pmos_width,
tx_type="pmos")
self.add_mod(self.inverter_pmos)
# create ptx for write transitors
self.write_nmos = ptx(width=write_nmos_width,
tx_type="nmos")
self.add_mod(self.write_nmos)
# create ptx for read transistors
self.read_nmos = ptx(width=read_nmos_width,
tx_type="nmos")
self.add_mod(self.read_nmos)
""" Define pbitcell global variables """
# determine metal contact extensions
self.ip_ex = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height)
self.w_ex = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height)
# calculation for transistor spacing
self.write_to_write_spacing = drc["minwidth_metal2"] + self.w_ex + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"]
self.read_to_read_spacing = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width
self.write_to_read_spacing = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.w_ex
# calculations for transistor tiling (includes transistor and spacing)
self.inverter_tile_width = self.inverter_nmos.active_width + 1.5*parameter["min_tx_size"]
self.write_tile_width = self.write_to_write_spacing + self.write_nmos.active_height
self.read_tile_width = self.read_to_read_spacing + self.read_nmos.active_height
# calculation for row line tiling
self.rowline_tile_height = drc["minwidth_metal1"] + contact.m1m2.width
# calculations related to inverter connections
self.inverter_gap = drc["poly_to_active"] + drc["poly_to_field_poly"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.ip_ex
self.cross_couple_lower_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + 0.5*contact.poly.width
self.cross_couple_upper_ypos = self.inverter_nmos.active_height + drc["poly_to_active"] + drc["poly_to_field_poly"] + 1.5*contact.poly.width
# calculations for the edges of the cell
if(self.num_read > 0):
read_port_flag = 1;
else:
read_port_flag = 0;
self.leftmost_xpos = -self.inverter_tile_width \
- self.num_write*self.write_tile_width \
- read_port_flag*(self.write_to_read_spacing - self.read_nmos.active_height - (self.num_read-1)*self.read_tile_width) \
- drc["minwidth_poly"] - contact.m1m2.height - 0.5*drc["minwidth_metal2"]
self.rightmost_xpos = -self.leftmost_xpos
self.botmost_ypos = -self.rowline_tile_height \
- self.num_write*self.rowline_tile_height \
- read_port_flag*(self.num_read*self.rowline_tile_height)
self.topmost_ypos = self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + drc["poly_extend_active"] + 2*drc["minwidth_metal1"]
# calculations for the cell dimensions
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.
The stored value of the cell is denoted as "Q" and the inverted values as "Q_bar".
"""
# calculate transistor offsets
left_inverter_xpos = -(self.inverter_nmos.active_width + 1.5*parameter["min_tx_size"])
right_inverter_xpos = 1.5*parameter["min_tx_size"]
inverter_pmos_ypos = self.inverter_gap + self.inverter_nmos.active_height
# create active for nmos
self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left",
mod=self.inverter_nmos,
offset=[left_inverter_xpos,0])
self.connect_inst(["Q_bar", "Q", "gnd", "gnd"])
self.inverter_nmos_right = self.add_inst(name="inverter_nmos_right",
mod=self.inverter_nmos,
offset=[right_inverter_xpos,0])
self.connect_inst(["gnd", "Q_bar", "Q", "gnd"])
# create active for pmos
self.inverter_pmos_left = self.add_inst(name="inverter_pmos_left",
mod=self.inverter_pmos,
offset=[left_inverter_xpos, inverter_pmos_ypos])
self.connect_inst(["Q_bar", "Q", "vdd", "vdd"])
self.inverter_pmos_right = self.add_inst(name="inverter_pmos_right",
mod=self.inverter_pmos,
offset=[right_inverter_xpos, inverter_pmos_ypos])
self.connect_inst(["vdd", "Q_bar", "Q", "vdd"])
# connect input (gate) of inverters
self.add_path("poly", [self.inverter_nmos_left.get_pin("G").uc(), self.inverter_pmos_left.get_pin("G").bc()])
self.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()])
# connect output (drain/source) of inverters
self.add_path("metal1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], width=contact.well.width)
self.add_path("metal1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.well.width)
# add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar)
offsetL = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.width, self.cross_couple_upper_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetL,
rotate=90)
offsetR = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.width, self.cross_couple_lower_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=offsetR,
rotate=90)
# connect contacts to gate poly (cross couple)
gate_offsetR = vector(self.inverter_nmos_right.get_pin("G").lc().x, offsetL.y)
self.add_path("poly", [offsetL, gate_offsetR])
gate_offsetL = vector(self.inverter_nmos_left.get_pin("G").rc().x, offsetR.y)
self.add_path("poly", [offsetR, gate_offsetL])
def add_rails(self):
"""
Adds gnd and vdd rails and connects them to the storage element
"""
""" Add rails for vdd and gnd """
self.gnd_position = vector(self.leftmost_xpos, -self.rowline_tile_height)
self.gnd = self.add_layout_pin(text="gnd",
layer="metal1",
offset=self.gnd_position,
width=self.cell_width,
height=contact.well.second_layer_width)
self.vdd_position = vector(self.leftmost_xpos, self.inverter_pmos_left.get_pin("S").uc().y + drc["minwidth_metal1"])
self.vdd = self.add_layout_pin(text="vdd",
layer="metal1",
offset=self.vdd_position,
width=self.cell_width,
height=drc["minwidth_metal1"])
""" Connect inverters to rails """
# connect nmos to gnd
gnd_posL = vector(self.inverter_nmos_left.get_pin("S").bc().x, self.gnd_position.y + drc["minwidth_metal1"])
self.add_path("metal1", [self.inverter_nmos_left.get_pin("S").bc(), gnd_posL])
gnd_posR = vector(self.inverter_nmos_right.get_pin("D").bc().x, self.gnd_position.y + drc["minwidth_metal1"])
self.add_path("metal1", [self.inverter_nmos_right.get_pin("D").bc(), gnd_posR])
# connect pmos to vdd
vdd_posL = vector(self.inverter_nmos_left.get_pin("S").uc().x, self.vdd_position.y)
self.add_path("metal1", [self.inverter_pmos_left.get_pin("S").uc(), vdd_posL])
vdd_posR = vector(self.inverter_nmos_right.get_pin("D").uc().x, self.vdd_position.y)
self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_posR])
def add_write_ports(self):
"""
Adds write ports to the bit cell. A single transistor acts as the write port.
A write is enabled by setting a Write-Rowline (WROW) high, subsequently turning on the transistor.
The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q).
Driving WBL high or low sets the value of the cell.
This is a differential design, so each write port has a mirrored port that connects WBL_bar to Q_bar.
"""
""" Define variables relevant to write transistors """
# define offset correction due to rotation of the ptx cell
write_rotation_correct = self.write_nmos.active_height
# define write transistor variables as empty arrays based on the number of write ports
self.write_nmos_left = [None] * self.num_write
self.write_nmos_right = [None] * self.num_write
self.wrow_positions = [None] * self.num_write
self.wbl_positions = [None] * self.num_write
self.wbl_bar_positions = [None] * self.num_write
# iterate over the number of write ports
for k in range(0,self.num_write):
""" Add transistors """
# calculate write transistor offsets
left_write_transistor_xpos = -self.inverter_tile_width \
- (k+1)*self.write_tile_width + write_rotation_correct
right_write_transistor_xpos = self.inverter_tile_width \
+ self.write_to_write_spacing + k*self.write_tile_width + write_rotation_correct
# add write transistors
self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k),
mod=self.write_nmos,
offset=[left_write_transistor_xpos,0],
rotate=90)
self.connect_inst(["Q", "wrow{}".format(k), "wbl{}".format(k), "gnd"])
self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k),
mod=self.write_nmos,
offset=[right_write_transistor_xpos,0],
rotate=90)
self.connect_inst(["Q_bar", "wrow{}".format(k), "wbl_bar{}".format(k), "gnd"])
""" Add WROW lines """
# calculate WROW position
wrow_ypos = self.gnd_position.y - (k+1)*self.rowline_tile_height
self.wrow_positions[k] = vector(self.leftmost_xpos, wrow_ypos)
# add pin for WROW
self.add_layout_pin(text="wrow{}".format(k),
layer="metal1",
offset=self.wrow_positions[k],
width=self.cell_width,
height=contact.m1m2.width)
""" Source/WBL/WBL_bar connections """
# add metal1-to-metal2 contacts on top of write transistor source pins for connection to WBL and WBL_bar
offsetL = self.write_nmos_left[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetL,
rotate=90)
offsetR = self.write_nmos_right[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetR,
rotate=90)
# add pins for WBL and WBL_bar, overlaid on source contacts
self.wbl_positions[k] = vector(self.write_nmos_left[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(self.write_nmos_right[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)
""" Gate/WROW connections """
# add poly-to-meltal2 contacts to connect gate of write transistors to WROW (contact next to gate)
contact_xpos = self.write_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width
contact_ypos = self.write_nmos_left[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height
left_gate_contact = vector(contact_xpos, contact_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=left_gate_contact)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=left_gate_contact)
self.add_rect_center(layer="poly",
offset=left_gate_contact,
width=contact.poly.width,
height=contact.poly.height)
contact_xpos = self.write_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width
contact_ypos = self.write_nmos_right[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height
right_gate_contact = vector(contact_xpos, contact_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=right_gate_contact)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=right_gate_contact)
self.add_rect_center(layer="poly",
offset=right_gate_contact,
width=contact.poly.width,
height=contact.poly.height)
# connect gate of write transistor to contact (poly path)
midL = vector(left_gate_contact.x, self.write_nmos_left[k].get_pin("G").lc().y)
self.add_path("poly", [self.write_nmos_left[k].get_pin("G").lc(), midL, left_gate_contact])
midR = vector(right_gate_contact.x, self.write_nmos_right[k].get_pin("G").rc().y)
self.add_path("poly", [self.write_nmos_right[k].get_pin("G").rc(), midR, right_gate_contact])
# add metal1-to-metal2 contacts to WROW lines
left_wrow_contact = vector(left_gate_contact.x, self.wrow_positions[k].y + 0.5*contact.m1m2.width)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=left_wrow_contact,
rotate=90)
right_wrow_contact = vector(right_gate_contact.x, self.wrow_positions[k].y + 0.5*contact.m1m2.width)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=right_wrow_contact,
rotate=90)
# connect write transistor gate contacts to WROW contacts (metal2 path)
self.add_path("metal2", [left_gate_contact, left_wrow_contact])
self.add_path("metal2", [right_gate_contact, right_wrow_contact])
""" Drain/Storage connections """
# this path only needs to be drawn once on the last iteration of the loop
if(k == self.num_write-1):
# add contacts to connect gate of inverters to drain of write transistors
left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_field_poly"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=left_storage_contact,
rotate=90)
right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_field_poly"] + 0.5*contact.poly.width, self.cross_couple_lower_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=right_storage_contact,
rotate=90)
# connect gate of inverters to contacts (poly path)
gate_offsetL = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_lower_ypos)
self.add_path("poly", [left_storage_contact, gate_offsetL])
gate_offsetR = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_lower_ypos)
self.add_path("poly", [right_storage_contact, gate_offsetR])
# connect contacts to drains of write transistors (metal1 path)
midL0 = vector(left_storage_contact.x - 0.5*contact.poly.height - 1.5*drc["minwidth_metal1"], left_storage_contact.y)
midL1 = vector(left_storage_contact.x - 0.5*contact.poly.height - 1.5*drc["minwidth_metal1"], self.write_nmos_left[k].get_pin("D").lc().y)
self.add_path("metal1", [left_storage_contact, midL0, midL1, self.write_nmos_left[k].get_pin("D").lc()])
midR0 = vector(right_storage_contact.x + 0.5*contact.poly.height + 1.5*drc["minwidth_metal1"], right_storage_contact.y)
midR1 = vector(right_storage_contact.x + 0.5*contact.poly.height + 1.5*drc["minwidth_metal1"], self.write_nmos_right[k].get_pin("D").rc().y)
self.add_path("metal1", [right_storage_contact, midR0, midR1, self.write_nmos_right[k].get_pin("D").rc()])
def add_read_ports(self):
"""
Adds read ports to the bit cell. Two transistors function as a read port.
The two transistors in the port are denoted as the "read transistor" and the "read-access transistor".
The read transistor is connected to RROW (gate), RBL (drain), and the read-access transistor (source).
The read-access transistor is connected to Q_bar (gate), gnd (source), and the read transistor (drain).
A read is enabled by setting a Read-Rowline (RROW) high, subsequently turning on the read transistor.
The Read-Bitline (RBL) is precharged to high, and when the value of Q_bar is also high, the read-access transistor
is turned on, creating a connection between RBL and gnd. RBL subsequently discharges allowing for a differential read
using sense amps. This is a differential design, so each read port has a mirrored port that connects RBL_bar to Q.
"""
""" Define variables relevant to read transistors """
# define offset correction due to rotation of the ptx cell
read_rotation_correct = self.read_nmos.active_height
# calculate offset to overlap the drain of the read-access transistor with the source of the read transistor
overlap_offset = self.read_nmos.get_pin("D").ll() - self.read_nmos.get_pin("S").ll()
# define read transistor variables as empty arrays based on the number of read ports
self.read_nmos_left = [None] * self.num_read
self.read_nmos_right = [None] * self.num_read
self.read_access_nmos_left = [None] * self.num_read
self.read_access_nmos_right = [None] * self.num_read
self.rrow_positions = [None] * self.num_read
self.rbl_positions = [None] * self.num_read
self.rbl_bar_positions = [None] * self.num_read
# iterate over the number of read ports
for k in range(0,self.num_read):
""" Add transistors """
# calculate transistor offsets
left_read_transistor_xpos = -self.inverter_tile_width \
- self.num_write*self.write_tile_width \
- self.write_to_read_spacing - self.read_nmos.active_height - k*self.read_tile_width + read_rotation_correct
right_read_transistor_xpos = self.inverter_tile_width \
+ self.num_write*self.write_tile_width \
+ self.write_to_read_spacing + k*self.read_tile_width + read_rotation_correct
# add read-access transistors
self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left",
mod=self.read_nmos,
offset=[left_read_transistor_xpos,0],
rotate=90)
self.connect_inst(["RA_to_R_left{}".format(k), " Q_bar", "gnd", "gnd"])
self.read_access_nmos_right[k] = self.add_inst(name="read_access_nmos_right",
mod=self.read_nmos,
offset=[right_read_transistor_xpos,0],
rotate=90)
self.connect_inst(["RA_to_R_right{}".format(k), "Q", "gnd", "gnd"])
# add read transistors
self.read_nmos_left[k] = self.add_inst(name="read_nmos_left",
mod=self.read_nmos,
offset=[left_read_transistor_xpos,overlap_offset.x],
rotate=90)
self.connect_inst(["rbl{}".format(k), "rrow{}".format(k), "RA_to_R_left{}".format(k), "gnd"])
self.read_nmos_right[k] = self.add_inst(name="read_nmos_right",
mod=self.read_nmos,
offset=[right_read_transistor_xpos,overlap_offset.x],
rotate=90)
self.connect_inst(["rbl_bar{}".format(k), "rrow{}".format(k), "RA_to_R_right{}".format(k), "gnd"])
""" Add RROW lines """
# calculate RROW position
rrow_ypos = self.gnd_position.y \
- self.num_write*self.rowline_tile_height \
- (k+1)*self.rowline_tile_height
self.rrow_positions[k] = vector(self.leftmost_xpos, rrow_ypos)
# add pin for RROW
self.add_layout_pin(text="rrow{}".format(k),
layer="metal1",
offset=self.rrow_positions[k],
width=self.cell_width,
height=contact.m1m2.width)
""" Source of read-access transistor / GND connection """
# connect source of read-access transistor to GND (metal1 path)
offset = vector(self.read_access_nmos_left[k].get_pin("S").bc().x, self.gnd_position.y)
self.add_path("metal1", [self.read_access_nmos_left[k].get_pin("S").bc(), offset])
offset = vector(self.read_access_nmos_right[k].get_pin("S").bc().x, self.gnd_position.y)
self.add_path("metal1", [self.read_access_nmos_right[k].get_pin("S").bc(), offset])
""" Drain of read transistor / RBL & RBL_bar connection """
# add metal1-to-metal2 contacts on top of read transistor drain pins for connection to RBL and RBL_bar
offsetL = self.read_nmos_left[k].get_pin("D").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetL,
rotate=90)
offsetR = self.read_nmos_right[k].get_pin("D").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offsetR,
rotate=90)
# add pins for RBL and RBL_bar, overlaid on drain contacts
self.rbl_positions[k] = vector(self.read_nmos_left[k].get_pin("D").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.read_nmos_right[k].get_pin("D").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)
""" Gate of read transistor / RROW connection """
# add poly-to-meltal2 contacts to connect gate of read transistors to RROW (contact next to gate)
contact_xpos = left_read_transistor_xpos - read_rotation_correct - drc["minwidth_poly"] - 0.5*contact.poly.width
contact_ypos = self.read_nmos_left[k].get_pin("G").lc().y
left_gate_contact = vector(contact_xpos, contact_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=left_gate_contact)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=left_gate_contact)
contact_xpos = right_read_transistor_xpos + drc["minwidth_poly"] + 0.5*contact.poly.width
contact_ypos = self.read_nmos_right[k].get_pin("G").rc().y
right_gate_contact = vector(contact_xpos, contact_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=right_gate_contact)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=right_gate_contact)
# connect gate of read transistor to contact (poly path)
self.add_path("poly", [self.read_nmos_left[k].get_pin("G").lc(), left_gate_contact])
self.add_path("poly", [self.read_nmos_right[k].get_pin("G").rc(), right_gate_contact])
# add metal1-to-metal2 contacts to RROW lines
left_rrow_contact = vector(left_gate_contact.x, self.rrow_positions[k].y + 0.5*contact.m1m2.width)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=left_rrow_contact,
rotate=90)
right_rrow_contact = vector(right_gate_contact.x, self.rrow_positions[k].y + 0.5*contact.m1m2.width)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=right_rrow_contact,
rotate=90)
# connect read transistor gate contacts to RROW contacts (metal2 path)
self.add_path("metal2", [left_gate_contact, left_rrow_contact])
self.add_path("metal2", [right_gate_contact, right_rrow_contact])
""" Gate of read-access transistor / storage connection """
# add poly-to-metal1 contacts to connect gate of read-access transistors to output of inverters (contact next to gate)
contact_xpos = left_read_transistor_xpos + drc["minwidth_poly"] + 0.5*contact.poly.width
contact_ypos = self.read_access_nmos_left[k].get_pin("G").rc().y
left_gate_contact = vector(contact_xpos, contact_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=left_gate_contact)
contact_xpos = right_read_transistor_xpos - read_rotation_correct - drc["minwidth_poly"] - 0.5*contact.poly.width
contact_ypos = self.read_access_nmos_right[k].get_pin("G").lc().y
right_gate_contact = vector(contact_xpos, contact_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=right_gate_contact)
# connect gate of read-access transistor to contact (poly path)
self.add_path("poly", [self.read_access_nmos_left[k].get_pin("G").rc(), left_gate_contact])
self.add_path("poly", [self.read_access_nmos_right[k].get_pin("G").lc(), right_gate_contact])
# connect contact to output of inverters (metal1 path)
# metal1 path must route over the read transistors (above drain of read transistor)
midL0 = vector(left_gate_contact.x, self.read_nmos_left[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"])
## continue metal1 path until inverter
midL1 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.read_nmos_left[0].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"])
## route down to be level with inverter output
midL2 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.cross_couple_upper_ypos)
## endpoint at drain of inverter
left_inverter_offset = vector(self.inverter_nmos_left.get_pin("D").center().x, self.cross_couple_upper_ypos)
self.add_path("metal1", [left_gate_contact, midL0, midL1, midL2, left_inverter_offset])
midR0 = vector(right_gate_contact.x, self.read_nmos_right[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"])
midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.read_nmos_right[k].get_pin("D").uc().y + 1.5*drc["minwidth_metal1"])
midR2 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.cross_couple_upper_ypos)
right_inverter_offset = vector(self.inverter_nmos_right.get_pin("S").center().x, self.cross_couple_upper_ypos)
self.add_path("metal1", [right_gate_contact, midR0, midR1, midR2, right_inverter_offset])
def extend_well(self):
""" extend pwell to encompass entire nmos region of the cell up to the height of the inverter nmos well """
offset = vector(self.leftmost_xpos, self.botmost_ypos)
well_height = -self.botmost_ypos + self.inverter_nmos.cell_well_height - drc["well_enclosure_active"]
self.add_rect(layer="pwell",
offset=offset,
width=self.cell_width,
height=well_height)
""" extend pwell over write transistors to the height of the write transistor well """
left_write_well_xpos = -(self.inverter_tile_width + self.write_to_write_spacing - drc["well_enclosure_active"])
right_write_well_xpos = self.inverter_tile_width + self.write_to_write_spacing - drc["well_enclosure_active"]
write_well_width = -(self.leftmost_xpos - left_write_well_xpos)
write_well_height = self.write_nmos.cell_well_width - drc["well_enclosure_active"]
offset = vector(left_write_well_xpos - write_well_width, 0)
self.add_rect(layer="pwell",
offset=offset,
width=write_well_width,
height=write_well_height)
offset = vector(right_write_well_xpos, 0)
self.add_rect(layer="pwell",
offset=offset,
width=write_well_width,
height=write_well_height)
""" extend pwell over the read transistors to the height of the read transistor well """
if(self.num_read > 0):
left_read_well_xpos = -(self.inverter_tile_width + self.num_write*self.write_tile_width + self.write_to_read_spacing - drc["well_enclosure_active"])
right_read_well_xpos = self.inverter_tile_width + self.num_write*self.write_tile_width + self.write_to_read_spacing - drc["well_enclosure_active"]
read_well_width = -(self.leftmost_xpos - left_read_well_xpos)
read_well_height = self.topmost_ypos
offset = vector(self.leftmost_xpos, 0)
self.add_rect(layer="pwell",
offset=offset,
width=read_well_width,
height=read_well_height)
offset = vector(right_read_well_xpos, 0)
self.add_rect(layer="pwell",
offset=offset,
width=read_well_width,
height=read_well_height)
""" extend nwell to encompass inverter_pmos """
inverter_well_xpos = -self.inverter_tile_width - drc["well_enclosure_active"]
inverter_well_ypos = self.inverter_nmos.active_height + self.inverter_gap - drc["well_enclosure_active"]
well_width = 2*self.inverter_pmos.active_width + 3*parameter["min_tx_size"] + 2*drc["well_enclosure_active"]
well_height = self.vdd_position.y - inverter_well_ypos + drc["well_enclosure_active"] + drc["minwidth_tx"]
offset = [inverter_well_xpos,inverter_well_ypos]
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*contact.well.second_layer_width)
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 when I want to observe the gds layout
frail_width = self.well_width = 2*drc["minwidth_metal1"]
frail_height = self.rail_height = drc["minwidth_metal1"]
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=fail_position,
width=frail_width,
height=frail_height)
fail_position2 = vector(-25*drc["minwidth_tx"], - 0.5 * drc["minwidth_metal1"])
self.add_layout_pin(text="gnd2",
layer="metal1",
offset=fail_position2,
width=frail_width,
height=frail_height)