mirror of https://github.com/VLSIDA/OpenRAM.git
updating pbitcell with latest layout optimizations
This commit is contained in:
parent
1ba626fce1
commit
d6ef91786b
|
|
@ -15,7 +15,7 @@ class pbitcell(pgate.pgate):
|
|||
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))
|
||||
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
|
||||
|
|
@ -24,20 +24,23 @@ class pbitcell(pgate.pgate):
|
|||
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))
|
||||
if(self.num_read > 0):
|
||||
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()
|
||||
|
|
@ -46,16 +49,19 @@ class pbitcell(pgate.pgate):
|
|||
if(self.num_read > 0):
|
||||
self.add_read_ports()
|
||||
self.extend_well()
|
||||
self.offset_all_coordinates()
|
||||
#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 there are no read ports then write transistors are being used as read/write ports, like in a 6T cell
|
||||
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"]
|
||||
read_nmos_width = parameter["min_tx_size"] # read transistor not necessary but included as to not generate errors in the code when referenced
|
||||
|
||||
# used for the dual port case where there are separate write and read ports
|
||||
else:
|
||||
inverter_nmos_width = 2*parameter["min_tx_size"]
|
||||
|
|
@ -84,17 +90,29 @@ class pbitcell(pgate.pgate):
|
|||
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)
|
||||
# calculate metal contact extensions over transistor active
|
||||
self.inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height)
|
||||
self.write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height - self.write_nmos.active_height)
|
||||
|
||||
# calculation for transistor spacing (exact solutions)
|
||||
#self.inverter_to_inverter_spacing = 3*parameter["min_tx_size"]
|
||||
#self.inverter_to_write_spacing = need to calculate
|
||||
#self.write_to_write_spacing = drc["minwidth_metal2"] + self.write_nmos_contact_extension + contact.poly.width + drc["poly_to_field_poly"] + drc["poly_extend_active"]
|
||||
#self.write_to_read_spacing = drc["minwidth_poly"] + drc["poly_to_field_poly"] + drc["minwidth_metal2"] + 2*contact.poly.width + self.write_nmos_contact_extension
|
||||
#self.read_to_read_spacing = 2*drc["minwidth_poly"] + drc["minwidth_metal1"] + 2*contact.poly.width
|
||||
|
||||
# calculation for transistor spacing (symmetric solutions)
|
||||
self.inverter_to_inverter_spacing = 3*parameter["min_tx_size"]
|
||||
#self.inverter_to_write_spacing = need to calculate
|
||||
spacing_option1 = contact.poly.width + 2*drc["poly_to_field_poly"] + 2*drc["poly_extend_active"]
|
||||
spacing_option2 = contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension
|
||||
self.write_to_write_spacing = max(spacing_option1, spacing_option2)
|
||||
self.write_to_read_spacing = drc["poly_to_field_poly"] + 2*contact.poly.width + 2*drc["minwidth_metal2"] + 2*self.write_nmos_contact_extension
|
||||
self.read_to_read_spacing = drc["minwidth_metal1"] + 2*contact.poly.width + 2*drc["minwidth_poly"]
|
||||
|
||||
# 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.inverter_tile_width = self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing
|
||||
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
|
||||
|
||||
|
|
@ -102,28 +120,34 @@ class pbitcell(pgate.pgate):
|
|||
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.inverter_gap = drc["poly_to_active"] + drc["poly_to_field_poly"] + 2*contact.poly.width + drc["minwidth_metal1"] + self.inverter_pmos_contact_extension
|
||||
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
|
||||
""" Calculations for the edges of the cell """
|
||||
# create a flag for excluding read port calculations if they are not included in the bitcell
|
||||
if(self.num_read > 0):
|
||||
read_port_flag = 1;
|
||||
read_port_flag = 1
|
||||
else:
|
||||
read_port_flag = 0;
|
||||
|
||||
read_port_flag = 0
|
||||
|
||||
# leftmost position = storage width + write ports width + read ports width + read transistor gate connections + metal spacing necessary for tiling the bitcell
|
||||
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"]
|
||||
- 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
|
||||
|
||||
# bottommost position = gnd height + wrow height + rrow height
|
||||
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"]
|
||||
# topmost position = height of the inverter + height of vdd
|
||||
self.topmost_ypos = self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height \
|
||||
+ self.inverter_pmos_contact_extension + 2*drc["minwidth_metal1"]
|
||||
|
||||
# calculations for the cell dimensions
|
||||
self.cell_width = -2*self.leftmost_xpos
|
||||
|
|
@ -133,13 +157,13 @@ class pbitcell(pgate.pgate):
|
|||
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".
|
||||
The stored value of the cell is denoted as "Q", and the inverted value 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
|
||||
left_inverter_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width
|
||||
right_inverter_xpos = 0.5*self.inverter_to_inverter_spacing
|
||||
inverter_pmos_ypos = self.inverter_nmos.active_height + self.inverter_gap
|
||||
|
||||
# create active for nmos
|
||||
self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left",
|
||||
|
|
@ -168,31 +192,31 @@ class pbitcell(pgate.pgate):
|
|||
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)
|
||||
self.add_path("metal1", [self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], width=contact.well.second_layer_width)
|
||||
self.add_path("metal1", [self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], width=contact.well.second_layer_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)
|
||||
contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.height, self.cross_couple_upper_ypos)
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=offsetL,
|
||||
offset=contact_offset_left,
|
||||
rotate=90)
|
||||
|
||||
offsetR = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.width, self.cross_couple_lower_ypos)
|
||||
contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.height, self.cross_couple_lower_ypos)
|
||||
self.add_contact_center(layers=("poly", "contact", "metal1"),
|
||||
offset=offsetR,
|
||||
offset=contact_offset_right,
|
||||
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_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, contact_offset_left.y)
|
||||
self.add_path("poly", [contact_offset_left, gate_offset_right])
|
||||
|
||||
gate_offsetL = vector(self.inverter_nmos_left.get_pin("G").rc().x, offsetR.y)
|
||||
self.add_path("poly", [offsetR, gate_offsetL])
|
||||
|
||||
gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x, contact_offset_right.y)
|
||||
self.add_path("poly", [contact_offset_right, gate_offset_left])
|
||||
|
||||
|
||||
def add_rails(self):
|
||||
"""
|
||||
Adds gnd and vdd rails and connects them to the storage element
|
||||
Add gnd and vdd rails and connects them to the inverters
|
||||
"""
|
||||
|
||||
""" Add rails for vdd and gnd """
|
||||
|
|
@ -211,19 +235,19 @@ class pbitcell(pgate.pgate):
|
|||
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])
|
||||
# connect inverter nmos to gnd
|
||||
gnd_pos_left = vector(self.inverter_nmos_left.get_pin("S").bc().x, self.gnd_position.y)
|
||||
self.add_path("metal1", [self.inverter_nmos_left.get_pin("S").bc(), gnd_pos_left])
|
||||
|
||||
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])
|
||||
gnd_pos_right = vector(self.inverter_nmos_right.get_pin("D").bc().x, self.gnd_position.y)
|
||||
self.add_path("metal1", [self.inverter_nmos_right.get_pin("D").bc(), gnd_pos_right])
|
||||
|
||||
# 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])
|
||||
# connect inverter pmos to vdd
|
||||
vdd_pos_left = 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_pos_left])
|
||||
|
||||
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])
|
||||
vdd_pos_right = 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_pos_right])
|
||||
|
||||
|
||||
def add_write_ports(self):
|
||||
|
|
@ -251,10 +275,12 @@ class pbitcell(pgate.pgate):
|
|||
""" Add transistors """
|
||||
# calculate write transistor offsets
|
||||
left_write_transistor_xpos = -self.inverter_tile_width \
|
||||
- (k+1)*self.write_tile_width + write_rotation_correct
|
||||
- (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
|
||||
+ 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),
|
||||
|
|
@ -283,14 +309,14 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
""" 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()
|
||||
offset_left = self.write_nmos_left[k].get_pin("S").center()
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=offsetL,
|
||||
offset=offset_left,
|
||||
rotate=90)
|
||||
|
||||
offsetR = self.write_nmos_right[k].get_pin("S").center()
|
||||
offset_right = self.write_nmos_right[k].get_pin("S").center()
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=offsetR,
|
||||
offset=offset_right,
|
||||
rotate=90)
|
||||
|
||||
# add pins for WBL and WBL_bar, overlaid on source contacts
|
||||
|
|
@ -310,6 +336,7 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
""" Gate/WROW connections """
|
||||
# add poly-to-meltal2 contacts to connect gate of write transistors to WROW (contact next to gate)
|
||||
# contact must be placed a metal width below the source pin to avoid drc from routing to the source pins
|
||||
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)
|
||||
|
|
@ -317,12 +344,7 @@ class pbitcell(pgate.pgate):
|
|||
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)
|
||||
|
||||
offset=left_gate_contact)
|
||||
|
||||
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
|
||||
|
|
@ -331,18 +353,14 @@ class pbitcell(pgate.pgate):
|
|||
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)
|
||||
offset=right_gate_contact)
|
||||
|
||||
# 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])
|
||||
self.add_path("poly", [self.write_nmos_left[k].get_pin("G").lc(), midL, left_gate_contact], width=contact.poly.width)
|
||||
|
||||
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])
|
||||
self.add_path("poly", [self.write_nmos_right[k].get_pin("G").rc(), midR, right_gate_contact], width=contact.poly.width)
|
||||
|
||||
# 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)
|
||||
|
|
@ -374,11 +392,11 @@ class pbitcell(pgate.pgate):
|
|||
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])
|
||||
inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_lower_ypos)
|
||||
self.add_path("poly", [left_storage_contact, inverter_gate_offset_left])
|
||||
|
||||
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])
|
||||
inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_lower_ypos)
|
||||
self.add_path("poly", [right_storage_contact, inverter_gate_offset_right])
|
||||
|
||||
# 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)
|
||||
|
|
@ -397,7 +415,7 @@ class pbitcell(pgate.pgate):
|
|||
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
|
||||
The Read-Bitline (RBL) is precharged to high, and when the value of Q_bar is 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.
|
||||
"""
|
||||
|
|
@ -424,11 +442,13 @@ class pbitcell(pgate.pgate):
|
|||
# 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
|
||||
- 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
|
||||
+ 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",
|
||||
|
|
@ -460,7 +480,7 @@ class pbitcell(pgate.pgate):
|
|||
# calculate RROW position
|
||||
rrow_ypos = self.gnd_position.y \
|
||||
- self.num_write*self.rowline_tile_height \
|
||||
- (k+1)*self.rowline_tile_height
|
||||
- (k+1)*self.rowline_tile_height
|
||||
self.rrow_positions[k] = vector(self.leftmost_xpos, rrow_ypos)
|
||||
|
||||
# add pin for RROW
|
||||
|
|
@ -469,25 +489,17 @@ class pbitcell(pgate.pgate):
|
|||
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()
|
||||
offset_left = self.read_nmos_left[k].get_pin("D").center()
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=offsetL,
|
||||
offset=offset_left,
|
||||
rotate=90)
|
||||
|
||||
offsetR = self.read_nmos_right[k].get_pin("D").center()
|
||||
offset_right = self.read_nmos_right[k].get_pin("D").center()
|
||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=offsetR,
|
||||
offset=offset_right,
|
||||
rotate=90)
|
||||
|
||||
# add pins for RBL and RBL_bar, overlaid on drain contacts
|
||||
|
|
@ -507,7 +519,7 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
""" 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_xpos = left_read_transistor_xpos - self.read_nmos.active_height - 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)
|
||||
|
||||
|
|
@ -543,7 +555,15 @@ class pbitcell(pgate.pgate):
|
|||
# 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])
|
||||
|
||||
|
||||
""" Source of read-access transistor / GND connection """
|
||||
# connect source of read-access transistor to GND (metal1 path)
|
||||
gnd_offset_left = 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(), gnd_offset_left])
|
||||
|
||||
gnd_offset_right = 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(), gnd_offset_right])
|
||||
|
||||
""" 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
|
||||
|
|
@ -553,7 +573,7 @@ class pbitcell(pgate.pgate):
|
|||
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_xpos = right_read_transistor_xpos - self.read_nmos.active_height - 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)
|
||||
|
||||
|
|
@ -565,13 +585,13 @@ class pbitcell(pgate.pgate):
|
|||
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)
|
||||
# mid0: metal1 path must route over the read transistors (above drain of read transistor)
|
||||
# mid1: continue metal1 path horizontally until at inverter
|
||||
# mid2: route down to be level with inverter output
|
||||
# endpoint at drain/source of inverter
|
||||
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])
|
||||
|
||||
|
|
@ -583,6 +603,12 @@ class pbitcell(pgate.pgate):
|
|||
|
||||
|
||||
def extend_well(self):
|
||||
"""
|
||||
Connects wells between ptx cells to avoid drc spacing issues.
|
||||
Since the pwell of the read ports rise higher than the pmos of the inverters,
|
||||
the well connections must be done piecewise to avoid pwell and nwell overlap.
|
||||
"""
|
||||
|
||||
""" 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"]
|
||||
|
|
@ -592,9 +618,11 @@ class pbitcell(pgate.pgate):
|
|||
height=well_height)
|
||||
|
||||
""" extend pwell over write transistors to the height of the write transistor well """
|
||||
# calculate the edge of the write transistor well closest to the center
|
||||
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"]
|
||||
|
||||
# calculate a width that will halt at the edge of the write transistors
|
||||
write_well_width = -(self.leftmost_xpos - left_write_well_xpos)
|
||||
write_well_height = self.write_nmos.cell_well_width - drc["well_enclosure_active"]
|
||||
|
||||
|
|
@ -610,11 +638,13 @@ class pbitcell(pgate.pgate):
|
|||
width=write_well_width,
|
||||
height=write_well_height)
|
||||
|
||||
""" extend pwell over the read transistors to the height of the read transistor well """
|
||||
""" extend pwell over the read transistors to the height of the bitcell """
|
||||
if(self.num_read > 0):
|
||||
# calculate the edge of the read transistor well clostest to the center
|
||||
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"]
|
||||
|
||||
# calculate a width that will halt at the edge of the read transistors
|
||||
read_well_width = -(self.leftmost_xpos - left_read_well_xpos)
|
||||
read_well_height = self.topmost_ypos
|
||||
|
||||
|
|
@ -631,10 +661,13 @@ class pbitcell(pgate.pgate):
|
|||
height=read_well_height)
|
||||
|
||||
""" extend nwell to encompass inverter_pmos """
|
||||
# calculate offset of the left pmos well
|
||||
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"]
|
||||
# calculate width of the two combined nwells
|
||||
# calculate height to encompass nimplant connected to vdd
|
||||
well_width = 2*self.inverter_tile_width + 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]
|
||||
|
|
|
|||
Loading…
Reference in New Issue