Commiting changes to pbitcell that separate the routing into individual functions and rename. The bitlines and wordlines are also renamed.

This commit is contained in:
Michael Timothy Grimes 2018-09-06 17:59:21 -07:00
parent af0756382f
commit 66a8a76fb0
1 changed files with 304 additions and 315 deletions

View File

@ -16,23 +16,25 @@ class pbitcell(pgate.pgate):
width = None
height = None
def __init__(self, num_rw_ports=OPTS.num_rw_ports, num_w_ports=OPTS.num_w_ports, num_r_ports=OPTS.num_r_ports):
name = "pbitcell_{0}RW_{1}W_{2}R".format(num_rw_ports, num_w_ports, num_r_ports)
unique_id = 1
def __init__(self, num_readwrite=OPTS.num_rw_ports, num_write=OPTS.num_w_ports, num_read=OPTS.num_r_ports):
name = "pbitcell_{0}RW_{1}W_{2}R_{3}".format(num_readwrite, num_write, num_read, pbitcell.unique_id)
pbitcell.unique_id += 1
pgate.pgate.__init__(self, name)
debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(num_rw_ports,
num_w_ports,
num_r_ports))
self.num_rw_ports = num_rw_ports
self.num_w_ports = num_w_ports
self.num_r_ports = num_r_ports
debug.info(2, "create a multi-port bitcell with {0} write ports and {1} read ports".format(num_write, num_read))
self.num_readwrite = num_readwrite
self.num_write = num_write
self.num_read = num_read
self.total_ports = num_readwrite + num_write + num_read
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
# FIXME: Why is this static set here?
# Since since pbitcell's size is dependent on port choice, class width and height are set after layout creation
# class width and height are necessary for modules that load bitcell attributes
pbitcell.width = self.width
pbitcell.height = self.height
@ -41,11 +43,11 @@ class pbitcell(pgate.pgate):
self.add_modules()
self.create_storage()
if(self.num_rw_ports > 0):
if(self.num_readwrite > 0):
self.create_readwrite_ports()
if(self.num_w_ports > 0):
if(self.num_write > 0):
self.create_write_ports()
if(self.num_r_ports > 0):
if(self.num_read > 0):
self.create_read_ports()
def create_layout(self):
@ -56,48 +58,77 @@ class pbitcell(pgate.pgate):
self.route_storage()
self.route_rails()
if(self.num_rw_ports > 0):
if(self.num_readwrite > 0):
self.place_readwrite_ports()
if(self.num_w_ports > 0):
self.route_readwrite_wordlines()
self.route_readwrite_bitlines()
if(self.num_write == 0): # routing for write to storage is the same as read/write to storage
self.route_readwrite_access()
if(self.num_write > 0):
self.place_write_ports()
if(self.num_r_ports > 0):
self.route_write_wordlines()
self.route_write_bitlines()
self.route_write_access()
if(self.num_read > 0):
self.place_read_ports()
self.route_read_wordlines()
self.route_read_bitlines()
self.route_read_access()
self.extend_well()
self.offset_all_coordinates()
self.DRC_LVS()
def add_pins(self):
for k in range(self.num_rw_ports):
self.add_pin("rwbl{}".format(k))
self.add_pin("rwbl_bar{}".format(k))
for k in range(self.num_w_ports):
self.add_pin("wbl{}".format(k))
self.add_pin("wbl_bar{}".format(k))
for k in range(self.num_r_ports):
self.add_pin("rbl{}".format(k))
self.add_pin("rbl_bar{}".format(k))
self.rw_bl_names = []
self.rw_br_names = []
self.w_bl_names = []
self.w_br_names = []
self.r_bl_names = []
self.r_br_names = []
self.wl_names = []
port = 0
for k in range(self.num_readwrite):
self.add_pin("bl{}".format(port))
self.add_pin("br{}".format(port))
self.rw_bl_names.append("bl{}".format(port))
self.rw_br_names.append("br{}".format(port))
port += 1
for k in range(self.num_write):
self.add_pin("bl{}".format(port))
self.add_pin("br{}".format(port))
self.w_bl_names.append("bl{}".format(port))
self.w_br_names.append("br{}".format(port))
port += 1
for k in range(self.num_read):
self.add_pin("bl{}".format(port))
self.add_pin("br{}".format(port))
self.r_bl_names.append("bl{}".format(port))
self.r_br_names.append("br{}".format(port))
port += 1
for k in range(self.num_rw_ports):
self.add_pin("rwwl{}".format(k))
for k in range(self.num_w_ports):
self.add_pin("wwl{}".format(k))
for k in range(self.num_r_ports):
self.add_pin("rwl{}".format(k))
port = 0
for k in range(self.total_ports):
self.add_pin("wl{}".format(port))
self.wl_names.append("wl{}".format(port))
self.add_pin("vdd")
self.add_pin("gnd")
def add_modules(self):
# if there are any read/write ports, then the inverter nmos is sized based the number of them
if(self.num_rw_ports > 0):
inverter_nmos_width = self.num_rw_ports*3*parameter["min_tx_size"]
"""
Determine size of transistors and add ptx modules
"""
# if there are any read/write ports, then the inverter nmos is sized based the number of read/write ports
if(self.num_readwrite > 0):
inverter_nmos_width = self.num_readwrite*3*parameter["min_tx_size"]
inverter_pmos_width = parameter["min_tx_size"]
readwrite_nmos_width = 1.5*parameter["min_tx_size"]
write_nmos_width = parameter["min_tx_size"]
read_nmos_width = 2*parameter["min_tx_size"]
# if there are no read/write ports, then the inverter nmos is sized for the dual port case
# if there are no read/write ports, then the inverter nmos is statically sized for the dual port case
else:
inverter_nmos_width = 2*parameter["min_tx_size"]
inverter_pmos_width = parameter["min_tx_size"]
@ -105,7 +136,6 @@ class pbitcell(pgate.pgate):
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")
@ -167,7 +197,7 @@ class pbitcell(pgate.pgate):
# write to read transistor spacing (also acts as readwrite to read transistor spacing)
# calculation is dependent on whether the read transistor is adjacent to a write transistor or a readwrite transistor
if(self.num_w_ports > 0):
if(self.num_write > 0):
if(self.write_nmos_contact_extension > self.gate_contact_thres):
write_portion = drc["minwidth_metal2"] + self.write_nmos_contact_extension
else:
@ -185,17 +215,17 @@ class pbitcell(pgate.pgate):
self.write_to_read_spacing = write_portion + read_portion + 2*contact.poly.width + drc["poly_to_polycontact"]
""" calculations for transistor tiling (transistor + spacing) """
# calculations for transistor tiling (transistor + spacing)
self.inverter_tile_width = self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing
self.readwrite_tile_width = self.readwrite_to_readwrite_spacing + self.readwrite_nmos.active_height
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.rail_tile_height = drc["active_to_body_active"] + contact.well.width #0.5*(drc["minwidth_tx"] - drc["minwidth_metal1"]) + drc["minwidth_metal1"]
# calculation for row line tiling
self.rail_tile_height = drc["active_to_body_active"] + contact.well.width
self.rowline_tile_height = drc["minwidth_metal1"] + contact.m1m2.width
""" calculations related to inverter connections """
# calculations related to inverter connections
self.inverter_gap = drc["poly_to_active"] + drc["poly_to_polycontact"] + 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_polycontact"] + 1.5*contact.poly.width
@ -203,26 +233,26 @@ class pbitcell(pgate.pgate):
def calculate_postions(self):
"""
Calculate positions that describe the edges of the cell
Calculate positions that describe the edges and dimensions of the cell
"""
# create flags for excluding readwrite, write, or read port calculations if they are not included in the bitcell
if(self.num_rw_ports > 0):
if(self.num_readwrite > 0):
self.readwrite_port_flag = True
else:
self.readwrite_port_flag = False
if(self.num_w_ports > 0):
if(self.num_write > 0):
self.write_port_flag = True
else:
self.write_port_flag = False
if(self.num_r_ports > 0):
if(self.num_read > 0):
self.read_port_flag = True
else:
self.read_port_flag = False
# determine the distance of the leftmost/rightmost transistor gate connection
if (self.num_r_ports > 0):
if (self.num_read > 0):
if(self.read_nmos_contact_extension > self.gate_contact_thres):
end_connection = drc["minwidth_metal2"] + self.read_nmos_contact_extension + contact.m1m2.height
else:
@ -236,11 +266,11 @@ class pbitcell(pgate.pgate):
# leftmost position = storage width + read/write ports width + write ports width + read ports width + end transistor gate connections + metal spacing necessary for tiling the bitcell
self.leftmost_xpos = -self.inverter_tile_width \
- self.inverter_to_write_spacing \
- self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_rw_ports-1)*self.readwrite_tile_width) \
- self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-1)*self.readwrite_tile_width) \
- self.write_port_flag*self.readwrite_port_flag*self.write_to_write_spacing \
- self.write_port_flag*(self.write_nmos.active_height + (self.num_w_ports-1)*self.write_tile_width) \
- self.write_port_flag*(self.write_nmos.active_height + (self.num_write-1)*self.write_tile_width) \
- self.read_port_flag*self.write_to_read_spacing \
- self.read_port_flag*(self.read_nmos.active_height + (self.num_r_ports-1)*self.read_tile_width) \
- self.read_port_flag*(self.read_nmos.active_height + (self.num_read-1)*self.read_tile_width) \
- end_connection \
- 0.5*drc["poly_to_polycontact"]
@ -249,9 +279,9 @@ class pbitcell(pgate.pgate):
# bottommost position = gnd height + rwwl height + wwl height + rwl height + space needed between tiled bitcells
array_tiling_offset = 0.5*drc["minwidth_metal2"]
self.botmost_ypos = -self.rail_tile_height \
- self.num_rw_ports*self.rowline_tile_height \
- self.num_w_ports*self.rowline_tile_height \
- self.num_r_ports*self.rowline_tile_height \
- self.num_readwrite*self.rowline_tile_height \
- self.num_write*self.rowline_tile_height \
- self.num_read*self.rowline_tile_height \
- array_tiling_offset
# topmost position = height of the inverter + height of vdd
@ -291,8 +321,7 @@ class pbitcell(pgate.pgate):
def place_storage(self):
"""
Places the crossed coupled inverters that act as storage for the bitcell.
The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar".
Places the transistors for the crossed coupled inverters in the bitcell
"""
# calculate transistor offsets
@ -309,7 +338,9 @@ class pbitcell(pgate.pgate):
self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos])
def route_storage(self):
"""
Routes inputs and outputs of inverters to cross couple them
"""
# 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()])
@ -343,9 +374,8 @@ class pbitcell(pgate.pgate):
def route_rails(self):
"""
Add gnd and vdd rails and connects them to the inverters
Adds gnd and vdd rails and connects them to the inverters
"""
# Add rails for vdd and gnd
self.gnd_position = vector(self.leftmost_xpos, -self.rail_tile_height)
self.gnd = self.add_layout_pin(text="gnd",
@ -390,11 +420,11 @@ class pbitcell(pgate.pgate):
"""
# define write transistor variables as empty arrays based on the number of write ports
self.readwrite_nmos_left = [None] * self.num_rw_ports
self.readwrite_nmos_right = [None] * self.num_rw_ports
self.readwrite_nmos_left = [None] * self.num_readwrite
self.readwrite_nmos_right = [None] * self.num_readwrite
# iterate over the number of read/write ports
for k in range(0,self.num_rw_ports):
for k in range(0,self.num_readwrite):
# add read/write transistors
self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k),
mod=self.readwrite_nmos)
@ -411,15 +441,15 @@ class pbitcell(pgate.pgate):
"""
# Define variables relevant to write transistors
self.rwwl_positions = [None] * self.num_rw_ports
self.rwbl_positions = [None] * self.num_rw_ports
self.rwbl_bar_positions = [None] * self.num_rw_ports
self.rwwl_positions = [None] * self.num_readwrite
self.rwbl_positions = [None] * self.num_readwrite
self.rwbl_bar_positions = [None] * self.num_readwrite
# define offset correction due to rotation of the ptx module
readwrite_rotation_correct = self.readwrite_nmos.active_height
# iterate over the number of read/write ports
for k in range(0,self.num_rw_ports):
for k in range(0,self.num_readwrite):
# Add transistors
# calculate read/write transistor offsets
left_readwrite_transistor_xpos = self.left_building_edge \
@ -445,39 +475,36 @@ class pbitcell(pgate.pgate):
self.rwwl_positions[k] = vector(self.leftmost_xpos, rwwl_ypos)
# add pin for RWWL
self.add_layout_pin(text="rwwl{}".format(k),
self.add_layout_pin(text=self.rw_wl_names[k],
layer="metal1",
offset=self.rwwl_positions[k],
width=self.width,
height=contact.m1m2.width)
# Source/RWBL/RWBL_bar connections
# add metal1-to-metal2 contacts on top of read/write transistor source pins for connection to WBL and WBL_bar
offset_left = self.readwrite_nmos_left[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_left,
rotate=90)
offset_right = self.readwrite_nmos_right[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_right,
rotate=90)
# add pins for RWBL and RWBL_bar, overlaid on source contacts
self.rwbl_positions[k] = vector(self.readwrite_nmos_left[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos)
self.add_layout_pin(text="rwbl{}".format(k),
self.add_layout_pin(text=self.rw_bl_names[k],
layer="metal2",
offset=self.rwbl_positions[k],
width=drc["minwidth_metal2"],
height=self.height)
self.rwbl_bar_positions[k] = vector(self.readwrite_nmos_right[k].get_pin("S").center().x - 0.5*drc["minwidth_metal2"], self.botmost_ypos)
self.add_layout_pin(text="rwbl_bar{}".format(k),
self.add_layout_pin(text=self.rw_br_names[k],
layer="metal2",
offset=self.rwbl_bar_positions[k],
width=drc["minwidth_metal2"],
height=self.height)
# update furthest left and right transistor edges
self.left_building_edge = left_readwrite_transistor_xpos - self.readwrite_nmos.active_height
self.right_building_edge = right_readwrite_transistor_xpos
def route_readwrite_wordlines(self):
"""
Routes read/write trnasistors to their respective wordlines
"""
for k in range(0,self.num_readwrite):
# Gate/RWWL connections
# add poly-to-meltal2 contacts to connect gate of read/write transistors to RWWL (contact next to gate)
# contact must be placed a metal1 width below the source pin to avoid drc from source pin routings
@ -526,45 +553,62 @@ class pbitcell(pgate.pgate):
# connect read/write transistor gate contacts to RWWL contacts (metal2 path)
self.add_path("metal2", [left_gate_contact, left_rwwl_contact])
self.add_path("metal2", [right_gate_contact, right_rwwl_contact])
# Drain/Storage connections
# this path only needs to be drawn once on the last iteration of the loop
if(k == self.num_rw_ports-1):
# add contacts to connect gate of inverters to drain of read/write transistors
left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 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_polycontact"] + 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)
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])
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 read/write transistors (metal1 path)
midL0 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], left_storage_contact.y)
midL1 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.readwrite_nmos_left[k].get_pin("D").lc().y)
self.add_path("metal1", [left_storage_contact, midL0], width=contact.poly.second_layer_width) # width needed to avoid drc error
self.add_path("metal1", [midL0+vector(0,0.5*contact.poly.second_layer_width), midL1, self.readwrite_nmos_left[k].get_pin("D").lc()])
midR0 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], right_storage_contact.y)
midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.readwrite_nmos_right[k].get_pin("D").rc().y)
self.add_path("metal1", [right_storage_contact, midR0], width=contact.poly.second_layer_width)
self.add_path("metal1", [midR0+vector(0,0.5*contact.poly.second_layer_width), midR1, self.readwrite_nmos_right[k].get_pin("D").rc()])
# end if
# end for
# update furthest left and right transistor edges
self.left_building_edge = left_readwrite_transistor_xpos - self.readwrite_nmos.active_height
self.right_building_edge = right_readwrite_transistor_xpos
def route_readwrite_bitlines(self):
"""
Routes read/write transistors to their respective bitlines
"""
for k in range(0,self.num_readwrite):
# Source/RWBL/RWBL_bar connections
# add metal1-to-metal2 contacts on top of read/write transistor source pins for connection to WBL and WBL_bar
offset_left = self.readwrite_nmos_left[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_left,
rotate=90)
offset_right = self.readwrite_nmos_right[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_right,
rotate=90)
def route_readwrite_access(self):
"""
Routes read/write transistors to the storage component of the bitcell
"""
last_inst = self.num_readwrite - 1
# Drain/Storage connections
# this path only needs to be drawn once on the last iteration of the loop
# add contacts to connect gate of inverters to drain of read/write transistors
left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - drc["poly_to_polycontact"] - 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_polycontact"] + 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)
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])
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 read/write transistors (metal1 path)
midL0 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], left_storage_contact.y)
midL1 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.readwrite_nmos_left[last_inst].get_pin("D").lc().y)
self.add_path("metal1", [left_storage_contact, midL0], width=contact.poly.second_layer_width) # width needed to avoid drc error
self.add_path("metal1", [midL0+vector(0,0.5*contact.poly.second_layer_width), midL1, self.readwrite_nmos_left[last_inst].get_pin("D").lc()])
midR0 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], right_storage_contact.y)
midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.readwrite_nmos_right[last_inst].get_pin("D").rc().y)
self.add_path("metal1", [right_storage_contact, midR0], width=contact.poly.second_layer_width)
self.add_path("metal1", [midR0+vector(0,0.5*contact.poly.second_layer_width), midR1, self.readwrite_nmos_right[last_inst].get_pin("D").rc()])
def create_write_ports(self):
"""
@ -580,11 +624,11 @@ class pbitcell(pgate.pgate):
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_w_ports
self.write_nmos_right = [None] * self.num_w_ports
self.write_nmos_left = [None] * self.num_write
self.write_nmos_right = [None] * self.num_write
# iterate over the number of write ports
for k in range(0,self.num_w_ports):
for k in range(0,self.num_write):
# add write transistors
self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k),
mod=self.write_nmos)
@ -599,17 +643,16 @@ class pbitcell(pgate.pgate):
"""
Places write ports in the bit cell.
"""
# Define variables relevant to write transistors
self.wwl_positions = [None] * self.num_w_ports
self.wbl_positions = [None] * self.num_w_ports
self.wbl_bar_positions = [None] * self.num_w_ports
self.wwl_positions = [None] * self.num_write
self.wbl_positions = [None] * self.num_write
self.wbl_bar_positions = [None] * self.num_write
# define offset correction due to rotation of the ptx module
write_rotation_correct = self.write_nmos.active_height
# iterate over the number of write ports
for k in range(0,self.num_w_ports):
for k in range(0,self.num_write):
# Add transistors
# calculate write transistor offsets
left_write_transistor_xpos = self.left_building_edge \
@ -634,44 +677,41 @@ class pbitcell(pgate.pgate):
# Add WWL lines
# calculate WWL position
wwl_ypos = self.gnd_position.y \
- self.num_rw_ports*self.rowline_tile_height \
- self.num_readwrite*self.rowline_tile_height \
- (k+1)*self.rowline_tile_height
self.wwl_positions[k] = vector(self.leftmost_xpos, wwl_ypos)
# add pin for WWL
self.add_layout_pin(text="wwl{}".format(k),
self.add_layout_pin(text=self.w_wl_names[k],
layer="metal1",
offset=self.wwl_positions[k],
width=self.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
offset_left = self.write_nmos_left[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_left,
rotate=90)
offset_right = self.write_nmos_right[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_right,
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),
self.add_layout_pin(text=self.w_bl_names[k],
layer="metal2",
offset=self.wbl_positions[k],
width=drc["minwidth_metal2"],
height=self.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),
self.add_layout_pin(text=self.w_br_names[k],
layer="metal2",
offset=self.wbl_bar_positions[k],
width=drc["minwidth_metal2"],
height=self.height)
# update furthest left and right transistor edges
self.left_building_edge = left_write_transistor_xpos - self.write_nmos.active_height
self.right_building_edge = right_write_transistor_xpos
def route_write_wordlines(self):
"""
Routes write transistors to their respective wordlines
"""
for k in range(0,self.num_write):
# Gate/WWL connections
# add poly-to-meltal2 contacts to connect gate of write transistors to WWL (contact next to gate)
# contact must be placed a metal width below the source pin to avoid drc from source pin routings
@ -720,42 +760,60 @@ class pbitcell(pgate.pgate):
# connect write transistor gate contacts to WWL contacts (metal2 path)
self.add_path("metal2", [left_gate_contact, left_wwl_contact])
self.add_path("metal2", [right_gate_contact, right_wwl_contact])
# Drain/Storage connections
# this path only needs to be drawn once on the last iteration of the loop
if(k == self.num_w_ports-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_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=left_storage_contact,
rotate=90)
def route_write_bitlines(self):
"""
Routes write transistors to their respective bitlines
"""
for k in range(0,self.num_write):
# Source/WBL/WBL_bar connections
# add metal1-to-metal2 contacts on top of write transistor source pins for connection to WBL and WBL_bar
offset_left = self.write_nmos_left[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_left,
rotate=90)
offset_right = self.write_nmos_right[k].get_pin("S").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_right,
rotate=90)
right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 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)
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])
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(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], left_storage_contact.y)
midL1 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.write_nmos_left[k].get_pin("D").lc().y)
self.add_path("metal1", [left_storage_contact, midL0], width=contact.poly.second_layer_width) # width needed to avoid drc error
self.add_path("metal1", [midL0+vector(0,0.5*contact.poly.second_layer_width), midL1, self.write_nmos_left[k].get_pin("D").lc()])
midR0 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], right_storage_contact.y)
midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.write_nmos_right[k].get_pin("D").rc().y)
self.add_path("metal1", [right_storage_contact, midR0], width=contact.poly.second_layer_width)
self.add_path("metal1", [midR0+vector(0,0.5*contact.poly.second_layer_width), midR1, self.write_nmos_right[k].get_pin("D").rc()])
def route_write_access(self):
"""
Routes write transistors to the storage component of the bitcell
"""
last_inst = self.num_write - 1
# Drain/Storage connections
# this path only needs to be drawn once on the last iteration of the loop
# 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_polycontact"] - 0.5*contact.poly.width, self.cross_couple_lower_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=left_storage_contact,
rotate=90)
# update furthest left and right transistor edges
self.left_building_edge = left_write_transistor_xpos - self.write_nmos.active_height
self.right_building_edge = right_write_transistor_xpos
right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + drc["poly_to_polycontact"] + 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)
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])
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(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], left_storage_contact.y)
midL1 = vector(self.inverter_nmos_left.get_pin("S").lc().x - 1.5*drc["minwidth_metal1"], self.write_nmos_left[last_inst].get_pin("D").lc().y)
self.add_path("metal1", [left_storage_contact, midL0], width=contact.poly.second_layer_width) # width needed to avoid drc error
self.add_path("metal1", [midL0+vector(0,0.5*contact.poly.second_layer_width), midL1, self.write_nmos_left[last_inst].get_pin("D").lc()])
midR0 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], right_storage_contact.y)
midR1 = vector(self.inverter_nmos_right.get_pin("D").rc().x + 1.5*drc["minwidth_metal1"], self.write_nmos_right[last_inst].get_pin("D").rc().y)
self.add_path("metal1", [right_storage_contact, midR0], width=contact.poly.second_layer_width)
self.add_path("metal1", [midR0+vector(0,0.5*contact.poly.second_layer_width), midR1, self.write_nmos_right[last_inst].get_pin("D").rc()])
def create_read_ports(self):
@ -771,13 +829,13 @@ class pbitcell(pgate.pgate):
"""
# define read transistor variables as empty arrays based on the number of read ports
self.read_nmos_left = [None] * self.num_r_ports
self.read_nmos_right = [None] * self.num_r_ports
self.read_access_nmos_left = [None] * self.num_r_ports
self.read_access_nmos_right = [None] * self.num_r_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
# iterate over the number of read ports
for k in range(0,self.num_r_ports):
for k in range(0,self.num_read):
# add read-access transistors
self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k),
mod=self.read_nmos)
@ -800,11 +858,10 @@ class pbitcell(pgate.pgate):
"""
Places the read ports in the bit cell.
"""
# Define variables relevant to read transistors
self.rwl_positions = [None] * self.num_r_ports
self.rbl_positions = [None] * self.num_r_ports
self.rbl_bar_positions = [None] * self.num_r_ports
self.rwl_positions = [None] * self.num_read
self.rbl_positions = [None] * self.num_read
self.rbl_bar_positions = [None] * self.num_read
# define offset correction due to rotation of the ptx module
read_rotation_correct = self.read_nmos.active_height
@ -813,7 +870,7 @@ class pbitcell(pgate.pgate):
overlap_offset = self.read_nmos.get_pin("D").ll() - self.read_nmos.get_pin("S").ll()
# iterate over the number of read ports
for k in range(0,self.num_r_ports):
for k in range(0,self.num_read):
# Add transistors
# calculate transistor offsets
left_read_transistor_xpos = self.left_building_edge \
@ -843,45 +900,38 @@ class pbitcell(pgate.pgate):
# Add RWL lines
# calculate RWL position
rwl_ypos = self.gnd_position.y \
- self.num_rw_ports*self.rowline_tile_height \
- self.num_w_ports*self.rowline_tile_height \
- self.num_readwrite*self.rowline_tile_height \
- self.num_write*self.rowline_tile_height \
- (k+1)*self.rowline_tile_height
self.rwl_positions[k] = vector(self.leftmost_xpos, rwl_ypos)
# add pin for RWL
self.add_layout_pin(text="rwl{}".format(k),
self.add_layout_pin(text=self.r_wl_names[k],
layer="metal1",
offset=self.rwl_positions[k],
width=self.width,
height=contact.m1m2.width)
# 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
offset_left = self.read_nmos_left[k].get_pin("D").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_left,
rotate=90)
offset_right = self.read_nmos_right[k].get_pin("D").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_right,
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),
self.add_layout_pin(text=self.r_bl_names[k],
layer="metal2",
offset=self.rbl_positions[k],
width=drc["minwidth_metal2"],
height=self.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),
self.add_layout_pin(text=self.r_br_names[k],
layer="metal2",
offset=self.rbl_bar_positions[k],
width=drc["minwidth_metal2"],
height=self.height)
def route_read_wordlines(self):
"""
Routes read transistors to their respective worlines
"""
for k in range(0,self.num_read):
# Gate of read transistor / RWL connection
# add poly-to-meltal2 contacts to connect gate of read transistors to RWL (contact next to gate)
if(self.read_nmos_contact_extension > self.gate_contact_thres):
@ -934,7 +984,29 @@ class pbitcell(pgate.pgate):
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])
def route_read_bitlines(self):
"""
Routes read transistors to their respective bitlines
"""
for k in range(0,self.num_read):
# 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
offset_left = self.read_nmos_left[k].get_pin("D").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_left,
rotate=90)
offset_right = self.read_nmos_right[k].get_pin("D").center()
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=offset_right,
rotate=90)
def route_read_access(self):
"""
Routes read access transistors to the storage component of the bitcell
"""
for k in range(0,self.num_read):
# 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)
if(self.read_nmos_contact_extension > self.gate_contact_thres):
@ -982,8 +1054,6 @@ class pbitcell(pgate.pgate):
midR2 = vector(right_gate_contact0.x, 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])
# end for
def extend_well(self):
"""
@ -1003,7 +1073,7 @@ class pbitcell(pgate.pgate):
# extend pwell over read/write and write transistors to the
# height of the write transistor well (read/write and write
# transistors are the same height)
if(self.num_w_ports > 0):
if(self.num_write > 0):
# calculate the edge of the write transistor well closest to the center
left_write_well_xpos = self.write_nmos_left[0].offset.x + drc["well_enclosure_active"]
right_write_well_xpos = self.write_nmos_right[0].offset.x - self.write_nmos.active_height - drc["well_enclosure_active"]
@ -1029,7 +1099,7 @@ class pbitcell(pgate.pgate):
height=write_well_height)
# extend pwell over the read transistors to the height of the bitcell
if(self.num_r_ports > 0):
if(self.num_read > 0):
# calculate the edge of the read transistor well clostest to the center
left_read_well_xpos = self.read_nmos_left[0].offset.x + drc["well_enclosure_active"]
right_read_well_xpos = self.read_nmos_right[0].offset.x - self.read_nmos.active_height - drc["well_enclosure_active"]
@ -1088,134 +1158,53 @@ class pbitcell(pgate.pgate):
def list_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
bitcell_pins = []
for k in range(self.num_rw_ports):
bitcell_pins.append("rwbl{0}[{1}]".format(k,col))
bitcell_pins.append("rwbl_bar{0}[{1}]".format(k,col))
for k in range(self.num_w_ports):
bitcell_pins.append("wbl{0}[{1}]".format(k,col))
bitcell_pins.append("wbl_bar{0}[{1}]".format(k,col))
for k in range(self.num_r_ports):
bitcell_pins.append("rbl{0}[{1}]".format(k,col))
bitcell_pins.append("rbl_bar{0}[{1}]".format(k,col))
for k in range(self.num_rw_ports):
bitcell_pins.append("rwwl{0}[{1}]".format(k,row))
for k in range(self.num_w_ports):
bitcell_pins.append("wwl{0}[{1}]".format(k,row))
for k in range(self.num_r_ports):
bitcell_pins.append("rwl{0}[{1}]".format(k,row))
for port in range(self.total_ports):
bitcell_pins.append("bl{0}[{1}]".format(port,col))
bitcell_pins.append("br{0}[{1}]".format(port,col))
for port in range(self.total_ports):
bitcell_pins.append("wl{0}[{1}]".format(port,row))
bitcell_pins.append("vdd")
bitcell_pins.append("gnd")
return bitcell_pins
def list_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = []
for k in range(self.num_rw_ports):
row_pins.append("rwwl{0}".format(k))
for k in range(self.num_w_ports):
row_pins.append("wwl{0}".format(k))
for k in range(self.num_r_ports):
row_pins.append("rwl{0}".format(k))
return row_pins
def list_read_wl_names(self):
""" Creates a list of wordline pin names associated with read ports """
row_pins = []
for k in range(self.num_rw_ports):
row_pins.append("rwwl{0}".format(k))
for k in range(self.num_r_ports):
row_pins.append("rwl{0}".format(k))
return row_pins
def list_write_wl_names(self):
""" Creates a list of wordline pin names associated with write ports """
row_pins = []
for k in range(self.num_rw_ports):
row_pins.append("rwwl{0}".format(k))
for k in range(self.num_w_ports):
row_pins.append("wwl{0}".format(k))
return row_pins
""" Creates a list of all wordline pin names """
return self.wl_names
def list_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
column_pins = []
for k in range(self.num_rw_ports):
column_pins.append("rwbl{0}".format(k))
column_pins.append("rwbl_bar{0}".format(k))
for k in range(self.num_w_ports):
column_pins.append("wbl{0}".format(k))
column_pins.append("wbl_bar{0}".format(k))
for k in range(self.num_r_ports):
column_pins.append("rbl{0}".format(k))
column_pins.append("rbl_bar{0}".format(k))
return column_pins
bitline_pins = []
for port in range(self.total_ports):
column_pins.append("bl{0}".format(port))
column_pins.append("br{0}".format(port))
return bitline_pins
def list_all_bl_names(self):
""" Creates a list of all bl pins names """
column_pins = []
for k in range(self.num_rw_ports):
column_pins.append("rwbl{0}".format(k))
for k in range(self.num_w_ports):
column_pins.append("wbl{0}".format(k))
for k in range(self.num_r_ports):
column_pins.append("rbl{0}".format(k))
return column_pins
bl_pins = [self.rw_bl_names, self.w_bl_names, self.r_bl_names]
return bl_pins
def list_all_br_names(self):
""" Creates a list of all br pins names """
column_pins = []
for k in range(self.num_rw_ports):
column_pins.append("rwbl_bar{0}".format(k))
for k in range(self.num_w_ports):
column_pins.append("wbl_bar{0}".format(k))
for k in range(self.num_r_ports):
column_pins.append("rbl_bar{0}".format(k))
return column_pins
br_pins = [self.rw_br_names, self.w_br_names, self.r_br_names]
return br_pins
def list_read_bl_names(self):
""" Creates a list of bl pin names associated with read ports """
column_pins = []
for k in range(self.num_rw_ports):
column_pins.append("rwbl{0}".format(k))
for k in range(self.num_r_ports):
column_pins.append("rbl{0}".format(k))
return column_pins
bl_pins = [self.rw_bl_names, self.r_bl_names]
return bl_pins
def list_read_br_names(self):
""" Creates a list of br pin names associated with read ports """
column_pins = []
for k in range(self.num_rw_ports):
column_pins.append("rwbl_bar{0}".format(k))
for k in range(self.num_r_ports):
column_pins.append("rbl_bar{0}".format(k))
return column_pins
br_pins = [self.rw_br_names, self.r_br_names]
return br_pins
def list_write_bl_names(self):
""" Creates a list of bl pin names associated with write ports """
column_pins = []
for k in range(self.num_rw_ports):
column_pins.append("rwbl{0}".format(k))
for k in range(self.num_w_ports):
column_pins.append("wbl{0}".format(k))
return column_pins
bl_pins = [self.rw_bl_names, self.w_bl_names]
return bl_pins
def list_write_br_names(self):
""" Creates a list of br pin names asscociated with write ports"""
column_pins = []
for k in range(self.num_rw_ports):
column_pins.append("rwbl_bar{0}".format(k))
for k in range(self.num_w_ports):
column_pins.append("wbl_bar{0}".format(k))
return column_pins
br_pins = [self.rw_br_names, self.w_br_names]
return br_pins