mirror of https://github.com/VLSIDA/OpenRAM.git
Refactor bitcell to bitcell_base. Pep8 format bitcells.
This commit is contained in:
parent
d722311822
commit
67c768d22c
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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."""
|
||||||
|
|
|
||||||
|
|
@ -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."""
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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 """
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue