Fixed merge conflict.

This commit is contained in:
jsowash 2019-08-11 14:39:36 -07:00
commit 2573b5f48b
53 changed files with 650 additions and 712 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()
@ -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 """

View File

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

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,10 +118,10 @@ 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,10 +116,10 @@ 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,8 +954,10 @@ 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"""
# 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. # 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

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

View File

@ -32,10 +32,17 @@ 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,10 +32,17 @@ 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

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

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

View File

@ -23,20 +23,20 @@ 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
@ -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']

View File

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

View File

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

View File

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

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

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

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

138
compiler/pgates/pand3.py Normal file
View File

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

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

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

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

View File

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

View File

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

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

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

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

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

View File

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

38
compiler/tests/04_pand3_test.py Executable file
View File

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

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,9 +51,12 @@ 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])
@ -62,12 +65,14 @@ class model_delay_test(openram_test):
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

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

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