Refactor bitcell to bitcell_base. Pep8 format bitcells.

This commit is contained in:
vagrant 2019-10-06 01:08:23 +00:00
parent d722311822
commit 67c768d22c
8 changed files with 599 additions and 439 deletions

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import utils
from tech import GDS,layer,parameter,drc from tech import GDS, layer
import logical_effort import bitcell_base
class bitcell(design.design):
class bitcell(bitcell_base.bitcell_base):
""" """
A single bit cell (6T, 8T, etc.) This module implements the A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so single memory cell used in the design. It is a hand-made cell, so
@ -22,12 +22,14 @@ class bitcell(design.design):
pin_names = ["bl", "br", "wl", "vdd", "gnd"] pin_names = ["bl", "br", "wl", "vdd", "gnd"]
storage_nets = ['Q', 'Qbar'] storage_nets = ['Q', 'Qbar']
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) (width, height) = utils.get_libcell_size("cell_6t",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"])
def __init__(self, name=""): def __init__(self, name=""):
# Ignore the name argument # Ignore the name argument
design.design.__init__(self, "cell_6t") bitcell_base.bitcell_base.__init__(self, "cell_6t")
debug.info(2, "Create bitcell") debug.info(2, "Create bitcell")
self.width = bitcell.width self.width = bitcell.width
@ -36,12 +38,6 @@ class bitcell(design.design):
self.add_pin_types(self.type_list) self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
return logical_effort.logical_effort('bitline', size, cin, load, parasitic_delay, False)
def get_all_wl_names(self): def get_all_wl_names(self):
""" Creates a list of all wordline pin names """ """ Creates a list of all wordline pin names """
row_pins = ["wl"] row_pins = ["wl"]
@ -77,30 +73,9 @@ class bitcell(design.design):
debug.check(port == 0, "One port for bitcell only.") debug.check(port == 0, "One port for bitcell only.")
return "wl" return "wl"
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary
total_power = self.return_power(dynamic, leakage)
return total_power
def get_storage_net_names(self):
"""Returns names of storage nodes in bitcell in [non-inverting, inverting] format."""
#Checks that they do exist
if self.nets_match:
return self.storage_nets
else:
debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets))
return None
def input_load(self):
"""Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function.""" """
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets) self.add_graph_edges(graph, port_nets)

View File

@ -5,13 +5,14 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import utils
from tech import GDS, layer, parameter, drc from tech import GDS, layer, parameter, drc
import logical_effort import logical_effort
import bitcell_base
class bitcell_1rw_1r(design.design):
class bitcell_1rw_1r(bitcell_base.bitcell_base):
""" """
A single bit cell (6T, 8T, etc.) This module implements the A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so single memory cell used in the design. It is a hand-made cell, so
@ -20,14 +21,17 @@ class bitcell_1rw_1r(design.design):
""" """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar'] storage_nets = ['Q', 'Q_bar']
(width,height) = utils.get_libcell_size("cell_1rw_1r", GDS["unit"], layer["boundary"]) (width, height) = utils.get_libcell_size("cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"])
def __init__(self, name=""): def __init__(self, name=""):
# Ignore the name argument # Ignore the name argument
design.design.__init__(self, "cell_1rw_1r") bitcell_base.bitcell_base.__init__(self, "cell_1rw_1r")
debug.info(2, "Create bitcell with 1RW and 1R Port") debug.info(2, "Create bitcell with 1RW and 1R Port")
self.width = bitcell_1rw_1r.width self.width = bitcell_1rw_1r.width
@ -36,15 +40,11 @@ class bitcell_1rw_1r(design.design):
self.add_pin_types(self.type_list) self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def get_bitcell_pins(self, col, row): def get_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 = ["bl0_{0}".format(col), bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col), "br0_{0}".format(col),
"bl1_{0}".format(col), "bl1_{0}".format(col),
@ -110,31 +110,6 @@ class bitcell_1rw_1r(design.design):
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.") debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port) return "wl{}".format(port)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary
total_power = self.return_power(dynamic, leakage)
return total_power
def input_load(self):
"""Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def get_storage_net_names(self):
"""Returns names of storage nodes in bitcell in [non-inverting, inverting] format."""
#Checks that they do exist
if self.nets_match:
return self.storage_nets
else:
debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets))
return None
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex """Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function.""" to use the add_graph_edges function."""

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import utils
from tech import GDS,layer,parameter,drc from tech import GDS, layer
import logical_effort import bitcell_base
class bitcell_1w_1r(design.design):
class bitcell_1w_1r(bitcell_base.bitcell_base):
""" """
A single bit cell (6T, 8T, etc.) This module implements the A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so single memory cell used in the design. It is a hand-made cell, so
@ -20,14 +20,17 @@ class bitcell_1w_1r(design.design):
""" """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar'] storage_nets = ['Q', 'Q_bar']
(width,height) = utils.get_libcell_size("cell_1w_1r", GDS["unit"], layer["boundary"]) (width, height) = utils.get_libcell_size("cell_1w_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"])
def __init__(self, name=""): def __init__(self, name=""):
# Ignore the name argument # Ignore the name argument
design.design.__init__(self, "cell_1w_1r") bitcell_base.bitcell_base.__init__(self, "cell_1w_1r")
debug.info(2, "Create bitcell with 1W and 1R Port") debug.info(2, "Create bitcell with 1W and 1R Port")
self.width = bitcell_1w_1r.width self.width = bitcell_1w_1r.width
@ -36,15 +39,11 @@ class bitcell_1w_1r(design.design):
self.add_pin_types(self.type_list) self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def get_bitcell_pins(self, col, row): def get_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 = ["bl0_{0}".format(col), bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col), "br0_{0}".format(col),
"bl1_{0}".format(col), "bl1_{0}".format(col),
@ -108,31 +107,6 @@ class bitcell_1w_1r(design.design):
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.") debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port) return "wl{}".format(port)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary
total_power = self.return_power(dynamic, leakage)
return total_power
def input_load(self):
"""Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def get_storage_net_names(self):
"""Returns names of storage nodes in bitcell in [non-inverting, inverting] format."""
#Checks that they do exist
if self.nets_match:
return self.storage_nets
else:
debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets))
return None
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex """Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function.""" to use the add_graph_edges function."""

View File

@ -0,0 +1,87 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
import design
import logical_effort
from tech import parameter, drc
class bitcell_base(design.design):
"""
Base bitcell parameters to be over-riden.
"""
def __init__(self, name):
design.design.__init__(self, name)
def get_stage_effort(self, load):
parasitic_delay = 1
# This accounts for bitline being drained
# thought the access TX and internal node
size = 0.5
# Assumes always a minimum sizes inverter.
# Could be specified in the tech.py file.
cin = 3
# min size NMOS gate load
read_port_load = 0.5
return logical_effort.logical_effort('bitline',
size,
cin,
load + read_port_load,
parasitic_delay,
False)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
# FIXME
dynamic = 0
total_power = self.return_power(dynamic, leakage)
return total_power
def input_load(self):
""" Return the relative capacitance of the access transistor gates """
# FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell.
# Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
# This is a handmade cell so the value must be entered
# in the tech.py file or estimated.
# Calculated in the tech file by summing the widths of all
# the related gates and dividing by the minimum width.
# FIXME: sizing is not accurate with the handmade cell.
# Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin
def get_storage_net_names(self):
"""
Returns names of storage nodes in bitcell in
[non-inverting, inverting] format.
"""
# Checks that they do exist
if self.nets_match:
return self.storage_nets
else:
fmt_str = "Storage nodes={} not found in spice file."
debug.info(1, fmt_str.format(self.storage_nets))
return None
def build_graph(self, graph, inst_name, port_nets):
"""
By default, bitcells won't be part of the graph.
"""
return

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import utils
from tech import GDS,layer,parameter,drc from tech import GDS, layer
import logical_effort import bitcell_base
class dummy_bitcell(design.design):
class dummy_bitcell(bitcell_base.bitcell_base):
""" """
A single bit cell (6T, 8T, etc.) This module implements the A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so single memory cell used in the design. It is a hand-made cell, so
@ -20,29 +20,18 @@ class dummy_bitcell(design.design):
""" """
pin_names = ["bl", "br", "wl", "vdd", "gnd"] pin_names = ["bl", "br", "wl", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("dummy_cell_6t", GDS["unit"], layer["boundary"]) (width, height) = utils.get_libcell_size("dummy_cell_6t",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_6t", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_6t", GDS["unit"])
def __init__(self, name=""): def __init__(self, name=""):
# Ignore the name argument # Ignore the name argument
design.design.__init__(self, "dummy_cell_6t") bitcell_base.bitcell_base.__init__(self, "dummy_cell_6t")
debug.info(2, "Create dummy bitcell") debug.info(2, "Create dummy bitcell")
self.width = dummy_bitcell.width self.width = dummy_bitcell.width
self.height = dummy_bitcell.height self.height = dummy_bitcell.height
self.pin_map = dummy_bitcell.pin_map self.pin_map = dummy_bitcell.pin_map
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary
total_power = self.return_power(dynamic, leakage)
return total_power
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin

View File

@ -5,12 +5,13 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import utils
from tech import GDS,layer,drc,parameter from tech import GDS, layer
import bitcell_base
class dummy_bitcell_1rw_1r(design.design):
class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base):
""" """
A single bit cell which is forced to store a 0. A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It This module implements the single memory cell used in the design. It
@ -18,13 +19,18 @@ class dummy_bitcell_1rw_1r(design.design):
the technology library. """ the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
(width,height) = utils.get_libcell_size("dummy_cell_1rw_1r", GDS["unit"], layer["boundary"]) "INPUT", "INPUT", "POWER", "GROUND"]
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1rw_1r", GDS["unit"]) (width, height) = utils.get_libcell_size("dummy_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"dummy_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""): def __init__(self, name=""):
# Ignore the name argument # Ignore the name argument
design.design.__init__(self, "dummy_cell_1rw_1r") bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r")
debug.info(2, "Create dummy bitcell 1rw+1r object") debug.info(2, "Create dummy bitcell 1rw+1r object")
self.width = dummy_bitcell_1rw_1r.width self.width = dummy_bitcell_1rw_1r.width
@ -32,14 +38,3 @@ class dummy_bitcell_1rw_1r(design.design):
self.pin_map = dummy_bitcell_1rw_1r.pin_map self.pin_map = dummy_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list) self.add_pin_types(self.type_list)
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
"""Dummy bitcells are cannot form a path and be part of the timing graph"""
return

View File

@ -5,12 +5,13 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import utils
from tech import GDS,layer,drc,parameter from tech import GDS, layer
import bitcell_base
class dummy_bitcell_1w_1r(design.design):
class dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
""" """
A single bit cell which is forced to store a 0. A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It This module implements the single memory cell used in the design. It
@ -18,13 +19,18 @@ class dummy_bitcell_1w_1r(design.design):
the technology library. """ the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
(width,height) = utils.get_libcell_size("dummy_cell_1w_1r", GDS["unit"], layer["boundary"]) "INPUT", "INPUT", "POWER", "GROUND"]
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1w_1r", GDS["unit"]) (width, height) = utils.get_libcell_size("dummy_cell_1w_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"dummy_cell_1w_1r",
GDS["unit"])
def __init__(self, name=""): def __init__(self, name=""):
# Ignore the name argument # Ignore the name argument
design.design.__init__(self, "dummy_cell_1w_1r") bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r")
debug.info(2, "Create dummy bitcell 1w+1r object") debug.info(2, "Create dummy bitcell 1w+1r object")
self.width = dummy_bitcell_1w_1r.width self.width = dummy_bitcell_1w_1r.width
@ -32,14 +38,4 @@ class dummy_bitcell_1w_1r(design.design):
self.pin_map = dummy_bitcell_1w_1r.pin_map self.pin_map = dummy_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list) self.add_pin_types(self.type_list)
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
"""Dummy bitcells are cannot form a path and be part of the timing graph"""
return

View File

@ -6,15 +6,16 @@
# All rights reserved. # All rights reserved.
# #
import contact import contact
import design
import debug import debug
from tech import drc, parameter, spice from tech import drc, parameter
from vector import vector from vector import vector
from ptx import ptx from ptx import ptx
from globals import OPTS from globals import OPTS
import logical_effort import logical_effort
import bitcell_base
class pbitcell(design.design):
class pbitcell(bitcell_base.bitcell_base):
""" """
This module implements a parametrically sized multi-port bitcell, This module implements a parametrically sized multi-port bitcell,
with a variable number of read/write, write, and read ports with a variable number of read/write, write, and read ports
@ -29,11 +30,13 @@ class pbitcell(design.design):
self.replica_bitcell = replica_bitcell self.replica_bitcell = replica_bitcell
self.dummy_bitcell = dummy_bitcell self.dummy_bitcell = dummy_bitcell
design.design.__init__(self, name) bitcell_base.bitcell_base.__init__(self, name)
info_string = "{0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, fmt_str = "{0} rw ports, {1} w ports and {2} r ports"
info_string = fmt_str.format(self.num_rw_ports,
self.num_w_ports, self.num_w_ports,
self.num_r_ports) self.num_r_ports)
debug.info(2, "create a multi-port bitcell with {}".format(info_string)) debug.info(2,
"create a multi-port bitcell with {}".format(info_string))
self.add_comment(info_string) self.add_comment(info_string)
if self.dummy_bitcell: if self.dummy_bitcell:
@ -86,12 +89,13 @@ 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 or translate_all will not be possible # in netlist_only mode, calling offset_all_coordinates or
# this function is not needed to calculate the dimensions of pbitcell in netlist_only mode though # translate_all will not be possible
# 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.translate_all(vector(self.leftmost_xpos, self.botmost_ypos)) self.translate_all(vector(self.leftmost_xpos, self.botmost_ypos))
def add_pins(self): def add_pins(self):
""" add pins and set names for bitlines and wordlines """ """ add pins and set names for bitlines and wordlines """
self.rw_bl_names = [] self.rw_bl_names = []
@ -197,12 +201,12 @@ class pbitcell(design.design):
""" Calculate transistor spacings """ """ Calculate transistor spacings """
# calculate metal contact extensions over transistor active # calculate metal contact extensions over transistor active
readwrite_nmos_contact_extension = 0.5*(self.readwrite_nmos.active_contact.height \ readwrite_nmos_contact_extension = 0.5 * \
- self.readwrite_nmos.active_height) (self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height)
write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height \ write_nmos_contact_extension = 0.5 * \
- self.write_nmos.active_height) (self.write_nmos.active_contact.height - self.write_nmos.active_height)
read_nmos_contact_extension = 0.5*(self.read_nmos.active_contact.height \ read_nmos_contact_extension = 0.5 * \
- self.read_nmos.active_height) (self.read_nmos.active_contact.height - self.read_nmos.active_height)
max_contact_extension = max(readwrite_nmos_contact_extension, max_contact_extension = max(readwrite_nmos_contact_extension,
write_nmos_contact_extension, write_nmos_contact_extension,
read_nmos_contact_extension) read_nmos_contact_extension)
@ -218,27 +222,38 @@ class pbitcell(design.design):
self.inverter_nmos_ypos = self.port_ypos self.inverter_nmos_ypos = self.port_ypos
# spacing between ports (same for read/write and write ports) # spacing between ports (same for read/write and write ports)
self.bitline_offset = -0.5*self.readwrite_nmos.active_width + 0.5*contact.m1m2.height \ self.bitline_offset = -0.5 * self.readwrite_nmos.active_width \
+ 0.5 * contact.m1m2.height \
+ self.m2_space + self.m2_width + self.m2_space + self.m2_width
m2_constraint = self.bitline_offset + self.m2_space + 0.5*contact.m1m2.height \ m2_constraint = self.bitline_offset + self.m2_space \
+ 0.5 * contact.m1m2.height \
- 0.5 * self.readwrite_nmos.active_width - 0.5 * self.readwrite_nmos.active_width
self.write_port_spacing = max(self.active_space, self.m1_space, m2_constraint) self.write_port_spacing = max(self.active_space,
self.m1_space,
m2_constraint)
self.read_port_spacing = self.bitline_offset + self.m2_space self.read_port_spacing = self.bitline_offset + self.m2_space
# spacing between cross coupled inverters # spacing between cross coupled inverters
self.inverter_to_inverter_spacing = contact.poly.width + self.m1_space self.inverter_to_inverter_spacing = contact.poly.width + self.m1_space
# calculations related to inverter connections # calculations related to inverter connections
inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) inverter_pmos_contact_extension = 0.5 * \
inverter_nmos_contact_extension = 0.5*(self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height) (self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height)
self.inverter_gap = max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ inverter_nmos_contact_extension = 0.5 * \
(self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height)
self.inverter_gap = max(self.poly_to_active,
self.m1_space + inverter_nmos_contact_extension) \
+ self.poly_to_polycontact + 2 * contact.poly.width \ + self.poly_to_polycontact + 2 * contact.poly.width \
+ self.m1_space + inverter_pmos_contact_extension + self.m1_space + inverter_pmos_contact_extension
self.cross_couple_lower_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ self.cross_couple_lower_ypos = self.inverter_nmos_ypos \
+ max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + self.inverter_nmos.active_height \
+ max(self.poly_to_active,
self.m1_space + inverter_nmos_contact_extension) \
+ 0.5 * contact.poly.width + 0.5 * contact.poly.width
self.cross_couple_upper_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ self.cross_couple_upper_ypos = self.inverter_nmos_ypos \
+ max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + self.inverter_nmos.active_height \
+ max(self.poly_to_active,
self.m1_space + inverter_nmos_contact_extension) \
+ self.poly_to_polycontact \ + self.poly_to_polycontact \
+ 1.5 * contact.poly.width + 1.5 * contact.poly.width
@ -246,7 +261,8 @@ class pbitcell(design.design):
self.m1_offset = -0.5 * self.m1_width self.m1_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 \ implant_constraint = max(inverter_pmos_contact_extension, 0) \
+ 2 * self.implant_enclose_active \
+ 0.5 * (contact.well.width - self.m1_width) + 0.5 * (contact.well.width - self.m1_width)
metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space
self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width
@ -256,27 +272,37 @@ 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 = self.m1_offset - self.total_ports * self.m1_pitch self.botmost_ypos = self.m1_offset - self.total_ports * self.m1_pitch
self.topmost_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ self.topmost_ypos = self.inverter_nmos_ypos \
+ self.inverter_gap + self.inverter_pmos.active_height \ + self.inverter_nmos.active_height \
+ self.inverter_gap \
+ self.inverter_pmos.active_height \
+ self.vdd_offset + self.vdd_offset
self.leftmost_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width \ self.leftmost_xpos = -0.5 * self.inverter_to_inverter_spacing \
- self.num_rw_ports*(self.readwrite_nmos.active_width + self.write_port_spacing) \ - self.inverter_nmos.active_width \
- self.num_w_ports*(self.write_nmos.active_width + self.write_port_spacing) \ - self.num_rw_ports * \
- self.num_r_ports*(self.read_port_width + self.read_port_spacing) \ (self.readwrite_nmos.active_width + self.write_port_spacing) \
- self.num_w_ports * \
(self.write_nmos.active_width + self.write_port_spacing) \
- self.num_r_ports * \
(self.read_port_width + self.read_port_spacing) \
- self.bitline_offset - 0.5 * contact.m1m2.width - self.bitline_offset - 0.5 * contact.m1m2.width
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.center_ypos = 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
The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". as storage for the bitcell.
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",
@ -297,57 +323,80 @@ class pbitcell(design.design):
self.connect_inst(["vdd", self.Q, self.Q_bar, "vdd"]) self.connect_inst(["vdd", self.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
inverter_pmos_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap inverter_pmos_ypos = self.inverter_nmos_ypos \
+ self.inverter_nmos.active_height \
+ self.inverter_gap
# create active for nmos # create active for nmos
self.inverter_nmos_left.place([left_inverter_xpos, self.inverter_nmos_ypos]) self.inverter_nmos_left.place([left_inverter_xpos,
self.inverter_nmos_right.place([right_inverter_xpos, self.inverter_nmos_ypos]) self.inverter_nmos_ypos])
self.inverter_nmos_right.place([right_inverter_xpos,
self.inverter_nmos_ypos])
# create active for pmos # create active for pmos
self.inverter_pmos_left.place([left_inverter_xpos, inverter_pmos_ypos]) self.inverter_pmos_left.place([left_inverter_xpos,
self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos]) 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) # 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.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()]) [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()])
# connect output (drain/source) of inverters # connect output (drain/source) of inverters
self.add_path("metal1", self.add_path("metal1",
[self.inverter_nmos_left.get_pin("D").uc(), self.inverter_pmos_left.get_pin("D").bc()], [self.inverter_nmos_left.get_pin("D").uc(),
self.inverter_pmos_left.get_pin("D").bc()],
width=contact.active.second_layer_width) width=contact.active.second_layer_width)
self.add_path("metal1", self.add_path("metal1",
[self.inverter_nmos_right.get_pin("S").uc(), self.inverter_pmos_right.get_pin("S").bc()], [self.inverter_nmos_right.get_pin("S").uc(),
self.inverter_pmos_right.get_pin("S").bc()],
width=contact.active.second_layer_width) width=contact.active.second_layer_width)
# add contacts to connect gate poly to drain/source metal1 (to connect Q to Q_bar) # add contacts to connect gate poly to drain/source
contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x + 0.5*contact.poly.height, # metal1 (to connect Q to Q_bar)
contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x \
+ 0.5 * contact.poly.height,
self.cross_couple_upper_ypos) self.cross_couple_upper_ypos)
self.add_via_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=contact_offset_left, offset=contact_offset_left,
directions=("H", "H")) directions=("H", "H"))
contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x - 0.5*contact.poly.height, contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \
- 0.5*contact.poly.height,
self.cross_couple_lower_ypos) self.cross_couple_lower_ypos)
self.add_via_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=contact_offset_right, offset=contact_offset_right,
directions=("H", "H")) directions=("H", "H"))
# connect contacts to gate poly (cross couple connections) # connect contacts to gate poly (cross couple connections)
gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, contact_offset_left.y) gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x,
contact_offset_left.y)
self.add_path("poly", [contact_offset_left, gate_offset_right]) self.add_path("poly", [contact_offset_left, gate_offset_right])
gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x, contact_offset_right.y) gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").rc().x,
contact_offset_right.y)
self.add_path("poly", [contact_offset_right, gate_offset_left]) self.add_path("poly", [contact_offset_right, gate_offset_left])
def route_rails(self): def route_rails(self):
@ -361,8 +410,10 @@ class pbitcell(design.design):
self.add_power_pin("gnd", vector(0, gnd_ypos)) self.add_power_pin("gnd", vector(0, gnd_ypos))
vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ vdd_ypos = self.inverter_nmos_ypos \
+ self.inverter_gap + self.inverter_pmos.active_height \ + self.inverter_nmos.active_height \
+ self.inverter_gap \
+ self.inverter_pmos.active_height \
+ self.vdd_offset + self.vdd_offset
self.vdd_position = vector(0, vdd_ypos) self.vdd_position = vector(0, vdd_ypos)
self.add_rect_center(layer="metal1", self.add_rect_center(layer="metal1",
@ -372,14 +423,21 @@ class pbitcell(design.design):
def create_readwrite_ports(self): def create_readwrite_ports(self):
""" """
Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell. Creates read/write ports to the bit cell. A differential
A read or write is enabled by setting a Read-Write-Wordline (RWWL) high, subsequently turning on the transistor. pair of transistor can both read and write, like in a 6T cell.
The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q). A read or write is enabled by setting a Read-Write-Wordline (RWWL)
In a write operation, driving RWBL high or low sets the value of the cell. high, subsequently turning on the transistor.
In a read operation, RWBL is precharged, then is either remains high or is discharged depending on the value of the cell. The transistor is connected between a Read-Write-Bitline (RWBL)
This is a differential design, so each write port has a mirrored port that connects RWBR to Q_bar. 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 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 RWBR to Q_bar.
""" """
# define read/write transistor variables as empty arrays based on the number of read/write ports # define read/write transistor variables as empty arrays based
# on the number of read/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
@ -398,11 +456,13 @@ class pbitcell(design.design):
self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k), self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k),
mod=self.readwrite_nmos) mod=self.readwrite_nmos)
self.connect_inst([self.Q_bar, self.rw_wl_names[k], br_name, "gnd"]) self.connect_inst([self.Q_bar,
self.rw_wl_names[k], br_name, "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 read/write transistor variables as empty arrays
# based on the number of read/write ports
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
@ -419,9 +479,11 @@ class pbitcell(design.design):
+ k * self.readwrite_nmos.active_width + k * self.readwrite_nmos.active_width
# place 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 pin for RWWL # add pin for RWWL
rwwl_ypos = self.m1_offset - k * self.m1_pitch rwwl_ypos = self.m1_offset - k * self.m1_pitch
@ -432,15 +494,19 @@ class pbitcell(design.design):
width=self.width) width=self.width)
# add pins for RWBL and RWBR # add pins for RWBL and RWBR
rwbl_xpos = left_readwrite_transistor_xpos - self.bitline_offset + 0.5*self.m2_width rwbl_xpos = left_readwrite_transistor_xpos \
- self.bitline_offset \
+ 0.5 * self.m2_width
self.rwbl_positions[k] = vector(rwbl_xpos, self.center_ypos) 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],
height=self.height) height=self.height)
rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width \ rwbr_xpos = right_readwrite_transistor_xpos \
+ self.bitline_offset - 0.5*self.m2_width + self.readwrite_nmos.active_width \
+ self.bitline_offset \
- 0.5 * self.m2_width
self.rwbr_positions[k] = vector(rwbr_xpos, self.center_ypos) 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",
@ -449,17 +515,24 @@ class pbitcell(design.design):
# update furthest left and right transistor edges # update furthest left and right transistor edges
self.left_building_edge = left_readwrite_transistor_xpos self.left_building_edge = left_readwrite_transistor_xpos
self.right_building_edge = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width self.right_building_edge = right_readwrite_transistor_xpos \
+ self.readwrite_nmos.active_width
def create_write_ports(self): def create_write_ports(self):
""" """
Creates write ports in the bit cell. A differential pair of transistors can write only. Creates write ports in the bit cell. A differential pair of
A write is enabled by setting a Write-Rowline (WWL) high, subsequently turning on the transistor. transistors can write only.
The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q). A write is enabled by setting a Write-Rowline (WWL) high,
In a write operation, driving WBL high or low sets the value of the cell. subsequently turning on the transistor.
This is a differential design, so each write port has a mirrored port that connects WBR to Q_bar. 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.
This is a differential design, so each write port has a
mirrored port that connects WBR to Q_bar.
""" """
# 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
@ -482,7 +555,8 @@ class pbitcell(design.design):
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 write transistor variables as empty arrays based
# on the number of write ports
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
@ -500,12 +574,15 @@ class pbitcell(design.design):
+ k * self.write_nmos.active_width + k * self.write_nmos.active_width
# add write transistors # add write transistors
self.write_nmos_left[k].place(offset=[left_write_transistor_xpos, self.port_ypos]) self.write_nmos_left[k].place(offset=[left_write_transistor_xpos,
self.port_ypos])
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 pin for WWL # add pin for WWL
wwl_ypos = rwwl_ypos = self.m1_offset - self.num_rw_ports*self.m1_pitch \ wwl_ypos = rwwl_ypos = self.m1_offset \
- self.num_rw_ports * self.m1_pitch \
- k * self.m1_pitch - k * self.m1_pitch
self.wwl_positions[k] = vector(0, wwl_ypos) 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],
@ -514,14 +591,18 @@ class pbitcell(design.design):
width=self.width) width=self.width)
# add pins for WBL and WBR # add pins for WBL and WBR
wbl_xpos = left_write_transistor_xpos - self.bitline_offset + 0.5*self.m2_width wbl_xpos = left_write_transistor_xpos \
- self.bitline_offset \
+ 0.5 * self.m2_width
self.wbl_positions[k] = vector(wbl_xpos, self.center_ypos) 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],
height=self.height) height=self.height)
wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset \ wbr_xpos = right_write_transistor_xpos \
+ self.write_nmos.active_width \
+ self.bitline_offset \
- 0.5 * self.m2_width - 0.5 * self.m2_width
self.wbr_positions[k] = vector(wbr_xpos, self.center_ypos) 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],
@ -531,21 +612,31 @@ class pbitcell(design.design):
# update furthest left and right transistor edges # update furthest left and right transistor edges
self.left_building_edge = left_write_transistor_xpos self.left_building_edge = left_write_transistor_xpos
self.right_building_edge = right_write_transistor_xpos + self.write_nmos.active_width self.right_building_edge = right_write_transistor_xpos \
+ self.write_nmos.active_width
def create_read_ports(self): def create_read_ports(self):
""" """
Creates read ports in the bit cell. A differential pair of ports can read only. Creates read ports in the bit cell. A differential pair
Two transistors function as a read port, denoted as the "read transistor" and the "read-access transistor". of ports can read only.
The read transistor is connected to RWL (gate), RBL (drain), and the read-access transistor (source). Two transistors function as a read port, denoted as the
The read-access transistor is connected to Q_bar (gate), gnd (source), and the read transistor (drain). "read transistor" and the "read-access transistor".
A read is enabled by setting a Read-Rowline (RWL) high, subsequently turning on the read transistor. The read transistor is connected to RWL (gate), RBL (drain),
The Read-Bitline (RBL) is precharged to high, and when the value of Q_bar is high, the read-access transistor and the read-access transistor (source).
is turned on, creating a connection between RBL and gnd. RBL subsequently discharges allowing for a differential read The read-access transistor is connected to Q_bar (gate),
using sense amps. This is a differential design, so each read port has a mirrored port that connects RBL_bar to Q. gnd (source), and the read transistor (drain).
A read is enabled by setting a Read-Rowline (RWL) high,
subsequently turning on the read transistor.
The Read-Bitline (RBL) is precharged to high, and when the
value of Q_bar is high, the read-access transistor
is turned on, creating a connection between RBL and gnd.
RBL subsequently discharges allowing for a differential read
using sense amps. This is a differential design, so each
read port has a mirrored port that connects RBL_bar to Q.
""" """
# 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_r_ports
self.read_nmos_right = [None] * self.num_r_ports self.read_nmos_right = [None] * self.num_r_ports
self.read_access_nmos_left = [None] * self.num_r_ports self.read_access_nmos_left = [None] * self.num_r_ports
@ -579,13 +670,16 @@ class pbitcell(design.design):
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 read transistor variables as empty arrays based
# on the number of read ports
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
# 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
overlap_offset = self.read_nmos.get_pin("D").cx() - self.read_nmos.get_pin("S").cx() # with the source of the read transistor
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):
@ -599,18 +693,24 @@ class pbitcell(design.design):
+ k * self.read_port_width + k * self.read_port_width
# add read-access transistors # add read-access transistors
self.read_access_nmos_left[k].place(offset=[left_read_transistor_xpos+overlap_offset, self.port_ypos]) self.read_access_nmos_left[k].place(offset=[left_read_transistor_xpos+overlap_offset,
self.port_ypos])
self.read_access_nmos_right[k].place(offset=[right_read_transistor_xpos, self.port_ypos]) self.read_access_nmos_right[k].place(offset=[right_read_transistor_xpos,
self.port_ypos])
# add read transistors # add read transistors
self.read_nmos_left[k].place(offset=[left_read_transistor_xpos, self.port_ypos]) self.read_nmos_left[k].place(offset=[left_read_transistor_xpos,
self.port_ypos])
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 pin for RWL # add pin for RWL
rwl_ypos = rwwl_ypos = self.m1_offset - self.num_rw_ports*self.m1_pitch \ rwl_ypos = rwwl_ypos = self.m1_offset \
- self.num_w_ports*self.m1_pitch - k*self.m1_pitch - self.num_rw_ports * self.m1_pitch \
- self.num_w_ports * self.m1_pitch \
- k * self.m1_pitch
self.rwl_positions[k] = vector(0, rwl_ypos) 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",
@ -618,14 +718,18 @@ class pbitcell(design.design):
width=self.width) width=self.width)
# add pins for RBL and RBR # add pins for RBL and RBR
rbl_xpos = left_read_transistor_xpos - self.bitline_offset + 0.5*self.m2_width rbl_xpos = left_read_transistor_xpos \
- self.bitline_offset \
+ 0.5 * self.m2_width
self.rbl_positions[k] = vector(rbl_xpos, self.center_ypos) 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],
height=self.height) height=self.height)
rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset \ rbr_xpos = right_read_transistor_xpos \
+ self.read_port_width \
+ self.bitline_offset \
- 0.5 * self.m2_width - 0.5 * self.m2_width
self.rbr_positions[k] = vector(rbr_xpos, self.center_ypos) 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],
@ -659,16 +763,20 @@ class pbitcell(design.design):
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 # 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_via_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=port_contact_offset) offset=port_contact_offset)
self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("poly", [gate_offset, port_contact_offset])
self.add_path("metal1", [port_contact_offset, wl_contact_offset]) self.add_path("metal1",
[port_contact_offset, wl_contact_offset])
else: else:
self.add_via_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
@ -681,7 +789,8 @@ class pbitcell(design.design):
directions=("H", "H")) directions=("H", "H"))
self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("poly", [gate_offset, port_contact_offset])
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 """
@ -718,7 +827,8 @@ class pbitcell(design.design):
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=port_contact_offest) offset=port_contact_offest)
self.add_path("metal2", [port_contact_offest, bl_offset], width=contact.m1m2.height) self.add_path("metal2",
[port_contact_offest, bl_offset], width=contact.m1m2.height)
for k in range(self.total_ports): for k in range(self.total_ports):
port_contact_offest = right_port_transistors[k].get_pin("D").center() port_contact_offest = right_port_transistors[k].get_pin("D").center()
@ -729,7 +839,8 @@ class pbitcell(design.design):
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=port_contact_offest) offset=port_contact_offest)
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 nmos to gnd. Route inverter pmos to vdd. """ """ Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """
@ -749,7 +860,8 @@ class pbitcell(design.design):
contact_correct = 0.5 * contact.m1m2.height contact_correct = 0.5 * contact.m1m2.height
else: else:
contact_correct = -0.5 * contact.m1m2.height contact_correct = -0.5 * contact.m1m2.height
supply_offset = vector(position.x + contact_correct, self.gnd_position.y) supply_offset = vector(position.x + contact_correct,
self.gnd_position.y)
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=supply_offset, offset=supply_offset,
directions=("H", "H")) directions=("H", "H"))
@ -757,44 +869,71 @@ class pbitcell(design.design):
self.add_path("metal2", [position, supply_offset]) self.add_path("metal2", [position, supply_offset])
# route inverter pmos to vdd # route inverter pmos to vdd
vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x, self.vdd_position.y) vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x,
self.add_path("metal1", [self.inverter_pmos_left.get_pin("S").uc(), vdd_pos_left]) self.vdd_position.y)
self.add_path("metal1",
[self.inverter_pmos_left.get_pin("S").uc(), vdd_pos_left])
vdd_pos_right = vector(self.inverter_nmos_right.get_pin("D").uc().x, self.vdd_position.y) vdd_pos_right = vector(self.inverter_nmos_right.get_pin("D").uc().x,
self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) self.vdd_position.y)
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,
Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) self.cross_couple_lower_ypos)
self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid, Q_pos]) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(),
self.cross_couple_lower_ypos)
self.add_path("metal1",
[self.readwrite_nmos_left[k].get_pin("D").uc(), mid, Q_pos])
mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x,
Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) self.cross_couple_lower_ypos)
self.add_path("metal1", [self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos]) Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(),
self.cross_couple_lower_ypos)
self.add_path("metal1",
[self.readwrite_nmos_right[k].get_pin("S").uc(), 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,
Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) self.cross_couple_lower_ypos)
self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), mid, Q_pos]) Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(),
self.cross_couple_lower_ypos)
self.add_path("metal1",
[self.write_nmos_left[k].get_pin("D").uc(), mid, Q_pos])
mid = vector(self.write_nmos_right[k].get_pin("S").uc().x, self.cross_couple_lower_ypos) mid = vector(self.write_nmos_right[k].get_pin("S").uc().x,
Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(), self.cross_couple_lower_ypos) self.cross_couple_lower_ypos)
self.add_path("metal1", [self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos]) Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(),
self.cross_couple_lower_ypos)
self.add_path("metal1",
[self.write_nmos_right[k].get_pin("S").uc(), 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 # add poly to metal1 contacts for gates of the inverters
left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - self.poly_to_polycontact - 0.5*contact.poly.width, left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x \
- self.poly_to_polycontact - 0.5*contact.poly.width,
self.cross_couple_upper_ypos) self.cross_couple_upper_ypos)
self.add_via_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=left_storage_contact, offset=left_storage_contact,
directions=("H", "H")) directions=("H", "H"))
right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x + self.poly_to_polycontact + 0.5*contact.poly.width, right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \
+ self.poly_to_polycontact + 0.5*contact.poly.width,
self.cross_couple_upper_ypos) self.cross_couple_upper_ypos)
self.add_via_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=right_storage_contact, offset=right_storage_contact,
@ -809,35 +948,48 @@ class pbitcell(design.design):
# add poly to metal1 contacts for gates of read-access transistors # add poly to metal1 contacts for gates of read-access transistors
# route from read-access contacts to inverter contacts on metal1 # 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)
self.add_via_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=port_contact_offset) offset=port_contact_offset)
self.add_path("poly", [self.read_access_nmos_left[k].get_pin("G").uc(), port_contact_offset]) self.add_path("poly",
[self.read_access_nmos_left[k].get_pin("G").uc(), port_contact_offset])
mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x,
self.add_path("metal1", [port_contact_offset, mid, left_storage_contact]) self.cross_couple_upper_ypos)
self.add_path("metal1",
[port_contact_offset, mid, left_storage_contact])
port_contact_offset = self.read_access_nmos_right[k].get_pin("G").uc() + vector(0, self.gate_contact_yoffset - self.poly_extend_active) port_contact_offset = self.read_access_nmos_right[k].get_pin("G").uc() \
+ vector(0,
self.gate_contact_yoffset - self.poly_extend_active)
self.add_via_center(layers=("poly", "contact", "metal1"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=port_contact_offset) offset=port_contact_offset)
self.add_path("poly", [self.read_access_nmos_right[k].get_pin("G").uc(), port_contact_offset]) self.add_path("poly",
[self.read_access_nmos_right[k].get_pin("G").uc(), port_contact_offset])
mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x, self.cross_couple_upper_ypos) mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x,
self.add_path("metal1", [port_contact_offset, mid, right_storage_contact]) self.cross_couple_upper_ypos)
self.add_path("metal1",
[port_contact_offset, mid, right_storage_contact])
def extend_well(self): def extend_well(self):
""" """
Connects wells between ptx modules and places well contacts""" Connects wells between ptx modules and places well contacts
# extend pwell to encompass entire nmos region of the cell up to the height of the tallest nmos transistor """
# extend pwell to encompass entire nmos region of the cell up to the
# height of the tallest nmos transistor
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
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,
@ -846,13 +998,17 @@ class pbitcell(design.design):
# extend nwell to encompass inverter_pmos # extend nwell to encompass inverter_pmos
# calculate offset of the left pmos well # calculate offset of the left pmos well
inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) - self.well_enclose_active inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - self.well_enclose_active - self.well_enclose_active
inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \
+ self.inverter_gap - self.well_enclose_active
# calculate width of the two combined nwells # calculate width of the two combined nwells
# calculate height to encompass nimplant connected to vdd # calculate height to encompass nimplant connected to vdd
well_width = 2*(self.inverter_nmos.active_width + 0.5*self.inverter_to_inverter_spacing) + 2*self.well_enclose_active well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
well_height = self.vdd_position.y - inverter_well_ypos + self.well_enclose_active + drc["minwidth_tx"] + 2 * self.well_enclose_active
well_height = self.vdd_position.y - inverter_well_ypos \
+ self.well_enclose_active + drc["minwidth_tx"]
offset = [inverter_well_xpos, inverter_well_ypos] offset = [inverter_well_xpos, inverter_well_ypos]
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
@ -878,7 +1034,10 @@ class pbitcell(design.design):
well_type="n") well_type="n")
def get_bitcell_pins(self, col, row): def get_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 port in range(self.total_ports): for port in range(self.total_ports):
bitcell_pins.append("bl{0}_{1}".format(port, col)) bitcell_pins.append("bl{0}_{1}".format(port, col))
@ -911,13 +1070,19 @@ class pbitcell(design.design):
return self.rw_br_names + self.w_br_names + self.r_br_names return self.rw_br_names + self.w_br_names + self.r_br_names
def route_rbc_short(self): def route_rbc_short(self):
""" 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()
self.add_path("metal1", [Q_bar_pos, vdd_pos]) self.add_path("metal1", [Q_bar_pos, vdd_pos])
def get_storage_net_names(self): def get_storage_net_names(self):
"""Returns names of storage nodes in bitcell in [non-inverting, inverting] format.""" """
Returns names of storage nodes in bitcell in
[non-inverting, inverting] format.
"""
return self.storage_nets return self.storage_nets
def get_bl_name(self, port=0): def get_bl_name(self, port=0):
@ -933,26 +1098,30 @@ class pbitcell(design.design):
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.") debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port) return "wl{}".format(port)
def get_stage_effort(self, load): def get_stage_effort(self, load):
parasitic_delay = 1 parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node # This accounts for bitline being drained thought the access
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file. # TX and internal node
size = 0.5
# Assumes always a minimum sizes inverter. Could be
# specified in the tech.py file.
cin = 3
#Internal loads due to port configs are halved. This is to account for the size already being halved # Internal loads due to port configs are halved.
#for stacked TXs, but internal loads do not see this size estimation. # This is to account for the size already being halved
write_port_load = self.num_w_ports*logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])/2 # for stacked TXs, but internal loads do not see this size
read_port_load = self.num_r_ports/2 #min size NMOS gate load # estimation.
write_port_load = self.num_w_ports \
* logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) / 2
# min size NMOS gate load
read_port_load = self.num_r_ports / 2
total_load = load + read_port_load + write_port_load total_load = load + read_port_load + write_port_load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False) return logical_effort.logical_effort('bitline',
size,
def analytical_power(self, corner, load): cin,
"""Bitcell power in nW. Only characterizes leakage.""" load + read_port_load,
from tech import spice parasitic_delay,
leakage = spice["bitcell_leakage"] False)
dynamic = 0 #temporary
total_power = self.return_power(dynamic, leakage)
return total_power
def input_load(self): def input_load(self):
""" Return the relative capacitance of the access transistor gates """ """ Return the relative capacitance of the access transistor gates """