mirror of https://github.com/VLSIDA/OpenRAM.git
Added graph functions to compute analytical delay based on graph path.
This commit is contained in:
parent
2ce7323838
commit
6860d3258e
|
|
@ -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,31 @@ 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 = []
|
||||||
|
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_mode = self.edge_mods[(path[i+1], node)]
|
||||||
|
cout+=output_edge_mode.input_load()
|
||||||
|
# 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))
|
||||||
|
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,27 @@ 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"
|
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 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,12 +36,22 @@ 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.
|
||||||
return logical_effort.logical_effort('bitline', size, cin, load, parasitic_delay, False)
|
return logical_effort.logical_effort('bitline', size, cin, load, parasitic_delay, False)
|
||||||
|
|
||||||
|
def input_load(self):
|
||||||
|
"""Return the relative capacitance of the access transistor gates"""
|
||||||
|
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
|
||||||
|
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
|
||||||
|
|
||||||
|
#FIXME: The graph algorithm will apply this capacitance to the bitline load as they cannot be
|
||||||
|
# distinguished currently
|
||||||
|
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
|
||||||
|
return 2*access_tx_cin
|
||||||
|
|
||||||
def get_all_wl_names(self):
|
def get_all_wl_names(self):
|
||||||
""" Creates a list of all wordline pin names """
|
""" Creates a list of all wordline pin names """
|
||||||
row_pins = ["wl"]
|
row_pins = ["wl"]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -973,6 +973,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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,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"])
|
||||||
graph.add_edge(pin_dict["wl1"], pin_dict["br1"])
|
graph.add_edge(pin_dict["wl1"], pin_dict["br1"])
|
||||||
|
|
@ -47,6 +47,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.
|
||||||
|
|
@ -1275,36 +1275,65 @@ 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_data = self.get_empty_measure_data_dict()
|
port = self.read_ports[0]
|
||||||
relative_loads = [logical_effort.convert_farad_to_relative_c(c_farad) for c_farad in loads]
|
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]
|
||||||
|
|
||||||
|
debug.info(1,'Slew, Load, Delay(ns), Slew(ns)')
|
||||||
for slew in slews:
|
for slew in slews:
|
||||||
for load in relative_loads:
|
for load in loads:
|
||||||
self.set_load_slew(load,slew)
|
path_delays = self.graph.get_timing(bl_path, self.corner, slew, load)
|
||||||
bank_delay = self.sram.analytical_delay(self.corner, self.slew,self.load)
|
total_delay = self.sum_delays(path_delays)
|
||||||
for port in self.all_ports:
|
debug.info(1,'{}, {}, {}, {}'.format(slew,load,total_delay.delay/1e3, total_delay.slew/1e3))
|
||||||
for mname in self.delay_meas_names+self.power_meas_names:
|
|
||||||
if "power" in mname:
|
debug.error('Not finished with graph delay characterization.', 1)
|
||||||
port_data[port][mname].append(power.dynamic)
|
# power = self.analytical_power(slews, loads)
|
||||||
elif "delay" in mname:
|
# port_data = self.get_empty_measure_data_dict()
|
||||||
port_data[port][mname].append(bank_delay[port].delay/1e3)
|
# relative_loads = [logical_effort.convert_farad_to_relative_c(c_farad) for c_farad in loads]
|
||||||
elif "slew" in mname:
|
# for slew in slews:
|
||||||
port_data[port][mname].append(bank_delay[port].slew/1e3)
|
# for load in relative_loads:
|
||||||
else:
|
# self.set_load_slew(load,slew)
|
||||||
debug.error("Measurement name not recognized: {}".format(mname),1)
|
# bank_delay = self.sram.analytical_delay(self.corner, self.slew,self.load)
|
||||||
period_margin = 0.1
|
# for port in self.all_ports:
|
||||||
risefall_delay = bank_delay[self.read_ports[0]].delay/1e3
|
# for mname in self.delay_meas_names+self.power_meas_names:
|
||||||
sram_data = { "min_period":risefall_delay*2*period_margin,
|
# if "power" in mname:
|
||||||
"leakage_power": power.leakage}
|
# port_data[port][mname].append(power.dynamic)
|
||||||
debug.info(2,"SRAM Data:\n{}".format(sram_data))
|
# elif "delay" in mname:
|
||||||
debug.info(2,"Port Data:\n{}".format(port_data))
|
# port_data[port][mname].append(bank_delay[port].delay/1e3)
|
||||||
|
# elif "slew" in mname:
|
||||||
|
# port_data[port][mname].append(bank_delay[port].slew/1e3)
|
||||||
|
# else:
|
||||||
|
# debug.error("Measurement name not recognized: {}".format(mname),1)
|
||||||
|
# period_margin = 0.1
|
||||||
|
# risefall_delay = bank_delay[self.read_ports[0]].delay/1e3
|
||||||
|
# sram_data = { "min_period":risefall_delay*2*period_margin,
|
||||||
|
# "leakage_power": power.leakage}
|
||||||
|
# debug.info(2,"SRAM Data:\n{}".format(sram_data))
|
||||||
|
# debug.info(2,"Port Data:\n{}".format(port_data))
|
||||||
return (sram_data,port_data)
|
return (sram_data,port_data)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,3 +15,5 @@ output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
|
||||||
drc_name = "magic"
|
drc_name = "magic"
|
||||||
lvs_name = "netgen"
|
lvs_name = "netgen"
|
||||||
pex_name = "magic"
|
pex_name = "magic"
|
||||||
|
|
||||||
|
netlist_only = True
|
||||||
|
|
@ -41,7 +41,7 @@ class sense_amp(design.design):
|
||||||
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/parameter["min_tx_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")
|
||||||
|
|
|
||||||
|
|
@ -277,6 +277,11 @@ class pinv(pgate.pgate):
|
||||||
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 input_load(self):
|
||||||
|
"""Return the capacitance of the gate connection in generic capacitive
|
||||||
|
units relative to the minimum width of a transistor"""
|
||||||
|
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.
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,10 @@ class pnand2(pgate.pgate):
|
||||||
"""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 input_load(self):
|
||||||
|
"""Return the relative input capacitance of a single input"""
|
||||||
|
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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue