mirror of https://github.com/VLSIDA/OpenRAM.git
Updating comments and cleaning up code for pbitcell.
This commit is contained in:
parent
ab7a83b7a5
commit
1a0568f244
|
|
@ -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])
|
||||||
|
|
||||||
Loading…
Reference in New Issue