Merge branch 'dev' into control_fix

This commit is contained in:
Matt Guthaus 2019-08-10 13:07:30 -07:00
commit 8d6a4c74e7
48 changed files with 241 additions and 475 deletions

View File

@ -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()
@ -93,7 +96,33 @@ class timing_graph():
# Remove current vertex from path[] and mark it as unvisited # Remove current vertex from path[] and mark it as unvisited
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 """

View File

@ -110,7 +110,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
"""Initializes parameters relevant to the graph creation""" """Initializes parameters relevant to the graph creation"""
#Only initializes a set for checking instances which should not be added #Only initializes a set for checking instances which should not be added
self.graph_inst_exclude = set() self.graph_inst_exclude = set()
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Recursively create graph from instances in module.""" """Recursively create graph from instances in module."""
@ -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 """

View File

@ -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,14 +304,48 @@ 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):
""" """
Calculate the delay of a mosfet by Calculate the delay of a mosfet by

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -31,6 +31,20 @@ class replica_bitcell(design.design):
self.height = replica_bitcell.height self.height = replica_bitcell.height
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."""
@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -84,8 +84,4 @@ class replica_pbitcell(design.design):
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port)) self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
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()

View File

@ -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
@ -1275,39 +1275,71 @@ 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"""

View File

@ -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()
@ -78,4 +78,7 @@ def calculate_relative_rise_fall_delays(stage_effort_list):
def convert_farad_to_relative_c(c_farad): 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']

View File

@ -945,40 +945,7 @@ class bank(design.design):
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
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

View File

@ -148,17 +148,7 @@ class bitcell_array(design.design):
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
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

View File

@ -924,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

View File

@ -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.

View File

@ -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()

View File

@ -177,16 +177,7 @@ class dff_buf(design.design):
self.add_path("metal1", [self.mid_qb_pos, qb_pos]) self.add_path("metal1", [self.mid_qb_pos, qb_pos])
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.

View File

@ -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()

View File

@ -150,15 +150,7 @@ class dff_inv(design.design):
offset=dout_pin.center()) offset=dout_pin.center())
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()

View File

@ -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()

View File

@ -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

View File

@ -56,21 +56,4 @@ class hierarchical_predecode2x4(hierarchical_predecode):
["A_0", "Abar_1"], ["A_0", "Abar_1"],
["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()

View File

@ -65,21 +65,4 @@ class hierarchical_predecode3x8(hierarchical_predecode):
["A_0", "Abar_1", "A_2"], ["A_0", "Abar_1", "A_2"],
["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()

View File

@ -827,22 +827,4 @@ class multibank(design.design):
rotate=90) rotate=90)
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

View File

@ -373,17 +373,7 @@ class replica_bitcell_array(design.design):
def get_rbl_br_name(self, port): def get_rbl_br_name(self, port):
""" 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

View File

@ -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")

View File

@ -144,10 +144,7 @@ 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()

View File

@ -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

View File

@ -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):

View File

@ -120,10 +120,4 @@ class tri_gate_array(design.design):
layer="metal1", layer="metal1",
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)

View File

@ -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 = []

View File

@ -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 = []

View File

@ -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 = []

View File

@ -173,30 +173,6 @@ class pdriver(pgate.pgate):
offset=a_pin.center(), offset=a_pin.center(),
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 """

View File

@ -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,11 +272,11 @@ 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
def get_stage_effort(self, cout, inp_is_rise=True): def get_stage_effort(self, cout, inp_is_rise=True):
"""Returns an object representing the parameters for delay in tau units. """Returns an object representing the parameters for delay in tau units.
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.
@ -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)

View File

@ -178,15 +178,7 @@ class pinvbuf(pgate.pgate):
offset=a_pin.center()) offset=a_pin.center())
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 = []

View File

@ -37,10 +37,7 @@ 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,23 +247,16 @@ 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
def get_stage_effort(self, cout, inp_is_rise=True): def get_stage_effort(self, cout, inp_is_rise=True):
"""Returns an object representing the parameters for delay in tau units. """Returns an object representing the parameters for delay in tau units.
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."""

View File

@ -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."""

View File

@ -227,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)

View File

@ -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"]

View File

@ -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.

View File

@ -562,31 +562,7 @@ class sram_base(design, verilog, lef):
self.sp_write_file(sp, usedMODS) self.sp_write_file(sp, usedMODS)
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 = []

View File

@ -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()))

View File

@ -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):

View File

@ -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):

View File

@ -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):