Updating comments and cleaning up code for pbitcell.

This commit is contained in:
Michael Timothy Grimes 2018-10-21 19:10:04 -07:00
parent ab7a83b7a5
commit 1a0568f244
1 changed files with 222 additions and 277 deletions

View File

@ -25,15 +25,14 @@ class pbitcell(design.design):
name = "replica_pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports) name = "replica_pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports)
else: else:
name = "pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports) name = "pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports)
# This is not a pgate because pgates depend on the bitcell height!
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
self.num_w_ports, self.num_w_ports,
self.num_r_ports)) self.num_r_ports))
self.create_netlist() self.create_netlist()
# We must always create the bitcell layout because # We must always create the bitcell layout because some transistor sizes in the other netlists depend on it
# some transistor sizes in the other netlists depend on it
self.create_layout() self.create_layout()
def create_netlist(self): def create_netlist(self):
@ -75,7 +74,7 @@ class pbitcell(design.design):
if self.replica_bitcell: if self.replica_bitcell:
self.route_rbc_short() self.route_rbc_short()
# in netlist_only mode, calling offset_all_coordinates will not be possible # in netlist_only mode, calling offset_all_coordinates or translate_all will not be possible
# this function is not needed to calculate the dimensions of pbitcell in netlist_only mode though # this function is not needed to calculate the dimensions of pbitcell in netlist_only mode though
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.offset_all_coordinates() self.offset_all_coordinates()
@ -84,6 +83,7 @@ class pbitcell(design.design):
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
""" add pins and set names for bitlines and wordlines """
self.rw_bl_names = [] self.rw_bl_names = []
self.rw_br_names = [] self.rw_br_names = []
self.w_bl_names = [] self.w_bl_names = []
@ -131,15 +131,14 @@ class pbitcell(design.design):
self.add_pin("vdd") self.add_pin("vdd")
self.add_pin("gnd") self.add_pin("gnd")
# if this is a replica bitcell, replace the instances of Q_bar with vdd
if self.replica_bitcell: if self.replica_bitcell:
self.Q_bar = "vdd" self.Q_bar = "vdd"
else: else:
self.Q_bar = "Q_bar" self.Q_bar = "Q_bar"
def add_modules(self): def add_modules(self):
""" """ Determine size of transistors and add ptx modules """
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 there are any read/write ports, then the inverter nmos is sized based the number of read/write ports
if(self.num_rw_ports > 0): if(self.num_rw_ports > 0):
inverter_nmos_width = self.num_rw_ports*parameter["6T_inv_nmos_size"] inverter_nmos_width = self.num_rw_ports*parameter["6T_inv_nmos_size"]
@ -220,6 +219,7 @@ class pbitcell(design.design):
# spacing between wordlines (and gnd) # spacing between wordlines (and gnd)
self.rowline_spacing = self.m1_space + contact.m1m2.width self.rowline_spacing = self.m1_space + contact.m1m2.width
self.rowline_offset = -0.5*self.m1_width
# spacing for vdd # spacing for vdd
implant_constraint = max(inverter_pmos_contact_extension, 0) + 2*self.implant_enclose_active + 0.5*(contact.well.width - self.m1_width) implant_constraint = max(inverter_pmos_contact_extension, 0) + 2*self.implant_enclose_active + 0.5*(contact.well.width - self.m1_width)
@ -231,9 +231,7 @@ class pbitcell(design.design):
self.read_port_width = 2*self.read_nmos.active_width - 2*width_reduction self.read_port_width = 2*self.read_nmos.active_width - 2*width_reduction
def calculate_postions(self): def calculate_postions(self):
""" """ Calculate positions that describe the edges and dimensions of the cell """
Calculate positions that describe the edges and dimensions of the cell
"""
self.botmost_ypos = -0.5*self.m1_width - self.total_ports*self.rowline_spacing self.botmost_ypos = -0.5*self.m1_width - self.total_ports*self.rowline_spacing
self.topmost_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset self.topmost_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset
@ -246,14 +244,13 @@ class pbitcell(design.design):
self.width = -2*self.leftmost_xpos self.width = -2*self.leftmost_xpos
self.height = self.topmost_ypos - self.botmost_ypos self.height = self.topmost_ypos - self.botmost_ypos
self.y_center = 0.5*(self.topmost_ypos + self.botmost_ypos) self.center_ypos = 0.5*(self.topmost_ypos + self.botmost_ypos)
def create_storage(self): def create_storage(self):
""" """
Creates the crossed coupled inverters that act as storage for the bitcell. Creates the crossed coupled inverters that act as storage for the bitcell.
The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar".
""" """
# create active for nmos # create active for nmos
self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left",
mod=self.inverter_nmos) mod=self.inverter_nmos)
@ -273,10 +270,7 @@ class pbitcell(design.design):
self.connect_inst(["vdd", "Q", self.Q_bar, "vdd"]) self.connect_inst(["vdd", "Q", self.Q_bar, "vdd"])
def place_storage(self): def place_storage(self):
""" """ Places the transistors for the crossed coupled inverters in the bitcell """
Places the transistors for the crossed coupled inverters in the bitcell
"""
# calculate transistor offsets # calculate transistor offsets
left_inverter_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width left_inverter_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width
right_inverter_xpos = 0.5*self.inverter_to_inverter_spacing right_inverter_xpos = 0.5*self.inverter_to_inverter_spacing
@ -290,13 +284,12 @@ class pbitcell(design.design):
self.inverter_pmos_left.place([left_inverter_xpos, inverter_pmos_ypos]) self.inverter_pmos_left.place([left_inverter_xpos, inverter_pmos_ypos])
self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos]) self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos])
# update furthest left and right transistor edges (this will propagate to further transistor offset calculations)
self.left_building_edge = left_inverter_xpos self.left_building_edge = left_inverter_xpos
self.right_building_edge = right_inverter_xpos + self.inverter_nmos.active_width self.right_building_edge = right_inverter_xpos + self.inverter_nmos.active_width
def route_storage(self): def route_storage(self):
""" """ Routes inputs and outputs of inverters to cross couple them """
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()])
@ -324,11 +317,9 @@ class pbitcell(design.design):
self.add_path("poly", [contact_offset_right, gate_offset_left]) self.add_path("poly", [contact_offset_right, gate_offset_left])
def route_rails(self): def route_rails(self):
""" """ Adds 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
gnd_ypos = -0.5*self.m1_width - self.total_ports*self.rowline_spacing gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing
self.gnd_position = vector(0, gnd_ypos) self.gnd_position = vector(0, gnd_ypos)
self.gnd = self.add_layout_pin_rect_center(text="gnd", self.gnd = self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
@ -351,10 +342,9 @@ class pbitcell(design.design):
The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q). The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q).
In a write operation, driving RWBL high or low sets the value of the cell. In a write operation, driving RWBL high or low sets the value of the cell.
In a read operation, RWBL is precharged, then is either remains high or is discharged depending on the value of the cell. In a read operation, RWBL is precharged, then is either remains high or is discharged depending on the value of the cell.
This is a differential design, so each write port has a mirrored port that connects RWBL_bar to Q_bar. This is a differential design, so each write port has a mirrored port that connects RWBR to Q_bar.
""" """
# define read/write transistor variables as empty arrays based on the number of read/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_rw_ports
self.readwrite_nmos_right = [None] * self.num_rw_ports self.readwrite_nmos_right = [None] * self.num_rw_ports
@ -370,18 +360,14 @@ class pbitcell(design.design):
self.connect_inst([self.Q_bar, self.rw_wl_names[k], self.rw_br_names[k], "gnd"]) self.connect_inst([self.Q_bar, self.rw_wl_names[k], self.rw_br_names[k], "gnd"])
def place_readwrite_ports(self): def place_readwrite_ports(self):
""" """ Places read/write ports in the bit cell """
Places read/write ports in the bit cell. # define read/write transistor variables as empty arrays based on the number of read/write ports
"""
# Define variables relevant to write transistors
self.rwwl_positions = [None] * self.num_rw_ports self.rwwl_positions = [None] * self.num_rw_ports
self.rwbl_positions = [None] * self.num_rw_ports self.rwbl_positions = [None] * self.num_rw_ports
self.rwbr_positions = [None] * self.num_rw_ports self.rwbr_positions = [None] * self.num_rw_ports
# 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_rw_ports):
# 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 \
- (k+1)*self.port_spacing \ - (k+1)*self.port_spacing \
@ -391,26 +377,23 @@ class pbitcell(design.design):
+ (k+1)*self.port_spacing \ + (k+1)*self.port_spacing \
+ k*self.readwrite_nmos.active_width + k*self.readwrite_nmos.active_width
# add read/write transistors # place read/write transistors
self.readwrite_nmos_left[k].place(offset=[left_readwrite_transistor_xpos, self.port_ypos]) self.readwrite_nmos_left[k].place(offset=[left_readwrite_transistor_xpos, self.port_ypos])
self.readwrite_nmos_right[k].place(offset=[right_readwrite_transistor_xpos, self.port_ypos]) self.readwrite_nmos_right[k].place(offset=[right_readwrite_transistor_xpos, self.port_ypos])
# Add RWWL lines
# calculate RWWL position
rwwl_ypos = -0.5*self.m1_width - k*self.rowline_spacing
self.rwwl_positions[k] = vector(0, rwwl_ypos)
# add pin for RWWL # add pin for RWWL
rwwl_ypos = self.rowline_offset - k*self.rowline_spacing
self.rwwl_positions[k] = vector(0, rwwl_ypos)
self.add_layout_pin_rect_center(text=self.rw_wl_names[k], self.add_layout_pin_rect_center(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=self.m1_width) height=self.m1_width)
# add pins for RWBL and RWBL_bar, overlaid on source contacts # add pins for RWBL and RWBR
rwbl_xpos = left_readwrite_transistor_xpos - self.bitline_offset + self.m2_width rwbl_xpos = left_readwrite_transistor_xpos - self.bitline_offset + self.m2_width
self.rwbl_positions[k] = vector(rwbl_xpos, self.y_center) self.rwbl_positions[k] = vector(rwbl_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.rw_bl_names[k], self.add_layout_pin_rect_center(text=self.rw_bl_names[k],
layer="metal2", layer="metal2",
offset=self.rwbl_positions[k], offset=self.rwbl_positions[k],
@ -418,7 +401,7 @@ class pbitcell(design.design):
height=self.height) height=self.height)
rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width + self.bitline_offset - self.m2_width rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width + self.bitline_offset - self.m2_width
self.rwbr_positions[k] = vector(rwbr_xpos, self.y_center) self.rwbr_positions[k] = vector(rwbr_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.rw_br_names[k], self.add_layout_pin_rect_center(text=self.rw_br_names[k],
layer="metal2", layer="metal2",
offset=self.rwbr_positions[k], offset=self.rwbr_positions[k],
@ -435,13 +418,8 @@ class pbitcell(design.design):
A write is enabled by setting a Write-Rowline (WWL) high, subsequently turning on the transistor. A write is enabled by setting a Write-Rowline (WWL) high, subsequently turning on the transistor.
The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q). The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q).
In a write operation, driving WBL high or low sets the value of the cell. In a write operation, driving WBL high or low sets the value of the cell.
This is a differential design, so each write port has a mirrored port that connects WBL_bar to Q_bar. This is a differential design, so each write port has a mirrored port that connects WBR to Q_bar.
""" """
# Define variables relevant to write transistors
# define offset correction due to rotation of the ptx module
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_w_ports
self.write_nmos_right = [None] * self.num_w_ports self.write_nmos_right = [None] * self.num_w_ports
@ -458,17 +436,12 @@ class pbitcell(design.design):
self.connect_inst([self.Q_bar, self.w_wl_names[k], self.w_br_names[k], "gnd"]) self.connect_inst([self.Q_bar, self.w_wl_names[k], self.w_br_names[k], "gnd"])
def place_write_ports(self): def place_write_ports(self):
""" """ Places write ports in the bit cell """
Places write ports in the bit cell. # define write transistor variables as empty arrays based on the number of write ports
"""
# Define variables relevant to write transistors
self.wwl_positions = [None] * self.num_w_ports self.wwl_positions = [None] * self.num_w_ports
self.wbl_positions = [None] * self.num_w_ports self.wbl_positions = [None] * self.num_w_ports
self.wbr_positions = [None] * self.num_w_ports self.wbr_positions = [None] * self.num_w_ports
# 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 # iterate over the number of write ports
for k in range(0,self.num_w_ports): for k in range(0,self.num_w_ports):
# Add transistors # Add transistors
@ -486,21 +459,18 @@ class pbitcell(design.design):
self.write_nmos_right[k].place(offset=[right_write_transistor_xpos, self.port_ypos]) self.write_nmos_right[k].place(offset=[right_write_transistor_xpos, self.port_ypos])
# Add WWL lines
# calculate WWL position
wwl_ypos = rwwl_ypos = -0.5*self.m1_width - self.num_rw_ports*self.rowline_spacing - k*self.rowline_spacing
self.wwl_positions[k] = vector(0, wwl_ypos)
# add pin for WWL # add pin for WWL
wwl_ypos = rwwl_ypos = self.rowline_offset - self.num_rw_ports*self.rowline_spacing - k*self.rowline_spacing
self.wwl_positions[k] = vector(0, wwl_ypos)
self.add_layout_pin_rect_center(text=self.w_wl_names[k], self.add_layout_pin_rect_center(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=self.m1_width) height=self.m1_width)
# add pins for WBL and WBL_bar, overlaid on source contacts # add pins for WBL and WBR
wbl_xpos = left_write_transistor_xpos - self.bitline_offset + self.m2_width wbl_xpos = left_write_transistor_xpos - self.bitline_offset + self.m2_width
self.wbl_positions[k] = vector(wbl_xpos, self.y_center) self.wbl_positions[k] = vector(wbl_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.w_bl_names[k], self.add_layout_pin_rect_center(text=self.w_bl_names[k],
layer="metal2", layer="metal2",
offset=self.wbl_positions[k], offset=self.wbl_positions[k],
@ -508,7 +478,7 @@ class pbitcell(design.design):
height=self.height) height=self.height)
wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset - self.m2_width wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset - self.m2_width
self.wbr_positions[k] = vector(wbr_xpos, self.y_center) self.wbr_positions[k] = vector(wbr_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.w_br_names[k], self.add_layout_pin_rect_center(text=self.w_br_names[k],
layer="metal2", layer="metal2",
offset=self.wbr_positions[k], offset=self.wbr_positions[k],
@ -558,23 +528,17 @@ class pbitcell(design.design):
self.connect_inst(["RA_to_R_right{}".format(k), self.r_wl_names[k], self.r_br_names[k], "gnd"]) self.connect_inst(["RA_to_R_right{}".format(k), self.r_wl_names[k], self.r_br_names[k], "gnd"])
def place_read_ports(self): def place_read_ports(self):
""" """ Places the read ports in the bit cell """
Places the read ports in the bit cell. # define read transistor variables as empty arrays based on the number of read ports
"""
# Define variables relevant to read transistors
self.rwl_positions = [None] * self.num_r_ports self.rwl_positions = [None] * self.num_r_ports
self.rbl_positions = [None] * self.num_r_ports self.rbl_positions = [None] * self.num_r_ports
self.rbr_positions = [None] * self.num_r_ports self.rbr_positions = [None] * self.num_r_ports
# define offset correction due to rotation of the ptx module
read_rotation_correct = self.read_nmos.active_height
# calculate offset to overlap the drain of the read-access transistor with the source of the read transistor # calculate offset to overlap the drain of the read-access transistor with the source of the read transistor
overlap_offset = self.read_nmos.get_pin("D").cx() - self.read_nmos.get_pin("S").cx() overlap_offset = self.read_nmos.get_pin("D").cx() - self.read_nmos.get_pin("S").cx()
# 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_r_ports):
# Add transistors
# calculate transistor offsets # calculate transistor offsets
left_read_transistor_xpos = self.left_building_edge \ left_read_transistor_xpos = self.left_building_edge \
- (k+1)*self.port_spacing \ - (k+1)*self.port_spacing \
@ -594,21 +558,18 @@ class pbitcell(design.design):
self.read_nmos_right[k].place(offset=[right_read_transistor_xpos+overlap_offset, self.port_ypos]) self.read_nmos_right[k].place(offset=[right_read_transistor_xpos+overlap_offset, self.port_ypos])
# Add RWL lines
# calculate RWL position
rwl_ypos = rwwl_ypos = -0.5*self.m1_width - self.num_rw_ports*self.rowline_spacing - self.num_w_ports*self.rowline_spacing - k*self.rowline_spacing
self.rwl_positions[k] = vector(0, rwl_ypos)
# add pin for RWL # add pin for RWL
rwl_ypos = rwwl_ypos = self.rowline_offset - self.num_rw_ports*self.rowline_spacing - self.num_w_ports*self.rowline_spacing - k*self.rowline_spacing
self.rwl_positions[k] = vector(0, rwl_ypos)
self.add_layout_pin_rect_center(text=self.r_wl_names[k], self.add_layout_pin_rect_center(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=self.m1_width) height=self.m1_width)
# add pins for RBL and RBL_bar, overlaid on drain contacts # add pins for RBL and RBR
rbl_xpos = left_read_transistor_xpos - self.bitline_offset + self.m2_width rbl_xpos = left_read_transistor_xpos - self.bitline_offset + self.m2_width
self.rbl_positions[k] = vector(rbl_xpos, self.y_center) self.rbl_positions[k] = vector(rbl_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.r_bl_names[k], self.add_layout_pin_rect_center(text=self.r_bl_names[k],
layer="metal2", layer="metal2",
offset=self.rbl_positions[k], offset=self.rbl_positions[k],
@ -616,7 +577,7 @@ class pbitcell(design.design):
height=self.height) height=self.height)
rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset - self.m2_width rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset - self.m2_width
self.rbr_positions[k] = vector(rbr_xpos, self.y_center) self.rbr_positions[k] = vector(rbr_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.r_br_names[k], self.add_layout_pin_rect_center(text=self.r_br_names[k],
layer="metal2", layer="metal2",
offset=self.rbr_positions[k], offset=self.rbr_positions[k],
@ -624,9 +585,7 @@ class pbitcell(design.design):
height=self.height) height=self.height)
def route_wordlines(self): def route_wordlines(self):
""" """ Routes gate of transistors to their respective wordlines """
Routes gate of transistors to their respective wordlines
"""
port_transistors = [] port_transistors = []
for k in range(self.num_rw_ports): for k in range(self.num_rw_ports):
port_transistors.append(self.readwrite_nmos_left[k]) port_transistors.append(self.readwrite_nmos_left[k])
@ -649,12 +608,12 @@ class pbitcell(design.design):
wl_positions.append(self.rwl_positions[k]) wl_positions.append(self.rwl_positions[k])
wl_positions.append(self.rwl_positions[k]) wl_positions.append(self.rwl_positions[k])
for k in range(2*self.total_ports): for k in range(2*self.total_ports):
gate_offset = port_transistors[k].get_pin("G").bc() gate_offset = port_transistors[k].get_pin("G").bc()
port_contact_offset = gate_offset + vector(0, -self.gate_contact_yoffset + self.poly_extend_active) port_contact_offset = gate_offset + vector(0, -self.gate_contact_yoffset + self.poly_extend_active)
wl_contact_offset = vector(gate_offset.x, wl_positions[k].y) wl_contact_offset = vector(gate_offset.x, wl_positions[k].y)
# first transistor on either side of the cross coupled inverters does not need to route to wordline on metal2
if (k == 0) or (k == 1): if (k == 0) or (k == 1):
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=port_contact_offset) offset=port_contact_offset)
@ -676,9 +635,7 @@ class pbitcell(design.design):
self.add_path("metal2", [port_contact_offset, wl_contact_offset]) self.add_path("metal2", [port_contact_offset, wl_contact_offset])
def route_bitlines(self): def route_bitlines(self):
""" """ Routes read/write transistors to their respective bitlines """
Routes read/write transistors to their respective bitlines
"""
left_port_transistors = [] left_port_transistors = []
right_port_transistors = [] right_port_transistors = []
for k in range(self.num_rw_ports): for k in range(self.num_rw_ports):
@ -722,7 +679,8 @@ class pbitcell(design.design):
self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height) self.add_path("metal2", [port_contact_offest, br_offset], width=contact.m1m2.height)
def route_supply(self): def route_supply(self):
# route inverter nmos and read-access transistors to gnd """ Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """
# route inverter nmos and read-access nmos to gnd
nmos_contact_positions = [] nmos_contact_positions = []
nmos_contact_positions.append(self.inverter_nmos_left.get_pin("S").center()) nmos_contact_positions.append(self.inverter_nmos_left.get_pin("S").center())
nmos_contact_positions.append(self.inverter_nmos_right.get_pin("D").center()) nmos_contact_positions.append(self.inverter_nmos_right.get_pin("D").center())
@ -749,9 +707,7 @@ class pbitcell(design.design):
self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right])
def route_readwrite_access(self): def route_readwrite_access(self):
""" """ Routes read/write transistors to the storage component of the bitcell """
Routes read/write transistors to the storage component of the bitcell
"""
for k in range(self.num_rw_ports): for k in range(self.num_rw_ports):
mid = vector(self.readwrite_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) mid = vector(self.readwrite_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos)
Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos)
@ -764,9 +720,7 @@ class pbitcell(design.design):
self.add_path("metal1", [mid, Q_bar_pos]) self.add_path("metal1", [mid, Q_bar_pos])
def route_write_access(self): def route_write_access(self):
""" """ Routes read/write transistors to the storage component of the bitcell """
Routes read/write transistors to the storage component of the bitcell
"""
for k in range(self.num_w_ports): for k in range(self.num_w_ports):
mid = vector(self.write_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) mid = vector(self.write_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos)
Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos)
@ -779,9 +733,8 @@ class pbitcell(design.design):
self.add_path("metal1", [mid, Q_bar_pos]) self.add_path("metal1", [mid, Q_bar_pos])
def route_read_access(self): def route_read_access(self):
""" """ Routes read access transistors to the storage component of the bitcell """
Routes read access transistors to the storage component of the bitcell # add poly to metal1 contacts for gates of the inverters
"""
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_upper_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_upper_ypos)
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_contact_center(layers=("poly", "contact", "metal1"),
offset=left_storage_contact, offset=left_storage_contact,
@ -798,6 +751,8 @@ class pbitcell(design.design):
inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_upper_ypos) inverter_gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").rc().x, self.cross_couple_upper_ypos)
self.add_path("poly", [right_storage_contact, inverter_gate_offset_right]) self.add_path("poly", [right_storage_contact, inverter_gate_offset_right])
# add poly to metal1 contacts for gates of read-access transistors
# route from read-access contacts to inverter contacts on metal1
for k in range(self.num_r_ports): for k in range(self.num_r_ports):
port_contact_offset = self.read_access_nmos_left[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) port_contact_offset = self.read_access_nmos_left[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active)
@ -823,19 +778,13 @@ class pbitcell(design.design):
def extend_well(self): def extend_well(self):
""" """
Connects wells between ptx modules to avoid drc spacing issues. Connects wells between ptx modules and places well contacts"""
Since the pwell of the read ports rise higher than the nwell of the inverters, # extend pwell to encompass entire nmos region of the cell up to the height of the tallest nmos transistor
the well connections must be done piecewise to avoid pwell and nwell overlap.
"""
max_nmos_well_height = max(self.inverter_nmos.cell_well_height, max_nmos_well_height = max(self.inverter_nmos.cell_well_height,
self.readwrite_nmos.cell_well_height, self.readwrite_nmos.cell_well_height,
self.write_nmos.cell_well_height, self.write_nmos.cell_well_height,
self.read_nmos.cell_well_height) self.read_nmos.cell_well_height)
well_height = max_nmos_well_height + self.port_ypos - self.well_enclose_active - self.gnd_position.y well_height = max_nmos_well_height + self.port_ypos - self.well_enclose_active - self.gnd_position.y
# extend pwell to encompass entire nmos region of the cell up to the height of the inverter nmos well
offset = vector(self.leftmost_xpos, self.botmost_ypos) offset = vector(self.leftmost_xpos, self.botmost_ypos)
self.add_rect(layer="pwell", self.add_rect(layer="pwell",
offset=offset, offset=offset,
@ -858,7 +807,6 @@ class pbitcell(design.design):
width=well_width, width=well_width,
height=well_height) height=well_height)
# add well contacts # add well contacts
# connect pimplants to gnd # connect pimplants to gnd
offset = vector(0, self.gnd_position.y) offset = vector(0, self.gnd_position.y)
@ -935,7 +883,4 @@ class pbitcell(design.design):
""" route the short from Q_bar to gnd necessary for the replica bitcell """ """ route the short from Q_bar to gnd necessary for the replica bitcell """
Q_bar_pos = self.inverter_pmos_right.get_pin("S").center() Q_bar_pos = self.inverter_pmos_right.get_pin("S").center()
vdd_pos = self.inverter_pmos_right.get_pin("D").center() vdd_pos = self.inverter_pmos_right.get_pin("D").center()
#vdd_pos = vector(Q_bar_pos.x, self.vdd_position.y)
self.add_path("metal1", [Q_bar_pos, vdd_pos]) self.add_path("metal1", [Q_bar_pos, vdd_pos])