mirror of https://github.com/VLSIDA/OpenRAM.git
Fixed merge conflict.
This commit is contained in:
commit
2573b5f48b
|
|
@ -18,13 +18,16 @@ class timing_graph():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.graph = defaultdict(set)
|
self.graph = defaultdict(set)
|
||||||
self.all_paths = []
|
self.all_paths = []
|
||||||
|
self.edge_mods = {}
|
||||||
|
|
||||||
def add_edge(self, src_node, dest_node):
|
def add_edge(self, src_node, dest_node, edge_mod):
|
||||||
"""Adds edge to graph. Nodes added as well if they do not exist."""
|
"""Adds edge to graph. Nodes added as well if they do not exist.
|
||||||
|
Module which defines the edge must be provided for timing information."""
|
||||||
|
|
||||||
src_node = src_node.lower()
|
src_node = src_node.lower()
|
||||||
dest_node = dest_node.lower()
|
dest_node = dest_node.lower()
|
||||||
self.graph[src_node].add(dest_node)
|
self.graph[src_node].add(dest_node)
|
||||||
|
self.edge_mods[(src_node, dest_node)] = edge_mod
|
||||||
|
|
||||||
def add_node(self, node):
|
def add_node(self, node):
|
||||||
"""Add node to graph with no edges"""
|
"""Add node to graph with no edges"""
|
||||||
|
|
@ -34,8 +37,8 @@ class timing_graph():
|
||||||
self.graph[node] = set()
|
self.graph[node] = set()
|
||||||
|
|
||||||
def remove_edges(self, node):
|
def remove_edges(self, node):
|
||||||
|
|
||||||
"""Helper function to remove edges, useful for removing vdd/gnd"""
|
"""Helper function to remove edges, useful for removing vdd/gnd"""
|
||||||
|
|
||||||
node = node.lower()
|
node = node.lower()
|
||||||
self.graph[node] = set()
|
self.graph[node] = set()
|
||||||
|
|
||||||
|
|
@ -95,6 +98,32 @@ class timing_graph():
|
||||||
path.pop()
|
path.pop()
|
||||||
visited.remove(cur_node)
|
visited.remove(cur_node)
|
||||||
|
|
||||||
|
def get_timing(self, path, corner, slew, load):
|
||||||
|
"""Returns the analytical delays in the input path"""
|
||||||
|
|
||||||
|
if len(path) == 0:
|
||||||
|
return []
|
||||||
|
|
||||||
|
delays = []
|
||||||
|
cur_slew = slew
|
||||||
|
for i in range(len(path)-1):
|
||||||
|
|
||||||
|
path_edge_mod = self.edge_mods[(path[i], path[i+1])]
|
||||||
|
|
||||||
|
# On the output of the current stage, get COUT from all other mods connected
|
||||||
|
cout = 0
|
||||||
|
for node in self.graph[path[i+1]]:
|
||||||
|
output_edge_mod = self.edge_mods[(path[i+1], node)]
|
||||||
|
cout+=output_edge_mod.get_cin()
|
||||||
|
# If at the last output, include the final output load
|
||||||
|
if i == len(path)-2:
|
||||||
|
cout+=load
|
||||||
|
|
||||||
|
delays.append(path_edge_mod.analytical_delay(corner, slew, cout))
|
||||||
|
cur_slew = delays[-1].slew
|
||||||
|
|
||||||
|
return delays
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
for inp in input_pins+inout_pins:
|
for inp in input_pins+inout_pins:
|
||||||
for out in output_pins+inout_pins:
|
for out in output_pins+inout_pins:
|
||||||
if inp != out: #do not add self loops
|
if inp != out: #do not add self loops
|
||||||
graph.add_edge(pin_dict[inp], pin_dict[out])
|
graph.add_edge(pin_dict[inp], pin_dict[out], self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import tech
|
||||||
from delay_data import *
|
from delay_data import *
|
||||||
from wire_spice_model import *
|
from wire_spice_model import *
|
||||||
from power_data import *
|
from power_data import *
|
||||||
|
import logical_effort
|
||||||
|
|
||||||
class spice():
|
class spice():
|
||||||
"""
|
"""
|
||||||
|
|
@ -304,13 +304,47 @@ class spice():
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
def analytical_delay(self, corner, slew, load=0.0):
|
||||||
"""Inform users undefined delay module while building new modules"""
|
"""Inform users undefined delay module while building new modules"""
|
||||||
debug.warning("Design Class {0} delay function needs to be defined"
|
|
||||||
|
# FIXME: Slew is not used in the model right now. Can be added heuristically as linear factor
|
||||||
|
relative_cap = logical_effort.convert_farad_to_relative_c(load)
|
||||||
|
stage_effort = self.get_stage_effort(relative_cap)
|
||||||
|
|
||||||
|
# If it fails, then keep running with a valid object.
|
||||||
|
if stage_effort == None:
|
||||||
|
return delay_data(0.0, 0.0)
|
||||||
|
|
||||||
|
abs_delay = stage_effort.get_absolute_delay()
|
||||||
|
corner_delay = self.apply_corners_analytically(abs_delay, corner)
|
||||||
|
SLEW_APPROXIMATION = 0.1
|
||||||
|
corner_slew = SLEW_APPROXIMATION*corner_delay
|
||||||
|
return delay_data(corner_delay, corner_slew)
|
||||||
|
|
||||||
|
def get_stage_effort(self, corner, slew, load=0.0):
|
||||||
|
"""Inform users undefined delay module while building new modules"""
|
||||||
|
debug.warning("Design Class {0} logical effort function needs to be defined"
|
||||||
.format(self.__class__.__name__))
|
.format(self.__class__.__name__))
|
||||||
debug.warning("Class {0} name {1}"
|
debug.warning("Class {0} name {1}"
|
||||||
.format(self.__class__.__name__,
|
.format(self.__class__.__name__,
|
||||||
self.name))
|
self.name))
|
||||||
# return 0 to keep code running while building
|
return None
|
||||||
return delay_data(0.0, 0.0)
|
|
||||||
|
def get_cin(self):
|
||||||
|
"""Returns input load in Femto-Farads. All values generated using
|
||||||
|
relative capacitance function then converted based on tech file parameter."""
|
||||||
|
|
||||||
|
# Override this function within a module if a more accurate input capacitance is needed.
|
||||||
|
# Input/outputs with differing capacitances is not implemented.
|
||||||
|
relative_cap = self.input_load()
|
||||||
|
return logical_effort.convert_relative_c_to_farad(relative_cap)
|
||||||
|
|
||||||
|
def input_load(self):
|
||||||
|
"""Inform users undefined relative capacitance functions used for analytical delays."""
|
||||||
|
debug.warning("Design Class {0} input capacitance function needs to be defined"
|
||||||
|
.format(self.__class__.__name__))
|
||||||
|
debug.warning("Class {0} name {1}"
|
||||||
|
.format(self.__class__.__name__,
|
||||||
|
self.name))
|
||||||
|
return 0
|
||||||
|
|
||||||
def cal_delay_with_rc(self, corner, r, c ,slew, swing = 0.5):
|
def cal_delay_with_rc(self, corner, r, c ,slew, swing = 0.5):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ 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 analytical_delay(self, corner, slew, load=0, swing = 0.5):
|
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
|
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.
|
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||||
|
|
@ -94,10 +94,10 @@ class bitcell(design.design):
|
||||||
debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets))
|
debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_wl_cin(self):
|
def input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""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: This applies to bitline capacitances as well.
|
||||||
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
||||||
return 2*access_tx_cin
|
return 2*access_tx_cin
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ 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 analytical_delay(self, corner, slew, load=0, swing = 0.5):
|
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
|
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.
|
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||||
|
|
@ -118,11 +118,11 @@ class bitcell_1rw_1r(design.design):
|
||||||
total_power = self.return_power(dynamic, leakage)
|
total_power = self.return_power(dynamic, leakage)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def get_wl_cin(self):
|
def input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""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: This applies to bitline capacitances as well.
|
||||||
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
|
# 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"]
|
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
||||||
return 2*access_tx_cin
|
return 2*access_tx_cin
|
||||||
|
|
||||||
|
|
@ -141,8 +141,8 @@ class bitcell_1rw_1r(design.design):
|
||||||
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 hardcoded here. Essentially wl->bl/br for both ports.
|
#Edges hardcoded here. Essentially wl->bl/br for both ports.
|
||||||
# Port 0 edges
|
# Port 0 edges
|
||||||
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"])
|
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self)
|
||||||
graph.add_edge(pin_dict["wl0"], pin_dict["br0"])
|
graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self)
|
||||||
# Port 1 edges
|
# Port 1 edges
|
||||||
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"])
|
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
|
||||||
graph.add_edge(pin_dict["wl1"], pin_dict["br1"])
|
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ 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 analytical_delay(self, corner, slew, load=0, swing = 0.5):
|
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
|
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.
|
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||||
|
|
@ -116,11 +116,11 @@ class bitcell_1w_1r(design.design):
|
||||||
total_power = self.return_power(dynamic, leakage)
|
total_power = self.return_power(dynamic, leakage)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def get_wl_cin(self):
|
def input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""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: This applies to bitline capacitances as well.
|
||||||
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
|
# 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"]
|
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
||||||
return 2*access_tx_cin
|
return 2*access_tx_cin
|
||||||
|
|
||||||
|
|
@ -139,6 +139,6 @@ class bitcell_1w_1r(design.design):
|
||||||
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 hardcoded here. Essentially wl->bl/br for both ports.
|
#Edges hardcoded here. Essentially wl->bl/br for both ports.
|
||||||
# Port 0 edges
|
# Port 0 edges
|
||||||
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"])
|
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
|
||||||
graph.add_edge(pin_dict["wl1"], pin_dict["br1"])
|
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
|
||||||
# Port 1 is a write port, so its timing is not considered here.
|
# Port 1 is a write port, so its timing is not considered here.
|
||||||
|
|
|
||||||
|
|
@ -934,7 +934,7 @@ class pbitcell(design.design):
|
||||||
return "wl{}".format(port)
|
return "wl{}".format(port)
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
|
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
|
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.
|
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
|
||||||
|
|
@ -954,9 +954,11 @@ class pbitcell(design.design):
|
||||||
total_power = self.return_power(dynamic, leakage)
|
total_power = self.return_power(dynamic, leakage)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def get_wl_cin(self):
|
def input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""Return the relative capacitance of the access transistor gates"""
|
||||||
#pbitcell uses the different sizing for the port access tx's. Not accounted for in this model.
|
|
||||||
|
# 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()
|
access_tx_cin = self.readwrite_nmos.get_cin()
|
||||||
return 2*access_tx_cin
|
return 2*access_tx_cin
|
||||||
|
|
||||||
|
|
@ -973,6 +975,6 @@ class pbitcell(design.design):
|
||||||
|
|
||||||
for pin_zip in [rw_pin_names, r_pin_names]:
|
for pin_zip in [rw_pin_names, r_pin_names]:
|
||||||
for wl,bl,br in pin_zip:
|
for wl,bl,br in pin_zip:
|
||||||
graph.add_edge(pin_dict[wl],pin_dict[bl])
|
graph.add_edge(pin_dict[wl],pin_dict[bl], self)
|
||||||
graph.add_edge(pin_dict[wl],pin_dict[br])
|
graph.add_edge(pin_dict[wl],pin_dict[br], self)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,20 @@ class replica_bitcell(design.design):
|
||||||
self.pin_map = replica_bitcell.pin_map
|
self.pin_map = replica_bitcell.pin_map
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
|
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 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 analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Bitcell power in nW. Only characterizes leakage."""
|
"""Bitcell power in nW. Only characterizes leakage."""
|
||||||
from tech import spice
|
from tech import spice
|
||||||
|
|
@ -40,13 +54,6 @@ class replica_bitcell(design.design):
|
||||||
total_power = self.return_power(dynamic, leakage)
|
total_power = self.return_power(dynamic, leakage)
|
||||||
return total_power
|
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
|
|
||||||
|
|
||||||
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)
|
||||||
|
|
@ -32,11 +32,18 @@ class replica_bitcell_1rw_1r(design.design):
|
||||||
self.pin_map = replica_bitcell_1rw_1r.pin_map
|
self.pin_map = replica_bitcell_1rw_1r.pin_map
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def get_wl_cin(self):
|
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 input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""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: This applies to bitline capacitances as well.
|
||||||
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
|
# 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"]
|
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
||||||
return 2*access_tx_cin
|
return 2*access_tx_cin
|
||||||
|
|
||||||
|
|
@ -46,8 +53,8 @@ class replica_bitcell_1rw_1r(design.design):
|
||||||
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 hardcoded here. Essentially wl->bl/br for both ports.
|
#Edges hardcoded here. Essentially wl->bl/br for both ports.
|
||||||
# Port 0 edges
|
# Port 0 edges
|
||||||
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"])
|
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self)
|
||||||
graph.add_edge(pin_dict["wl0"], pin_dict["br0"])
|
graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self)
|
||||||
# Port 1 edges
|
# Port 1 edges
|
||||||
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"])
|
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
|
||||||
graph.add_edge(pin_dict["wl1"], pin_dict["br1"])
|
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
|
||||||
|
|
@ -32,11 +32,18 @@ class replica_bitcell_1w_1r(design.design):
|
||||||
self.pin_map = replica_bitcell_1w_1r.pin_map
|
self.pin_map = replica_bitcell_1w_1r.pin_map
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def get_wl_cin(self):
|
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 input_load(self):
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
"""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: This applies to bitline capacitances as well.
|
||||||
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
|
# 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"]
|
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
||||||
return 2*access_tx_cin
|
return 2*access_tx_cin
|
||||||
|
|
||||||
|
|
@ -47,6 +54,6 @@ class replica_bitcell_1w_1r(design.design):
|
||||||
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 hardcoded here. Essentially wl->bl/br for the read port.
|
#Edges hardcoded here. Essentially wl->bl/br for the read port.
|
||||||
# Port 1 edges
|
# Port 1 edges
|
||||||
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"])
|
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
|
||||||
graph.add_edge(pin_dict["wl1"], pin_dict["br1"])
|
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
|
||||||
# Port 0 is a write port, so its timing is not considered here.
|
# Port 0 is a write port, so its timing is not considered here.
|
||||||
|
|
@ -85,7 +85,3 @@ class replica_pbitcell(design.design):
|
||||||
self.copy_layout_pin(self.prbc_inst, "vdd")
|
self.copy_layout_pin(self.prbc_inst, "vdd")
|
||||||
self.copy_layout_pin(self.prbc_inst, "gnd")
|
self.copy_layout_pin(self.prbc_inst, "gnd")
|
||||||
|
|
||||||
def get_wl_cin(self):
|
|
||||||
"""Return the relative capacitance of the access transistor gates"""
|
|
||||||
#This module is made using a pbitcell. Get the cin from that module
|
|
||||||
return self.prbc.get_wl_cin()
|
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,7 @@ class delay(simulation):
|
||||||
def create_graph(self):
|
def create_graph(self):
|
||||||
"""Creates timing graph to generate the timing paths for the SRAM output."""
|
"""Creates timing graph to generate the timing paths for the SRAM output."""
|
||||||
|
|
||||||
self.sram.bank.bitcell_array.init_graph_params() # Removes previous bit exclusions
|
self.sram.bank.bitcell_array.bitcell_array.init_graph_params() # Removes previous bit exclusions
|
||||||
self.sram.bank.bitcell_array.graph_exclude_bits(self.wordline_row, self.bitline_column)
|
self.sram.bank.bitcell_array.graph_exclude_bits(self.wordline_row, self.bitline_column)
|
||||||
|
|
||||||
# Generate new graph every analysis as edges might change depending on test bit
|
# Generate new graph every analysis as edges might change depending on test bit
|
||||||
|
|
@ -800,6 +800,8 @@ class delay(simulation):
|
||||||
|
|
||||||
debug.info(2,"{}={}".format(meas.name,val))
|
debug.info(2,"{}={}".format(meas.name,val))
|
||||||
|
|
||||||
|
dout_success = True
|
||||||
|
bl_success = False
|
||||||
for meas in self.dout_volt_meas:
|
for meas in self.dout_volt_meas:
|
||||||
val = meas.retrieve_measure(port=port)
|
val = meas.retrieve_measure(port=port)
|
||||||
debug.info(2,"{}={}".format(meas.name, val))
|
debug.info(2,"{}={}".format(meas.name, val))
|
||||||
|
|
@ -813,15 +815,7 @@ class delay(simulation):
|
||||||
dout_success = False
|
dout_success = False
|
||||||
debug.info(1, "Debug measurement failed. Value {}V was read on read 0 cycle.".format(val))
|
debug.info(1, "Debug measurement failed. Value {}V was read on read 0 cycle.".format(val))
|
||||||
bl_success = self.check_bitline_meas(br_vals[sram_op.READ_ONE], bl_vals[sram_op.READ_ONE])
|
bl_success = self.check_bitline_meas(br_vals[sram_op.READ_ONE], bl_vals[sram_op.READ_ONE])
|
||||||
elif meas.meta_str == sram_op.READ_ONE and val > self.vdd_voltage*0.9:
|
|
||||||
dout_success = True
|
|
||||||
bl_success = True
|
|
||||||
elif meas.meta_str == sram_op.READ_ZERO and val < self.vdd_voltage*0.1:
|
|
||||||
dout_success = True
|
|
||||||
bl_success = True
|
|
||||||
else:
|
|
||||||
dout_success = False
|
|
||||||
bl_success = False
|
|
||||||
# If the bitlines have a correct value while the output does not then that is a
|
# If the bitlines have a correct value while the output does not then that is a
|
||||||
# sen error. FIXME: there are other checks that can be done to solidfy this conclusion.
|
# sen error. FIXME: there are other checks that can be done to solidfy this conclusion.
|
||||||
if not dout_success and bl_success:
|
if not dout_success and bl_success:
|
||||||
|
|
@ -1275,38 +1269,70 @@ class delay(simulation):
|
||||||
# Add test cycle of read/write port pair. One port could have been used already, but the other has not.
|
# Add test cycle of read/write port pair. One port could have been used already, but the other has not.
|
||||||
self.gen_test_cycles_one_port(cur_read_port, cur_write_port)
|
self.gen_test_cycles_one_port(cur_read_port, cur_write_port)
|
||||||
|
|
||||||
|
def sum_delays(self, delays):
|
||||||
|
"""Adds the delays (delay_data objects) so the correct slew is maintained"""
|
||||||
|
|
||||||
|
delay = delays[0]
|
||||||
|
for i in range(1, len(delays)):
|
||||||
|
delay+=delays[i]
|
||||||
|
return delay
|
||||||
|
|
||||||
def analytical_delay(self, slews, loads):
|
def analytical_delay(self, slews, loads):
|
||||||
"""
|
"""
|
||||||
Return the analytical model results for the SRAM.
|
Return the analytical model results for the SRAM.
|
||||||
"""
|
"""
|
||||||
if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0:
|
if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0:
|
||||||
debug.warning("Analytical characterization results are not supported for multiport.")
|
debug.warning("Analytical characterization results are not supported for multiport.")
|
||||||
|
|
||||||
|
# Probe set to 0th bit, does not matter for analytical delay.
|
||||||
|
self.set_probe('0', 0)
|
||||||
|
self.create_graph()
|
||||||
|
self.set_internal_spice_names()
|
||||||
self.create_measurement_names()
|
self.create_measurement_names()
|
||||||
power = self.analytical_power(slews, loads)
|
|
||||||
|
port = self.read_ports[0]
|
||||||
|
self.graph.get_all_paths('{}{}'.format(tech.spice["clk"], port),
|
||||||
|
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
|
||||||
|
|
||||||
|
# Select the path with the bitline (bl)
|
||||||
|
bl_name,br_name = self.get_bl_name(self.graph.all_paths, port)
|
||||||
|
bl_path = [path for path in self.graph.all_paths if bl_name in path][0]
|
||||||
|
|
||||||
|
# Set delay/power for slews and loads
|
||||||
port_data = self.get_empty_measure_data_dict()
|
port_data = self.get_empty_measure_data_dict()
|
||||||
relative_loads = [logical_effort.convert_farad_to_relative_c(c_farad) for c_farad in loads]
|
power = self.analytical_power(slews, loads)
|
||||||
|
debug.info(1,'Slew, Load, Delay(ns), Slew(ns)')
|
||||||
|
max_delay = 0.0
|
||||||
for slew in slews:
|
for slew in slews:
|
||||||
for load in relative_loads:
|
for load in loads:
|
||||||
self.set_load_slew(load,slew)
|
# Calculate delay based on slew and load
|
||||||
bank_delay = self.sram.analytical_delay(self.corner, self.slew,self.load)
|
path_delays = self.graph.get_timing(bl_path, self.corner, slew, load)
|
||||||
|
|
||||||
|
total_delay = self.sum_delays(path_delays)
|
||||||
|
max_delay = max(max_delay, total_delay.delay)
|
||||||
|
debug.info(1,'{}, {}, {}, {}'.format(slew,load,total_delay.delay/1e3, total_delay.slew/1e3))
|
||||||
|
|
||||||
|
# Delay is only calculated on a single port and replicated for now.
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
for mname in self.delay_meas_names+self.power_meas_names:
|
for mname in self.delay_meas_names+self.power_meas_names:
|
||||||
if "power" in mname:
|
if "power" in mname:
|
||||||
port_data[port][mname].append(power.dynamic)
|
port_data[port][mname].append(power.dynamic)
|
||||||
elif "delay" in mname:
|
elif "delay" in mname and port in self.read_ports:
|
||||||
port_data[port][mname].append(bank_delay[port].delay/1e3)
|
port_data[port][mname].append(total_delay.delay/1e3)
|
||||||
elif "slew" in mname:
|
elif "slew" in mname and port in self.read_ports:
|
||||||
port_data[port][mname].append(bank_delay[port].slew/1e3)
|
port_data[port][mname].append(total_delay.slew/1e3)
|
||||||
else:
|
else:
|
||||||
debug.error("Measurement name not recognized: {}".format(mname),1)
|
debug.error("Measurement name not recognized: {}".format(mname),1)
|
||||||
|
|
||||||
|
# Estimate the period as double the delay with margin
|
||||||
period_margin = 0.1
|
period_margin = 0.1
|
||||||
risefall_delay = bank_delay[self.read_ports[0]].delay/1e3
|
sram_data = { "min_period":(max_delay/1e3)*2*period_margin,
|
||||||
sram_data = { "min_period":risefall_delay*2*period_margin,
|
|
||||||
"leakage_power": power.leakage}
|
"leakage_power": power.leakage}
|
||||||
|
|
||||||
debug.info(2,"SRAM Data:\n{}".format(sram_data))
|
debug.info(2,"SRAM Data:\n{}".format(sram_data))
|
||||||
debug.info(2,"Port Data:\n{}".format(port_data))
|
debug.info(2,"Port Data:\n{}".format(port_data))
|
||||||
return (sram_data,port_data)
|
|
||||||
|
|
||||||
|
return (sram_data,port_data)
|
||||||
|
|
||||||
def analytical_power(self, slews, loads):
|
def analytical_power(self, slews, loads):
|
||||||
"""Get the dynamic and leakage power from the SRAM"""
|
"""Get the dynamic and leakage power from the SRAM"""
|
||||||
|
|
|
||||||
|
|
@ -23,23 +23,23 @@ class logical_effort():
|
||||||
self.cin = cin
|
self.cin = cin
|
||||||
self.cout = cout
|
self.cout = cout
|
||||||
self.logical_effort = (self.cin/size)/logical_effort.min_inv_cin
|
self.logical_effort = (self.cin/size)/logical_effort.min_inv_cin
|
||||||
self.eletrical_effort = self.cout/self.cin
|
self.electrical_effort = self.cout/self.cin
|
||||||
self.parasitic_scale = parasitic
|
self.parasitic_scale = parasitic
|
||||||
self.is_rise = out_is_rise
|
self.is_rise = out_is_rise
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Name={}, g={}, h={}, p={}*pinv, rise_delay={}".format(self.name,
|
return "Name={}, g={}, h={}, p={}*pinv, rise_delay={}".format(self.name,
|
||||||
self.logical_effort,
|
self.logical_effort,
|
||||||
self.eletrical_effort,
|
self.electrical_effort,
|
||||||
self.parasitic_scale,
|
self.parasitic_scale,
|
||||||
self.is_rise
|
self.is_rise
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_stage_effort(self):
|
def get_stage_effort(self):
|
||||||
return self.logical_effort*self.eletrical_effort
|
return self.logical_effort*self.electrical_effort
|
||||||
|
|
||||||
def get_parasitic_delay(self):
|
def get_parasitic_delay(self):
|
||||||
return logical_effort.pinv * self.parasitic_scale
|
return logical_effort.pinv*self.parasitic_scale
|
||||||
|
|
||||||
def get_stage_delay(self):
|
def get_stage_delay(self):
|
||||||
return self.get_stage_effort()+self.get_parasitic_delay()
|
return self.get_stage_effort()+self.get_parasitic_delay()
|
||||||
|
|
@ -79,3 +79,6 @@ def convert_farad_to_relative_c(c_farad):
|
||||||
"""Converts capacitance in Femto-Farads to relative capacitance."""
|
"""Converts capacitance in Femto-Farads to relative capacitance."""
|
||||||
return c_farad*parameter['cap_relative_per_ff']
|
return c_farad*parameter['cap_relative_per_ff']
|
||||||
|
|
||||||
|
def convert_relative_c_to_farad(c_relative):
|
||||||
|
"""Converts capacitance in logical effort relative units to Femto-Farads."""
|
||||||
|
return c_relative/parameter['cap_relative_per_ff']
|
||||||
|
|
@ -82,7 +82,7 @@ class bank(design.design):
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
self.add_pin("dout{0}_{1}".format(port,bit),"OUTPUT")
|
self.add_pin("dout{0}_{1}".format(port,bit),"OUTPUT")
|
||||||
for port in self.read_ports:
|
for port in self.all_ports:
|
||||||
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]),"OUTPUT")
|
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]),"OUTPUT")
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
|
|
@ -128,7 +128,6 @@ class bank(design.design):
|
||||||
def route_rbl(self,port):
|
def route_rbl(self,port):
|
||||||
""" Route the rbl_bl and rbl_wl """
|
""" Route the rbl_bl and rbl_wl """
|
||||||
|
|
||||||
if self.port_data[port].has_rbl():
|
|
||||||
bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
||||||
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
|
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
|
||||||
self.add_layout_pin(text="rbl_bl{0}".format(port),
|
self.add_layout_pin(text="rbl_bl{0}".format(port),
|
||||||
|
|
@ -202,10 +201,7 @@ class bank(design.design):
|
||||||
|
|
||||||
# LOWER RIGHT QUADRANT
|
# LOWER RIGHT QUADRANT
|
||||||
# Below the bitcell array
|
# Below the bitcell array
|
||||||
if self.port_data[port].has_rbl():
|
|
||||||
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width,0)
|
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width,0)
|
||||||
else:
|
|
||||||
self.port_data_offsets[port] = vector(self.main_bitcell_array_left,0)
|
|
||||||
|
|
||||||
# UPPER LEFT QUADRANT
|
# UPPER LEFT QUADRANT
|
||||||
# To the left of the bitcell array
|
# To the left of the bitcell array
|
||||||
|
|
@ -364,36 +360,15 @@ class bank(design.design):
|
||||||
self.add_mod(self.port_address)
|
self.add_mod(self.port_address)
|
||||||
|
|
||||||
|
|
||||||
# The number of replica lines depends on the port configuration
|
self.port_rbl_map = self.all_ports
|
||||||
rbl_counts = [self.read_ports.count(p) for p in self.all_ports]
|
self.num_rbl = len(self.all_ports)
|
||||||
self.num_rbl = sum(rbl_counts)
|
|
||||||
|
|
||||||
# The replica array indices always start at 0, so this will map them to
|
|
||||||
# the correct SRAM port
|
|
||||||
# (e.g. if port 0 is w, then port 1 will use RBL 0 in replica bitcell array
|
|
||||||
# because write ports don't use an RBL)
|
|
||||||
self.port_rbl_map = {}
|
|
||||||
index = 0
|
|
||||||
for (i,num) in enumerate(rbl_counts):
|
|
||||||
if num>0:
|
|
||||||
self.port_rbl_map[i]=index
|
|
||||||
index += 1
|
|
||||||
if len(rbl_counts)<2:
|
|
||||||
rbl_counts.append(0)
|
|
||||||
|
|
||||||
|
|
||||||
# Which bitcell port should be used in the RBL
|
|
||||||
# For now (since only 2 ports), if port 0 is not a read port, skip it in the RBLs
|
|
||||||
bitcell_ports=list(range(len(self.read_ports)))
|
|
||||||
if 0 not in self.read_ports:
|
|
||||||
bitcell_ports = [x+1 for x in bitcell_ports]
|
|
||||||
|
|
||||||
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
|
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
|
||||||
cols=self.num_cols,
|
cols=self.num_cols,
|
||||||
rows=self.num_rows,
|
rows=self.num_rows,
|
||||||
left_rbl=rbl_counts[0],
|
left_rbl=1,
|
||||||
right_rbl=rbl_counts[1],
|
right_rbl=1 if len(self.all_ports)>1 else 0,
|
||||||
bitcell_ports=bitcell_ports)
|
bitcell_ports=self.all_ports)
|
||||||
self.add_mod(self.bitcell_array)
|
self.add_mod(self.bitcell_array)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -421,7 +396,6 @@ class bank(design.design):
|
||||||
for wordline in self.wl_names:
|
for wordline in self.wl_names:
|
||||||
temp.append("{0}_{1}".format(wordline,row))
|
temp.append("{0}_{1}".format(wordline,row))
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
if self.port_data[port].has_rbl():
|
|
||||||
temp.append("wl_en{0}".format(port))
|
temp.append("wl_en{0}".format(port))
|
||||||
temp.append("vdd")
|
temp.append("vdd")
|
||||||
temp.append("gnd")
|
temp.append("gnd")
|
||||||
|
|
@ -442,7 +416,6 @@ class bank(design.design):
|
||||||
mod=self.port_data[port])
|
mod=self.port_data[port])
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
if self.port_data[port].has_rbl():
|
|
||||||
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
||||||
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port])
|
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port])
|
||||||
temp.append(rbl_bl_name)
|
temp.append(rbl_bl_name)
|
||||||
|
|
@ -710,7 +683,6 @@ class bank(design.design):
|
||||||
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
|
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
|
||||||
|
|
||||||
# Connect the replica bitlines
|
# Connect the replica bitlines
|
||||||
if self.port_data[port].has_rbl():
|
|
||||||
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
||||||
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port])
|
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port])
|
||||||
self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl")
|
self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl")
|
||||||
|
|
@ -943,7 +915,6 @@ class bank(design.design):
|
||||||
connection = []
|
connection = []
|
||||||
connection.append((self.prefix+"p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar").lc()))
|
connection.append((self.prefix+"p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar").lc()))
|
||||||
|
|
||||||
if port in self.read_ports:
|
|
||||||
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
|
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
|
||||||
connection.append((self.prefix+"wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
|
connection.append((self.prefix+"wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
|
||||||
|
|
||||||
|
|
@ -975,39 +946,6 @@ class bank(design.design):
|
||||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||||
offset=control_pos)
|
offset=control_pos)
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load, port):
|
|
||||||
""" return analytical delay of the bank. This will track the clock to output path"""
|
|
||||||
#FIXME: This delay is determined in the control logic. Should be moved here.
|
|
||||||
# word_driver_delay = self.wordline_driver.analytical_delay(corner,
|
|
||||||
# slew,
|
|
||||||
# self.bitcell_array.input_load())
|
|
||||||
|
|
||||||
#FIXME: Array delay is the same for every port.
|
|
||||||
word_driver_slew = 0
|
|
||||||
if self.words_per_row > 1:
|
|
||||||
bitline_ext_load = self.port_data[port].column_mux_array.get_drain_cin()
|
|
||||||
else:
|
|
||||||
bitline_ext_load = self.port_data[port].sense_amp_array.get_drain_cin()
|
|
||||||
|
|
||||||
bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_slew, bitline_ext_load)
|
|
||||||
|
|
||||||
bitcell_array_slew = 0
|
|
||||||
#This also essentially creates the same delay for each port. Good structure, no substance
|
|
||||||
if self.words_per_row > 1:
|
|
||||||
sa_load = self.port_data[port].sense_amp_array.get_drain_cin()
|
|
||||||
column_mux_delay = self.port_data[port].column_mux_array.analytical_delay(corner,
|
|
||||||
bitcell_array_slew,
|
|
||||||
sa_load)
|
|
||||||
else:
|
|
||||||
column_mux_delay = []
|
|
||||||
|
|
||||||
column_mux_slew = 0
|
|
||||||
sense_amp_delay = self.port_data[port].sense_amp_array.analytical_delay(corner,
|
|
||||||
column_mux_slew,
|
|
||||||
load)
|
|
||||||
# output load of bitcell_array is set to be only small part of bl for sense amp.
|
|
||||||
return bitcell_array_delay + column_mux_delay + sense_amp_delay
|
|
||||||
|
|
||||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
||||||
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
|
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
|
||||||
#Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption
|
#Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption
|
||||||
|
|
|
||||||
|
|
@ -149,16 +149,6 @@ class bitcell_array(design.design):
|
||||||
for pin in inst.get_pins(pin_name):
|
for pin in inst.get_pins(pin_name):
|
||||||
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
|
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load):
|
|
||||||
"""Returns relative delay of the bitline in the bitcell array"""
|
|
||||||
from tech import parameter
|
|
||||||
#The load being driven/drained is mostly the bitline but could include the sense amp or the column mux.
|
|
||||||
#The load from the bitlines is due to the drain capacitances from all the other bitlines and wire parasitics.
|
|
||||||
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
|
|
||||||
wire_unit_load = .05 * drain_load #Wires add 5% to this.
|
|
||||||
bitline_load = (drain_load+wire_unit_load)*self.row_size
|
|
||||||
return [self.cell.analytical_delay(corner, slew, load+bitline_load)]
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Power of Bitcell array and bitline in nW."""
|
"""Power of Bitcell array and bitline in nW."""
|
||||||
from tech import drc, parameter
|
from tech import drc, parameter
|
||||||
|
|
@ -197,15 +187,6 @@ class bitcell_array(design.design):
|
||||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
||||||
return bl_wire
|
return bl_wire
|
||||||
|
|
||||||
def output_load(self, bl_pos=0):
|
|
||||||
bl_wire = self.gen_bl_wire()
|
|
||||||
return bl_wire.wire_c # sense amp only need to charge small portion of the bl
|
|
||||||
# set as one segment for now
|
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
wl_wire = self.gen_wl_wire()
|
|
||||||
return wl_wire.return_input_cap()
|
|
||||||
|
|
||||||
def get_wordline_cin(self):
|
def get_wordline_cin(self):
|
||||||
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
||||||
#A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
#A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||||
|
|
|
||||||
|
|
@ -125,16 +125,16 @@ class control_logic(design.design):
|
||||||
self.add_mod(self.wl_en_driver)
|
self.add_mod(self.wl_en_driver)
|
||||||
|
|
||||||
# w_en drives every write driver
|
# w_en drives every write driver
|
||||||
self.wen_and2 = factory.create(module_type="pand2",
|
self.wen_and = factory.create(module_type="pand3",
|
||||||
size=self.word_size+8,
|
size=self.word_size+8,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.wen_and2)
|
self.add_mod(self.wen_and)
|
||||||
|
|
||||||
# s_en drives every sense amp
|
# s_en drives every sense amp
|
||||||
self.sen_and2 = factory.create(module_type="pand2",
|
self.sen_and3 = factory.create(module_type="pand3",
|
||||||
size=self.word_size,
|
size=self.word_size,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.sen_and2)
|
self.add_mod(self.sen_and3)
|
||||||
|
|
||||||
# used to generate inverted signals with low fanout
|
# used to generate inverted signals with low fanout
|
||||||
self.inv = factory.create(module_type="pinv",
|
self.inv = factory.create(module_type="pinv",
|
||||||
|
|
@ -147,11 +147,13 @@ class control_logic(design.design):
|
||||||
# prepended 3 inverter stages to guarantee it is slower and odd polarity
|
# prepended 3 inverter stages to guarantee it is slower and odd polarity
|
||||||
self.p_en_bar_driver = factory.create(module_type="pdriver",
|
self.p_en_bar_driver = factory.create(module_type="pdriver",
|
||||||
fanout=self.num_cols,
|
fanout=self.num_cols,
|
||||||
neg_polarity=True,
|
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.p_en_bar_driver)
|
self.add_mod(self.p_en_bar_driver)
|
||||||
|
|
||||||
|
|
||||||
|
self.nand2 = factory.create(module_type="pnand2",
|
||||||
|
height=dff_height)
|
||||||
|
self.add_mod(self.nand2)
|
||||||
|
|
||||||
# if (self.port_type == "rw") or (self.port_type == "r"):
|
# if (self.port_type == "rw") or (self.port_type == "r"):
|
||||||
# from importlib import reload
|
# from importlib import reload
|
||||||
|
|
@ -330,12 +332,9 @@ class control_logic(design.design):
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
self.input_list = ["csb", "web"]
|
self.input_list = ["csb", "web"]
|
||||||
self.rbl_list = ["rbl_bl"]
|
self.rbl_list = ["rbl_bl"]
|
||||||
elif self.port_type == "r":
|
|
||||||
self.input_list = ["csb"]
|
|
||||||
self.rbl_list = ["rbl_bl"]
|
|
||||||
else:
|
else:
|
||||||
self.input_list = ["csb"]
|
self.input_list = ["csb"]
|
||||||
self.rbl_list = []
|
self.rbl_list = ["rbl_bl"]
|
||||||
|
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"]
|
self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"]
|
||||||
|
|
@ -344,11 +343,11 @@ class control_logic(design.design):
|
||||||
|
|
||||||
# list of output control signals (for making a vertical bus)
|
# list of output control signals (for making a vertical bus)
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"]
|
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"]
|
||||||
elif self.port_type == "r":
|
elif self.port_type == "r":
|
||||||
self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"]
|
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"]
|
||||||
else:
|
else:
|
||||||
self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
|
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
|
||||||
# leave space for the bus plus one extra space
|
# leave space for the bus plus one extra space
|
||||||
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
|
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
|
||||||
|
|
||||||
|
|
@ -382,6 +381,7 @@ class control_logic(design.design):
|
||||||
self.create_gated_clk_buf_row()
|
self.create_gated_clk_buf_row()
|
||||||
self.create_wlen_row()
|
self.create_wlen_row()
|
||||||
if (self.port_type == "rw") or (self.port_type == "w"):
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
||||||
|
self.create_rbl_delay_row()
|
||||||
self.create_wen_row()
|
self.create_wen_row()
|
||||||
if (self.port_type == "rw") or (self.port_type == "r"):
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
||||||
self.create_sen_row()
|
self.create_sen_row()
|
||||||
|
|
@ -419,6 +419,9 @@ class control_logic(design.design):
|
||||||
row += 1
|
row += 1
|
||||||
self.place_pen_row(row)
|
self.place_pen_row(row)
|
||||||
row += 1
|
row += 1
|
||||||
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
||||||
|
self.place_rbl_delay_row(row)
|
||||||
|
row += 1
|
||||||
if (self.port_type == "rw") or (self.port_type == "r"):
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
||||||
self.place_sen_row(row)
|
self.place_sen_row(row)
|
||||||
row += 1
|
row += 1
|
||||||
|
|
@ -443,9 +446,11 @@ class control_logic(design.design):
|
||||||
self.route_dffs()
|
self.route_dffs()
|
||||||
self.route_wlen()
|
self.route_wlen()
|
||||||
if (self.port_type == "rw") or (self.port_type == "w"):
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
||||||
|
self.route_rbl_delay()
|
||||||
self.route_wen()
|
self.route_wen()
|
||||||
if (self.port_type == "rw") or (self.port_type == "r"):
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
||||||
self.route_sen()
|
self.route_sen()
|
||||||
|
self.route_delay()
|
||||||
self.route_pen()
|
self.route_pen()
|
||||||
self.route_clk_buf()
|
self.route_clk_buf()
|
||||||
self.route_gated_clk_bar()
|
self.route_gated_clk_bar()
|
||||||
|
|
@ -457,7 +462,7 @@ class control_logic(design.design):
|
||||||
""" Create the replica bitline """
|
""" Create the replica bitline """
|
||||||
self.delay_inst=self.add_inst(name="delay_chain",
|
self.delay_inst=self.add_inst(name="delay_chain",
|
||||||
mod=self.delay_chain)
|
mod=self.delay_chain)
|
||||||
self.connect_inst(["rbl_bl", "pre_s_en", "vdd", "gnd"])
|
self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_delay(self,row):
|
def place_delay(self,row):
|
||||||
""" Place the replica bitline """
|
""" Place the replica bitline """
|
||||||
|
|
@ -468,6 +473,22 @@ class control_logic(design.design):
|
||||||
offset = vector(self.delay_chain.width, y_off)
|
offset = vector(self.delay_chain.width, y_off)
|
||||||
self.delay_inst.place(offset, mirror="MY")
|
self.delay_inst.place(offset, mirror="MY")
|
||||||
|
|
||||||
|
def route_delay(self):
|
||||||
|
|
||||||
|
out_pos = self.delay_inst.get_pin("out").bc()
|
||||||
|
# Connect to the rail level with the vdd rail
|
||||||
|
# Use pen since it is in every type of control logic
|
||||||
|
vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by()
|
||||||
|
in_pos = vector(self.rail_offsets["rbl_bl_delay"].x,vdd_ypos)
|
||||||
|
mid1 = vector(out_pos.x,in_pos.y)
|
||||||
|
self.add_wire(("metal1","via1","metal2"),[out_pos, mid1, in_pos])
|
||||||
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
offset=in_pos)
|
||||||
|
|
||||||
|
|
||||||
|
# Input from RBL goes to the delay line for futher delay
|
||||||
|
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
|
||||||
|
|
||||||
|
|
||||||
def create_clk_buf_row(self):
|
def create_clk_buf_row(self):
|
||||||
""" Create the multistage and gated clock buffer """
|
""" Create the multistage and gated clock buffer """
|
||||||
|
|
@ -476,12 +497,9 @@ class control_logic(design.design):
|
||||||
self.connect_inst(["clk","clk_buf","vdd","gnd"])
|
self.connect_inst(["clk","clk_buf","vdd","gnd"])
|
||||||
|
|
||||||
def place_clk_buf_row(self,row):
|
def place_clk_buf_row(self,row):
|
||||||
""" Place the multistage clock buffer below the control flops """
|
x_offset = self.control_x_offset
|
||||||
x_off = self.control_x_offset
|
|
||||||
(y_off,mirror)=self.get_offset(row)
|
|
||||||
|
|
||||||
offset = vector(x_off,y_off)
|
x_offset = self.place_util(self.clk_buf_inst, x_offset, row)
|
||||||
self.clk_buf_inst.place(offset, mirror)
|
|
||||||
|
|
||||||
self.row_end_inst.append(self.clk_buf_inst)
|
self.row_end_inst.append(self.clk_buf_inst)
|
||||||
|
|
||||||
|
|
@ -518,17 +536,10 @@ class control_logic(design.design):
|
||||||
self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"])
|
self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"])
|
||||||
|
|
||||||
def place_gated_clk_bar_row(self,row):
|
def place_gated_clk_bar_row(self,row):
|
||||||
""" Place the gated clk logic below the control flops """
|
x_offset = self.control_x_offset
|
||||||
x_off = self.control_x_offset
|
|
||||||
(y_off,mirror)=self.get_offset(row)
|
|
||||||
|
|
||||||
offset = vector(x_off,y_off)
|
x_offset = self.place_util(self.clk_bar_inst, x_offset, row)
|
||||||
self.clk_bar_inst.place(offset, mirror)
|
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
|
||||||
|
|
||||||
x_off += self.inv.width
|
|
||||||
|
|
||||||
offset = vector(x_off,y_off)
|
|
||||||
self.gated_clk_bar_inst.place(offset, mirror)
|
|
||||||
|
|
||||||
self.row_end_inst.append(self.gated_clk_bar_inst)
|
self.row_end_inst.append(self.gated_clk_bar_inst)
|
||||||
|
|
||||||
|
|
@ -562,12 +573,9 @@ class control_logic(design.design):
|
||||||
self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"])
|
self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"])
|
||||||
|
|
||||||
def place_gated_clk_buf_row(self,row):
|
def place_gated_clk_buf_row(self,row):
|
||||||
""" Place the gated clk logic below the control flops """
|
x_offset = self.control_x_offset
|
||||||
x_off = self.control_x_offset
|
|
||||||
(y_off,mirror)=self.get_offset(row)
|
|
||||||
|
|
||||||
offset = vector(x_off,y_off)
|
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
|
||||||
self.gated_clk_buf_inst.place(offset, mirror)
|
|
||||||
|
|
||||||
self.row_end_inst.append(self.gated_clk_buf_inst)
|
self.row_end_inst.append(self.gated_clk_buf_inst)
|
||||||
|
|
||||||
|
|
@ -589,11 +597,9 @@ class control_logic(design.design):
|
||||||
self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"])
|
self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_wlen_row(self, row):
|
def place_wlen_row(self, row):
|
||||||
x_off = self.control_x_offset
|
x_offset = self.control_x_offset
|
||||||
(y_off,mirror)=self.get_offset(row)
|
|
||||||
|
|
||||||
offset = vector(x_off, y_off)
|
x_offset = self.place_util(self.wl_en_inst, x_offset, row)
|
||||||
self.wl_en_inst.place(offset, mirror)
|
|
||||||
|
|
||||||
self.row_end_inst.append(self.wl_en_inst)
|
self.row_end_inst.append(self.wl_en_inst)
|
||||||
|
|
||||||
|
|
@ -604,25 +610,32 @@ class control_logic(design.design):
|
||||||
self.connect_output(self.wl_en_inst, "Z", "wl_en")
|
self.connect_output(self.wl_en_inst, "Z", "wl_en")
|
||||||
|
|
||||||
def create_pen_row(self):
|
def create_pen_row(self):
|
||||||
# input: gated_clk_bar, output: p_en_bar
|
self.p_en_bar_nand_inst=self.add_inst(name="nand_p_en_bar",
|
||||||
self.p_en_bar_inst=self.add_inst(name="inv_p_en_bar",
|
mod=self.nand2)
|
||||||
mod=self.p_en_bar_driver)
|
self.connect_inst(["gated_clk_buf", "rbl_bl_delay", "p_en_bar_unbuf", "vdd", "gnd"])
|
||||||
self.connect_inst(["gated_clk_buf", "p_en_bar", "vdd", "gnd"])
|
|
||||||
|
|
||||||
|
self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar",
|
||||||
|
mod=self.p_en_bar_driver)
|
||||||
|
self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_pen_row(self,row):
|
def place_pen_row(self,row):
|
||||||
x_off = self.control_x_offset
|
x_offset = self.control_x_offset
|
||||||
(y_off,mirror)=self.get_offset(row)
|
|
||||||
offset = vector(x_off,y_off)
|
|
||||||
self.p_en_bar_inst.place(offset, mirror)
|
|
||||||
|
|
||||||
self.row_end_inst.append(self.p_en_bar_inst)
|
x_offset = self.place_util(self.p_en_bar_nand_inst, x_offset, row)
|
||||||
|
x_offset = self.place_util(self.p_en_bar_driver_inst, x_offset, row)
|
||||||
|
|
||||||
|
self.row_end_inst.append(self.p_en_bar_driver_inst)
|
||||||
|
|
||||||
def route_pen(self):
|
def route_pen(self):
|
||||||
in_map = zip(["A"], ["gated_clk_buf"])
|
in_map = zip(["A", "B"], ["gated_clk_buf", "rbl_bl_delay"])
|
||||||
self.connect_vertical_bus(in_map, self.p_en_bar_inst, self.rail_offsets)
|
self.connect_vertical_bus(in_map, self.p_en_bar_nand_inst, self.rail_offsets)
|
||||||
|
|
||||||
self.connect_output(self.p_en_bar_inst, "Z", "p_en_bar")
|
out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc()
|
||||||
|
in_pos = self.p_en_bar_driver_inst.get_pin("A").lc()
|
||||||
|
mid1 = vector(out_pos.x,in_pos.y)
|
||||||
|
self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos])
|
||||||
|
|
||||||
|
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
|
||||||
|
|
||||||
def create_sen_row(self):
|
def create_sen_row(self):
|
||||||
""" Create the sense enable buffer. """
|
""" Create the sense enable buffer. """
|
||||||
|
|
@ -632,20 +645,14 @@ class control_logic(design.design):
|
||||||
input_name = "cs_bar"
|
input_name = "cs_bar"
|
||||||
# GATE FOR S_EN
|
# GATE FOR S_EN
|
||||||
self.s_en_gate_inst = self.add_inst(name="buf_s_en_and",
|
self.s_en_gate_inst = self.add_inst(name="buf_s_en_and",
|
||||||
mod=self.sen_and2)
|
mod=self.sen_and3)
|
||||||
self.connect_inst(["pre_s_en", input_name, "s_en", "vdd", "gnd"])
|
self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"])
|
||||||
|
|
||||||
|
|
||||||
def place_sen_row(self,row):
|
def place_sen_row(self,row):
|
||||||
"""
|
x_offset = self.control_x_offset
|
||||||
The sense enable buffer gets placed to the far right of the
|
|
||||||
row.
|
|
||||||
"""
|
|
||||||
x_off = self.control_x_offset
|
|
||||||
(y_off,mirror)=self.get_offset(row)
|
|
||||||
|
|
||||||
offset = vector(x_off, y_off)
|
x_offset = self.place_util(self.s_en_gate_inst, x_offset, row)
|
||||||
self.s_en_gate_inst.place(offset, mirror)
|
|
||||||
|
|
||||||
self.row_end_inst.append(self.s_en_gate_inst)
|
self.row_end_inst.append(self.s_en_gate_inst)
|
||||||
|
|
||||||
|
|
@ -657,22 +664,41 @@ class control_logic(design.design):
|
||||||
else:
|
else:
|
||||||
input_name = "cs_bar"
|
input_name = "cs_bar"
|
||||||
|
|
||||||
sen_map = zip(["B"], [input_name])
|
sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name])
|
||||||
self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.rail_offsets)
|
self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.rail_offsets)
|
||||||
|
|
||||||
out_pos = self.delay_inst.get_pin("out").bc()
|
|
||||||
in_pos = self.s_en_gate_inst.get_pin("A").lc()
|
|
||||||
mid1 = vector(out_pos.x,in_pos.y)
|
|
||||||
self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos])
|
|
||||||
|
|
||||||
self.connect_output(self.s_en_gate_inst, "Z", "s_en")
|
self.connect_output(self.s_en_gate_inst, "Z", "s_en")
|
||||||
|
|
||||||
# Input from RBL goes to the delay line for futher delay
|
|
||||||
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
|
def create_rbl_delay_row(self):
|
||||||
|
|
||||||
|
self.rbl_bl_delay_inv_inst = self.add_inst(name="rbl_bl_delay_inv",
|
||||||
|
mod=self.inv)
|
||||||
|
self.connect_inst(["rbl_bl_delay", "rbl_bl_delay_bar", "vdd", "gnd"])
|
||||||
|
|
||||||
|
def place_rbl_delay_row(self,row):
|
||||||
|
x_offset = self.control_x_offset
|
||||||
|
|
||||||
|
x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row)
|
||||||
|
|
||||||
|
self.row_end_inst.append(self.rbl_bl_delay_inv_inst)
|
||||||
|
|
||||||
|
def route_rbl_delay(self):
|
||||||
|
# Connect from delay line
|
||||||
|
# Connect to rail
|
||||||
|
|
||||||
|
rbl_map = zip(["Z"], ["rbl_bl_delay_bar"])
|
||||||
|
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
|
||||||
|
# The pin is on M1, so we need another via as well
|
||||||
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center())
|
||||||
|
|
||||||
|
|
||||||
|
rbl_map = zip(["A"], ["rbl_bl_delay"])
|
||||||
|
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets)
|
||||||
|
|
||||||
def create_wen_row(self):
|
def create_wen_row(self):
|
||||||
|
|
||||||
# input: we (or cs) output: w_en
|
# input: we (or cs) output: w_en
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
input_name = "we"
|
input_name = "we"
|
||||||
|
|
@ -682,16 +708,14 @@ class control_logic(design.design):
|
||||||
|
|
||||||
# GATE THE W_EN
|
# GATE THE W_EN
|
||||||
self.w_en_gate_inst = self.add_inst(name="w_en_and",
|
self.w_en_gate_inst = self.add_inst(name="w_en_and",
|
||||||
mod=self.wen_and2)
|
mod=self.wen_and)
|
||||||
self.connect_inst([input_name, "gated_clk_bar", "w_en", "vdd", "gnd"])
|
self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"])
|
||||||
|
|
||||||
|
|
||||||
def place_wen_row(self,row):
|
def place_wen_row(self,row):
|
||||||
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
|
x_offset = self.control_x_offset
|
||||||
(y_off,mirror)=self.get_offset(row)
|
|
||||||
|
|
||||||
offset = vector(x_off, y_off)
|
x_offset = self.place_util(self.w_en_gate_inst, x_offset, row)
|
||||||
self.w_en_gate_inst.place(offset, mirror)
|
|
||||||
|
|
||||||
self.row_end_inst.append(self.w_en_gate_inst)
|
self.row_end_inst.append(self.w_en_gate_inst)
|
||||||
|
|
||||||
|
|
@ -702,7 +726,7 @@ class control_logic(design.design):
|
||||||
# No we for write-only reports, so use cs
|
# No we for write-only reports, so use cs
|
||||||
input_name = "cs"
|
input_name = "cs"
|
||||||
|
|
||||||
wen_map = zip(["A", "B"], [input_name, "gated_clk_bar"])
|
wen_map = zip(["A", "B", "C"], [input_name, "rbl_bl_delay_bar", "gated_clk_bar"])
|
||||||
self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.rail_offsets)
|
self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.rail_offsets)
|
||||||
|
|
||||||
self.connect_output(self.w_en_gate_inst, "Z", "w_en")
|
self.connect_output(self.w_en_gate_inst, "Z", "w_en")
|
||||||
|
|
@ -781,7 +805,6 @@ class control_logic(design.design):
|
||||||
self.add_power_pin("gnd", pin_loc)
|
self.add_power_pin("gnd", pin_loc)
|
||||||
self.add_path("metal1", [row_loc, pin_loc])
|
self.add_path("metal1", [row_loc, pin_loc])
|
||||||
|
|
||||||
if (self.port_type == "rw") or (self.port_type == "r"):
|
|
||||||
self.copy_layout_pin(self.delay_inst,"gnd")
|
self.copy_layout_pin(self.delay_inst,"gnd")
|
||||||
self.copy_layout_pin(self.delay_inst,"vdd")
|
self.copy_layout_pin(self.delay_inst,"vdd")
|
||||||
|
|
||||||
|
|
@ -901,7 +924,8 @@ class control_logic(design.design):
|
||||||
last_stage_rise = False
|
last_stage_rise = False
|
||||||
|
|
||||||
#First stage(s), clk -(pdriver)-> clk_buf.
|
#First stage(s), clk -(pdriver)-> clk_buf.
|
||||||
clk_buf_cout = self.replica_bitline.get_en_cin()
|
#clk_buf_cout = self.replica_bitline.get_en_cin()
|
||||||
|
clk_buf_cout = 0
|
||||||
stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise)
|
stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise)
|
||||||
last_stage_rise = stage_effort_list[-1].is_rise
|
last_stage_rise = stage_effort_list[-1].is_rise
|
||||||
|
|
||||||
|
|
@ -948,3 +972,11 @@ class control_logic(design.design):
|
||||||
self.graph_inst_exclude.add(self.ctrl_dff_inst)
|
self.graph_inst_exclude.add(self.ctrl_dff_inst)
|
||||||
if self.port_type=="rw" or self.port_type=="w":
|
if self.port_type=="rw" or self.port_type=="w":
|
||||||
self.graph_inst_exclude.add(self.w_en_gate_inst)
|
self.graph_inst_exclude.add(self.w_en_gate_inst)
|
||||||
|
|
||||||
|
def place_util(self, inst, x_offset, row):
|
||||||
|
""" Utility to place a row and compute the next offset """
|
||||||
|
|
||||||
|
(y_offset,mirror)=self.get_offset(row)
|
||||||
|
offset = vector(x_offset, y_offset)
|
||||||
|
inst.place(offset, mirror)
|
||||||
|
return x_offset+inst.width
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,6 @@ class dff(design.design):
|
||||||
transition_prob = spice["flop_transition_prob"]
|
transition_prob = spice["flop_transition_prob"]
|
||||||
return transition_prob*(c_load + c_para)
|
return transition_prob*(c_load + c_para)
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load = 0.0):
|
|
||||||
# dont know how to calculate this now, use constant in tech file
|
|
||||||
result = self.return_delay(spice["dff_delay"], spice["dff_slew"])
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_clk_cin(self):
|
def get_clk_cin(self):
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
|
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
|
||||||
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
|
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
|
||||||
|
|
|
||||||
|
|
@ -160,11 +160,6 @@ class dff_array(design.design):
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||||
offset=vector(clk_pin.cx(),clk_ypos))
|
offset=vector(clk_pin.cx(),clk_ypos))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
return self.dff.analytical_delay(corner, slew=slew, load=load)
|
|
||||||
|
|
||||||
def get_clk_cin(self):
|
def get_clk_cin(self):
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
|
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
|
||||||
dff_clk_cin = self.dff.get_clk_cin()
|
dff_clk_cin = self.dff.get_clk_cin()
|
||||||
|
|
|
||||||
|
|
@ -178,15 +178,6 @@ class dff_buf(design.design):
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
offset=qb_pos)
|
offset=qb_pos)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
""" Calculate the analytical delay of DFF-> INV -> INV """
|
|
||||||
dff_delay=self.dff.analytical_delay(corner, slew=slew, load=self.inv1.input_load())
|
|
||||||
inv1_delay = self.inv1.analytical_delay(corner, slew=dff_delay.slew, load=self.inv2.input_load())
|
|
||||||
inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load)
|
|
||||||
return dff_delay + inv1_delay + inv2_delay
|
|
||||||
|
|
||||||
def get_clk_cin(self):
|
def get_clk_cin(self):
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
|
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
|
||||||
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
|
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
|
||||||
|
|
|
||||||
|
|
@ -193,11 +193,6 @@ class dff_buf_array(design.design):
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||||
offset=vector(clk_pin.cx(),clk_ypos))
|
offset=vector(clk_pin.cx(),clk_ypos))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
return self.dff.analytical_delay(slew=slew, load=load)
|
|
||||||
|
|
||||||
def get_clk_cin(self):
|
def get_clk_cin(self):
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
|
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
|
||||||
dff_clk_cin = self.dff.get_clk_cin()
|
dff_clk_cin = self.dff.get_clk_cin()
|
||||||
|
|
|
||||||
|
|
@ -151,14 +151,6 @@ class dff_inv(design.design):
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
offset=dout_pin.center())
|
offset=dout_pin.center())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
""" Calculate the analytical delay of DFF-> INV -> INV """
|
|
||||||
dff_delay=self.dff.analytical_delay(corner, slew=slew, load=self.inv1.input_load())
|
|
||||||
inv1_delay = self.inv1.analytical_delay(corner, slew=dff_delay.slew, load=load)
|
|
||||||
return dff_delay + inv1_delay
|
|
||||||
|
|
||||||
def get_clk_cin(self):
|
def get_clk_cin(self):
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
|
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
|
||||||
return self.dff.get_clk_cin()
|
return self.dff.get_clk_cin()
|
||||||
|
|
|
||||||
|
|
@ -190,12 +190,6 @@ class dff_inv_array(design.design):
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||||
offset=vector(clk_pin.cx(),clk_ypos))
|
offset=vector(clk_pin.cx(),clk_ypos))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
return self.dff.analytical_delay(corner, slew=slew, load=load)
|
|
||||||
|
|
||||||
def get_clk_cin(self):
|
def get_clk_cin(self):
|
||||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
|
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
|
||||||
dff_clk_cin = self.dff.get_clk_cin()
|
dff_clk_cin = self.dff.get_clk_cin()
|
||||||
|
|
|
||||||
|
|
@ -596,28 +596,6 @@ class hierarchical_decoder(design.design):
|
||||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||||
offset=rail_pos)
|
offset=rail_pos)
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load = 0.0):
|
|
||||||
# A -> out
|
|
||||||
if self.determine_predecodes(self.num_inputs)[1]==0:
|
|
||||||
pre = self.pre2_4
|
|
||||||
nand = self.nand2
|
|
||||||
else:
|
|
||||||
pre = self.pre3_8
|
|
||||||
nand = self.nand3
|
|
||||||
a_t_out_delay = pre.analytical_delay(corner, slew=slew,load = nand.input_load())
|
|
||||||
|
|
||||||
# out -> z
|
|
||||||
out_t_z_delay = nand.analytical_delay(corner, slew= a_t_out_delay.slew,
|
|
||||||
load = self.inv.input_load())
|
|
||||||
result = a_t_out_delay + out_t_z_delay
|
|
||||||
|
|
||||||
# Z -> decode_out
|
|
||||||
z_t_decodeout_delay = self.inv.analytical_delay(corner, slew = out_t_z_delay.slew , load = load)
|
|
||||||
result = result + z_t_decodeout_delay
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
if self.determine_predecodes(self.num_inputs)[1]==0:
|
if self.determine_predecodes(self.num_inputs)[1]==0:
|
||||||
pre = self.pre2_4
|
pre = self.pre2_4
|
||||||
|
|
|
||||||
|
|
@ -57,20 +57,3 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
||||||
["Abar_0", "A_1"],
|
["Abar_0", "A_1"],
|
||||||
["A_0", "A_1"]]
|
["A_0", "A_1"]]
|
||||||
return combination
|
return combination
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load = 0.0 ):
|
|
||||||
# in -> inbar
|
|
||||||
a_t_b_delay = self.inv.analytical_delay(corner, slew=slew, load=self.nand.input_load())
|
|
||||||
|
|
||||||
# inbar -> z
|
|
||||||
b_t_z_delay = self.nand.analytical_delay(corner, slew=a_t_b_delay.slew, load=self.inv.input_load())
|
|
||||||
|
|
||||||
# Z -> out
|
|
||||||
a_t_out_delay = self.inv.analytical_delay(corner, slew=b_t_z_delay.slew, load=load)
|
|
||||||
|
|
||||||
return a_t_b_delay + b_t_z_delay + a_t_out_delay
|
|
||||||
|
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
return self.nand.input_load()
|
|
||||||
|
|
|
||||||
|
|
@ -66,20 +66,3 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
||||||
["Abar_0", "A_1", "A_2"],
|
["Abar_0", "A_1", "A_2"],
|
||||||
["A_0", "A_1", "A_2"]]
|
["A_0", "A_1", "A_2"]]
|
||||||
return combination
|
return combination
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load = 0.0 ):
|
|
||||||
# A -> Abar
|
|
||||||
a_t_b_delay = self.inv.analytical_delay(corner, slew=slew, load=self.nand.input_load())
|
|
||||||
|
|
||||||
# Abar -> z
|
|
||||||
b_t_z_delay = self.nand.analytical_delay(corner, slew=a_t_b_delay.slew, load=self.inv.input_load())
|
|
||||||
|
|
||||||
# Z -> out
|
|
||||||
a_t_out_delay = self.inv.analytical_delay(corner, slew=b_t_z_delay.slew, load=load)
|
|
||||||
|
|
||||||
return a_t_b_delay + b_t_z_delay + a_t_out_delay
|
|
||||||
|
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
return self.nand.input_load()
|
|
||||||
|
|
|
||||||
|
|
@ -828,21 +828,3 @@ class multibank(design.design):
|
||||||
self.add_via(layers=("metal2","via2","metal3"),
|
self.add_via(layers=("metal2","via2","metal3"),
|
||||||
offset=in_pin + self.m2m3_via_offset,
|
offset=in_pin + self.m2m3_via_offset,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load):
|
|
||||||
""" return analytical delay of the bank"""
|
|
||||||
decoder_delay = self.row_decoder.analytical_delay(corner, slew, self.wordline_driver.input_load())
|
|
||||||
|
|
||||||
word_driver_delay = self.wordline_driver.analytical_delay(corner, decoder_delay.slew, self.bitcell_array.input_load())
|
|
||||||
|
|
||||||
bitcell_array_delay = self.bitcell_array.analytical_delay(corner, word_driver_delay.slew)
|
|
||||||
|
|
||||||
bl_t_data_out_delay = self.sense_amp_array.analytical_delay(corner, bitcell_array_delay.slew,
|
|
||||||
self.bitcell_array.output_load())
|
|
||||||
# output load of bitcell_array is set to be only small part of bl for sense amp.
|
|
||||||
|
|
||||||
data_t_DATA_delay = self.tri_gate_array.analytical_delay(corner, bl_t_data_out_delay.slew, load)
|
|
||||||
|
|
||||||
result = decoder_delay + word_driver_delay + bitcell_array_delay + bl_t_data_out_delay + data_t_DATA_delay
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,6 @@ class port_data(design.design):
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
""" Adding pins for port address module"""
|
""" Adding pins for port address module"""
|
||||||
|
|
||||||
if self.has_rbl():
|
|
||||||
self.add_pin("rbl_bl","INOUT")
|
self.add_pin("rbl_bl","INOUT")
|
||||||
self.add_pin("rbl_br","INOUT")
|
self.add_pin("rbl_br","INOUT")
|
||||||
for bit in range(self.num_cols):
|
for bit in range(self.num_cols):
|
||||||
|
|
@ -155,7 +154,6 @@ class port_data(design.design):
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
|
|
||||||
if self.port in self.read_ports:
|
|
||||||
# Extra column +1 is for RBL
|
# Extra column +1 is for RBL
|
||||||
# Precharge will be shifted left if needed
|
# Precharge will be shifted left if needed
|
||||||
self.precharge_array = factory.create(module_type="precharge_array",
|
self.precharge_array = factory.create(module_type="precharge_array",
|
||||||
|
|
@ -164,20 +162,12 @@ class port_data(design.design):
|
||||||
bitcell_br=self.br_names[self.port])
|
bitcell_br=self.br_names[self.port])
|
||||||
self.add_mod(self.precharge_array)
|
self.add_mod(self.precharge_array)
|
||||||
|
|
||||||
|
if self.port in self.read_ports:
|
||||||
self.sense_amp_array = factory.create(module_type="sense_amp_array",
|
self.sense_amp_array = factory.create(module_type="sense_amp_array",
|
||||||
word_size=self.word_size,
|
word_size=self.word_size,
|
||||||
words_per_row=self.words_per_row)
|
words_per_row=self.words_per_row)
|
||||||
self.add_mod(self.sense_amp_array)
|
self.add_mod(self.sense_amp_array)
|
||||||
else:
|
else:
|
||||||
# Precharge is needed when we have a column mux or for byte writes
|
|
||||||
# to prevent corruption of half-selected cells, so just always add it
|
|
||||||
# This is a little power inefficient for write ports without a column mux,
|
|
||||||
# but it is simpler.
|
|
||||||
self.precharge_array = factory.create(module_type="precharge_array",
|
|
||||||
columns=self.num_cols,
|
|
||||||
bitcell_bl=self.bl_names[self.port],
|
|
||||||
bitcell_br=self.br_names[self.port])
|
|
||||||
self.add_mod(self.precharge_array)
|
|
||||||
self.sense_amp_array = None
|
self.sense_amp_array = None
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -205,7 +195,7 @@ class port_data(design.design):
|
||||||
write_size=self.write_size)
|
write_size=self.write_size)
|
||||||
self.add_mod(self.write_mask_and_array)
|
self.add_mod(self.write_mask_and_array)
|
||||||
else:
|
else:
|
||||||
self.write_mask_and_array_inst = None
|
self.write_mask_and_array = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.write_driver_array = None
|
self.write_driver_array = None
|
||||||
|
|
@ -243,14 +233,14 @@ class port_data(design.design):
|
||||||
|
|
||||||
temp = []
|
temp = []
|
||||||
# Use left BLs for RBL
|
# Use left BLs for RBL
|
||||||
if self.has_rbl() and self.port==0:
|
if self.port==0:
|
||||||
temp.append("rbl_bl")
|
temp.append("rbl_bl")
|
||||||
temp.append("rbl_br")
|
temp.append("rbl_br")
|
||||||
for bit in range(self.num_cols):
|
for bit in range(self.num_cols):
|
||||||
temp.append(self.bl_names[self.port]+"_{0}".format(bit))
|
temp.append(self.bl_names[self.port]+"_{0}".format(bit))
|
||||||
temp.append(self.br_names[self.port]+"_{0}".format(bit))
|
temp.append(self.br_names[self.port]+"_{0}".format(bit))
|
||||||
# Use right BLs for RBL
|
# Use right BLs for RBL
|
||||||
if self.has_rbl() and self.port==1:
|
if self.port==1:
|
||||||
temp.append("rbl_bl")
|
temp.append("rbl_bl")
|
||||||
temp.append("rbl_br")
|
temp.append("rbl_br")
|
||||||
temp.extend(["p_en_bar", "vdd"])
|
temp.extend(["p_en_bar", "vdd"])
|
||||||
|
|
@ -379,7 +369,7 @@ class port_data(design.design):
|
||||||
vertical_port_order.append(self.write_mask_and_array_inst)
|
vertical_port_order.append(self.write_mask_and_array_inst)
|
||||||
|
|
||||||
# Add one column for the the RBL
|
# Add one column for the the RBL
|
||||||
if self.has_rbl() and self.port == 0:
|
if self.port==0:
|
||||||
x_offset = self.bitcell.width
|
x_offset = self.bitcell.width
|
||||||
else:
|
else:
|
||||||
x_offset = 0
|
x_offset = 0
|
||||||
|
|
@ -478,7 +468,7 @@ class port_data(design.design):
|
||||||
|
|
||||||
inst1 = self.column_mux_array_inst
|
inst1 = self.column_mux_array_inst
|
||||||
inst2 = self.precharge_array_inst
|
inst2 = self.precharge_array_inst
|
||||||
if self.has_rbl() and self.port==0:
|
if self.port==0:
|
||||||
self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1)
|
self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1)
|
||||||
else:
|
else:
|
||||||
self.connect_bitlines(inst1, inst2, self.num_cols)
|
self.connect_bitlines(inst1, inst2, self.num_cols)
|
||||||
|
|
@ -499,7 +489,7 @@ class port_data(design.design):
|
||||||
inst1 = self.precharge_array_inst
|
inst1 = self.precharge_array_inst
|
||||||
inst1_bl_name = "bl_{}"
|
inst1_bl_name = "bl_{}"
|
||||||
inst1_br_name = "br_{}"
|
inst1_br_name = "br_{}"
|
||||||
if self.has_rbl() and self.port==0:
|
if self.port==0:
|
||||||
start_bit=1
|
start_bit=1
|
||||||
else:
|
else:
|
||||||
start_bit=0
|
start_bit=0
|
||||||
|
|
@ -524,7 +514,7 @@ class port_data(design.design):
|
||||||
inst1 = self.precharge_array_inst
|
inst1 = self.precharge_array_inst
|
||||||
inst1_bl_name = "bl_{}"
|
inst1_bl_name = "bl_{}"
|
||||||
inst1_br_name = "br_{}"
|
inst1_br_name = "br_{}"
|
||||||
if self.has_rbl() and self.port==0:
|
if self.port==0:
|
||||||
start_bit=1
|
start_bit=1
|
||||||
else:
|
else:
|
||||||
start_bit=0
|
start_bit=0
|
||||||
|
|
@ -560,11 +550,11 @@ class port_data(design.design):
|
||||||
""" Add the bitline pins for the given port """
|
""" Add the bitline pins for the given port """
|
||||||
|
|
||||||
# Connect one bitline to the RBL and offset the indices for the other BLs
|
# Connect one bitline to the RBL and offset the indices for the other BLs
|
||||||
if self.has_rbl() and self.port==0:
|
if self.port==0:
|
||||||
self.copy_layout_pin(self.precharge_array_inst, "bl_0", "rbl_bl")
|
self.copy_layout_pin(self.precharge_array_inst, "bl_0", "rbl_bl")
|
||||||
self.copy_layout_pin(self.precharge_array_inst, "br_0", "rbl_br")
|
self.copy_layout_pin(self.precharge_array_inst, "br_0", "rbl_br")
|
||||||
bit_offset=1
|
bit_offset=1
|
||||||
elif self.has_rbl() and self.port==1:
|
elif self.port==1:
|
||||||
self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(self.num_cols), "rbl_bl")
|
self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(self.num_cols), "rbl_bl")
|
||||||
self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(self.num_cols), "rbl_br")
|
self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(self.num_cols), "rbl_br")
|
||||||
bit_offset=0
|
bit_offset=0
|
||||||
|
|
@ -669,5 +659,3 @@ class port_data(design.design):
|
||||||
if self.precharge_array_inst:
|
if self.precharge_array_inst:
|
||||||
self.graph_inst_exclude.add(self.precharge_array_inst)
|
self.graph_inst_exclude.add(self.precharge_array_inst)
|
||||||
|
|
||||||
def has_rbl(self):
|
|
||||||
return self.port in self.read_ports
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ class replica_bitcell_array(design.design):
|
||||||
self.right_rbl = right_rbl
|
self.right_rbl = right_rbl
|
||||||
self.bitcell_ports = bitcell_ports
|
self.bitcell_ports = bitcell_ports
|
||||||
|
|
||||||
debug.check(left_rbl+right_rbl==len(self.read_ports),"Invalid number of RBLs for port configuration.")
|
debug.check(left_rbl+right_rbl==len(self.all_ports),"Invalid number of RBLs for port configuration.")
|
||||||
debug.check(left_rbl+right_rbl==len(self.bitcell_ports),"Bitcell ports must match total RBLs.")
|
debug.check(left_rbl+right_rbl==len(self.bitcell_ports),"Bitcell ports must match total RBLs.")
|
||||||
|
|
||||||
# Two dummy rows/cols plus replica for each port
|
# Two dummy rows/cols plus replica for each port
|
||||||
|
|
@ -374,16 +374,6 @@ class replica_bitcell_array(design.design):
|
||||||
""" Return the BR for the given RBL port """
|
""" Return the BR for the given RBL port """
|
||||||
return self.rbl_br_names[port]
|
return self.rbl_br_names[port]
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load):
|
|
||||||
"""Returns relative delay of the bitline in the bitcell array"""
|
|
||||||
from tech import parameter
|
|
||||||
#The load being driven/drained is mostly the bitline but could include the sense amp or the column mux.
|
|
||||||
#The load from the bitlines is due to the drain capacitances from all the other bitlines and wire parasitics.
|
|
||||||
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
|
|
||||||
wire_unit_load = 0.05 * drain_load #Wires add 5% to this.
|
|
||||||
bitline_load = (drain_load+wire_unit_load)*self.row_size
|
|
||||||
return [self.cell.analytical_delay(corner, slew, load+bitline_load)]
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Power of Bitcell array and bitline in nW."""
|
"""Power of Bitcell array and bitline in nW."""
|
||||||
from tech import drc, parameter
|
from tech import drc, parameter
|
||||||
|
|
@ -403,15 +393,6 @@ class replica_bitcell_array(design.design):
|
||||||
cell_power.leakage * self.column_size * self.row_size)
|
cell_power.leakage * self.column_size * self.row_size)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def gen_wl_wire(self):
|
|
||||||
if OPTS.netlist_only:
|
|
||||||
width = 0
|
|
||||||
else:
|
|
||||||
width = self.width
|
|
||||||
wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_metal1"))
|
|
||||||
wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
|
|
||||||
return wl_wire
|
|
||||||
|
|
||||||
def gen_bl_wire(self):
|
def gen_bl_wire(self):
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
height = 0
|
height = 0
|
||||||
|
|
@ -422,15 +403,6 @@ class replica_bitcell_array(design.design):
|
||||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
||||||
return bl_wire
|
return bl_wire
|
||||||
|
|
||||||
def output_load(self, bl_pos=0):
|
|
||||||
bl_wire = self.gen_bl_wire()
|
|
||||||
return bl_wire.wire_c # sense amp only need to charge small portion of the bl
|
|
||||||
# set as one segment for now
|
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
wl_wire = self.gen_wl_wire()
|
|
||||||
return wl_wire.return_input_cap()
|
|
||||||
|
|
||||||
def get_wordline_cin(self):
|
def get_wordline_cin(self):
|
||||||
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
||||||
#A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
#A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||||
|
|
|
||||||
|
|
@ -33,15 +33,18 @@ class sense_amp(design.design):
|
||||||
self.pin_map = sense_amp.pin_map
|
self.pin_map = sense_amp.pin_map
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def input_load(self):
|
def get_cin(self):
|
||||||
|
|
||||||
|
# FIXME: This input load will be applied to both the s_en timing and bitline timing.
|
||||||
|
|
||||||
#Input load for the bitlines which are connected to the source/drain of a TX. Not the selects.
|
#Input load for the bitlines which are connected to the source/drain of a TX. Not the selects.
|
||||||
from tech import spice, parameter
|
from tech import spice, parameter
|
||||||
# Default is 8x. Per Samira and Hodges-Jackson book:
|
# Default is 8x. Per Samira and Hodges-Jackson book:
|
||||||
# "Column-mux transistors driven by the decoder must be sized for optimal speed"
|
# "Column-mux transistors driven by the decoder must be sized for optimal speed"
|
||||||
bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file.
|
bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file.
|
||||||
return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff
|
return spice["min_tx_drain_c"]*(bitline_pmos_size)#ff
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load):
|
def get_stage_effort(self, load):
|
||||||
#Delay of the sense amp will depend on the size of the amp and the output load.
|
#Delay of the sense amp will depend on the size of the amp and the output load.
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
cin = (parameter["sa_inv_pmos_size"] + parameter["sa_inv_nmos_size"])/drc("minwidth_tx")
|
cin = (parameter["sa_inv_pmos_size"] + parameter["sa_inv_nmos_size"])/drc("minwidth_tx")
|
||||||
|
|
|
||||||
|
|
@ -145,9 +145,6 @@ class sense_amp_array(design.design):
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
return self.amp.input_load()
|
return self.amp.input_load()
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load):
|
|
||||||
return [self.amp.analytical_delay(corner, slew=slew, load=load)]
|
|
||||||
|
|
||||||
def get_en_cin(self):
|
def get_en_cin(self):
|
||||||
"""Get the relative capacitance of all the sense amp enable connections in the array"""
|
"""Get the relative capacitance of all the sense amp enable connections in the array"""
|
||||||
sense_amp_en_cin = self.amp.get_en_cin()
|
sense_amp_en_cin = self.amp.get_en_cin()
|
||||||
|
|
|
||||||
|
|
@ -210,15 +210,6 @@ class single_level_column_mux_array(design.design):
|
||||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||||
offset=br_out_offset)
|
offset=br_out_offset)
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load):
|
|
||||||
from tech import parameter
|
|
||||||
"""Returns relative delay that the column mux adds"""
|
|
||||||
#Single level column mux will add parasitic loads from other mux pass transistors and the sense amp.
|
|
||||||
drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap'])
|
|
||||||
array_load = drain_load*self.words_per_row
|
|
||||||
return [self.mux.analytical_delay(corner, slew, load+array_load)]
|
|
||||||
|
|
||||||
def get_drain_cin(self):
|
def get_drain_cin(self):
|
||||||
"""Get the relative capacitance of the drain of the NMOS pass TX"""
|
"""Get the relative capacitance of the drain of the NMOS pass TX"""
|
||||||
from tech import parameter
|
from tech import parameter
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from tech import GDS,layer
|
||||||
|
|
||||||
class tri_gate(design.design):
|
class tri_gate(design.design):
|
||||||
"""
|
"""
|
||||||
This module implements the tri gate cell used in the design for
|
This module implements the tri gate cell used in the design forS
|
||||||
bit-line isolation. It is a hand-made cell, so the layout and
|
bit-line isolation. It is a hand-made cell, so the layout and
|
||||||
netlist should be available in the technology library.
|
netlist should be available in the technology library.
|
||||||
"""
|
"""
|
||||||
|
|
@ -36,19 +36,13 @@ class tri_gate(design.design):
|
||||||
self.pin_map = tri_gate.pin_map
|
self.pin_map = tri_gate.pin_map
|
||||||
self.add_pin_types(self.type_list)
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
from tech import spice
|
|
||||||
r = spice["min_tx_r"]
|
|
||||||
c_para = spice["min_tx_drain_c"]
|
|
||||||
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Returns dynamic and leakage power. Results in nW"""
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
|
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
|
||||||
total_power = self.return_power()
|
total_power = self.return_power()
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def input_load(self):
|
def get_cin(self):
|
||||||
return 9*spice["min_tx_gate_c"]
|
return 9*spice["min_tx_gate_c"]
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
|
|
|
||||||
|
|
@ -121,9 +121,3 @@ class tri_gate_array(design.design):
|
||||||
offset=enbar_pin.ll().scale(0, 1),
|
offset=enbar_pin.ll().scale(0, 1),
|
||||||
width=width,
|
width=width,
|
||||||
height=drc("minwidth_metal1"))
|
height=drc("minwidth_metal1"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
return self.tri.analytical_delay(corner, slew = slew, load = load)
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -210,21 +210,6 @@ class wordline_driver(design.design):
|
||||||
start=wl_offset,
|
start=wl_offset,
|
||||||
end=wl_offset-vector(self.m1_width,0))
|
end=wl_offset-vector(self.m1_width,0))
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0):
|
|
||||||
# decode -> net
|
|
||||||
decode_t_net = self.nand2.analytical_delay(corner, slew, self.inv.input_load())
|
|
||||||
|
|
||||||
# net -> wl
|
|
||||||
net_t_wl = self.inv.analytical_delay(corner, decode_t_net.slew, load)
|
|
||||||
|
|
||||||
return decode_t_net + net_t_wl
|
|
||||||
|
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
"""Gets the capacitance of the wordline driver in absolute units (fF)"""
|
|
||||||
return self.nand2.input_load()
|
|
||||||
|
|
||||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
||||||
"""Follows the clk_buf to a wordline signal adding each stages stage effort to a list"""
|
"""Follows the clk_buf to a wordline signal adding each stages stage effort to a list"""
|
||||||
stage_effort_list = []
|
stage_effort_list = []
|
||||||
|
|
|
||||||
|
|
@ -110,14 +110,6 @@ class pand2(pgate.pgate):
|
||||||
width=pin.width(),
|
width=pin.width(),
|
||||||
height=pin.height())
|
height=pin.height())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
""" Calculate the analytical delay of DFF-> INV -> INV """
|
|
||||||
nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load())
|
|
||||||
inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load)
|
|
||||||
return nand_delay + inv_delay
|
|
||||||
|
|
||||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
"""Get the stage efforts of the A or B -> Z path"""
|
"""Get the stage efforts of the A or B -> Z path"""
|
||||||
stage_effort_list = []
|
stage_effort_list = []
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
# 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
|
||||||
|
from tech import drc
|
||||||
|
from math import log
|
||||||
|
from vector import vector
|
||||||
|
from globals import OPTS
|
||||||
|
import pgate
|
||||||
|
from sram_factory import factory
|
||||||
|
|
||||||
|
class pand3(pgate.pgate):
|
||||||
|
"""
|
||||||
|
This is a simple buffer used for driving loads.
|
||||||
|
"""
|
||||||
|
def __init__(self, name, size=1, height=None):
|
||||||
|
debug.info(1, "Creating pand3 {}".format(name))
|
||||||
|
self.add_comment("size: {}".format(size))
|
||||||
|
|
||||||
|
self.size = size
|
||||||
|
|
||||||
|
# Creates the netlist and layout
|
||||||
|
pgate.pgate.__init__(self, name, height)
|
||||||
|
|
||||||
|
def create_netlist(self):
|
||||||
|
self.add_pins()
|
||||||
|
self.create_modules()
|
||||||
|
self.create_insts()
|
||||||
|
|
||||||
|
def create_modules(self):
|
||||||
|
# Shield the cap, but have at least a stage effort of 4
|
||||||
|
self.nand = factory.create(module_type="pnand3",height=self.height)
|
||||||
|
self.add_mod(self.nand)
|
||||||
|
|
||||||
|
self.inv = factory.create(module_type="pinv", size=self.size, height=self.height)
|
||||||
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
|
def create_layout(self):
|
||||||
|
self.width = self.nand.width + self.inv.width
|
||||||
|
self.place_insts()
|
||||||
|
self.add_wires()
|
||||||
|
self.add_layout_pins()
|
||||||
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
def add_pins(self):
|
||||||
|
self.add_pin("A", "INPUT")
|
||||||
|
self.add_pin("B", "INPUT")
|
||||||
|
self.add_pin("C", "INPUT")
|
||||||
|
self.add_pin("Z", "OUTPUT")
|
||||||
|
self.add_pin("vdd", "POWER")
|
||||||
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
|
def create_insts(self):
|
||||||
|
self.nand_inst=self.add_inst(name="pand3_nand",
|
||||||
|
mod=self.nand)
|
||||||
|
self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"])
|
||||||
|
|
||||||
|
self.inv_inst=self.add_inst(name="pand3_inv",
|
||||||
|
mod=self.inv)
|
||||||
|
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||||
|
|
||||||
|
def place_insts(self):
|
||||||
|
# Add NAND to the right
|
||||||
|
self.nand_inst.place(offset=vector(0,0))
|
||||||
|
|
||||||
|
# Add INV to the right
|
||||||
|
self.inv_inst.place(offset=vector(self.nand_inst.rx(),0))
|
||||||
|
|
||||||
|
def add_wires(self):
|
||||||
|
# nand Z to inv A
|
||||||
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
|
a2_pin = self.inv_inst.get_pin("A")
|
||||||
|
mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
|
||||||
|
mid2_point = vector(mid1_point, a2_pin.cy())
|
||||||
|
self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
|
||||||
|
|
||||||
|
|
||||||
|
def add_layout_pins(self):
|
||||||
|
# Continous vdd rail along with label.
|
||||||
|
vdd_pin=self.inv_inst.get_pin("vdd")
|
||||||
|
self.add_layout_pin(text="vdd",
|
||||||
|
layer="metal1",
|
||||||
|
offset=vdd_pin.ll().scale(0,1),
|
||||||
|
width=self.width,
|
||||||
|
height=vdd_pin.height())
|
||||||
|
|
||||||
|
# Continous gnd rail along with label.
|
||||||
|
gnd_pin=self.inv_inst.get_pin("gnd")
|
||||||
|
self.add_layout_pin(text="gnd",
|
||||||
|
layer="metal1",
|
||||||
|
offset=gnd_pin.ll().scale(0,1),
|
||||||
|
width=self.width,
|
||||||
|
height=vdd_pin.height())
|
||||||
|
|
||||||
|
pin = self.inv_inst.get_pin("Z")
|
||||||
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
for pin_name in ["A","B", "C"]:
|
||||||
|
pin = self.nand_inst.get_pin(pin_name)
|
||||||
|
self.add_layout_pin_rect_center(text=pin_name,
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def analytical_delay(self, corner, slew, load=0.0):
|
||||||
|
""" Calculate the analytical delay of DFF-> INV -> INV """
|
||||||
|
nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load())
|
||||||
|
inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load)
|
||||||
|
return nand_delay + inv_delay
|
||||||
|
|
||||||
|
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
|
"""Get the stage efforts of the A or B -> Z path"""
|
||||||
|
stage_effort_list = []
|
||||||
|
stage1_cout = self.inv.get_cin()
|
||||||
|
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||||
|
stage_effort_list.append(stage1)
|
||||||
|
last_stage_is_rise = stage1.is_rise
|
||||||
|
|
||||||
|
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
||||||
|
stage_effort_list.append(stage2)
|
||||||
|
|
||||||
|
return stage_effort_list
|
||||||
|
|
||||||
|
def get_cin(self):
|
||||||
|
"""Return the relative input capacitance of a single input"""
|
||||||
|
return self.nand.get_cin()
|
||||||
|
|
||||||
|
|
@ -113,14 +113,6 @@ class pbuf(pgate.pgate):
|
||||||
width=a_pin.width(),
|
width=a_pin.width(),
|
||||||
height=a_pin.height())
|
height=a_pin.height())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
""" Calculate the analytical delay of DFF-> INV -> INV """
|
|
||||||
inv1_delay = self.inv1.analytical_delay(corner, slew=slew, load=self.inv2.input_load())
|
|
||||||
inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load)
|
|
||||||
return inv1_delay + inv2_delay
|
|
||||||
|
|
||||||
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
"""Get the stage efforts of the A -> Z path"""
|
"""Get the stage efforts of the A -> Z path"""
|
||||||
stage_effort_list = []
|
stage_effort_list = []
|
||||||
|
|
|
||||||
|
|
@ -174,30 +174,6 @@ class pdriver(pgate.pgate):
|
||||||
width = a_pin.width(),
|
width = a_pin.width(),
|
||||||
height = a_pin.height())
|
height = a_pin.height())
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
return self.inv_list[0].input_load()
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
""" Calculate the analytical delay of INV1 -> ... -> INVn """
|
|
||||||
|
|
||||||
cout_list = []
|
|
||||||
for prev_inv,inv in zip(self.inv_list, self.inv_list[1:]):
|
|
||||||
cout_list.append(inv.input_load())
|
|
||||||
cout_list.append(load)
|
|
||||||
|
|
||||||
input_slew = slew
|
|
||||||
|
|
||||||
delays = []
|
|
||||||
for inv,cout in zip(self.inv_list,cout_list):
|
|
||||||
delays.append(inv.analytical_delay(corner, slew=input_slew, load=cout))
|
|
||||||
input_slew = delays[-1].slew
|
|
||||||
|
|
||||||
delay = delays[0]
|
|
||||||
for i in range(len(delays)-1):
|
|
||||||
delay += delays[i]
|
|
||||||
|
|
||||||
return delay
|
|
||||||
|
|
||||||
def get_sizes(self):
|
def get_sizes(self):
|
||||||
""" Return the relative sizes of the buffers """
|
""" Return the relative sizes of the buffers """
|
||||||
return self.size_list
|
return self.size_list
|
||||||
|
|
|
||||||
|
|
@ -255,15 +255,6 @@ class pinv(pgate.pgate):
|
||||||
|
|
||||||
self.connect_pin_to_rail(self.pmos_inst,"S","vdd")
|
self.connect_pin_to_rail(self.pmos_inst,"S","vdd")
|
||||||
|
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"])
|
|
||||||
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
|
|
||||||
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Returns dynamic and leakage power. Results in nW"""
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
c_eff = self.calculate_effective_capacitance(load)
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
|
|
@ -281,7 +272,7 @@ class pinv(pgate.pgate):
|
||||||
transition_prob = spice["inv_transition_prob"]
|
transition_prob = spice["inv_transition_prob"]
|
||||||
return transition_prob*(c_load + c_para)
|
return transition_prob*(c_load + c_para)
|
||||||
|
|
||||||
def get_cin(self):
|
def input_load(self):
|
||||||
"""Return the capacitance of the gate connection in generic capacitive
|
"""Return the capacitance of the gate connection in generic capacitive
|
||||||
units relative to the minimum width of a transistor"""
|
units relative to the minimum width of a transistor"""
|
||||||
return self.nmos_size + self.pmos_size
|
return self.nmos_size + self.pmos_size
|
||||||
|
|
@ -293,7 +284,7 @@ class pinv(pgate.pgate):
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
return logical_effort.logical_effort(self.name,
|
return logical_effort.logical_effort(self.name,
|
||||||
self.size,
|
self.size,
|
||||||
self.get_cin(),
|
self.input_load(),
|
||||||
cout,
|
cout,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
not inp_is_rise)
|
not inp_is_rise)
|
||||||
|
|
|
||||||
|
|
@ -179,14 +179,6 @@ class pinvbuf(pgate.pgate):
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
offset=a_pin.center())
|
offset=a_pin.center())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
""" Calculate the analytical delay of DFF-> INV -> INV """
|
|
||||||
inv1_delay = self.inv1.analytical_delay(corner, slew=slew, load=self.inv2.input_load())
|
|
||||||
inv2_delay = self.inv2.analytical_delay(corner, slew=inv1_delay.slew, load=load)
|
|
||||||
return inv1_delay + inv2_delay
|
|
||||||
|
|
||||||
def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False):
|
def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
"""Get the stage efforts of the clk -> clk_buf path"""
|
"""Get the stage efforts of the clk -> clk_buf path"""
|
||||||
stage_effort_list = []
|
stage_effort_list = []
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,6 @@ class pnand2(pgate.pgate):
|
||||||
# Creates the netlist and layout
|
# Creates the netlist and layout
|
||||||
pgate.pgate.__init__(self, name, height)
|
pgate.pgate.__init__(self, name, height)
|
||||||
|
|
||||||
#For characterization purposes only
|
|
||||||
#self.exclude_nmos_from_graph()
|
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_ptx()
|
self.add_ptx()
|
||||||
|
|
@ -233,17 +230,6 @@ class pnand2(pgate.pgate):
|
||||||
width=contact.m1m2.first_layer_height,
|
width=contact.m1m2.first_layer_height,
|
||||||
height=contact.m1m2.first_layer_width)
|
height=contact.m1m2.first_layer_width)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"])
|
|
||||||
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
|
|
||||||
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Returns dynamic and leakage power. Results in nW"""
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
c_eff = self.calculate_effective_capacitance(load)
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
|
|
@ -261,7 +247,7 @@ class pnand2(pgate.pgate):
|
||||||
transition_prob = spice["nand2_transition_prob"]
|
transition_prob = spice["nand2_transition_prob"]
|
||||||
return transition_prob*(c_load + c_para)
|
return transition_prob*(c_load + c_para)
|
||||||
|
|
||||||
def get_cin(self):
|
def input_load(self):
|
||||||
"""Return the relative input capacitance of a single input"""
|
"""Return the relative input capacitance of a single input"""
|
||||||
return self.nmos_size+self.pmos_size
|
return self.nmos_size+self.pmos_size
|
||||||
|
|
||||||
|
|
@ -270,14 +256,7 @@ class pnand2(pgate.pgate):
|
||||||
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
|
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
|
||||||
"""
|
"""
|
||||||
parasitic_delay = 2
|
parasitic_delay = 2
|
||||||
return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise)
|
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise)
|
||||||
|
|
||||||
def exclude_nmos_from_graph(self):
|
|
||||||
"""Exclude the nmos TXs from the graph to reduce complexity"""
|
|
||||||
#The pull-down network has an internal net which causes 2 different in->out paths
|
|
||||||
#Removing them simplifies generic path searching.
|
|
||||||
self.graph_inst_exclude.add(self.nmos1_inst)
|
|
||||||
self.graph_inst_exclude.add(self.nmos2_inst)
|
|
||||||
|
|
||||||
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."""
|
||||||
|
|
|
||||||
|
|
@ -243,16 +243,6 @@ class pnand3(pgate.pgate):
|
||||||
width=contact.m1m2.first_layer_width,
|
width=contact.m1m2.first_layer_width,
|
||||||
height=contact.m1m2.first_layer_height)
|
height=contact.m1m2.first_layer_height)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"])
|
|
||||||
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
|
|
||||||
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Returns dynamic and leakage power. Results in nW"""
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
c_eff = self.calculate_effective_capacitance(load)
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
|
|
@ -270,7 +260,7 @@ class pnand3(pgate.pgate):
|
||||||
transition_prob = spice["nand3_transition_prob"]
|
transition_prob = spice["nand3_transition_prob"]
|
||||||
return transition_prob*(c_load + c_para)
|
return transition_prob*(c_load + c_para)
|
||||||
|
|
||||||
def get_cin(self):
|
def input_load(self):
|
||||||
"""Return the relative input capacitance of a single input"""
|
"""Return the relative input capacitance of a single input"""
|
||||||
return self.nmos_size+self.pmos_size
|
return self.nmos_size+self.pmos_size
|
||||||
|
|
||||||
|
|
@ -279,7 +269,7 @@ class pnand3(pgate.pgate):
|
||||||
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
|
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
|
||||||
"""
|
"""
|
||||||
parasitic_delay = 3
|
parasitic_delay = 3
|
||||||
return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise)
|
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise)
|
||||||
|
|
||||||
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."""
|
||||||
|
|
|
||||||
|
|
@ -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 contact
|
||||||
import pgate
|
import pgate
|
||||||
import debug
|
import debug
|
||||||
from tech import drc, parameter, spice
|
from tech import drc, parameter, spice
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import contact
|
import logical_effort
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
class pnor2(pgate.pgate):
|
class pnor2(pgate.pgate):
|
||||||
|
|
@ -38,28 +39,30 @@ class pnor2(pgate.pgate):
|
||||||
pgate.pgate.__init__(self, name, height)
|
pgate.pgate.__init__(self, name, height)
|
||||||
|
|
||||||
|
|
||||||
def add_pins(self):
|
|
||||||
""" Adds pins for spice netlist """
|
|
||||||
pin_list = ["A", "B", "Z", "vdd", "gnd"]
|
|
||||||
dir_list = ["INPUT", "INPUT", "OUTPUT", "INOUT", "INOUT"]
|
|
||||||
self.add_pin_list(pin_list, dir_list)
|
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
|
self.add_ptx()
|
||||||
self.create_ptx()
|
self.create_ptx()
|
||||||
self.setup_layout_constants()
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
""" Calls all functions related to the generation of the layout """
|
""" Calls all functions related to the generation of the layout """
|
||||||
self.add_supply_rails()
|
|
||||||
self.add_ptx()
|
self.setup_layout_constants()
|
||||||
|
self.route_supply_rails()
|
||||||
|
self.place_ptx()
|
||||||
self.connect_rails()
|
self.connect_rails()
|
||||||
self.add_well_contacts()
|
self.add_well_contacts()
|
||||||
self.extend_wells(self.well_pos)
|
self.extend_wells(self.well_pos)
|
||||||
self.route_inputs()
|
self.route_inputs()
|
||||||
self.route_output()
|
self.route_output()
|
||||||
|
|
||||||
def create_ptx(self):
|
def add_pins(self):
|
||||||
|
""" Adds pins for spice netlist """
|
||||||
|
pin_list = ["A", "B", "Z", "vdd", "gnd"]
|
||||||
|
dir_list = ["INPUT", "INPUT", "OUTPUT", "INOUT", "INOUT"]
|
||||||
|
self.add_pin_list(pin_list, dir_list)
|
||||||
|
|
||||||
|
def add_ptx(self):
|
||||||
""" Create the PMOS and NMOS transistors. """
|
""" Create the PMOS and NMOS transistors. """
|
||||||
self.nmos = factory.create(module_type="ptx",
|
self.nmos = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
|
|
@ -104,7 +107,7 @@ class pnor2(pgate.pgate):
|
||||||
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
|
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
|
||||||
drc("poly_extend_active"), self.poly_space)
|
drc("poly_extend_active"), self.poly_space)
|
||||||
|
|
||||||
def add_supply_rails(self):
|
def route_supply_rails(self):
|
||||||
""" Add vdd/gnd rails to the top and bottom. """
|
""" Add vdd/gnd rails to the top and bottom. """
|
||||||
self.add_layout_pin_rect_center(text="gnd",
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
layer="metal1",
|
layer="metal1",
|
||||||
|
|
@ -116,7 +119,31 @@ class pnor2(pgate.pgate):
|
||||||
offset=vector(0.5*self.width,self.height),
|
offset=vector(0.5*self.width,self.height),
|
||||||
width=self.width)
|
width=self.width)
|
||||||
|
|
||||||
def add_ptx(self):
|
def create_ptx(self):
|
||||||
|
"""
|
||||||
|
Add PMOS and NMOS to the layout at the upper-most and lowest position
|
||||||
|
to provide maximum routing in channel
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.pmos1_inst=self.add_inst(name="pnor2_pmos1",
|
||||||
|
mod=self.pmos)
|
||||||
|
self.connect_inst(["vdd", "A", "net1", "vdd"])
|
||||||
|
|
||||||
|
self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
|
||||||
|
mod=self.pmos)
|
||||||
|
self.connect_inst(["net1", "B", "Z", "vdd"])
|
||||||
|
|
||||||
|
|
||||||
|
self.nmos1_inst=self.add_inst(name="pnor2_nmos1",
|
||||||
|
mod=self.nmos)
|
||||||
|
self.connect_inst(["Z", "A", "gnd", "gnd"])
|
||||||
|
|
||||||
|
self.nmos2_inst=self.add_inst(name="pnor2_nmos2",
|
||||||
|
mod=self.nmos)
|
||||||
|
self.connect_inst(["Z", "B", "gnd", "gnd"])
|
||||||
|
|
||||||
|
|
||||||
|
def place_ptx(self):
|
||||||
"""
|
"""
|
||||||
Add PMOS and NMOS to the layout at the upper-most and lowest position
|
Add PMOS and NMOS to the layout at the upper-most and lowest position
|
||||||
to provide maximum routing in channel
|
to provide maximum routing in channel
|
||||||
|
|
@ -124,29 +151,16 @@ class pnor2(pgate.pgate):
|
||||||
|
|
||||||
pmos1_pos = vector(self.pmos.active_offset.x,
|
pmos1_pos = vector(self.pmos.active_offset.x,
|
||||||
self.height - self.pmos.active_height - self.top_bottom_space)
|
self.height - self.pmos.active_height - self.top_bottom_space)
|
||||||
self.pmos1_inst=self.add_inst(name="pnor2_pmos1",
|
self.pmos1_inst.place(pmos1_pos)
|
||||||
mod=self.pmos,
|
|
||||||
offset=pmos1_pos)
|
|
||||||
self.connect_inst(["vdd", "A", "net1", "vdd"])
|
|
||||||
|
|
||||||
self.pmos2_pos = pmos1_pos + self.overlap_offset
|
self.pmos2_pos = pmos1_pos + self.overlap_offset
|
||||||
self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
|
self.pmos2_inst.place(self.pmos2_pos)
|
||||||
mod=self.pmos,
|
|
||||||
offset=self.pmos2_pos)
|
|
||||||
self.connect_inst(["net1", "B", "Z", "vdd"])
|
|
||||||
|
|
||||||
|
|
||||||
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
|
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
|
||||||
self.nmos1_inst=self.add_inst(name="pnor2_nmos1",
|
self.nmos1_inst.place(nmos1_pos)
|
||||||
mod=self.nmos,
|
|
||||||
offset=nmos1_pos)
|
|
||||||
self.connect_inst(["Z", "A", "gnd", "gnd"])
|
|
||||||
|
|
||||||
self.nmos2_pos = nmos1_pos + self.overlap_offset
|
self.nmos2_pos = nmos1_pos + self.overlap_offset
|
||||||
self.nmos2_inst=self.add_inst(name="pnor2_nmos2",
|
self.nmos2_inst.place(self.nmos2_pos)
|
||||||
mod=self.nmos,
|
|
||||||
offset=self.nmos2_pos)
|
|
||||||
self.connect_inst(["Z", "B", "gnd", "gnd"])
|
|
||||||
|
|
||||||
# Output position will be in between the PMOS and NMOS
|
# Output position will be in between the PMOS and NMOS
|
||||||
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
|
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
|
||||||
|
|
@ -213,16 +227,6 @@ class pnor2(pgate.pgate):
|
||||||
width=contact.m1m2.first_layer_height,
|
width=contact.m1m2.first_layer_height,
|
||||||
height=contact.m1m2.first_layer_width)
|
height=contact.m1m2.first_layer_width)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def input_load(self):
|
|
||||||
return ((self.nmos_size+self.pmos_size)/parameter["min_tx_size"])*spice["min_tx_gate_c"]
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
r = spice["min_tx_r"]/(self.nmos_size/parameter["min_tx_size"])
|
|
||||||
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
|
|
||||||
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Returns dynamic and leakage power. Results in nW"""
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
c_eff = self.calculate_effective_capacitance(load)
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
|
|
|
||||||
|
|
@ -210,18 +210,11 @@ class ptristate_inv(pgate.pgate):
|
||||||
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd")
|
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd")
|
||||||
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
|
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load=0.0):
|
|
||||||
from tech import spice
|
|
||||||
r = spice["min_tx_r"]
|
|
||||||
c_para = spice["min_tx_drain_c"]
|
|
||||||
return self.cal_delay_with_rc(corner, r = r, c = c_para+load, slew = slew)
|
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Returns dynamic and leakage power. Results in nW"""
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
|
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
|
||||||
total_power = self.return_power()
|
total_power = self.return_power()
|
||||||
return total_power
|
return total_power
|
||||||
|
|
||||||
def input_load(self):
|
def get_cin(self):
|
||||||
return 9*spice["min_tx_gate_c"]
|
return 9*spice["min_tx_gate_c"]
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ class single_level_column_mux(pgate.pgate):
|
||||||
width=self.bitcell.width,
|
width=self.bitcell.width,
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew, load):
|
def get_stage_effort(self, corner, slew, load):
|
||||||
"""Returns relative delay that the column mux. Difficult to convert to LE model."""
|
"""Returns relative delay that the column mux. Difficult to convert to LE model."""
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
cin = 2*self.tx_size #This is not CMOS, so using this may be incorrect.
|
cin = 2*self.tx_size #This is not CMOS, so using this may be incorrect.
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,7 @@ class sram_1bank(sram_base):
|
||||||
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
|
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
|
||||||
self.connect_vbus_m2m3(src_pin, dest_pin)
|
self.connect_vbus_m2m3(src_pin, dest_pin)
|
||||||
|
|
||||||
for port in self.read_ports:
|
for port in self.all_ports:
|
||||||
# Only input (besides pins) is the replica bitline
|
# Only input (besides pins) is the replica bitline
|
||||||
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
|
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
|
||||||
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
|
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
|
||||||
|
|
|
||||||
|
|
@ -339,7 +339,7 @@ class sram_base(design, verilog, lef):
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
temp.append("DOUT{0}[{1}]".format(port,bit))
|
temp.append("DOUT{0}[{1}]".format(port,bit))
|
||||||
for port in self.read_ports:
|
for port in self.all_ports:
|
||||||
temp.append("rbl_bl{0}".format(port))
|
temp.append("rbl_bl{0}".format(port))
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
|
|
@ -499,8 +499,6 @@ class sram_base(design, verilog, lef):
|
||||||
if port in self.readwrite_ports:
|
if port in self.readwrite_ports:
|
||||||
temp.append("web{}".format(port))
|
temp.append("web{}".format(port))
|
||||||
temp.append("clk{}".format(port))
|
temp.append("clk{}".format(port))
|
||||||
|
|
||||||
if port in self.read_ports:
|
|
||||||
temp.append("rbl_bl{}".format(port))
|
temp.append("rbl_bl{}".format(port))
|
||||||
|
|
||||||
# Ouputs
|
# Ouputs
|
||||||
|
|
@ -565,30 +563,6 @@ class sram_base(design, verilog, lef):
|
||||||
del usedMODS
|
del usedMODS
|
||||||
sp.close()
|
sp.close()
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, corner, slew,load):
|
|
||||||
""" Estimates the delay from clk -> DOUT
|
|
||||||
LH and HL are the same in analytical model. """
|
|
||||||
delays = {}
|
|
||||||
for port in self.all_ports:
|
|
||||||
if port in self.readonly_ports:
|
|
||||||
control_logic = self.control_logic_r
|
|
||||||
elif port in self.readwrite_ports:
|
|
||||||
control_logic = self.control_logic_rw
|
|
||||||
else:
|
|
||||||
delays[port] = self.return_delay(0,0) #Write ports do not have a lib defined delay, marked as 0
|
|
||||||
continue
|
|
||||||
clk_to_wlen_delays = control_logic.analytical_delay(corner, slew, load)
|
|
||||||
wlen_to_dout_delays = self.bank.analytical_delay(corner,slew,load,port) #port should probably be specified...
|
|
||||||
all_delays = clk_to_wlen_delays+wlen_to_dout_delays
|
|
||||||
total_delay = logical_effort.calculate_absolute_delay(all_delays)
|
|
||||||
total_delay = self.apply_corners_analytically(total_delay, corner)
|
|
||||||
last_slew = .1*all_delays[-1].get_absolute_delay() #slew approximated as 10% of delay
|
|
||||||
last_slew = self.apply_corners_analytically(last_slew, corner)
|
|
||||||
delays[port] = self.return_delay(delay=total_delay, slew=last_slew)
|
|
||||||
|
|
||||||
return delays
|
|
||||||
|
|
||||||
def get_wordline_stage_efforts(self, inp_is_rise=True):
|
def get_wordline_stage_efforts(self, inp_is_rise=True):
|
||||||
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
|
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
|
||||||
stage_effort_list = []
|
stage_effort_list = []
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# 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 unittest
|
||||||
|
from testutils import *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class pand3_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||||
|
global verify
|
||||||
|
import verify
|
||||||
|
|
||||||
|
import pand3
|
||||||
|
|
||||||
|
debug.info(2, "Testing pand3 gate 4x")
|
||||||
|
a = pand3.pand3(name="pand3x4", size=4)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# instantiate a copdsay of the class to actually run the test
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main(testRunner=debugTestRunner())
|
||||||
|
|
@ -15,7 +15,7 @@ from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
@unittest.skip("SKIPPING 21_model_delay_test")
|
# @unittest.skip("SKIPPING 21_model_delay_test")
|
||||||
class model_delay_test(openram_test):
|
class model_delay_test(openram_test):
|
||||||
""" Compare the accuracy of the analytical model with a spice simulation. """
|
""" Compare the accuracy of the analytical model with a spice simulation. """
|
||||||
|
|
||||||
|
|
@ -51,23 +51,28 @@ class model_delay_test(openram_test):
|
||||||
import tech
|
import tech
|
||||||
loads = [tech.spice["msflop_in_cap"]*4]
|
loads = [tech.spice["msflop_in_cap"]*4]
|
||||||
slews = [tech.spice["rise_time"]*2]
|
slews = [tech.spice["rise_time"]*2]
|
||||||
|
|
||||||
|
# Run a spice characterization
|
||||||
spice_data, port_data = d.analyze(probe_address, probe_data, slews, loads)
|
spice_data, port_data = d.analyze(probe_address, probe_data, slews, loads)
|
||||||
spice_data.update(port_data[0])
|
spice_data.update(port_data[0])
|
||||||
|
|
||||||
|
# Run analytical characterization
|
||||||
model_data, port_data = d.analytical_delay(slews, loads)
|
model_data, port_data = d.analytical_delay(slews, loads)
|
||||||
model_data.update(port_data[0])
|
model_data.update(port_data[0])
|
||||||
|
|
||||||
#Only compare the delays
|
# Only compare the delays
|
||||||
spice_delays = {key:value for key, value in spice_data.items() if 'delay' in key}
|
spice_delays = {key:value for key, value in spice_data.items() if 'delay' in key}
|
||||||
model_delays = {key:value for key, value in model_data.items() if 'delay' in key}
|
model_delays = {key:value for key, value in model_data.items() if 'delay' in key}
|
||||||
debug.info(1,"Spice Delays={}".format(spice_delays))
|
debug.info(1,"Spice Delays={}".format(spice_delays))
|
||||||
debug.info(1,"Model Delays={}".format(model_delays))
|
debug.info(1,"Model Delays={}".format(model_delays))
|
||||||
|
|
||||||
if OPTS.tech_name == "freepdk45":
|
if OPTS.tech_name == "freepdk45":
|
||||||
error_tolerance = 0.25
|
error_tolerance = 0.25
|
||||||
elif OPTS.tech_name == "scn4m_subm":
|
elif OPTS.tech_name == "scn4m_subm":
|
||||||
error_tolerance = 0.25
|
error_tolerance = 0.25
|
||||||
else:
|
else:
|
||||||
self.assertTrue(False) # other techs fail
|
self.assertTrue(False) # other techs fail
|
||||||
|
|
||||||
# Check if no too many or too few results
|
# Check if no too many or too few results
|
||||||
self.assertTrue(len(spice_delays.keys())==len(model_delays.keys()))
|
self.assertTrue(len(spice_delays.keys())==len(model_delays.keys()))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,27 +54,27 @@ class timing_sram_test(openram_test):
|
||||||
data.update(port_data[0])
|
data.update(port_data[0])
|
||||||
|
|
||||||
if OPTS.tech_name == "freepdk45":
|
if OPTS.tech_name == "freepdk45":
|
||||||
golden_data = {'delay_hl': [0.22609590000000002],
|
golden_data = {'delay_hl': [0.2264205],
|
||||||
'delay_lh': [0.22609590000000002],
|
'delay_lh': [0.2264205],
|
||||||
'leakage_power': 0.003317743,
|
'leakage_power': 0.0021017429999999997,
|
||||||
'min_period': 0.859,
|
'min_period': 0.859,
|
||||||
'read0_power': [0.3271056],
|
'read0_power': [0.3339161],
|
||||||
'read1_power': [0.3064244],
|
'read1_power': [0.31329440000000003],
|
||||||
'slew_hl': [0.2153979],
|
'slew_hl': [0.2590786],
|
||||||
'slew_lh': [0.2153979],
|
'slew_lh': [0.2590786],
|
||||||
'write0_power': [0.3532067],
|
'write0_power': [0.36360849999999995],
|
||||||
'write1_power': [0.3381259]}
|
'write1_power': [0.3486931]}
|
||||||
elif OPTS.tech_name == "scn4m_subm":
|
elif OPTS.tech_name == "scn4m_subm":
|
||||||
golden_data = {'delay_hl': [1.709791],
|
golden_data = {'delay_hl': [1.7083549999999998],
|
||||||
'delay_lh': [1.709791],
|
'delay_lh': [1.7083549999999998],
|
||||||
'leakage_power': 0.06803324999999999,
|
'leakage_power': 0.001119657,
|
||||||
'min_period': 7.812,
|
'min_period': 7.812,
|
||||||
'read0_power': [7.9499070000000005],
|
'read0_power': [8.013845],
|
||||||
'read1_power': [7.619662999999999],
|
'read1_power': [7.6889389999999995],
|
||||||
'slew_hl': [1.390261],
|
'slew_hl': [1.31918],
|
||||||
'slew_lh': [1.390261],
|
'slew_lh': [1.31918],
|
||||||
'write0_power': [8.913003],
|
'write0_power': [8.791557000000001],
|
||||||
'write1_power': [8.166687000000001]}
|
'write1_power': [8.70443]}
|
||||||
else:
|
else:
|
||||||
self.assertTrue(False) # other techs fail
|
self.assertTrue(False) # other techs fail
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import globals
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
@unittest.skip("SKIPPING 23_lib_sram_model_corners_test")
|
#@unittest.skip("SKIPPING 23_lib_sram_model_corners_test")
|
||||||
class lib_model_corners_lib_test(openram_test):
|
class lib_model_corners_lib_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import globals
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
@unittest.skip("SKIPPING 23_lib_sram_model_test")
|
#@unittest.skip("SKIPPING 23_lib_sram_model_test")
|
||||||
class lib_sram_model_test(openram_test):
|
class lib_sram_model_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ from sram_factory import factory
|
||||||
import debug
|
import debug
|
||||||
import getpass
|
import getpass
|
||||||
|
|
||||||
@unittest.skip("SKIPPING 30_openram_front_end_test")
|
#@unittest.skip("SKIPPING 30_openram_front_end_test")
|
||||||
class openram_front_end_test(openram_test):
|
class openram_front_end_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue