Merge branch 'dev' into delay_ctrl

This commit is contained in:
Sam Crow
2023-06-05 16:24:48 -07:00
690 changed files with 347277 additions and 7674 deletions
+3 -4
View File
@@ -1,13 +1,13 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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 os
import debug
from globals import OPTS, find_exe, get_tool
from openram import debug
from openram import OPTS, find_exe, get_tool
from .lib import *
from .delay import *
from .elmore import *
@@ -56,4 +56,3 @@ if not OPTS.analytical_delay:
else:
debug.info(1, "Analytical model enabled.")
+325 -325
View File
@@ -1,325 +1,325 @@
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
import csv
import math
import numpy as np
import os
process_transform = {'SS':0.0, 'TT': 0.5, 'FF':1.0}
def get_data_names(file_name, exclude_area=True):
"""
Returns just the data names in the first row of the CSV
"""
with open(file_name, newline='') as csvfile:
csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|')
row_iter = 0
# reader is iterable not a list, probably a better way to do this
for row in csv_reader:
# Return names from first row
names = row[0].split(',')
break
if exclude_area:
try:
area_ind = names.index('area')
except ValueError:
area_ind = -1
if area_ind != -1:
names = names[:area_ind] + names[area_ind+1:]
return names
def get_data(file_name):
"""
Returns data in CSV as lists of features
"""
with open(file_name, newline='') as csvfile:
csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|')
row_iter = 0
removed_items = 1
for row in csv_reader:
row_iter += 1
if row_iter == 1:
feature_names = row[0].split(',')
input_list = [[] for _ in range(len(feature_names)-removed_items)]
try:
# Save to remove area
area_ind = feature_names.index('area')
except ValueError:
area_ind = -1
try:
process_ind = feature_names.index('process')
except:
debug.error('Process not included as a feature.')
continue
data = []
split_str = row[0].split(',')
for i in range(len(split_str)):
if i == process_ind:
data.append(process_transform[split_str[i]])
elif i == area_ind:
continue
else:
data.append(float(split_str[i]))
data[0] = math.log(data[0], 2)
for i in range(len(data)):
input_list[i].append(data[i])
return input_list
def apply_samples_to_data(all_data, algo_samples):
# Take samples from algorithm and match them to samples in data
data_samples, unused_data = [], []
sample_positions = set()
for sample in algo_samples:
sample_positions.add(find_sample_position_with_min_error(all_data, sample))
for i in range(len(all_data)):
if i in sample_positions:
data_samples.append(all_data[i])
else:
unused_data.append(all_data[i])
return data_samples, unused_data
def find_sample_position_with_min_error(data, sampled_vals):
min_error = 0
sample_pos = 0
count = 0
for data_slice in data:
error = squared_error(data_slice, sampled_vals)
if min_error == 0 or error < min_error:
min_error = error
sample_pos = count
count += 1
return sample_pos
def squared_error(list_a, list_b):
error_sum = 0;
for a,b in zip(list_a, list_b):
error_sum+=(a-b)**2
return error_sum
def get_max_min_from_datasets(dir):
if not os.path.isdir(dir):
debug.warning("Input Directory not found:{}".format(dir))
return [], [], []
# Assuming all files are CSV
data_files = [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))]
maxs,mins,sums,total_count = [],[],[],0
for file in data_files:
data = get_data(os.path.join(dir, file))
# Get max, min, sum, and count from every file
data_max, data_min, data_sum, count = [],[],[], 0
for feature_list in data:
data_max.append(max(feature_list))
data_min.append(min(feature_list))
data_sum.append(sum(feature_list))
count = len(feature_list)
# Aggregate the data
if not maxs or not mins or not sums:
maxs,mins,sums,total_count = data_max,data_min,data_sum,count
else:
for i in range(len(maxs)):
maxs[i] = max(data_max[i], maxs[i])
mins[i] = min(data_min[i], mins[i])
sums[i] = data_sum[i]+sums[i]
total_count+=count
avgs = [s/total_count for s in sums]
return maxs,mins,avgs
def get_max_min_from_file(path):
if not os.path.isfile(path):
debug.warning("Input file not found: {}".format(path))
return [], [], []
data = get_data(path)
# Get max, min, sum, and count from every file
data_max, data_min, data_sum, count = [],[],[], 0
for feature_list in data:
data_max.append(max(feature_list))
data_min.append(min(feature_list))
data_sum.append(sum(feature_list))
count = len(feature_list)
avgs = [s/count for s in data_sum]
return data_max, data_min, avgs
def get_data_and_scale(file_name, sample_dir):
maxs,mins,avgs = get_max_min_from_datasets(sample_dir)
# Get data
all_data = get_data(file_name)
# Scale data from file
self_scaled_data = [[] for _ in range(len(all_data[0]))]
self_maxs,self_mins = [],[]
for feature_list, cur_max, cur_min in zip(all_data,maxs, mins):
for i in range(len(feature_list)):
self_scaled_data[i].append((feature_list[i]-cur_min)/(cur_max-cur_min))
return np.asarray(self_scaled_data)
def rescale_data(data, old_maxs, old_mins, new_maxs, new_mins):
# unscale from old values, rescale by new values
data_new_scaling = []
for data_row in data:
scaled_row = []
for val, old_max,old_min, cur_max, cur_min in zip(data_row, old_maxs,old_mins, new_maxs, new_mins):
unscaled_data = val*(old_max-old_min) + old_min
scaled_row.append((unscaled_data-cur_min)/(cur_max-cur_min))
data_new_scaling.append(scaled_row)
return data_new_scaling
def sample_from_file(num_samples, file_name, sample_dir=None):
"""
Get a portion of the data from CSV file and scale it based on max/min of dataset.
Duplicate samples are trimmed.
"""
if sample_dir:
maxs,mins,avgs = get_max_min_from_datasets(sample_dir)
else:
maxs,mins,avgs = [], [], []
# Get data
all_data = get_data(file_name)
# Get algorithms sample points, assuming hypercube for now
num_labels = 1
inp_dims = len(all_data) - num_labels
samples = np.random.rand(num_samples, inp_dims)
# Scale data from file
self_scaled_data = [[] for _ in range(len(all_data[0]))]
self_maxs,self_mins = [],[]
for feature_list in all_data:
max_val = max(feature_list)
self_maxs.append(max_val)
min_val = min(feature_list)
self_mins.append(min_val)
for i in range(len(feature_list)):
self_scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val))
# Apply algorithm sampling points to available data
sampled_data, unused_data = apply_samples_to_data(self_scaled_data,samples)
#unscale values and rescale using all available data (both sampled and unused points rescaled)
if len(maxs)!=0 and len(mins)!=0:
sampled_data = rescale_data(sampled_data, self_maxs,self_mins, maxs, mins)
unused_new_scaling = rescale_data(unused_data, self_maxs,self_mins, maxs, mins)
return np.asarray(sampled_data), np.asarray(unused_new_scaling)
def get_scaled_data(file_name):
"""Get data from CSV file and scale it based on max/min of dataset"""
if file_name:
maxs,mins,avgs = get_max_min_from_file(file_name)
else:
maxs,mins,avgs = [], [], []
# Get data
all_data = get_data(file_name)
# Data is scaled by max/min and data format is changed to points vs feature lists
self_scaled_data = scale_data_and_transform(all_data)
data_np = np.asarray(self_scaled_data)
return data_np
def scale_data_and_transform(data):
"""
Assume data is a list of features, change to a list of points and max/min scale
"""
scaled_data = [[] for _ in range(len(data[0]))]
for feature_list in data:
max_val = max(feature_list)
min_val = min(feature_list)
for i in range(len(feature_list)):
if max_val == min_val:
scaled_data[i].append(0.0)
else:
scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val))
return scaled_data
def scale_input_datapoint(point, file_path):
"""
Input data has no output and needs to be scaled like the model inputs during
training.
"""
maxs, mins, avgs = get_max_min_from_file(file_path)
debug.info(3, "maxs={}".format(maxs))
debug.info(3, "mins={}".format(mins))
debug.info(3, "point={}".format(point))
scaled_point = []
for feature, mx, mn in zip(point, maxs, mins):
if mx == mn:
scaled_point.append(0.0)
else:
scaled_point.append((feature-mn)/(mx-mn))
return scaled_point
def unscale_data(data, file_path, pos=None):
if file_path:
maxs,mins,avgs = get_max_min_from_file(file_path)
else:
debug.error("Must provide reference data to unscale")
return None
# Hard coded to only convert the last max/min (i.e. the label of the data)
if pos == None:
maxs,mins,avgs = maxs[-1],mins[-1],avgs[-1]
else:
maxs,mins,avgs = maxs[pos],mins[pos],avgs[pos]
unscaled_data = []
for data_row in data:
unscaled_val = data_row*(maxs-mins) + mins
unscaled_data.append(unscaled_val)
return unscaled_data
def abs_error(labels, preds):
total_error = 0
for label_i, pred_i in zip(labels, preds):
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
total_error += cur_error
return total_error/len(labels)
def max_error(labels, preds):
mx_error = 0
for label_i, pred_i in zip(labels, preds):
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
mx_error = max(cur_error, mx_error)
return mx_error
def min_error(labels, preds):
mn_error = 1
for label_i, pred_i in zip(labels, preds):
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
mn_error = min(cur_error, mn_error)
return mn_error
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 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 os
import csv
import math
import numpy as np
from openram import debug
process_transform = {'SS':0.0, 'TT': 0.5, 'FF':1.0}
def get_data_names(file_name, exclude_area=True):
"""
Returns just the data names in the first row of the CSV
"""
with open(file_name, newline='') as csvfile:
csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|')
row_iter = 0
# reader is iterable not a list, probably a better way to do this
for row in csv_reader:
# Return names from first row
names = row[0].split(',')
break
if exclude_area:
try:
area_ind = names.index('area')
except ValueError:
area_ind = -1
if area_ind != -1:
names = names[:area_ind] + names[area_ind+1:]
return names
def get_data(file_name):
"""
Returns data in CSV as lists of features
"""
with open(file_name, newline='') as csvfile:
csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|')
row_iter = 0
removed_items = 1
for row in csv_reader:
row_iter += 1
if row_iter == 1:
feature_names = row[0].split(',')
input_list = [[] for _ in range(len(feature_names)-removed_items)]
try:
# Save to remove area
area_ind = feature_names.index('area')
except ValueError:
area_ind = -1
try:
process_ind = feature_names.index('process')
except:
debug.error('Process not included as a feature.')
continue
data = []
split_str = row[0].split(',')
for i in range(len(split_str)):
if i == process_ind:
data.append(process_transform[split_str[i]])
elif i == area_ind:
continue
else:
data.append(float(split_str[i]))
data[0] = math.log(data[0], 2)
for i in range(len(data)):
input_list[i].append(data[i])
return input_list
def apply_samples_to_data(all_data, algo_samples):
# Take samples from algorithm and match them to samples in data
data_samples, unused_data = [], []
sample_positions = set()
for sample in algo_samples:
sample_positions.add(find_sample_position_with_min_error(all_data, sample))
for i in range(len(all_data)):
if i in sample_positions:
data_samples.append(all_data[i])
else:
unused_data.append(all_data[i])
return data_samples, unused_data
def find_sample_position_with_min_error(data, sampled_vals):
min_error = 0
sample_pos = 0
count = 0
for data_slice in data:
error = squared_error(data_slice, sampled_vals)
if min_error == 0 or error < min_error:
min_error = error
sample_pos = count
count += 1
return sample_pos
def squared_error(list_a, list_b):
error_sum = 0;
for a,b in zip(list_a, list_b):
error_sum+=(a-b)**2
return error_sum
def get_max_min_from_datasets(dir):
if not os.path.isdir(dir):
debug.warning("Input Directory not found:{}".format(dir))
return [], [], []
# Assuming all files are CSV
data_files = [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))]
maxs,mins,sums,total_count = [],[],[],0
for file in data_files:
data = get_data(os.path.join(dir, file))
# Get max, min, sum, and count from every file
data_max, data_min, data_sum, count = [],[],[], 0
for feature_list in data:
data_max.append(max(feature_list))
data_min.append(min(feature_list))
data_sum.append(sum(feature_list))
count = len(feature_list)
# Aggregate the data
if not maxs or not mins or not sums:
maxs,mins,sums,total_count = data_max,data_min,data_sum,count
else:
for i in range(len(maxs)):
maxs[i] = max(data_max[i], maxs[i])
mins[i] = min(data_min[i], mins[i])
sums[i] = data_sum[i]+sums[i]
total_count+=count
avgs = [s/total_count for s in sums]
return maxs,mins,avgs
def get_max_min_from_file(path):
if not os.path.isfile(path):
debug.warning("Input file not found: {}".format(path))
return [], [], []
data = get_data(path)
# Get max, min, sum, and count from every file
data_max, data_min, data_sum, count = [],[],[], 0
for feature_list in data:
data_max.append(max(feature_list))
data_min.append(min(feature_list))
data_sum.append(sum(feature_list))
count = len(feature_list)
avgs = [s/count for s in data_sum]
return data_max, data_min, avgs
def get_data_and_scale(file_name, sample_dir):
maxs,mins,avgs = get_max_min_from_datasets(sample_dir)
# Get data
all_data = get_data(file_name)
# Scale data from file
self_scaled_data = [[] for _ in range(len(all_data[0]))]
self_maxs,self_mins = [],[]
for feature_list, cur_max, cur_min in zip(all_data,maxs, mins):
for i in range(len(feature_list)):
self_scaled_data[i].append((feature_list[i]-cur_min)/(cur_max-cur_min))
return np.asarray(self_scaled_data)
def rescale_data(data, old_maxs, old_mins, new_maxs, new_mins):
# unscale from old values, rescale by new values
data_new_scaling = []
for data_row in data:
scaled_row = []
for val, old_max,old_min, cur_max, cur_min in zip(data_row, old_maxs,old_mins, new_maxs, new_mins):
unscaled_data = val*(old_max-old_min) + old_min
scaled_row.append((unscaled_data-cur_min)/(cur_max-cur_min))
data_new_scaling.append(scaled_row)
return data_new_scaling
def sample_from_file(num_samples, file_name, sample_dir=None):
"""
Get a portion of the data from CSV file and scale it based on max/min of dataset.
Duplicate samples are trimmed.
"""
if sample_dir:
maxs,mins,avgs = get_max_min_from_datasets(sample_dir)
else:
maxs,mins,avgs = [], [], []
# Get data
all_data = get_data(file_name)
# Get algorithms sample points, assuming hypercube for now
num_labels = 1
inp_dims = len(all_data) - num_labels
samples = np.random.rand(num_samples, inp_dims)
# Scale data from file
self_scaled_data = [[] for _ in range(len(all_data[0]))]
self_maxs,self_mins = [],[]
for feature_list in all_data:
max_val = max(feature_list)
self_maxs.append(max_val)
min_val = min(feature_list)
self_mins.append(min_val)
for i in range(len(feature_list)):
self_scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val))
# Apply algorithm sampling points to available data
sampled_data, unused_data = apply_samples_to_data(self_scaled_data,samples)
#unscale values and rescale using all available data (both sampled and unused points rescaled)
if len(maxs)!=0 and len(mins)!=0:
sampled_data = rescale_data(sampled_data, self_maxs,self_mins, maxs, mins)
unused_new_scaling = rescale_data(unused_data, self_maxs,self_mins, maxs, mins)
return np.asarray(sampled_data), np.asarray(unused_new_scaling)
def get_scaled_data(file_name):
"""Get data from CSV file and scale it based on max/min of dataset"""
if file_name:
maxs,mins,avgs = get_max_min_from_file(file_name)
else:
maxs,mins,avgs = [], [], []
# Get data
all_data = get_data(file_name)
# Data is scaled by max/min and data format is changed to points vs feature lists
self_scaled_data = scale_data_and_transform(all_data)
data_np = np.asarray(self_scaled_data)
return data_np
def scale_data_and_transform(data):
"""
Assume data is a list of features, change to a list of points and max/min scale
"""
scaled_data = [[] for _ in range(len(data[0]))]
for feature_list in data:
max_val = max(feature_list)
min_val = min(feature_list)
for i in range(len(feature_list)):
if max_val == min_val:
scaled_data[i].append(0.0)
else:
scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val))
return scaled_data
def scale_input_datapoint(point, file_path):
"""
Input data has no output and needs to be scaled like the model inputs during
training.
"""
maxs, mins, avgs = get_max_min_from_file(file_path)
debug.info(3, "maxs={}".format(maxs))
debug.info(3, "mins={}".format(mins))
debug.info(3, "point={}".format(point))
scaled_point = []
for feature, mx, mn in zip(point, maxs, mins):
if mx == mn:
scaled_point.append(0.0)
else:
scaled_point.append((feature-mn)/(mx-mn))
return scaled_point
def unscale_data(data, file_path, pos=None):
if file_path:
maxs,mins,avgs = get_max_min_from_file(file_path)
else:
debug.error("Must provide reference data to unscale")
return None
# Hard coded to only convert the last max/min (i.e. the label of the data)
if pos == None:
maxs,mins,avgs = maxs[-1],mins[-1],avgs[-1]
else:
maxs,mins,avgs = maxs[pos],mins[pos],avgs[pos]
unscaled_data = []
for data_row in data:
unscaled_val = data_row*(maxs-mins) + mins
unscaled_data.append(unscaled_val)
return unscaled_data
def abs_error(labels, preds):
total_error = 0
for label_i, pred_i in zip(labels, preds):
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
total_error += cur_error
return total_error/len(labels)
def max_error(labels, preds):
mx_error = 0
for label_i, pred_i in zip(labels, preds):
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
mx_error = max(cur_error, mx_error)
return mx_error
def min_error(labels, preds):
mn_error = 1
for label_i, pred_i in zip(labels, preds):
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
mn_error = min(cur_error, mn_error)
return mn_error
+1 -1
View File
@@ -1,6 +1,6 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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.
+16 -19
View File
@@ -1,30 +1,29 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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.
#
from .simulation import simulation
from globals import OPTS
import debug
import tech
import math
from openram import debug
from openram import tech
from openram import OPTS
from .simulation import simulation
class cacti(simulation):
class cacti(simulation):
"""
Delay model for the SRAM which which
"""
def __init__(self, sram, spfile, corner):
super().__init__(sram, spfile, corner)
# self.targ_read_ports = []
# self.targ_write_ports = []
# self.period = 0
# if self.write_size:
# if self.write_size != self.word_size:
# self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
# else:
# self.num_wmasks = 0
@@ -33,8 +32,8 @@ class cacti(simulation):
self.create_signal_names()
self.add_graph_exclusions()
self.set_params()
def set_params(self):
def set_params(self):
"""Set parameters specific to the corner being simulated"""
self.params = {}
# Set the specific functions to use for timing defined in the SRAM module
@@ -42,16 +41,16 @@ class cacti(simulation):
# Only parameter right now is r_on which is dependent on Vdd
self.params["r_nch_on"] = self.vdd_voltage / tech.spice["i_on_n"]
self.params["r_pch_on"] = self.vdd_voltage / tech.spice["i_on_p"]
def get_lib_values(self, load_slews):
"""
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:
debug.warning("In analytical mode, all ports have the timing of the first read port.")
# Probe set to 0th bit, does not matter for analytical delay.
self.set_probe('0' * self.addr_size, 0)
self.set_probe('0' * self.bank_addr_size, 0)
self.create_graph()
self.set_internal_spice_names()
self.create_measurement_names()
@@ -77,7 +76,7 @@ class cacti(simulation):
slew = 0
path_delays = self.graph.get_timing(bl_path, self.corner, slew, load_farad, self.params)
total_delay = self.sum_delays(path_delays)
delay_ns = total_delay.delay/1e-9
slew_ns = total_delay.slew/1e-9
max_delay = max(max_delay, total_delay.delay)
@@ -95,7 +94,7 @@ class cacti(simulation):
elif "slew" in mname and port in self.read_ports:
port_data[port][mname].append(total_delay.slew / 1e-9)
# Margin for error in period. Calculated by averaging required margin for a small and large
# Margin for error in period. Calculated by averaging required margin for a small and large
# memory. FIXME: margin is quite large, should be looked into.
period_margin = 1.85
sram_data = {"min_period": (max_delay / 1e-9) * 2 * period_margin,
@@ -118,5 +117,3 @@ class cacti(simulation):
debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
return power
+4 -4
View File
@@ -1,14 +1,14 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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 os
import re
import debug
from globals import OPTS
from openram import debug
from openram import OPTS
def relative_compare(value1, value2, error_tolerance=0.001):
@@ -37,7 +37,7 @@ def parse_spice_list(filename, key):
except IOError:
debug.error("Unable to open spice output file: {0}".format(full_filename),1)
debug.archive()
contents = f.read().lower()
f.close()
# val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents)
+33 -33
View File
@@ -1,20 +1,20 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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 shutil
import debug
import tech
import math
import shutil
from openram import debug
from openram import tech
from openram import OPTS
from .stimuli import *
from .trim_spice import *
from .charutils import *
from .sram_op import *
from .bit_polarity import *
from globals import OPTS
from .simulation import simulation
from .measurements import *
@@ -43,7 +43,7 @@ class delay(simulation):
self.targ_read_ports = []
self.targ_write_ports = []
self.period = 0
if self.write_size:
if self.write_size != self.word_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
@@ -235,10 +235,10 @@ class delay(simulation):
qbar_meas = voltage_at_measure("v_qbar_{0}".format(meas_tag), qbar_name)
return {bit_polarity.NONINVERTING: q_meas, bit_polarity.INVERTING: qbar_meas}
def create_sen_and_bitline_path_measures(self):
"""Create measurements for the s_en and bitline paths for individual delays per stage."""
# FIXME: There should be a default_read_port variable in this case, pathing is done with this
# but is never mentioned otherwise
port = self.read_ports[0]
@@ -253,37 +253,37 @@ class delay(simulation):
debug.check(len(bl_paths)==1, 'Found {0} paths which contain the bitline net.'.format(len(bl_paths)))
sen_path = sen_paths[0]
bitline_path = bl_paths[0]
# Get the measures
self.sen_path_meas = self.create_delay_path_measures(sen_path)
self.bl_path_meas = self.create_delay_path_measures(bitline_path)
all_meas = self.sen_path_meas + self.bl_path_meas
# Paths could have duplicate measurements, remove them before they go to the stim file
all_meas = self.remove_duplicate_meas_names(all_meas)
# FIXME: duplicate measurements still exist in the member variables, since they have the same
# name it will still work, but this could cause an issue in the future.
return all_meas
return all_meas
def remove_duplicate_meas_names(self, measures):
"""Returns new list of measurements without duplicate names"""
name_set = set()
unique_measures = []
for meas in measures:
if meas.name not in name_set:
name_set.add(meas.name)
unique_measures.append(meas)
return unique_measures
def create_delay_path_measures(self, path):
"""Creates measurements for each net along given path."""
# Determine the directions (RISE/FALL) of signals
path_dirs = self.get_meas_directions(path)
# Create the measurements
path_meas = []
for i in range(len(path) - 1):
@@ -297,26 +297,26 @@ class delay(simulation):
# Some bitcell logic is hardcoded for only read zeroes, force that here as well.
path_meas[-1].meta_str = sram_op.READ_ZERO
path_meas[-1].meta_add_delay = True
return path_meas
def get_meas_directions(self, path):
"""Returns SPICE measurements directions based on path."""
# Get the edges modules which define the path
edge_mods = self.graph.get_edge_mods(path)
# Convert to booleans based on function of modules (inverting/non-inverting)
mod_type_bools = [mod.is_non_inverting() for mod in edge_mods]
# FIXME: obtuse hack to differentiate s_en input from bitline in sense amps
if self.sen_name in path:
# Force the sense amp to be inverting for s_en->DOUT.
# Force the sense amp to be inverting for s_en->DOUT.
# bitline->DOUT is non-inverting, but the module cannot differentiate inputs.
s_en_index = path.index(self.sen_name)
mod_type_bools[s_en_index] = False
debug.info(2, 'Forcing sen->dout to be inverting.')
# Use these to determine direction list assuming delay start on neg. edge of clock (FALL)
# Also, use shorthand that 'FALL' == False, 'RISE' == True to simplify logic
bool_dirs = [False]
@@ -324,9 +324,9 @@ class delay(simulation):
for mod_bool in mod_type_bools:
cur_dir = (cur_dir == mod_bool)
bool_dirs.append(cur_dir)
# Convert from boolean to string
return ['RISE' if dbool else 'FALL' for dbool in bool_dirs]
return ['RISE' if dbool else 'FALL' for dbool in bool_dirs]
def set_load_slew(self, load, slew):
""" Set the load and slew """
@@ -342,7 +342,7 @@ class delay(simulation):
except ValueError:
debug.error("Probe Address is not of binary form: {0}".format(self.probe_address), 1)
if len(self.probe_address) != self.addr_size:
if len(self.probe_address) != self.bank_addr_size:
debug.error("Probe Address's number of bits does not correspond to given SRAM", 1)
if not isinstance(self.probe_data, int) or self.probe_data>self.word_size or self.probe_data<0:
@@ -455,7 +455,7 @@ class delay(simulation):
self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name, write_port, i),
v_val=0)
for port in self.all_ports:
for i in range(self.addr_size):
for i in range(self.bank_addr_size):
self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name, port, i),
v_val=0)
@@ -827,7 +827,7 @@ class delay(simulation):
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict), 1)
result[port].update(read_port_dict)
self.path_delays = self.check_path_measures()
return (True, result)
@@ -932,7 +932,7 @@ class delay(simulation):
def check_path_measures(self):
"""Get and check all the delays along the sen and bitline paths"""
# Get and set measurement, no error checking done other than prints.
debug.info(2, "Checking measures in Delay Path")
value_dict = {}
@@ -1179,7 +1179,7 @@ class delay(simulation):
#char_sram_data["sen_path_names"] = sen_names
# FIXME: low-to-high delays are altered to be independent of the period. This makes the lib results less accurate.
self.alter_lh_char_data(char_port_data)
return (char_sram_data, char_port_data)
def alter_lh_char_data(self, char_port_data):
@@ -1222,14 +1222,14 @@ class delay(simulation):
for meas in self.sen_path_meas:
sen_name_list.append(meas.name)
sen_delay_list.append(value_dict[meas.name])
bl_name_list = []
bl_delay_list = []
for meas in self.bl_path_meas:
bl_name_list.append(meas.name)
bl_delay_list.append(value_dict[meas.name])
return sen_name_list, sen_delay_list, bl_name_list, bl_delay_list
return sen_name_list, sen_delay_list, bl_name_list, bl_delay_list
def calculate_inverse_address(self):
"""Determine dummy test address based on probe address and column mux size."""
@@ -1391,7 +1391,7 @@ class delay(simulation):
"""
for port in self.all_ports:
for i in range(self.addr_size):
for i in range(self.bank_addr_size):
sig_name = "{0}{1}_{2}".format(self.addr_name, port, i)
self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05)
+14 -14
View File
@@ -1,27 +1,27 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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.
#
from openram import debug
from openram import OPTS
from .simulation import simulation
from globals import OPTS
import debug
class elmore(simulation):
class elmore(simulation):
"""
Delay model for the SRAM which calculates Elmore delays along the SRAM critical path.
"""
def __init__(self, sram, spfile, corner):
super().__init__(sram, spfile, corner)
# self.targ_read_ports = []
# self.targ_write_ports = []
# self.period = 0
# if self.write_size:
# if self.write_size != self.word_size:
# self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
# else:
# self.num_wmasks = 0
@@ -30,13 +30,13 @@ class elmore(simulation):
self.set_corner(corner)
self.create_signal_names()
self.add_graph_exclusions()
def set_params(self):
def set_params(self):
"""Set parameters specific to the corner being simulated"""
self.params = {}
# Set the specific functions to use for timing defined in the SRAM module
self.params["model_name"] = OPTS.model_name
def get_lib_values(self, load_slews):
"""
Return the analytical model results for the SRAM.
@@ -45,7 +45,7 @@ class elmore(simulation):
debug.warning("In analytical mode, all ports have the timing of the first read port.")
# Probe set to 0th bit, does not matter for analytical delay.
self.set_probe('0' * self.addr_size, 0)
self.set_probe('0' * self.bank_addr_size, 0)
self.create_graph()
self.set_internal_spice_names()
self.create_measurement_names()
@@ -66,7 +66,7 @@ class elmore(simulation):
for load,slew in load_slews:
# Calculate delay based on slew and load
path_delays = self.graph.get_timing(bl_path, self.corner, slew, load, self.params)
total_delay = self.sum_delays(path_delays)
max_delay = max(max_delay, total_delay.delay)
debug.info(1,
@@ -84,7 +84,7 @@ class elmore(simulation):
elif "slew" in mname and port in self.read_ports:
port_data[port][mname].append(total_delay.slew / 1e3)
# Margin for error in period. Calculated by averaging required margin for a small and large
# Margin for error in period. Calculated by averaging required margin for a small and large
# memory. FIXME: margin is quite large, should be looked into.
period_margin = 1.85
sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin,
@@ -106,4 +106,4 @@ class elmore(simulation):
power.leakage /= 1e6
debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
return power
return power
+15 -15
View File
@@ -1,18 +1,18 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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 collections
import debug
import random
import math
import random
import collections
from numpy import binary_repr
from openram import debug
from openram import OPTS
from .stimuli import *
from .charutils import *
from globals import OPTS
from .simulation import simulation
@@ -44,7 +44,7 @@ class functional(simulation):
else:
self.output_path = output_path
if self.write_size:
if self.write_size != self.word_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
@@ -60,7 +60,7 @@ class functional(simulation):
self.addr_spare_index = -int(math.log(self.words_per_row) / math.log(2))
else:
# This will select the entire address when one word per row
self.addr_spare_index = self.addr_size
self.addr_spare_index = self.bank_addr_size
# If trim is set, specify the valid addresses
self.valid_addresses = set()
self.max_address = self.num_rows * self.words_per_row - 1
@@ -68,7 +68,7 @@ class functional(simulation):
for i in range(self.words_per_row):
self.valid_addresses.add(i)
self.valid_addresses.add(self.max_address - i - 1)
self.probe_address, self.probe_data = '0' * self.addr_size, 0
self.probe_address, self.probe_data = '0' * self.bank_addr_size, 0
self.set_corner(corner)
self.set_spice_constants()
self.set_stimulus_variables()
@@ -133,7 +133,7 @@ class functional(simulation):
def create_random_memory_sequence(self):
# Select randomly, but have 3x more reads to increase probability
if self.write_size:
if self.write_size != self.word_size:
rw_ops = ["noop", "write", "partial_write", "read", "read"]
w_ops = ["noop", "write", "partial_write"]
else:
@@ -142,7 +142,7 @@ class functional(simulation):
r_ops = ["noop", "read"]
# First cycle idle is always an idle cycle
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current)
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.bank_addr_size, "0" * self.num_wmasks, 0, self.t_current)
self.add_noop_all_ports(comment)
@@ -244,7 +244,7 @@ class functional(simulation):
self.t_current += self.period
# Last cycle idle needed to correctly measure the value on the second to last clock edge
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current)
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.bank_addr_size, "0" * self.num_wmasks, 0, self.t_current)
self.add_noop_all_ports(comment)
def gen_masked_data(self, old_word, word, wmask):
@@ -363,10 +363,10 @@ class functional(simulation):
def gen_addr(self):
""" Generates a random address value to write to. """
if self.valid_addresses:
random_value = random.sample(self.valid_addresses, 1)[0]
random_value = random.sample(list(self.valid_addresses), 1)[0]
else:
random_value = random.randint(0, self.max_address)
addr_bits = binary_repr(random_value, self.addr_size)
addr_bits = binary_repr(random_value, self.bank_addr_size)
return addr_bits
def get_data(self):
@@ -426,7 +426,7 @@ class functional(simulation):
# Generate address bits
for port in self.all_ports:
for bit in range(self.addr_size):
for bit in range(self.bank_addr_size):
sig_name="{0}{1}_{2} ".format(self.addr_name, port, bit)
self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][bit], self.period, self.slew, 0.05)
@@ -440,7 +440,7 @@ class functional(simulation):
# Generate wmask bits
for port in self.write_ports:
if self.write_size:
if self.write_size != self.word_size:
self.sf.write("\n* Generation of wmask signals\n")
for bit in range(self.num_wmasks):
sig_name = "WMASK{0}_{1} ".format(port, bit)
+12 -11
View File
@@ -1,21 +1,21 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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 os,sys,re
import os, sys, re
import time
import debug
import datetime
import numpy as np
from openram import debug
from openram import tech
from openram.tech import spice
from openram import OPTS
from .setup_hold import *
from .delay import *
from .charutils import *
import tech
import numpy as np
from globals import OPTS
from tech import spice
class lib:
@@ -183,7 +183,8 @@ class lib:
# set the read and write port as inputs.
self.write_data_bus(port)
self.write_addr_bus(port)
if self.sram.write_size and port in self.write_ports:
if self.sram.write_size != self.sram.word_size and \
port in self.write_ports:
self.write_wmask_bus(port)
# need to split this into sram and port control signals
self.write_control_pins(port)
@@ -193,8 +194,8 @@ class lib:
def write_footer(self):
""" Write the footer """
self.lib.write(" }\n") #Closing brace for the cell
self.lib.write("}\n") #Closing brace for the library
self.lib.write(" }\n") # Closing brace for the cell
self.lib.write("}\n") # Closing brace for the library
def write_header(self):
""" Write the header information """
@@ -378,7 +379,7 @@ class lib:
self.lib.write(" bit_to : 0;\n")
self.lib.write(" }\n\n")
if self.sram.write_size:
if self.sram.write_size != self.sram.word_size:
self.lib.write(" type (wmask){\n")
self.lib.write(" base_type : array;\n")
self.lib.write(" data_type : bit;\n")
+8 -11
View File
@@ -1,17 +1,15 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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.
#
from .regression_model import regression_model
from sklearn.linear_model import Ridge
from globals import OPTS
import debug
from sklearn.linear_model import LinearRegression
from openram import debug
from openram import OPTS
from .regression_model import regression_model
class linear_regression(regression_model):
@@ -26,18 +24,17 @@ class linear_regression(regression_model):
"""
Supervised training of model.
"""
#model = LinearRegression()
model = self.get_model()
model.fit(features, labels)
return model
def model_prediction(self, model, features):
def model_prediction(self, model, features):
"""
Have the model perform a prediction and unscale the prediction
as the model is trained with scaled values.
"""
pred = model.predict(features)
return pred
+5 -5
View File
@@ -1,16 +1,17 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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, parameter, spice
from abc import ABC, abstractmethod
from openram import debug
from openram.tech import drc, parameter, spice
from .stimuli import *
from .charutils import *
class spice_measurement(ABC):
"""Base class for spice stimulus measurements."""
def __init__(self, measure_name, measure_scale=None, has_port=True):
@@ -184,7 +185,7 @@ class voltage_when_measure(spice_measurement):
trig_voltage = self.trig_val_of_vdd * vdd_voltage
return (meas_name, trig_name, targ_name, trig_voltage, self.trig_dir_str, trig_td)
class voltage_at_measure(spice_measurement):
"""Generates a spice measurement to measure the voltage at a specific time.
The time is considered variant with different periods."""
@@ -211,4 +212,3 @@ class voltage_at_measure(spice_measurement):
meas_name = self.name
targ_name = self.targ_name_no_port
return (meas_name, targ_name, time_at)
+5 -8
View File
@@ -1,16 +1,16 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
import tech
from openram import debug
from openram import tech
from openram import OPTS
from .stimuli import *
from .trim_spice import *
from .charutils import *
from globals import OPTS
from .delay import delay
from .measurements import *
@@ -82,7 +82,7 @@ class model_check(delay):
replicated here.
"""
delay.create_signal_names(self)
# Signal names are all hardcoded, need to update to make it work for probe address and different configurations.
wl_en_driver_signals = ["Xsram{1}Xcontrol{{}}.Xbuf_wl_en.Zb{0}_int".format(stage, OPTS.hier_seperator) for stage in range(1, self.get_num_wl_en_driver_stages())]
wl_driver_signals = ["Xsram{2}Xbank0{2}Xwordline_driver{{}}{2}Xwl_driver_inv{0}{2}Zb{1}_int".format(self.wordline_row, stage, OPTS.hier_seperator) for stage in range(1, self.get_num_wl_driver_stages())]
@@ -448,6 +448,3 @@ class model_check(delay):
name_dict[self.sae_model_name] = name_dict["sae_measures"]
return name_dict
+9 -11
View File
@@ -1,15 +1,14 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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.
#
from .regression_model import regression_model
from globals import OPTS
import debug
from sklearn.neural_network import MLPRegressor
from openram import debug
from openram import OPTS
from .regression_model import regression_model
class neural_network(regression_model):
@@ -25,20 +24,19 @@ class neural_network(regression_model):
"""
Training multilayer model
"""
flat_labels = np.ravel(labels)
model = self.get_model()
model.fit(features, flat_labels)
return model
def model_prediction(self, model, features):
def model_prediction(self, model, features):
"""
Have the model perform a prediction and unscale the prediction
as the model is trained with scaled values.
"""
pred = model.predict(features)
reshape_pred = np.reshape(pred, (len(pred),1))
return reshape_pred
+36 -38
View File
@@ -1,17 +1,16 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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 math
from openram import debug
from openram import OPTS
from .analytical_util import *
from .simulation import simulation
from globals import OPTS
import debug
import math
relative_data_path = "sim_data"
data_file = "sim_data.csv"
@@ -25,7 +24,7 @@ data_fnames = ["rise_delay.csv",
"read0_power.csv",
"leakage_data.csv",
"sim_time.csv"]
# Positions must correspond to data_fname list
# Positions must correspond to data_fname list
lib_dnames = ["delay_lh",
"delay_hl",
"slew_lh",
@@ -35,13 +34,13 @@ lib_dnames = ["delay_lh",
"read1_power",
"read0_power",
"leakage_power",
"sim_time"]
"sim_time"]
# Check if another data dir was specified
if OPTS.sim_data_path == None:
if OPTS.sim_data_path == None:
data_dir = OPTS.openram_tech+relative_data_path
else:
data_dir = OPTS.sim_data_path
data_dir = OPTS.sim_data_path
data_path = data_dir + '/' + data_file
class regression_model(simulation):
@@ -52,23 +51,23 @@ class regression_model(simulation):
def get_lib_values(self, load_slews):
"""
A model and prediction is created for each output needed for the LIB
A model and prediction is created for each output needed for the LIB
"""
debug.info(1, "Characterizing SRAM using regression models.")
log_num_words = math.log(OPTS.num_words, 2)
model_inputs = [log_num_words,
OPTS.word_size,
model_inputs = [log_num_words,
OPTS.word_size,
OPTS.words_per_row,
OPTS.local_array_size,
process_transform[self.process],
self.vdd_voltage,
self.temperature]
process_transform[self.process],
self.vdd_voltage,
self.temperature]
# Area removed for now
# self.sram.width * self.sram.height,
# Include above inputs, plus load and slew which are added below
self.num_inputs = len(model_inputs)+2
self.create_measurement_names()
models = self.train_models()
@@ -85,22 +84,22 @@ class regression_model(simulation):
port_data[port]['delay_hl'].append(sram_vals['fall_delay'])
port_data[port]['slew_lh'].append(sram_vals['rise_slew'])
port_data[port]['slew_hl'].append(sram_vals['fall_slew'])
port_data[port]['write1_power'].append(sram_vals['write1_power'])
port_data[port]['write0_power'].append(sram_vals['write0_power'])
port_data[port]['read1_power'].append(sram_vals['read1_power'])
port_data[port]['read0_power'].append(sram_vals['read0_power'])
# Disabled power not modeled. Copied from other power predictions
port_data[port]['disabled_write1_power'].append(sram_vals['write1_power'])
port_data[port]['disabled_write0_power'].append(sram_vals['write0_power'])
port_data[port]['disabled_read1_power'].append(sram_vals['read1_power'])
port_data[port]['disabled_read0_power'].append(sram_vals['read0_power'])
debug.info(1, '{}, {}, {}, {}, {}'.format(slew,
load,
port,
sram_vals['rise_delay'],
debug.info(1, '{}, {}, {}, {}, {}'.format(slew,
load,
port,
sram_vals['rise_delay'],
sram_vals['rise_slew']))
# Estimate the period as double the delay with margin
period_margin = 0.1
@@ -112,19 +111,19 @@ class regression_model(simulation):
return (sram_data, port_data)
def get_predictions(self, model_inputs, models):
def get_predictions(self, model_inputs, models):
"""
Generate a model and prediction for LIB output
"""
#Scaled the inputs using first data file as a reference
#Scaled the inputs using first data file as a reference
scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, data_path)])
predictions = {}
out_pos = 0
for dname in self.output_names:
m = models[dname]
scaled_pred = self.model_prediction(m, scaled_inputs)
pred = unscale_data(scaled_pred.tolist(), data_path, pos=self.num_inputs+out_pos)
debug.info(2,"Unscaled Prediction = {}".format(pred))
@@ -149,7 +148,7 @@ class regression_model(simulation):
output_num+=1
return models
def score_model(self):
num_inputs = 9 #FIXME - should be defined somewhere else
self.output_names = get_data_names(data_path)[num_inputs:]
@@ -165,15 +164,15 @@ class regression_model(simulation):
scr = model.score(features, output_label)
debug.info(1, "{}, {}".format(o_name, scr))
output_num+=1
def cross_validation(self, test_only=None):
"""Wrapper for sklean cross validation function for OpenRAM regression models.
Returns the mean accuracy for each model/output."""
from sklearn.model_selection import cross_val_score
untrained_model = self.get_model()
num_inputs = 9 #FIXME - should be defined somewhere else
self.output_names = get_data_names(data_path)[num_inputs:]
data = get_scaled_data(data_path)
@@ -193,9 +192,9 @@ class regression_model(simulation):
debug.info(1, "{}, {}, {}".format(o_name, scores.mean(), scores.std()))
model_scores[o_name] = scores.mean()
output_num+=1
return model_scores
return model_scores
# Fixme - only will work for sklearn regression models
def save_model(self, model_name, model):
try:
@@ -205,4 +204,3 @@ class regression_model(simulation):
OPTS.model_dict[model_name+"_coef"] = list(model.coef_[0])
debug.info(1,"Coefs of {}:{}".format(model_name,OPTS.model_dict[model_name+"_coef"]))
OPTS.model_dict[model_name+"_intercept"] = float(model.intercept_)
+9 -9
View File
@@ -1,16 +1,16 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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 tech
from openram import debug
from openram.sram_factory import factory
from openram import tech
from openram import OPTS
from .stimuli import *
import debug
from .charutils import *
from globals import OPTS
from sram_factory import factory
class setup_hold():
@@ -22,7 +22,7 @@ class setup_hold():
def __init__(self, corner):
# This must match the spice model order
self.dff = factory.create(module_type=OPTS.dff)
self.period = tech.spice["feasible_period"]
debug.info(2, "Feasible period from technology file: {0} ".format(self.period))
@@ -106,8 +106,8 @@ class setup_hold():
setup=0)
def write_clock(self):
"""
Create the clock signal for setup/hold analysis.
"""
Create the clock signal for setup/hold analysis.
First period initializes the FF
while the second is used for characterization.
"""
@@ -206,7 +206,7 @@ class setup_hold():
self.stim.run_sim(self.stim_sp)
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
# We use a 1/2 speed clock for some reason...
# We use a 1/2 speed clock for some reason...
setuphold_time = (target_time - 2 * self.period)
if mode == "SETUP": # SETUP is clk-din, not din-clk
passing_setuphold_time = -1 * setuphold_time
+14 -14
View File
@@ -1,16 +1,16 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
import math
import tech
from globals import OPTS
from sram_factory import factory
from base import timing_graph
from openram import debug
from openram.base import timing_graph
from openram.sram_factory import factory
from openram import tech
from openram import OPTS
class simulation():
@@ -20,7 +20,7 @@ class simulation():
self.name = self.sram.name
self.word_size = self.sram.word_size
self.addr_size = self.sram.addr_size
self.bank_addr_size = self.sram.bank_addr_size
self.write_size = self.sram.write_size
self.num_spare_rows = self.sram.num_spare_rows
if not self.sram.num_spare_cols:
@@ -39,7 +39,7 @@ class simulation():
self.words_per_row = self.sram.words_per_row
self.num_rows = self.sram.num_rows
self.num_cols = self.sram.num_cols
if self.write_size:
if self.write_size != self.word_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
@@ -80,7 +80,7 @@ class simulation():
self.dout_name = "dout"
self.pins = self.gen_pin_names(port_signal_names=(self.addr_name, self.din_name, self.dout_name),
port_info=(len(self.all_ports), self.write_ports, self.read_ports),
abits=self.addr_size,
abits=self.bank_addr_size,
dbits=self.word_size + self.num_spare_cols)
debug.check(len(self.sram.pins) == len(self.pins),
"Number of pins generated for characterization \
@@ -103,7 +103,7 @@ class simulation():
self.spare_wen_value = {port: [] for port in self.write_ports}
# Three dimensional list to handle each addr and data bits for each port over the number of checks
self.addr_values = {port: [[] for bit in range(self.addr_size)] for port in self.all_ports}
self.addr_values = {port: [[] for bit in range(self.bank_addr_size)] for port in self.all_ports}
self.data_values = {port: [[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports}
self.wmask_values = {port: [[] for bit in range(self.num_wmasks)] for port in self.write_ports}
self.spare_wen_values = {port: [[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
@@ -174,10 +174,10 @@ class simulation():
def add_address(self, address, port):
""" Add the array of address values """
debug.check(len(address)==self.addr_size, "Invalid address size.")
debug.check(len(address)==self.bank_addr_size, "Invalid address size.")
self.addr_value[port].append(address)
bit = self.addr_size - 1
bit = self.bank_addr_size - 1
for c in address:
if c=="0":
self.addr_values[port][bit].append(0)
@@ -330,7 +330,7 @@ class simulation():
try:
self.add_address(self.addr_value[port][-1], port)
except:
self.add_address("0" * self.addr_size, port)
self.add_address("0" * self.bank_addr_size, port)
# If the port is also a readwrite then add
# the same value as previous cycle
@@ -464,7 +464,7 @@ class simulation():
for port in range(total_ports):
pin_names.append("{0}{1}".format("clk", port))
if self.write_size:
if self.write_size != self.word_size:
for port in write_index:
for bit in range(self.num_wmasks):
pin_names.append("WMASK{0}_{1}".format(port, bit))
+1 -1
View File
@@ -1,6 +1,6 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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.
+10 -5
View File
@@ -1,6 +1,6 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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.
@@ -11,12 +11,12 @@ various functions that can be be used to generate stimulus for other
simulations as well.
"""
import tech
import debug
import subprocess
import os
import subprocess
import numpy as np
from globals import OPTS
from openram import debug
from openram import tech
from openram import OPTS
class stimuli():
@@ -405,6 +405,11 @@ class stimuli():
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')
# Wrap the command with conda activate & conda deactivate
# FIXME: Should use verify/run_script.py here but run_script doesn't return
# the return code of the subprocess. File names might also mismatch.
from openram import CONDA_HOME
cmd = "source {0}/bin/activate && {1} && conda deactivate".format(CONDA_HOME, cmd)
debug.info(2, cmd)
retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True)
+6 -6
View File
@@ -1,13 +1,13 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# Copyright (c) 2016-2023 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 math import log,ceil
import re
from math import log, ceil
from openram import debug
class trim_spice():
@@ -46,9 +46,9 @@ class trim_spice():
self.col_addr_size = int(log(self.words_per_row, 2))
self.bank_addr_size = self.col_addr_size + self.row_addr_size
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
def trim(self, address, data_bit):
"""
"""
Reduce the spice netlist but KEEP the given bits at the
address (and things that will add capacitive load!)
"""
@@ -62,7 +62,7 @@ class trim_spice():
col_address = int(address[0:self.col_addr_size], 2)
else:
col_address = 0
# 1. Keep cells in the bitcell array based on WL and BL
wl_name = "wl_{}".format(wl_address)
bl_name = "bl_{}".format(int(self.words_per_row*data_bit + col_address))