diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index cad069a5..b22c9a46 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -5,13 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design import debug import utils -from tech import GDS,layer,parameter,drc -import logical_effort +from tech import GDS, layer +import bitcell_base -class bitcell(design.design): + +class bitcell(bitcell_base.bitcell_base): """ 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 @@ -21,13 +21,15 @@ class bitcell(design.design): pin_names = ["bl", "br", "wl", "vdd", "gnd"] storage_nets = ['Q', 'Qbar'] - type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - (width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) + type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] + (width, height) = utils.get_libcell_size("cell_6t", + GDS["unit"], + layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) def __init__(self, name=""): # Ignore the name argument - design.design.__init__(self, "cell_6t") + bitcell_base.bitcell_base.__init__(self, "cell_6t") debug.info(2, "Create bitcell") self.width = bitcell.width @@ -36,15 +38,9 @@ class bitcell(design.design): self.add_pin_types(self.type_list) 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): """ Creates a list of all wordline pin names """ - row_pins = ["wl"] + row_pins = ["wl"] return row_pins def get_all_bitline_names(self): @@ -64,43 +60,22 @@ class bitcell(design.design): def get_bl_name(self, port=0): """Get bl name""" - debug.check(port==0,"One port for bitcell only.") + debug.check(port == 0, "One port for bitcell only.") return "bl" def get_br_name(self, port=0): """Get bl name""" - debug.check(port==0,"One port for bitcell only.") - return "br" + debug.check(port == 0, "One port for bitcell only.") + return "br" def get_wl_name(self, port=0): """Get wl name""" - debug.check(port==0,"One port for bitcell only.") - return "wl" + debug.check(port == 0, "One port for bitcell only.") + 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): - """Adds edges based on inputs/outputs. Overrides base class function.""" - self.add_graph_edges(graph, port_nets) + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) diff --git a/compiler/bitcells/bitcell_1rw_1r.py b/compiler/bitcells/bitcell_1rw_1r.py index 0d536b38..0280f3cb 100644 --- a/compiler/bitcells/bitcell_1rw_1r.py +++ b/compiler/bitcells/bitcell_1rw_1r.py @@ -5,13 +5,14 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design import debug import utils -from tech import GDS,layer,parameter,drc +from tech import GDS, layer, parameter, drc 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 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"] - 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'] - (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"]) def __init__(self, name=""): # 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") self.width = bitcell_1rw_1r.width @@ -36,15 +40,11 @@ class bitcell_1rw_1r(design.design): self.add_pin_types(self.type_list) 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): - """ 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), "br0_{0}".format(col), "bl1_{0}".format(col), @@ -57,7 +57,7 @@ class bitcell_1rw_1r(design.design): def get_all_wl_names(self): """ Creates a list of all wordline pin names """ - row_pins = ["wl0", "wl1"] + row_pins = ["wl0", "wl1"] return row_pins def get_all_bitline_names(self): @@ -97,52 +97,27 @@ class bitcell_1rw_1r(design.design): def get_bl_name(self, port=0): """Get bl name by port""" - debug.check(port<2,"Two ports for bitcell_1rw_1r only.") + debug.check(port < 2, "Two ports for bitcell_1rw_1r only.") return "bl{}".format(port) def get_br_name(self, port=0): """Get bl name by port""" - debug.check(port<2,"Two ports for bitcell_1rw_1r only.") + debug.check(port < 2, "Two ports for bitcell_1rw_1r only.") return "br{}".format(port) def get_wl_name(self, port=0): """Get wl name by port""" - 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) - 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 to use the add_graph_edges function.""" - pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} - #Edges hardcoded here. Essentially wl->bl/br for both ports. + pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} + # Edges hardcoded here. Essentially wl->bl/br for both ports. # Port 0 edges - graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self) - graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self) + graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self) + graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self) # Port 1 edges - graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) - graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) diff --git a/compiler/bitcells/bitcell_1w_1r.py b/compiler/bitcells/bitcell_1w_1r.py index 7e8c9e75..a92dc75d 100644 --- a/compiler/bitcells/bitcell_1w_1r.py +++ b/compiler/bitcells/bitcell_1w_1r.py @@ -5,13 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design import debug import utils -from tech import GDS,layer,parameter,drc -import logical_effort +from tech import GDS, layer +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 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"] - type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] - storage_nets = ['Q', 'Q_bar'] - (width,height) = utils.get_libcell_size("cell_1w_1r", GDS["unit"], layer["boundary"]) + type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", + "INPUT", "INPUT", "POWER", "GROUND"] + storage_nets = ['Q', 'Q_bar'] + (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"]) def __init__(self, name=""): # 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") self.width = bitcell_1w_1r.width @@ -36,15 +39,11 @@ class bitcell_1w_1r(design.design): self.add_pin_types(self.type_list) 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): - """ 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), "br0_{0}".format(col), "bl1_{0}".format(col), @@ -57,7 +56,7 @@ class bitcell_1w_1r(design.design): def get_all_wl_names(self): """ Creates a list of all wordline pin names """ - row_pins = ["wl0", "wl1"] + row_pins = ["wl0", "wl1"] return row_pins def get_all_bitline_names(self): @@ -105,40 +104,15 @@ class bitcell_1w_1r(design.design): def get_wl_name(self, port=0): """Get wl name by port""" - 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) - 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 to use the add_graph_edges function.""" - pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} - #Edges hardcoded here. Essentially wl->bl/br for both ports. + pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} + # Edges hardcoded here. Essentially wl->bl/br for both ports. # Port 0 edges - graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) - graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) - # Port 1 is a write port, so its timing is not considered here. + graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) + graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self) + # Port 1 is a write port, so its timing is not considered here. diff --git a/compiler/bitcells/bitcell_base.py b/compiler/bitcells/bitcell_base.py new file mode 100644 index 00000000..5265904b --- /dev/null +++ b/compiler/bitcells/bitcell_base.py @@ -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 diff --git a/compiler/bitcells/dummy_bitcell.py b/compiler/bitcells/dummy_bitcell.py index db748203..98da96e2 100644 --- a/compiler/bitcells/dummy_bitcell.py +++ b/compiler/bitcells/dummy_bitcell.py @@ -5,13 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design import debug import utils -from tech import GDS,layer,parameter,drc -import logical_effort +from tech import GDS, layer +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 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"] - (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"]) def __init__(self, name=""): # 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") self.width = dummy_bitcell.width self.height = dummy_bitcell.height 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 diff --git a/compiler/bitcells/dummy_bitcell_1rw_1r.py b/compiler/bitcells/dummy_bitcell_1rw_1r.py index f8986f2d..401e9f85 100644 --- a/compiler/bitcells/dummy_bitcell_1rw_1r.py +++ b/compiler/bitcells/dummy_bitcell_1rw_1r.py @@ -5,12 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design import debug 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. 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. """ pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] - type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"] - (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"]) + type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", + "INPUT", "INPUT", "POWER", "GROUND"] + (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=""): - # Ignore the name argument - design.design.__init__(self, "dummy_cell_1rw_1r") + # Ignore the name argument + bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r") debug.info(2, "Create dummy bitcell 1rw+1r object") 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.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 diff --git a/compiler/bitcells/dummy_bitcell_1w_1r.py b/compiler/bitcells/dummy_bitcell_1w_1r.py index ef451b8c..54192f71 100644 --- a/compiler/bitcells/dummy_bitcell_1w_1r.py +++ b/compiler/bitcells/dummy_bitcell_1w_1r.py @@ -5,12 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design import debug 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. 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. """ pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] - type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] - (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"]) + type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", + "INPUT", "INPUT", "POWER", "GROUND"] + (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=""): - # Ignore the name argument - design.design.__init__(self, "dummy_cell_1w_1r") + # Ignore the name argument + bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r") debug.info(2, "Create dummy bitcell 1w+1r object") 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.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 diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 919a0316..bbfdf942 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -6,15 +6,16 @@ # All rights reserved. # import contact -import design import debug -from tech import drc, parameter, spice +from tech import drc, parameter from vector import vector from ptx import ptx from globals import OPTS 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, 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.dummy_bitcell = dummy_bitcell - design.design.__init__(self, name) - info_string = "{0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, - self.num_w_ports, - self.num_r_ports) - debug.info(2, "create a multi-port bitcell with {}".format(info_string)) + bitcell_base.bitcell_base.__init__(self, name) + 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_r_ports) + debug.info(2, + "create a multi-port bitcell with {}".format(info_string)) self.add_comment(info_string) if self.dummy_bitcell: @@ -86,12 +89,13 @@ class pbitcell(design.design): if self.replica_bitcell: self.route_rbc_short() - # in netlist_only mode, calling offset_all_coordinates or translate_all will not be possible - # this function is not needed to calculate the dimensions of pbitcell in netlist_only mode though + # in netlist_only mode, calling offset_all_coordinates or + # translate_all will not be possible + # this function is not needed to calculate the dimensions + # of pbitcell in netlist_only mode though if not OPTS.netlist_only: self.translate_all(vector(self.leftmost_xpos, self.botmost_ypos)) - def add_pins(self): """ add pins and set names for bitlines and wordlines """ self.rw_bl_names = [] @@ -154,20 +158,20 @@ class pbitcell(design.design): # if there are any read/write ports, # then the inverter nmos is sized based the number of read/write ports if(self.num_rw_ports > 0): - inverter_nmos_width = self.num_rw_ports*parameter["6T_inv_nmos_size"] + inverter_nmos_width = self.num_rw_ports * parameter["6T_inv_nmos_size"] inverter_pmos_width = parameter["6T_inv_pmos_size"] readwrite_nmos_width = parameter["6T_access_size"] write_nmos_width = parameter["6T_access_size"] - read_nmos_width = 2*parameter["6T_inv_pmos_size"] + read_nmos_width = 2 * parameter["6T_inv_pmos_size"] # if there are no read/write ports, # then the inverter nmos is statically sized for the dual port case else: - inverter_nmos_width = 2*parameter["6T_inv_pmos_size"] + inverter_nmos_width = 2 * parameter["6T_inv_pmos_size"] inverter_pmos_width = parameter["6T_inv_pmos_size"] readwrite_nmos_width = parameter["6T_access_size"] write_nmos_width = parameter["6T_access_size"] - read_nmos_width = 2*parameter["6T_inv_pmos_size"] + read_nmos_width = 2 * parameter["6T_inv_pmos_size"] # create ptx for inverter transistors self.inverter_nmos = ptx(width=inverter_nmos_width, @@ -180,7 +184,7 @@ class pbitcell(design.design): # create ptx for readwrite transitors self.readwrite_nmos = ptx(width=readwrite_nmos_width, - tx_type="nmos") + tx_type="nmos") self.add_mod(self.readwrite_nmos) # create ptx for write transitors @@ -197,86 +201,108 @@ class pbitcell(design.design): """ Calculate transistor spacings """ # calculate metal contact extensions over transistor active - readwrite_nmos_contact_extension = 0.5*(self.readwrite_nmos.active_contact.height \ - - self.readwrite_nmos.active_height) - write_nmos_contact_extension = 0.5*(self.write_nmos.active_contact.height \ - - self.write_nmos.active_height) - read_nmos_contact_extension = 0.5*(self.read_nmos.active_contact.height \ - - self.read_nmos.active_height) + readwrite_nmos_contact_extension = 0.5 * \ + (self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height) + write_nmos_contact_extension = 0.5 * \ + (self.write_nmos.active_contact.height - self.write_nmos.active_height) + read_nmos_contact_extension = 0.5 * \ + (self.read_nmos.active_contact.height - self.read_nmos.active_height) max_contact_extension = max(readwrite_nmos_contact_extension, write_nmos_contact_extension, read_nmos_contact_extension) # y-offset for the access transistor's gate contact self.gate_contact_yoffset = max_contact_extension + self.m2_space \ - + 0.5*max(contact.poly.height, contact.m1m2.height) + + 0.5 * max(contact.poly.height, contact.m1m2.height) # y-position of access transistors - self.port_ypos = self.m1_space + 0.5*contact.m1m2.height + self.gate_contact_yoffset + self.port_ypos = self.m1_space + 0.5 * contact.m1m2.height + self.gate_contact_yoffset # y-position of inverter nmos self.inverter_nmos_ypos = self.port_ypos # 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 - m2_constraint = self.bitline_offset + self.m2_space + 0.5*contact.m1m2.height \ - - 0.5*self.readwrite_nmos.active_width - self.write_port_spacing = max(self.active_space, self.m1_space, m2_constraint) + m2_constraint = self.bitline_offset + self.m2_space \ + + 0.5 * contact.m1m2.height \ + - 0.5 * self.readwrite_nmos.active_width + self.write_port_spacing = max(self.active_space, + self.m1_space, + m2_constraint) self.read_port_spacing = self.bitline_offset + self.m2_space # spacing between cross coupled inverters self.inverter_to_inverter_spacing = contact.poly.width + self.m1_space # calculations related to inverter connections - inverter_pmos_contact_extension = 0.5*(self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) - 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.m1_space + inverter_pmos_contact_extension - self.cross_couple_lower_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ - + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ - + 0.5*contact.poly.width - self.cross_couple_upper_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ - + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + inverter_pmos_contact_extension = 0.5 * \ + (self.inverter_pmos.active_contact.height - self.inverter_pmos.active_height) + 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.m1_space + inverter_pmos_contact_extension + self.cross_couple_lower_ypos = self.inverter_nmos_ypos \ + + self.inverter_nmos.active_height \ + + max(self.poly_to_active, + self.m1_space + inverter_nmos_contact_extension) \ + + 0.5 * contact.poly.width + self.cross_couple_upper_ypos = self.inverter_nmos_ypos \ + + self.inverter_nmos.active_height \ + + max(self.poly_to_active, + self.m1_space + inverter_nmos_contact_extension) \ + self.poly_to_polycontact \ - + 1.5*contact.poly.width + + 1.5 * contact.poly.width # spacing between wordlines (and gnd) - self.m1_offset = -0.5*self.m1_width + self.m1_offset = -0.5 * self.m1_width # spacing for vdd - implant_constraint = max(inverter_pmos_contact_extension, 0) + 2*self.implant_enclose_active \ - + 0.5*(contact.well.width - self.m1_width) + implant_constraint = max(inverter_pmos_contact_extension, 0) \ + + 2 * self.implant_enclose_active \ + + 0.5 * (contact.well.width - self.m1_width) metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width # read port dimensions width_reduction = self.read_nmos.active_width - self.read_nmos.get_pin("D").cx() - 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): - """ Calculate positions that describe the edges and dimensions of the cell """ - 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.inverter_gap + self.inverter_pmos.active_height \ + """ + Calculate positions that describe the edges + and dimensions of the cell + """ + 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.inverter_gap \ + + self.inverter_pmos.active_height \ + self.vdd_offset - self.leftmost_xpos = -0.5*self.inverter_to_inverter_spacing - self.inverter_nmos.active_width \ - - self.num_rw_ports*(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.leftmost_xpos = -0.5 * self.inverter_to_inverter_spacing \ + - self.inverter_nmos.active_width \ + - self.num_rw_ports * \ + (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.width = -2*self.leftmost_xpos + self.width = -2 * self.leftmost_xpos 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): """ - Creates the crossed coupled inverters that act as storage for the bitcell. - The stored value of the cell is denoted as "Q", and the inverted value as "Q_bar". + Creates the crossed coupled inverters that act + as storage for the bitcell. + The stored value of the cell is denoted as "Q", + and the inverted value as "Q_bar". """ # create active for nmos self.inverter_nmos_left = self.add_inst(name="inverter_nmos_left", @@ -297,94 +323,126 @@ class pbitcell(design.design): self.connect_inst(["vdd", self.Q, self.Q_bar, "vdd"]) 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 - 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 - inverter_pmos_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + 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 + inverter_pmos_ypos = self.inverter_nmos_ypos \ + + self.inverter_nmos.active_height \ + + self.inverter_gap # create active for nmos - self.inverter_nmos_left.place([left_inverter_xpos, self.inverter_nmos_ypos]) - self.inverter_nmos_right.place([right_inverter_xpos, self.inverter_nmos_ypos]) + self.inverter_nmos_left.place([left_inverter_xpos, + self.inverter_nmos_ypos]) + self.inverter_nmos_right.place([right_inverter_xpos, + self.inverter_nmos_ypos]) # create active for pmos - self.inverter_pmos_left.place([left_inverter_xpos, inverter_pmos_ypos]) - self.inverter_pmos_right.place([right_inverter_xpos, inverter_pmos_ypos]) + self.inverter_pmos_left.place([left_inverter_xpos, + inverter_pmos_ypos]) + self.inverter_pmos_right.place([right_inverter_xpos, + inverter_pmos_ypos]) - # update furthest left and right transistor edges (this will propagate to further transistor offset calculations) + # update furthest left and right transistor edges + # (this will propagate to further transistor offset calculations) 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): """ Routes inputs and outputs of inverters to cross couple them """ # connect input (gate) of inverters - self.add_path("poly", [self.inverter_nmos_left.get_pin("G").uc(), self.inverter_pmos_left.get_pin("G").bc()]) - self.add_path("poly", [self.inverter_nmos_right.get_pin("G").uc(), self.inverter_pmos_right.get_pin("G").bc()]) + self.add_path("poly", + [self.inverter_nmos_left.get_pin("G").uc(), + self.inverter_pmos_left.get_pin("G").bc()]) + self.add_path("poly", + [self.inverter_nmos_right.get_pin("G").uc(), + self.inverter_pmos_right.get_pin("G").bc()]) # connect output (drain/source) of inverters 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) 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) - # add contacts to connect gate poly to drain/source 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, + # add contacts to connect gate poly to drain/source + # 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.add_via_center(layers=("poly", "contact", "metal1"), 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.add_via_center(layers=("poly", "contact", "metal1"), offset=contact_offset_right, - directions=("H","H")) + directions=("H", "H")) # 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]) - 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]) def route_rails(self): """ Adds gnd and vdd rails and connects them to the inverters """ # Add rails for vdd and gnd - gnd_ypos = self.m1_offset - self.total_ports*self.m1_pitch + gnd_ypos = self.m1_offset - self.total_ports * self.m1_pitch self.gnd_position = vector(0, gnd_ypos) self.add_rect_center(layer="metal1", offset=self.gnd_position, width=self.width) - 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 \ - + self.inverter_gap + self.inverter_pmos.active_height \ + vdd_ypos = self.inverter_nmos_ypos \ + + self.inverter_nmos.active_height \ + + self.inverter_gap \ + + self.inverter_pmos.active_height \ + self.vdd_offset self.vdd_position = vector(0, vdd_ypos) self.add_rect_center(layer="metal1", offset=self.vdd_position, width=self.width) - self.add_power_pin("vdd", vector(0,vdd_ypos)) + self.add_power_pin("vdd", vector(0, vdd_ypos)) 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. - A read or write is enabled by setting a Read-Write-Wordline (RWWL) high, subsequently turning on the transistor. - The transistor is connected between a Read-Write-Bitline (RWBL) and the storage component of the cell (Q). - In a write operation, driving RWBL high or low sets the value of the cell. - In a 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. + Creates read/write ports to the bit cell. A differential + pair of transistor can both read and write, like in a 6T cell. + A read or write is enabled by setting a Read-Write-Wordline (RWWL) + high, subsequently turning on the transistor. + The transistor is connected between a Read-Write-Bitline (RWBL) + and the storage component of the cell (Q). + In a write operation, driving RWBL high or low sets the value + of the cell. + In a 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_right = [None] * self.num_rw_ports # iterate over the number of read/write ports - for k in range(0,self.num_rw_ports): + for k in range(0, self.num_rw_ports): bl_name = self.rw_bl_names[k] br_name = self.rw_br_names[k] if self.dummy_bitcell: @@ -398,33 +456,37 @@ class pbitcell(design.design): self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k), 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): """ 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.rwbl_positions = [None] * self.num_rw_ports self.rwbr_positions = [None] * self.num_rw_ports # iterate over the number of read/write ports - for k in range(0,self.num_rw_ports): + for k in range(0, self.num_rw_ports): # calculate read/write transistor offsets left_readwrite_transistor_xpos = self.left_building_edge \ - - (k+1)*self.write_port_spacing \ - - (k+1)*self.readwrite_nmos.active_width + - (k + 1) * self.write_port_spacing \ + - (k + 1) * self.readwrite_nmos.active_width right_readwrite_transistor_xpos = self.right_building_edge \ - + (k+1)*self.write_port_spacing \ - + k*self.readwrite_nmos.active_width + + (k + 1) * self.write_port_spacing \ + + k * self.readwrite_nmos.active_width # 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 - rwwl_ypos = self.m1_offset - k*self.m1_pitch + rwwl_ypos = self.m1_offset - k * self.m1_pitch self.rwwl_positions[k] = vector(0, rwwl_ypos) self.add_layout_pin_rect_center(text=self.rw_wl_names[k], layer="metal1", @@ -432,15 +494,19 @@ class pbitcell(design.design): width=self.width) # 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.add_layout_pin_rect_center(text=self.rw_bl_names[k], layer="metal2", offset=self.rwbl_positions[k], height=self.height) - rwbr_xpos = right_readwrite_transistor_xpos + self.readwrite_nmos.active_width \ - + self.bitline_offset - 0.5*self.m2_width + rwbr_xpos = right_readwrite_transistor_xpos \ + + self.readwrite_nmos.active_width \ + + self.bitline_offset \ + - 0.5 * self.m2_width self.rwbr_positions[k] = vector(rwbr_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.rw_br_names[k], layer="metal2", @@ -449,22 +515,29 @@ class pbitcell(design.design): # update furthest left and right transistor edges 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): """ - Creates write ports in the bit cell. A differential pair of transistors can write only. - A write is enabled by setting a Write-Rowline (WWL) high, subsequently turning on the transistor. - The transistor is connected between a Write-Bitline (WBL) and the storage component of the cell (Q). - 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. + Creates write ports in the bit cell. A differential pair of + transistors can write only. + A write is enabled by setting a Write-Rowline (WWL) high, + subsequently turning on the transistor. + The transistor is connected between a Write-Bitline (WBL) + and the storage component of the cell (Q). + 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_right = [None] * self.num_w_ports # iterate over the number of write ports - for k in range(0,self.num_w_ports): + for k in range(0, self.num_w_ports): bl_name = self.w_bl_names[k] br_name = self.w_br_names[k] if self.dummy_bitcell: @@ -482,31 +555,35 @@ class pbitcell(design.design): def place_write_ports(self): """ 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.wbl_positions = [None] * self.num_w_ports self.wbr_positions = [None] * self.num_w_ports # iterate over the number of write ports - for k in range(0,self.num_w_ports): + for k in range(0, self.num_w_ports): # Add transistors # calculate write transistor offsets left_write_transistor_xpos = self.left_building_edge \ - - (k+1)*self.write_port_spacing \ - - (k+1)*self.write_nmos.active_width + - (k + 1) * self.write_port_spacing \ + - (k + 1) * self.write_nmos.active_width right_write_transistor_xpos = self.right_building_edge \ - + (k+1)*self.write_port_spacing \ - + k*self.write_nmos.active_width + + (k + 1) * self.write_port_spacing \ + + k * self.write_nmos.active_width # 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 - wwl_ypos = rwwl_ypos = self.m1_offset - self.num_rw_ports*self.m1_pitch \ - - k*self.m1_pitch + wwl_ypos = rwwl_ypos = self.m1_offset \ + - self.num_rw_ports * self.m1_pitch \ + - k * self.m1_pitch self.wwl_positions[k] = vector(0, wwl_ypos) self.add_layout_pin_rect_center(text=self.w_wl_names[k], layer="metal1", @@ -514,15 +591,19 @@ class pbitcell(design.design): width=self.width) # 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.add_layout_pin_rect_center(text=self.w_bl_names[k], layer="metal2", offset=self.wbl_positions[k], height=self.height) - wbr_xpos = right_write_transistor_xpos + self.write_nmos.active_width + self.bitline_offset \ - - 0.5*self.m2_width + wbr_xpos = right_write_transistor_xpos \ + + self.write_nmos.active_width \ + + self.bitline_offset \ + - 0.5 * self.m2_width self.wbr_positions[k] = vector(wbr_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.w_br_names[k], layer="metal2", @@ -531,28 +612,38 @@ class pbitcell(design.design): # update furthest left and right transistor edges 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): """ - Creates read ports in the bit cell. A differential pair of ports can read only. - Two transistors function as a read port, denoted as the "read transistor" and the "read-access transistor". - The read transistor is connected to RWL (gate), RBL (drain), and the read-access transistor (source). - The read-access transistor is connected to Q_bar (gate), 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. + Creates read ports in the bit cell. A differential pair + of ports can read only. + Two transistors function as a read port, denoted as the + "read transistor" and the "read-access transistor". + The read transistor is connected to RWL (gate), RBL (drain), + and the read-access transistor (source). + The read-access transistor is connected to Q_bar (gate), + 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_right = [None] * self.num_r_ports self.read_access_nmos_left = [None] * self.num_r_ports self.read_access_nmos_right = [None] * self.num_r_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): bl_name = self.r_bl_names[k] br_name = self.r_br_names[k] if self.dummy_bitcell: @@ -579,38 +670,47 @@ class pbitcell(design.design): def place_read_ports(self): """ 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.rbl_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 - overlap_offset = self.read_nmos.get_pin("D").cx() - self.read_nmos.get_pin("S").cx() + # calculate offset to overlap the drain of the read-access transistor + # with the source of the read transistor + overlap_offset = self.read_nmos.get_pin("D").cx() \ + - self.read_nmos.get_pin("S").cx() # 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): # calculate transistor offsets left_read_transistor_xpos = self.left_building_edge \ - - (k+1)*self.read_port_spacing \ - - (k+1)*self.read_port_width + - (k + 1) * self.read_port_spacing \ + - (k + 1) * self.read_port_width right_read_transistor_xpos = self.right_building_edge \ - + (k+1)*self.read_port_spacing \ - + k*self.read_port_width + + (k + 1) * self.read_port_spacing \ + + k * self.read_port_width # 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 - 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 - rwl_ypos = rwwl_ypos = self.m1_offset - self.num_rw_ports*self.m1_pitch \ - - self.num_w_ports*self.m1_pitch - k*self.m1_pitch + rwl_ypos = rwwl_ypos = self.m1_offset \ + - 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.add_layout_pin_rect_center(text=self.r_wl_names[k], layer="metal1", @@ -618,15 +718,19 @@ class pbitcell(design.design): width=self.width) # 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.add_layout_pin_rect_center(text=self.r_bl_names[k], layer="metal2", offset=self.rbl_positions[k], height=self.height) - rbr_xpos = right_read_transistor_xpos + self.read_port_width + self.bitline_offset \ - - 0.5*self.m2_width + rbr_xpos = right_read_transistor_xpos \ + + self.read_port_width \ + + self.bitline_offset \ + - 0.5 * self.m2_width self.rbr_positions[k] = vector(rbr_xpos, self.center_ypos) self.add_layout_pin_rect_center(text=self.r_br_names[k], layer="metal2", @@ -657,18 +761,22 @@ class pbitcell(design.design): wl_positions.append(self.rwl_positions[k]) wl_positions.append(self.rwl_positions[k]) - for k in range(2*self.total_ports): + for k in range(2 * self.total_ports): gate_offset = port_transistors[k].get_pin("G").bc() - 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) - # 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): self.add_via_center(layers=("poly", "contact", "metal1"), 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: self.add_via_center(layers=("poly", "contact", "metal1"), @@ -678,10 +786,11 @@ class pbitcell(design.design): self.add_via_center(layers=("metal1", "via1", "metal2"), offset=wl_contact_offset, - directions=("H","H")) + directions=("H", "H")) 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): """ Routes read/write transistors to their respective bitlines """ @@ -718,7 +827,8 @@ class pbitcell(design.design): self.add_via_center(layers=("metal1", "via1", "metal2"), 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): 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"), 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): """ Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """ @@ -746,59 +857,87 @@ class pbitcell(design.design): offset=position) if position.x > 0: - contact_correct = 0.5*contact.m1m2.height + contact_correct = 0.5 * contact.m1m2.height else: - contact_correct = -0.5*contact.m1m2.height - supply_offset = vector(position.x + contact_correct, self.gnd_position.y) + contact_correct = -0.5 * contact.m1m2.height + supply_offset = vector(position.x + contact_correct, + self.gnd_position.y) self.add_via_center(layers=("metal1", "via1", "metal2"), offset=supply_offset, - directions=("H","H")) + directions=("H", "H")) self.add_path("metal2", [position, supply_offset]) # route inverter pmos to vdd - vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x, self.vdd_position.y) - self.add_path("metal1", [self.inverter_pmos_left.get_pin("S").uc(), vdd_pos_left]) + vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x, + 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) - self.add_path("metal1", [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) + vdd_pos_right = vector(self.inverter_nmos_right.get_pin("D").uc().x, + self.vdd_position.y) + self.add_path("metal1", + [self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right]) 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): - mid = vector(self.readwrite_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) - Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) - self.add_path("metal1", [self.readwrite_nmos_left[k].get_pin("D").uc(), mid, Q_pos]) + mid = vector(self.readwrite_nmos_left[k].get_pin("D").uc().x, + self.cross_couple_lower_ypos) + Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), + self.cross_couple_lower_ypos) + 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) - 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]) + mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x, + self.cross_couple_lower_ypos) + 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): - """ 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): - mid = vector(self.write_nmos_left[k].get_pin("D").uc().x, self.cross_couple_lower_ypos) - Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), self.cross_couple_lower_ypos) - self.add_path("metal1", [self.write_nmos_left[k].get_pin("D").uc(), mid, Q_pos]) + mid = vector(self.write_nmos_left[k].get_pin("D").uc().x, + self.cross_couple_lower_ypos) + Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(), + self.cross_couple_lower_ypos) + 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) - 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]) + mid = vector(self.write_nmos_right[k].get_pin("S").uc().x, + self.cross_couple_lower_ypos) + 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): - """ Routes read access transistors to the storage component of the bitcell """ + """ + Routes read access transistors to the storage + component of the bitcell + """ # add poly to metal1 contacts for gates of the inverters - left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x - 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.add_via_center(layers=("poly", "contact", "metal1"), 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.add_via_center(layers=("poly", "contact", "metal1"), offset=right_storage_contact, - directions=("H","H")) + directions=("H", "H")) inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos) self.add_path("poly", [left_storage_contact, inverter_gate_offset_left]) @@ -809,35 +948,48 @@ class pbitcell(design.design): # add poly to metal1 contacts for gates of read-access transistors # route from read-access contacts to inverter contacts on metal1 for k in range(self.num_r_ports): - 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"), 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) - self.add_path("metal1", [port_contact_offset, mid, left_storage_contact]) + mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x, + 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"), 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) - self.add_path("metal1", [port_contact_offset, mid, right_storage_contact]) + mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x, + self.cross_couple_upper_ypos) + self.add_path("metal1", + [port_contact_offset, mid, right_storage_contact]) def extend_well(self): """ - 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 + 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 max_nmos_well_height = max(self.inverter_nmos.cell_well_height, self.readwrite_nmos.cell_well_height, self.write_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) self.add_rect(layer="pwell", offset=offset, @@ -846,15 +998,19 @@ class pbitcell(design.design): # extend nwell to encompass inverter_pmos # 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_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap - self.well_enclose_active + inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ + - 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 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_height = self.vdd_position.y - inverter_well_ypos + self.well_enclose_active + drc["minwidth_tx"] + well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ + + 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", offset=offset, width=well_width, @@ -865,7 +1021,7 @@ class pbitcell(design.design): offset = vector(0, self.gnd_position.y) self.add_via_center(layers=("active", "contact", "metal1"), offset=offset, - directions=("H","H"), + directions=("H", "H"), implant_type="p", well_type="p") @@ -873,18 +1029,21 @@ class pbitcell(design.design): offset = vector(0, self.vdd_position.y) self.add_via_center(layers=("active", "contact", "metal1"), offset=offset, - directions=("H","H"), + directions=("H", "H"), implant_type="n", well_type="n") 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 = [] for port in range(self.total_ports): - bitcell_pins.append("bl{0}_{1}".format(port,col)) - bitcell_pins.append("br{0}_{1}".format(port,col)) + bitcell_pins.append("bl{0}_{1}".format(port, col)) + bitcell_pins.append("br{0}_{1}".format(port, col)) for port in range(self.total_ports): - bitcell_pins.append("wl{0}_{1}".format(port,row)) + bitcell_pins.append("wl{0}_{1}".format(port, row)) bitcell_pins.append("vdd") bitcell_pins.append("gnd") return bitcell_pins @@ -911,13 +1070,19 @@ class pbitcell(design.design): return self.rw_br_names + self.w_br_names + self.r_br_names 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() vdd_pos = self.inverter_pmos_right.get_pin("D").center() self.add_path("metal1", [Q_bar_pos, vdd_pos]) 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 def get_bl_name(self, port=0): @@ -930,51 +1095,55 @@ class pbitcell(design.design): def get_wl_name(self, port=0): """Get wl name by port""" - 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) - 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. + # 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 - #Internal loads due to port configs are halved. This is to account for the size already being halved - #for stacked TXs, but internal loads do not see this size estimation. - write_port_load = self.num_w_ports*logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])/2 - read_port_load = self.num_r_ports/2 #min size NMOS gate 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) - - 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 + # Internal loads due to port configs are halved. + # This is to account for the size already being halved + # for stacked TXs, but internal loads do not see this size + # 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 + return logical_effort.logical_effort('bitline', + size, + cin, + load + read_port_load, + parasitic_delay, + False) def input_load(self): - """Return the relative capacitance of the access transistor gates""" + """ Return the relative capacitance of the access transistor gates """ # FIXME: This applies to bitline capacitances as well. # pbitcell uses the different sizing for the port access tx's. Not accounted for in this model. access_tx_cin = self.readwrite_nmos.get_cin() - return 2*access_tx_cin + 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 to graph for pbitcell. Only readwrite and read ports.""" if self.dummy_bitcell: return - pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} # Edges added wl->bl, wl->br for every port except write ports rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names) r_pin_names = zip(self.rw_wl_names, self.rw_bl_names, self.rw_br_names) - for pin_zip in [rw_pin_names, r_pin_names]: - for wl,bl,br in pin_zip: - graph.add_edge(pin_dict[wl],pin_dict[bl], self) - graph.add_edge(pin_dict[wl],pin_dict[br], self) + for pin_zip in [rw_pin_names, r_pin_names]: + for wl, bl, br in pin_zip: + graph.add_edge(pin_dict[wl], pin_dict[bl], self) + graph.add_edge(pin_dict[wl], pin_dict[br], self)