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 width = None
height = 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): unique_id = 1
name = "pbitcell_{0}RW_{1}W_{2}R".format(num_rw_ports, num_w_ports, num_r_ports) 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) 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, debug.info(2, "create a multi-port bitcell with {0} write ports and {1} read ports".format(num_write, num_read))
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
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() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() 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.width = self.width
pbitcell.height = self.height pbitcell.height = self.height
@ -41,11 +43,11 @@ class pbitcell(pgate.pgate):
self.add_modules() self.add_modules()
self.create_storage() self.create_storage()
if(self.num_rw_ports > 0): if(self.num_readwrite > 0):
self.create_readwrite_ports() self.create_readwrite_ports()
if(self.num_w_ports > 0): if(self.num_write > 0):
self.create_write_ports() self.create_write_ports()
if(self.num_r_ports > 0): if(self.num_read > 0):
self.create_read_ports() self.create_read_ports()
def create_layout(self): def create_layout(self):
@ -56,48 +58,77 @@ class pbitcell(pgate.pgate):
self.route_storage() self.route_storage()
self.route_rails() self.route_rails()
if(self.num_rw_ports > 0): if(self.num_readwrite > 0):
self.place_readwrite_ports() 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() 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.place_read_ports()
self.route_read_wordlines()
self.route_read_bitlines()
self.route_read_access()
self.extend_well() self.extend_well()
self.offset_all_coordinates() self.offset_all_coordinates()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
for k in range(self.num_rw_ports): self.rw_bl_names = []
self.add_pin("rwbl{}".format(k)) self.rw_br_names = []
self.add_pin("rwbl_bar{}".format(k)) self.w_bl_names = []
for k in range(self.num_w_ports): self.w_br_names = []
self.add_pin("wbl{}".format(k)) self.r_bl_names = []
self.add_pin("wbl_bar{}".format(k)) self.r_br_names = []
for k in range(self.num_r_ports): self.wl_names = []
self.add_pin("rbl{}".format(k)) port = 0
self.add_pin("rbl_bar{}".format(k))
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): port = 0
self.add_pin("rwwl{}".format(k)) for k in range(self.total_ports):
for k in range(self.num_w_ports): self.add_pin("wl{}".format(port))
self.add_pin("wwl{}".format(k)) self.wl_names.append("wl{}".format(port))
for k in range(self.num_r_ports):
self.add_pin("rwl{}".format(k))
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
def add_modules(self): 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): Determine size of transistors and add ptx modules
inverter_nmos_width = self.num_rw_ports*3*parameter["min_tx_size"] """
# 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"] inverter_pmos_width = parameter["min_tx_size"]
readwrite_nmos_width = 1.5*parameter["min_tx_size"] readwrite_nmos_width = 1.5*parameter["min_tx_size"]
write_nmos_width = parameter["min_tx_size"] write_nmos_width = parameter["min_tx_size"]
read_nmos_width = 2*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: else:
inverter_nmos_width = 2*parameter["min_tx_size"] inverter_nmos_width = 2*parameter["min_tx_size"]
inverter_pmos_width = 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"] write_nmos_width = parameter["min_tx_size"]
read_nmos_width = 2*parameter["min_tx_size"] read_nmos_width = 2*parameter["min_tx_size"]
""" Create ptx for all transistors """
# create ptx for inverter transistors # create ptx for inverter transistors
self.inverter_nmos = ptx(width=inverter_nmos_width, self.inverter_nmos = ptx(width=inverter_nmos_width,
tx_type="nmos") tx_type="nmos")
@ -167,7 +197,7 @@ class pbitcell(pgate.pgate):
# write to read transistor spacing (also acts as readwrite to read transistor spacing) # 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 # 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): if(self.write_nmos_contact_extension > self.gate_contact_thres):
write_portion = drc["minwidth_metal2"] + self.write_nmos_contact_extension write_portion = drc["minwidth_metal2"] + self.write_nmos_contact_extension
else: 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"] 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.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.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.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 self.read_tile_width = self.read_to_read_spacing + self.read_nmos.active_height
""" calculation for row line tiling """ # 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"] self.rail_tile_height = drc["active_to_body_active"] + contact.well.width
self.rowline_tile_height = drc["minwidth_metal1"] + contact.m1m2.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.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_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 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): 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 # 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 self.readwrite_port_flag = True
else: else:
self.readwrite_port_flag = False self.readwrite_port_flag = False
if(self.num_w_ports > 0): if(self.num_write > 0):
self.write_port_flag = True self.write_port_flag = True
else: else:
self.write_port_flag = False self.write_port_flag = False
if(self.num_r_ports > 0): if(self.num_read > 0):
self.read_port_flag = True self.read_port_flag = True
else: else:
self.read_port_flag = False self.read_port_flag = False
# determine the distance of the leftmost/rightmost transistor gate connection # 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): if(self.read_nmos_contact_extension > self.gate_contact_thres):
end_connection = drc["minwidth_metal2"] + self.read_nmos_contact_extension + contact.m1m2.height end_connection = drc["minwidth_metal2"] + self.read_nmos_contact_extension + contact.m1m2.height
else: 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 # 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.leftmost_xpos = -self.inverter_tile_width \
- self.inverter_to_write_spacing \ - 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.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.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 \ - end_connection \
- 0.5*drc["poly_to_polycontact"] - 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 # bottommost position = gnd height + rwwl height + wwl height + rwl height + space needed between tiled bitcells
array_tiling_offset = 0.5*drc["minwidth_metal2"] array_tiling_offset = 0.5*drc["minwidth_metal2"]
self.botmost_ypos = -self.rail_tile_height \ self.botmost_ypos = -self.rail_tile_height \
- self.num_rw_ports*self.rowline_tile_height \ - self.num_readwrite*self.rowline_tile_height \
- self.num_w_ports*self.rowline_tile_height \ - self.num_write*self.rowline_tile_height \
- self.num_r_ports*self.rowline_tile_height \ - self.num_read*self.rowline_tile_height \
- array_tiling_offset - array_tiling_offset
# topmost position = height of the inverter + height of vdd # topmost position = height of the inverter + height of vdd
@ -291,8 +321,7 @@ class pbitcell(pgate.pgate):
def place_storage(self): def place_storage(self):
""" """
Places the crossed coupled inverters that act as storage for the bitcell. Places the transistors for the crossed coupled inverters in the bitcell
The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar".
""" """
# calculate transistor offsets # calculate transistor offsets
@ -309,7 +338,9 @@ class pbitcell(pgate.pgate):
self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos]) self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos])
def route_storage(self): def route_storage(self):
"""
Routes inputs and outputs of inverters to cross couple them
"""
# connect input (gate) of inverters # 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_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()]) 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): 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 # Add rails for vdd and gnd
self.gnd_position = vector(self.leftmost_xpos, -self.rail_tile_height) self.gnd_position = vector(self.leftmost_xpos, -self.rail_tile_height)
self.gnd = self.add_layout_pin(text="gnd", 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 # 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_left = [None] * self.num_readwrite
self.readwrite_nmos_right = [None] * self.num_rw_ports self.readwrite_nmos_right = [None] * self.num_readwrite
# iterate over the number of read/write ports # 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 # add read/write transistors
self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k), self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k),
mod=self.readwrite_nmos) mod=self.readwrite_nmos)
@ -411,15 +441,15 @@ class pbitcell(pgate.pgate):
""" """
# Define variables relevant to write transistors # Define variables relevant to write transistors
self.rwwl_positions = [None] * self.num_rw_ports self.rwwl_positions = [None] * self.num_readwrite
self.rwbl_positions = [None] * self.num_rw_ports self.rwbl_positions = [None] * self.num_readwrite
self.rwbl_bar_positions = [None] * self.num_rw_ports self.rwbl_bar_positions = [None] * self.num_readwrite
# define offset correction due to rotation of the ptx module # define offset correction due to rotation of the ptx module
readwrite_rotation_correct = self.readwrite_nmos.active_height readwrite_rotation_correct = self.readwrite_nmos.active_height
# iterate over the number of read/write ports # 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 # Add transistors
# calculate read/write transistor offsets # calculate read/write transistor offsets
left_readwrite_transistor_xpos = self.left_building_edge \ 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) self.rwwl_positions[k] = vector(self.leftmost_xpos, rwwl_ypos)
# add pin for RWWL # add pin for RWWL
self.add_layout_pin(text="rwwl{}".format(k), self.add_layout_pin(text=self.rw_wl_names[k],
layer="metal1", layer="metal1",
offset=self.rwwl_positions[k], offset=self.rwwl_positions[k],
width=self.width, width=self.width,
height=contact.m1m2.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 # 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.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", layer="metal2",
offset=self.rwbl_positions[k], offset=self.rwbl_positions[k],
width=drc["minwidth_metal2"], width=drc["minwidth_metal2"],
height=self.height) 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.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", layer="metal2",
offset=self.rwbl_bar_positions[k], offset=self.rwbl_bar_positions[k],
width=drc["minwidth_metal2"], width=drc["minwidth_metal2"],
height=self.height) 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 # Gate/RWWL connections
# add poly-to-meltal2 contacts to connect gate of read/write transistors to RWWL (contact next to gate) # 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 # 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) # 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", [left_gate_contact, left_rwwl_contact])
self.add_path("metal2", [right_gate_contact, right_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): def create_write_ports(self):
""" """
@ -580,11 +624,11 @@ class pbitcell(pgate.pgate):
write_rotation_correct = self.write_nmos.active_height write_rotation_correct = self.write_nmos.active_height
# define write transistor variables as empty arrays based on the number of write ports # 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_left = [None] * self.num_write
self.write_nmos_right = [None] * self.num_w_ports self.write_nmos_right = [None] * self.num_write
# iterate over the number of write ports # 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 # add write transistors
self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k), self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k),
mod=self.write_nmos) mod=self.write_nmos)
@ -599,17 +643,16 @@ class pbitcell(pgate.pgate):
""" """
Places write ports in the bit cell. Places write ports in the bit cell.
""" """
# Define variables relevant to write transistors # Define variables relevant to write transistors
self.wwl_positions = [None] * self.num_w_ports self.wwl_positions = [None] * self.num_write
self.wbl_positions = [None] * self.num_w_ports self.wbl_positions = [None] * self.num_write
self.wbl_bar_positions = [None] * self.num_w_ports self.wbl_bar_positions = [None] * self.num_write
# define offset correction due to rotation of the ptx module # define offset correction due to rotation of the ptx module
write_rotation_correct = self.write_nmos.active_height write_rotation_correct = self.write_nmos.active_height
# iterate over the number of write ports # 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 # Add transistors
# calculate write transistor offsets # calculate write transistor offsets
left_write_transistor_xpos = self.left_building_edge \ left_write_transistor_xpos = self.left_building_edge \
@ -634,44 +677,41 @@ class pbitcell(pgate.pgate):
# Add WWL lines # Add WWL lines
# calculate WWL position # calculate WWL position
wwl_ypos = self.gnd_position.y \ 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 - (k+1)*self.rowline_tile_height
self.wwl_positions[k] = vector(self.leftmost_xpos, wwl_ypos) self.wwl_positions[k] = vector(self.leftmost_xpos, wwl_ypos)
# add pin for WWL # add pin for WWL
self.add_layout_pin(text="wwl{}".format(k), self.add_layout_pin(text=self.w_wl_names[k],
layer="metal1", layer="metal1",
offset=self.wwl_positions[k], offset=self.wwl_positions[k],
width=self.width, width=self.width,
height=contact.m1m2.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 # 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.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", layer="metal2",
offset=self.wbl_positions[k], offset=self.wbl_positions[k],
width=drc["minwidth_metal2"], width=drc["minwidth_metal2"],
height=self.height) 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.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", layer="metal2",
offset=self.wbl_bar_positions[k], offset=self.wbl_bar_positions[k],
width=drc["minwidth_metal2"], width=drc["minwidth_metal2"],
height=self.height) 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 # Gate/WWL connections
# add poly-to-meltal2 contacts to connect gate of write transistors to WWL (contact next to gate) # 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 # 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) # connect write transistor gate contacts to WWL contacts (metal2 path)
self.add_path("metal2", [left_gate_contact, left_wwl_contact]) self.add_path("metal2", [left_gate_contact, left_wwl_contact])
self.add_path("metal2", [right_gate_contact, right_wwl_contact]) self.add_path("metal2", [right_gate_contact, right_wwl_contact])
# Drain/Storage connections def route_write_bitlines(self):
# this path only needs to be drawn once on the last iteration of the loop """
if(k == self.num_w_ports-1): Routes write transistors to their respective bitlines
# 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) for k in range(0,self.num_write):
self.add_contact_center(layers=("poly", "contact", "metal1"), # Source/WBL/WBL_bar connections
offset=left_storage_contact, # add metal1-to-metal2 contacts on top of write transistor source pins for connection to WBL and WBL_bar
rotate=90) 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) def route_write_access(self):
self.add_contact_center(layers=("poly", "contact", "metal1"), """
offset=right_storage_contact, Routes write transistors to the storage component of the bitcell
rotate=90) """
last_inst = self.num_write - 1
# 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) # Drain/Storage connections
self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) # 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
inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_lower_ypos) 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_path("poly", [right_storage_contact, inverter_gate_offset_right]) self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=left_storage_contact,
# connect contacts to drains of write transistors (metal1 path) rotate=90)
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()])
# update furthest left and right transistor edges 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.left_building_edge = left_write_transistor_xpos - self.write_nmos.active_height self.add_contact_center(layers=("poly", "contact", "metal1"),
self.right_building_edge = right_write_transistor_xpos 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): 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 # 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_left = [None] * self.num_read
self.read_nmos_right = [None] * self.num_r_ports self.read_nmos_right = [None] * self.num_read
self.read_access_nmos_left = [None] * self.num_r_ports self.read_access_nmos_left = [None] * self.num_read
self.read_access_nmos_right = [None] * self.num_r_ports self.read_access_nmos_right = [None] * self.num_read
# iterate over the number of read ports # 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 # add read-access transistors
self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k), self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k),
mod=self.read_nmos) mod=self.read_nmos)
@ -800,11 +858,10 @@ class pbitcell(pgate.pgate):
""" """
Places the read ports in the bit cell. Places the read ports in the bit cell.
""" """
# Define variables relevant to read transistors # Define variables relevant to read transistors
self.rwl_positions = [None] * self.num_r_ports self.rwl_positions = [None] * self.num_read
self.rbl_positions = [None] * self.num_r_ports self.rbl_positions = [None] * self.num_read
self.rbl_bar_positions = [None] * self.num_r_ports self.rbl_bar_positions = [None] * self.num_read
# define offset correction due to rotation of the ptx module # define offset correction due to rotation of the ptx module
read_rotation_correct = self.read_nmos.active_height 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() overlap_offset = self.read_nmos.get_pin("D").ll() - self.read_nmos.get_pin("S").ll()
# iterate over the number of read ports # 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 # Add transistors
# calculate transistor offsets # calculate transistor offsets
left_read_transistor_xpos = self.left_building_edge \ left_read_transistor_xpos = self.left_building_edge \
@ -843,45 +900,38 @@ class pbitcell(pgate.pgate):
# Add RWL lines # Add RWL lines
# calculate RWL position # calculate RWL position
rwl_ypos = self.gnd_position.y \ rwl_ypos = self.gnd_position.y \
- self.num_rw_ports*self.rowline_tile_height \ - self.num_readwrite*self.rowline_tile_height \
- self.num_w_ports*self.rowline_tile_height \ - self.num_write*self.rowline_tile_height \
- (k+1)*self.rowline_tile_height - (k+1)*self.rowline_tile_height
self.rwl_positions[k] = vector(self.leftmost_xpos, rwl_ypos) self.rwl_positions[k] = vector(self.leftmost_xpos, rwl_ypos)
# add pin for RWL # add pin for RWL
self.add_layout_pin(text="rwl{}".format(k), self.add_layout_pin(text=self.r_wl_names[k],
layer="metal1", layer="metal1",
offset=self.rwl_positions[k], offset=self.rwl_positions[k],
width=self.width, width=self.width,
height=contact.m1m2.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 # 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.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", layer="metal2",
offset=self.rbl_positions[k], offset=self.rbl_positions[k],
width=drc["minwidth_metal2"], width=drc["minwidth_metal2"],
height=self.height) 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.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", layer="metal2",
offset=self.rbl_bar_positions[k], offset=self.rbl_bar_positions[k],
width=drc["minwidth_metal2"], width=drc["minwidth_metal2"],
height=self.height) 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 # Gate of read transistor / RWL connection
# add poly-to-meltal2 contacts to connect gate of read transistors to RWL (contact next to gate) # 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): 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) 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]) 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 # 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) # 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): 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) 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) 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]) self.add_path("metal1", [right_gate_contact, midR0, midR1, midR2, right_inverter_offset])
# end for
def extend_well(self): def extend_well(self):
""" """
@ -1003,7 +1073,7 @@ class pbitcell(pgate.pgate):
# extend pwell over read/write and write transistors to the # extend pwell over read/write and write transistors to the
# height of the write transistor well (read/write and write # height of the write transistor well (read/write and write
# transistors are the same height) # 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 # 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"] 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"] 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) height=write_well_height)
# extend pwell over the read transistors to the height of the bitcell # 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 # 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"] 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"] 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): 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 """ """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
bitcell_pins = [] bitcell_pins = []
for k in range(self.num_rw_ports): for port in range(self.total_ports):
bitcell_pins.append("rwbl{0}[{1}]".format(k,col)) bitcell_pins.append("bl{0}[{1}]".format(port,col))
bitcell_pins.append("rwbl_bar{0}[{1}]".format(k,col)) bitcell_pins.append("br{0}[{1}]".format(port,col))
for k in range(self.num_w_ports): for port in range(self.total_ports):
bitcell_pins.append("wbl{0}[{1}]".format(k,col)) bitcell_pins.append("wl{0}[{1}]".format(port,row))
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))
bitcell_pins.append("vdd") bitcell_pins.append("vdd")
bitcell_pins.append("gnd") bitcell_pins.append("gnd")
return bitcell_pins return bitcell_pins
def list_all_wl_names(self): def list_all_wl_names(self):
""" Creates a list of all wordline pin names """ """ Creates a list of all wordline pin names """
row_pins = [] return self.wl_names
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
def list_all_bitline_names(self): def list_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """ """ Creates a list of all bitline pin names (both bl and br) """
column_pins = [] bitline_pins = []
for k in range(self.num_rw_ports): for port in range(self.total_ports):
column_pins.append("rwbl{0}".format(k)) column_pins.append("bl{0}".format(port))
column_pins.append("rwbl_bar{0}".format(k)) column_pins.append("br{0}".format(port))
for k in range(self.num_w_ports): return bitline_pins
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
def list_all_bl_names(self): def list_all_bl_names(self):
""" Creates a list of all bl pins names """ """ Creates a list of all bl pins names """
column_pins = [] bl_pins = [self.rw_bl_names, self.w_bl_names, self.r_bl_names]
for k in range(self.num_rw_ports): return bl_pins
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
def list_all_br_names(self): def list_all_br_names(self):
""" Creates a list of all br pins names """ """ Creates a list of all br pins names """
column_pins = [] br_pins = [self.rw_br_names, self.w_br_names, self.r_br_names]
for k in range(self.num_rw_ports): return br_pins
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
def list_read_bl_names(self): def list_read_bl_names(self):
""" Creates a list of bl pin names associated with read ports """ """ Creates a list of bl pin names associated with read ports """
column_pins = [] bl_pins = [self.rw_bl_names, self.r_bl_names]
for k in range(self.num_rw_ports): return bl_pins
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
def list_read_br_names(self): def list_read_br_names(self):
""" Creates a list of br pin names associated with read ports """ """ Creates a list of br pin names associated with read ports """
column_pins = [] br_pins = [self.rw_br_names, self.r_br_names]
for k in range(self.num_rw_ports): return br_pins
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
def list_write_bl_names(self): def list_write_bl_names(self):
""" Creates a list of bl pin names associated with write ports """ """ Creates a list of bl pin names associated with write ports """
column_pins = [] bl_pins = [self.rw_bl_names, self.w_bl_names]
for k in range(self.num_rw_ports): return bl_pins
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
def list_write_br_names(self): def list_write_br_names(self):
""" Creates a list of br pin names asscociated with write ports""" """ Creates a list of br pin names asscociated with write ports"""
column_pins = [] br_pins = [self.rw_br_names, self.w_br_names]
for k in range(self.num_rw_ports): return br_pins
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