From 1143dbec94cdffcae7449bcb96f10077a06a18b5 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Fri, 20 Nov 2020 12:40:04 -0800 Subject: [PATCH 01/59] Added initial scripts and data to generate analytical model --- technology/scn4m_subm/sim_data/data.csv | 9 + .../setup_scripts/gen_analytical_model.py | 32 +++ .../gen_model_util/linreg_scikit.py | 25 ++ .../setup_scripts/gen_model_util/mapping.py | 216 ++++++++++++++++++ 4 files changed, 282 insertions(+) create mode 100644 technology/scn4m_subm/sim_data/data.csv create mode 100644 technology/setup_scripts/gen_analytical_model.py create mode 100644 technology/setup_scripts/gen_model_util/linreg_scikit.py create mode 100644 technology/setup_scripts/gen_model_util/mapping.py diff --git a/technology/scn4m_subm/sim_data/data.csv b/technology/scn4m_subm/sim_data/data.csv new file mode 100644 index 00000000..043403e8 --- /dev/null +++ b/technology/scn4m_subm/sim_data/data.csv @@ -0,0 +1,9 @@ +num_words,word_size,words_per_row,area,max_delay +16,2,1,88873,2.272 +64,2,4,108116,2.721 +16,1,1,86004,2.267 +32,3,2,101618,2.634 +32,2,2,95878,2.594 +16,3,1,93009,2.292 +64,1,4,95878,2.705 +32,1,2,90139,2.552 diff --git a/technology/setup_scripts/gen_analytical_model.py b/technology/setup_scripts/gen_analytical_model.py new file mode 100644 index 00000000..85d1c970 --- /dev/null +++ b/technology/setup_scripts/gen_analytical_model.py @@ -0,0 +1,32 @@ +import mapping +import lr_scikit +import keras_models + +train_sets = [] +test_sets = [] + +filename = "delays.csv" +reference_dir = "data" +file_path = reference_dir +'/'+filename +num_points_train = 7 +mp = mapping.mapping() + +non_ip_samples, unused_samples = mp.sample_from_file(num_points_train, file_path, reference_dir) +nip_features_subset, nip_labels_subset = non_ip_samples[:, :-1], non_ip_samples[:,-1:] +nip_test_feature_subset, nip_test_labels_subset = unused_samples[:, :-1], unused_samples[:,-1:] + +train_sets = [(nip_features_subset, nip_labels_subset)] +test_sets = [(nip_test_feature_subset, nip_test_labels_subset)] + +runs_per_model = 1 + +for train_tuple, test_tuple in zip(train_sets, test_sets): + train_x, train_y = train_tuple + test_x, test_y = test_tuple + + errors = {} + min_train_set = None + for _ in range(runs_per_model): + #new_error = lr_scikit.run_model(train_x, train_y, test_x, test_y) + new_error = keras_models.run_model(train_x, train_y, test_x, test_y) + print(new_error) \ No newline at end of file diff --git a/technology/setup_scripts/gen_model_util/linreg_scikit.py b/technology/setup_scripts/gen_model_util/linreg_scikit.py new file mode 100644 index 00000000..f5fd6de2 --- /dev/null +++ b/technology/setup_scripts/gen_model_util/linreg_scikit.py @@ -0,0 +1,25 @@ +import os +from sklearn.linear_model import LinearRegression +import mapping + +reference_dir = "data" + +def run_model(x,y,test_x,test_y): + mp = mapping.mapping() + model = LinearRegression() + model.fit(x, y) + print(model.coef_) + print(model.intercept_) + + pred = model.predict(test_x) + + #print(pred) + unscaled_labels = mp.unscale_data(test_y.tolist(), reference_dir) + unscaled_preds = mp.unscale_data(pred.tolist(), reference_dir) + unscaled_labels, unscaled_preds = (list(t) for t in zip(*sorted(zip(unscaled_labels, unscaled_preds)))) + avg_error = mp.abs_error(unscaled_labels, unscaled_preds) + max_error = mp.max_error(unscaled_labels, unscaled_preds) + min_error = mp.min_error(unscaled_labels, unscaled_preds) + + errors = {"avg_error": avg_error, "max_error":max_error, "min_error":min_error} + return errors \ No newline at end of file diff --git a/technology/setup_scripts/gen_model_util/mapping.py b/technology/setup_scripts/gen_model_util/mapping.py new file mode 100644 index 00000000..e24f6e07 --- /dev/null +++ b/technology/setup_scripts/gen_model_util/mapping.py @@ -0,0 +1,216 @@ +import diversipy as dp +import csv +import math +import numpy as np +import os + +class mapping(): + + def get_data_names(self, file_name): + 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 + return row[0].split(',') + + def get_data(self, file_name): + with open(file_name, newline='') as csvfile: + csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|') + row_iter = 0 + for row in csv_reader: + #data = [int(csv_str) for csv_str in ', '.join(row)] + row_iter += 1 + if row_iter == 1: + feature_names = row[0].split(',') + input_list = [[] for _ in feature_names] + scaled_list = [[] for _ in feature_names] + #label_list = [] + continue + #print(row[0]) + data = [float(csv_str) for csv_str in row[0].split(',')] + data[0] = math.log(data[0], 2) + #input_list.append(data) + for i in range(len(data)): + input_list[i].append(data[i]) + #label_list.append([data[-1]]) + #print(data) + return input_list + + def apply_samples_to_data(self, 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(self.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(self, data, sampled_vals): + min_error = 0 + sample_pos = 0 + count = 0 + for data_slice in data: + error = self.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(self, list_a, list_b): + #print('a:',list_a, 'b:', 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(self, dir): + if not os.path.isdir(dir): + print("Input Directory not found:",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 = self.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_data_and_scale(self, file_name, sample_dir): + maxs,mins,avgs = self.get_max_min_from_datasets(sample_dir) + + # Get data + all_data = self.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(self, 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(self, num_samples, file_name, sample_dir=None): + if sample_dir: + maxs,mins,avgs = self.get_max_min_from_datasets(sample_dir) + else: + maxs,mins,avgs = [], [], [] + + # Get data + all_data = self.get_data(file_name) + + # Get algorithms sample points, assuming hypercube for now + num_labels = 1 + inp_dims = len(all_data) - num_labels + #samples = dp.hycusampling.lhd_matrix(num_samples, inp_dims)/num_samples + #samples = dp.hycusampling.halton(num_samples, inp_dims) + samples = dp.hycusampling.random_uniform(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 = self.apply_samples_to_data(self_scaled_data,samples) + #print(sampled_data) + + #unscale values and rescale using all available data (both sampled and unused points rescaled) + if len(maxs)!=0 and len(mins)!=0: + sampled_data = self.rescale_data(sampled_data, self_maxs,self_mins, maxs, mins) + unused_new_scaling = self.rescale_data(unused_data, self_maxs,self_mins, maxs, mins) + + return np.asarray(sampled_data), np.asarray(unused_new_scaling) + + def unscale_data(self, data, ref_dir, pos=None): + if ref_dir: + maxs,mins,avgs = self.get_max_min_from_datasets(ref_dir) + else: + print("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_row = [] + for val, cur_max, cur_min in zip(data_row, maxs, mins): + unscaled_val = val*(cur_max-cur_min) + cur_min + unscaled_row.append(unscaled_val) + unscaled_data.append(unscaled_row) + + return unscaled_data + + def abs_error(self, 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] + # print(cur_error) + total_error += cur_error + return total_error/len(labels) + + def max_error(self, 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(self, 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 + \ No newline at end of file From acf8e46b55917372a956a5e0c7ecba7fab4a4ee3 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Fri, 20 Nov 2020 13:43:36 -0800 Subject: [PATCH 02/59] Fixed import of utility scripts for model generation --- technology/setup_scripts/gen_analytical_model.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/technology/setup_scripts/gen_analytical_model.py b/technology/setup_scripts/gen_analytical_model.py index 85d1c970..0faf87d2 100644 --- a/technology/setup_scripts/gen_analytical_model.py +++ b/technology/setup_scripts/gen_analytical_model.py @@ -1,6 +1,13 @@ +import os +import sys + +util_dir = "gen_model_util" +cur_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(1, cur_dir+'/'+util_dir) + import mapping -import lr_scikit -import keras_models +import linreg_scikit +#import keras_models train_sets = [] test_sets = [] From ce9036af76807d5376dc0187ffe9eb22b5302c5f Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 2 Dec 2020 13:25:03 -0800 Subject: [PATCH 03/59] Moved model scripts to characterizer dir --- compiler/characterizer/analytical_util.py | 213 +++++++++++++++++ .../characterizer/linear_regression.py | 0 .../setup_scripts/gen_model_util/mapping.py | 216 ------------------ 3 files changed, 213 insertions(+), 216 deletions(-) create mode 100644 compiler/characterizer/analytical_util.py rename technology/setup_scripts/gen_model_util/linreg_scikit.py => compiler/characterizer/linear_regression.py (100%) delete mode 100644 technology/setup_scripts/gen_model_util/mapping.py diff --git a/compiler/characterizer/analytical_util.py b/compiler/characterizer/analytical_util.py new file mode 100644 index 00000000..e1fa8076 --- /dev/null +++ b/compiler/characterizer/analytical_util.py @@ -0,0 +1,213 @@ +import diversipy as dp +import csv +import math +import numpy as np +import os + +def get_data_names(self, file_name): + 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 + return row[0].split(',') + +def get_data(self, file_name): + with open(file_name, newline='') as csvfile: + csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|') + row_iter = 0 + for row in csv_reader: + #data = [int(csv_str) for csv_str in ', '.join(row)] + row_iter += 1 + if row_iter == 1: + feature_names = row[0].split(',') + input_list = [[] for _ in feature_names] + scaled_list = [[] for _ in feature_names] + #label_list = [] + continue + #print(row[0]) + data = [float(csv_str) for csv_str in row[0].split(',')] + data[0] = math.log(data[0], 2) + #input_list.append(data) + for i in range(len(data)): + input_list[i].append(data[i]) + #label_list.append([data[-1]]) + #print(data) + return input_list + +def apply_samples_to_data(self, 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(self.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(self, data, sampled_vals): + min_error = 0 + sample_pos = 0 + count = 0 + for data_slice in data: + error = self.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(self, list_a, list_b): + #print('a:',list_a, 'b:', 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(self, dir): + if not os.path.isdir(dir): + print("Input Directory not found:",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 = self.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_data_and_scale(self, file_name, sample_dir): + maxs,mins,avgs = self.get_max_min_from_datasets(sample_dir) + + # Get data + all_data = self.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(self, 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(self, num_samples, file_name, sample_dir=None): + if sample_dir: + maxs,mins,avgs = self.get_max_min_from_datasets(sample_dir) + else: + maxs,mins,avgs = [], [], [] + + # Get data + all_data = self.get_data(file_name) + + # Get algorithms sample points, assuming hypercube for now + num_labels = 1 + inp_dims = len(all_data) - num_labels + #samples = dp.hycusampling.lhd_matrix(num_samples, inp_dims)/num_samples + #samples = dp.hycusampling.halton(num_samples, inp_dims) + samples = dp.hycusampling.random_uniform(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 = self.apply_samples_to_data(self_scaled_data,samples) + #print(sampled_data) + + #unscale values and rescale using all available data (both sampled and unused points rescaled) + if len(maxs)!=0 and len(mins)!=0: + sampled_data = self.rescale_data(sampled_data, self_maxs,self_mins, maxs, mins) + unused_new_scaling = self.rescale_data(unused_data, self_maxs,self_mins, maxs, mins) + + return np.asarray(sampled_data), np.asarray(unused_new_scaling) + +def unscale_data(self, data, ref_dir, pos=None): + if ref_dir: + maxs,mins,avgs = self.get_max_min_from_datasets(ref_dir) + else: + print("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_row = [] + for val, cur_max, cur_min in zip(data_row, maxs, mins): + unscaled_val = val*(cur_max-cur_min) + cur_min + unscaled_row.append(unscaled_val) + unscaled_data.append(unscaled_row) + + return unscaled_data + +def abs_error(self, 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] + # print(cur_error) + total_error += cur_error + return total_error/len(labels) + +def max_error(self, 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(self, 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 diff --git a/technology/setup_scripts/gen_model_util/linreg_scikit.py b/compiler/characterizer/linear_regression.py similarity index 100% rename from technology/setup_scripts/gen_model_util/linreg_scikit.py rename to compiler/characterizer/linear_regression.py diff --git a/technology/setup_scripts/gen_model_util/mapping.py b/technology/setup_scripts/gen_model_util/mapping.py deleted file mode 100644 index e24f6e07..00000000 --- a/technology/setup_scripts/gen_model_util/mapping.py +++ /dev/null @@ -1,216 +0,0 @@ -import diversipy as dp -import csv -import math -import numpy as np -import os - -class mapping(): - - def get_data_names(self, file_name): - 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 - return row[0].split(',') - - def get_data(self, file_name): - with open(file_name, newline='') as csvfile: - csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|') - row_iter = 0 - for row in csv_reader: - #data = [int(csv_str) for csv_str in ', '.join(row)] - row_iter += 1 - if row_iter == 1: - feature_names = row[0].split(',') - input_list = [[] for _ in feature_names] - scaled_list = [[] for _ in feature_names] - #label_list = [] - continue - #print(row[0]) - data = [float(csv_str) for csv_str in row[0].split(',')] - data[0] = math.log(data[0], 2) - #input_list.append(data) - for i in range(len(data)): - input_list[i].append(data[i]) - #label_list.append([data[-1]]) - #print(data) - return input_list - - def apply_samples_to_data(self, 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(self.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(self, data, sampled_vals): - min_error = 0 - sample_pos = 0 - count = 0 - for data_slice in data: - error = self.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(self, list_a, list_b): - #print('a:',list_a, 'b:', 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(self, dir): - if not os.path.isdir(dir): - print("Input Directory not found:",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 = self.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_data_and_scale(self, file_name, sample_dir): - maxs,mins,avgs = self.get_max_min_from_datasets(sample_dir) - - # Get data - all_data = self.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(self, 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(self, num_samples, file_name, sample_dir=None): - if sample_dir: - maxs,mins,avgs = self.get_max_min_from_datasets(sample_dir) - else: - maxs,mins,avgs = [], [], [] - - # Get data - all_data = self.get_data(file_name) - - # Get algorithms sample points, assuming hypercube for now - num_labels = 1 - inp_dims = len(all_data) - num_labels - #samples = dp.hycusampling.lhd_matrix(num_samples, inp_dims)/num_samples - #samples = dp.hycusampling.halton(num_samples, inp_dims) - samples = dp.hycusampling.random_uniform(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 = self.apply_samples_to_data(self_scaled_data,samples) - #print(sampled_data) - - #unscale values and rescale using all available data (both sampled and unused points rescaled) - if len(maxs)!=0 and len(mins)!=0: - sampled_data = self.rescale_data(sampled_data, self_maxs,self_mins, maxs, mins) - unused_new_scaling = self.rescale_data(unused_data, self_maxs,self_mins, maxs, mins) - - return np.asarray(sampled_data), np.asarray(unused_new_scaling) - - def unscale_data(self, data, ref_dir, pos=None): - if ref_dir: - maxs,mins,avgs = self.get_max_min_from_datasets(ref_dir) - else: - print("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_row = [] - for val, cur_max, cur_min in zip(data_row, maxs, mins): - unscaled_val = val*(cur_max-cur_min) + cur_min - unscaled_row.append(unscaled_val) - unscaled_data.append(unscaled_row) - - return unscaled_data - - def abs_error(self, 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] - # print(cur_error) - total_error += cur_error - return total_error/len(labels) - - def max_error(self, 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(self, 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 - \ No newline at end of file From d111041385084609e6618282caccf6f0f4863a98 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 2 Dec 2020 14:06:39 -0800 Subject: [PATCH 04/59] Refactored analytical model to be it's own module with shared code moved to simulation --- compiler/characterizer/__init__.py | 1 + compiler/characterizer/delay.py | 53 +------------- compiler/characterizer/elmore.py | 105 +++++++++++++++++++++++++++ compiler/characterizer/lib.py | 4 +- compiler/characterizer/simulation.py | 58 ++++++++++++++- 5 files changed, 168 insertions(+), 53 deletions(-) create mode 100644 compiler/characterizer/elmore.py diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index d1f45575..d1f7d5f3 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -11,6 +11,7 @@ import globals from globals import OPTS,find_exe,get_tool from .lib import * from .delay import * +from .elmore import * from .setup_hold import * from .functional import * from .simulation import * diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index dfb05db3..93e1b0f0 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -52,21 +52,6 @@ class delay(simulation): self.create_signal_names() self.add_graph_exclusions() - def create_measurement_names(self): - """ Create measurement names. The names themselves currently define the type of measurement """ - - self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] - self.power_meas_names = ["read0_power", - "read1_power", - "write0_power", - "write1_power", - "disabled_read0_power", - "disabled_read1_power", - "disabled_write0_power", - "disabled_write1_power"] - # self.voltage_when_names = ["volt_bl", "volt_br"] - # self.bitline_delay_names = ["delay_bl", "delay_br"] - def create_measurement_objects(self): """ Create the measurements used for read and write ports """ @@ -927,29 +912,11 @@ class delay(simulation): """ Probe address and data can be set separately to utilize other functions in this characterizer besides analyze. + Netlist reduced for simulation. """ - - self.probe_address = probe_address - self.probe_data = probe_data - self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) - self.wordline_row = self.get_address_row_number(probe_address) + super().set_probe(probe_address, probe_data) self.prepare_netlist() - def get_data_bit_column_number(self, probe_address, probe_data): - """Calculates bitline column number of data bit under test using bit position and mux size""" - - if self.sram.col_addr_size>0: - col_address = int(probe_address[0:self.sram.col_addr_size], 2) - else: - col_address = 0 - bl_column = int(self.sram.words_per_row * probe_data + col_address) - return bl_column - - def get_address_row_number(self, probe_address): - """Calculates wordline row number of data bit under test using address and column mux size""" - - return int(probe_address[self.sram.col_addr_size:], 2) - def prepare_netlist(self): """ Prepare a trimmed netlist and regular netlist. """ @@ -1193,14 +1160,6 @@ class delay(simulation): # 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) - 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): """ Return the analytical model results for the SRAM. @@ -1301,11 +1260,3 @@ class delay(simulation): self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) if port in self.readwrite_ports: self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) - - def get_empty_measure_data_dict(self): - """Make a dict of lists for each type of delay and power measurement to append results to""" - - measure_names = self.delay_meas_names + self.power_meas_names - # Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. - measure_data = [{mname: [] for mname in measure_names} for i in self.all_ports] - return measure_data diff --git a/compiler/characterizer/elmore.py b/compiler/characterizer/elmore.py new file mode 100644 index 00000000..80ced306 --- /dev/null +++ b/compiler/characterizer/elmore.py @@ -0,0 +1,105 @@ +# 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. +# + +from .simulation import simulation +from globals import OPTS +import debug + +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: + # self.num_wmasks = int(math.ceil(self.word_size / self.write_size)) + # else: + # self.num_wmasks = 0 + #self.set_load_slew(0, 0) + self.set_corner(corner) + self.create_signal_names() + self.add_graph_exclusions() + + def analytical_delay(self, slews, loads): + """ + 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.create_graph() + self.set_internal_spice_names() + self.create_measurement_names() + + port = self.read_ports[0] + self.graph.get_all_paths('{}{}'.format("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() + power = self.analytical_power(slews, loads) + debug.info(1, 'Slew, Load, Delay(ns), Slew(ns)') + max_delay = 0.0 + for slew in slews: + for load in loads: + # Calculate delay based on slew and 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 mname in self.delay_meas_names + self.power_meas_names: + if "power" in mname: + port_data[port][mname].append(power.dynamic) + elif "delay" in mname and port in self.read_ports: + port_data[port][mname].append(total_delay.delay / 1e3) + elif "slew" in mname and port in self.read_ports: + port_data[port][mname].append(total_delay.slew / 1e3) + else: + debug.error("Measurement name not recognized: {}".format(mname), 1) + + # Estimate the period as double the delay with margin + period_margin = 0.1 + sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin, + "leakage_power": power.leakage} + + debug.info(2, "SRAM Data:\n{}".format(sram_data)) + debug.info(2, "Port Data:\n{}".format(port_data)) + + return (sram_data, port_data) + + def analytical_power(self, slews, loads): + """Get the dynamic and leakage power from the SRAM""" + + # slews unused, only last load is used + load = loads[-1] + power = self.sram.analytical_power(self.corner, load) + # convert from nW to mW + power.dynamic /= 1e6 + 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 \ No newline at end of file diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index e5628c5d..a1e2ca44 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -11,6 +11,7 @@ import math import datetime from .setup_hold import * from .delay import * +from .elmore import * from .charutils import * import tech import numpy as np @@ -582,11 +583,12 @@ class lib: def compute_delay(self): """Compute SRAM delays for current corner""" - self.d = delay(self.sram, self.sp_file, self.corner) if self.use_model: + self.d = elmore(self.sram, self.sp_file, self.corner) char_results = self.d.analytical_delay(self.slews,self.loads) self.char_sram_results, self.char_port_results = char_results else: + self.d = delay(self.sram, self.sp_file, self.corner) if (self.sram.num_spare_rows == 0): probe_address = "1" * self.sram.addr_size else: diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index af30df9b..61f5914a 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -39,6 +39,21 @@ class simulation(): else: self.num_wmasks = 0 + def create_measurement_names(self): + """ Create measurement names. The names themselves currently define the type of measurement """ + + self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] + self.power_meas_names = ["read0_power", + "read1_power", + "write0_power", + "write1_power", + "disabled_read0_power", + "disabled_read1_power", + "disabled_write0_power", + "disabled_write1_power"] + # self.voltage_when_names = ["volt_bl", "volt_br"] + # self.bitline_delay_names = ["delay_bl", "delay_br"] + def set_corner(self, corner): """ Set the corner values """ self.corner = corner @@ -92,6 +107,32 @@ class simulation(): self.cycle_comments = [] self.fn_cycle_comments = [] + def set_probe(self, probe_address, probe_data): + """ + Probe address and data can be set separately to utilize other + functions in this characterizer besides analyze. + """ + + self.probe_address = probe_address + self.probe_data = probe_data + self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) + self.wordline_row = self.get_address_row_number(probe_address) + + def get_data_bit_column_number(self, probe_address, probe_data): + """Calculates bitline column number of data bit under test using bit position and mux size""" + + if self.sram.col_addr_size>0: + col_address = int(probe_address[0:self.sram.col_addr_size], 2) + else: + col_address = 0 + bl_column = int(self.sram.words_per_row * probe_data + col_address) + return bl_column + + def get_address_row_number(self, probe_address): + """Calculates wordline row number of data bit under test using address and column mux size""" + + return int(probe_address[self.sram.col_addr_size:], 2) + def add_control_one_port(self, port, op): """Appends control signals for operation to a given port""" # Determine values to write to port @@ -539,6 +580,21 @@ class simulation(): for i in range(len(bl_names)): bl_names[i] = bl_names[i].split('.')[-1] return bl_names[0], bl_names[1] - + + def get_empty_measure_data_dict(self): + """Make a dict of lists for each type of delay and power measurement to append results to""" + + measure_names = self.delay_meas_names + self.power_meas_names + # Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. + measure_data = [{mname: [] for mname in measure_names} for i in self.all_ports] + return measure_data + + 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 From dcd20a250ae0980d7219a0218cacf9e55e7bb9b0 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 2 Dec 2020 15:20:50 -0800 Subject: [PATCH 05/59] Changed linear regression model to reference data in tech dir vs local ref. --- compiler/characterizer/__init__.py | 2 + compiler/characterizer/analytical_util.py | 53 ++++++++------- compiler/characterizer/lib.py | 11 ++- compiler/characterizer/linear_regression.py | 75 ++++++++++++++++----- 4 files changed, 94 insertions(+), 47 deletions(-) diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index d1f7d5f3..933f919b 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -12,11 +12,13 @@ from globals import OPTS,find_exe,get_tool from .lib import * from .delay import * from .elmore import * +from .linear_regression import * from .setup_hold import * from .functional import * from .simulation import * from .measurements import * from .model_check import * +from .analytical_util import * debug.info(1,"Initializing characterizer...") OPTS.spice_exe = "" diff --git a/compiler/characterizer/analytical_util.py b/compiler/characterizer/analytical_util.py index e1fa8076..dacc1f94 100644 --- a/compiler/characterizer/analytical_util.py +++ b/compiler/characterizer/analytical_util.py @@ -1,10 +1,10 @@ -import diversipy as dp +#import diversipy as dp import csv import math import numpy as np import os -def get_data_names(self, file_name): +def get_data_names(file_name): with open(file_name, newline='') as csvfile: csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|') row_iter = 0 @@ -13,7 +13,7 @@ def get_data_names(self, file_name): # Return names from first row return row[0].split(',') -def get_data(self, file_name): +def get_data(file_name): with open(file_name, newline='') as csvfile: csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|') row_iter = 0 @@ -36,12 +36,12 @@ def get_data(self, file_name): #print(data) return input_list -def apply_samples_to_data(self, all_data, algo_samples): +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(self.find_sample_position_with_min_error(all_data, sample)) + sample_positions.add(find_sample_position_with_min_error(all_data, sample)) for i in range(len(all_data)): if i in sample_positions: @@ -51,19 +51,19 @@ def apply_samples_to_data(self, all_data, algo_samples): return data_samples, unused_data -def find_sample_position_with_min_error(self, data, sampled_vals): +def find_sample_position_with_min_error(data, sampled_vals): min_error = 0 sample_pos = 0 count = 0 for data_slice in data: - error = self.squared_error(data_slice, sampled_vals) + 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(self, list_a, list_b): +def squared_error(list_a, list_b): #print('a:',list_a, 'b:', list_b) error_sum = 0; for a,b in zip(list_a, list_b): @@ -71,7 +71,7 @@ def squared_error(self, list_a, list_b): return error_sum -def get_max_min_from_datasets(self, dir): +def get_max_min_from_datasets(dir): if not os.path.isdir(dir): print("Input Directory not found:",dir) return [], [], [] @@ -80,7 +80,7 @@ def get_max_min_from_datasets(self, dir): 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 = self.get_data(os.path.join(dir, file)) + 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: @@ -102,11 +102,11 @@ def get_max_min_from_datasets(self, dir): avgs = [s/total_count for s in sums] return maxs,mins,avgs -def get_data_and_scale(self, file_name, sample_dir): - maxs,mins,avgs = self.get_max_min_from_datasets(sample_dir) +def get_data_and_scale(file_name, sample_dir): + maxs,mins,avgs = get_max_min_from_datasets(sample_dir) # Get data - all_data = self.get_data(file_name) + all_data = get_data(file_name) # Scale data from file self_scaled_data = [[] for _ in range(len(all_data[0]))] @@ -117,7 +117,7 @@ def get_data_and_scale(self, file_name, sample_dir): return np.asarray(self_scaled_data) -def rescale_data(self, data, old_maxs, old_mins, new_maxs, new_mins): +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: @@ -130,21 +130,22 @@ def rescale_data(self, data, old_maxs, old_mins, new_maxs, new_mins): return data_new_scaling -def sample_from_file(self, num_samples, file_name, sample_dir=None): +def sample_from_file(num_samples, file_name, sample_dir=None): if sample_dir: - maxs,mins,avgs = self.get_max_min_from_datasets(sample_dir) + maxs,mins,avgs = get_max_min_from_datasets(sample_dir) else: maxs,mins,avgs = [], [], [] # Get data - all_data = self.get_data(file_name) + 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 = dp.hycusampling.lhd_matrix(num_samples, inp_dims)/num_samples #samples = dp.hycusampling.halton(num_samples, inp_dims) - samples = dp.hycusampling.random_uniform(num_samples, inp_dims) + #samples = dp.hycusampling.random_uniform(num_samples, inp_dims) + samples = None # Scale data from file @@ -158,19 +159,19 @@ def sample_from_file(self, num_samples, file_name, sample_dir=None): 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 = self.apply_samples_to_data(self_scaled_data,samples) + sampled_data, unused_data = apply_samples_to_data(self_scaled_data,samples) #print(sampled_data) #unscale values and rescale using all available data (both sampled and unused points rescaled) if len(maxs)!=0 and len(mins)!=0: - sampled_data = self.rescale_data(sampled_data, self_maxs,self_mins, maxs, mins) - unused_new_scaling = self.rescale_data(unused_data, self_maxs,self_mins, maxs, mins) + 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 unscale_data(self, data, ref_dir, pos=None): +def unscale_data(data, ref_dir, pos=None): if ref_dir: - maxs,mins,avgs = self.get_max_min_from_datasets(ref_dir) + maxs,mins,avgs = get_max_min_from_datasets(ref_dir) else: print("Must provide reference data to unscale") return None @@ -190,7 +191,7 @@ def unscale_data(self, data, ref_dir, pos=None): return unscaled_data -def abs_error(self, labels, preds): +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] @@ -198,14 +199,14 @@ def abs_error(self, labels, preds): total_error += cur_error return total_error/len(labels) -def max_error(self, labels, preds): +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(self, labels, preds): +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] diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index a1e2ca44..e6f3f305 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -13,6 +13,7 @@ from .setup_hold import * from .delay import * from .elmore import * from .charutils import * +from .linear_regression import * import tech import numpy as np from globals import OPTS @@ -584,9 +585,13 @@ class lib: def compute_delay(self): """Compute SRAM delays for current corner""" if self.use_model: - self.d = elmore(self.sram, self.sp_file, self.corner) - char_results = self.d.analytical_delay(self.slews,self.loads) - self.char_sram_results, self.char_port_results = char_results + #FIXME: ML models only designed for delay. Cannot produce all values for Lib + d = linear_regression() + char_results = d.get_prediction() + + #self.d = elmore(self.sram, self.sp_file, self.corner) + # char_results = self.d.analytical_delay(self.slews,self.loads) + # self.char_sram_results, self.char_port_results = char_results else: self.d = delay(self.sram, self.sp_file, self.corner) if (self.sram.num_spare_rows == 0): diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index f5fd6de2..b0d0ab8f 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -1,25 +1,64 @@ +# 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 os from sklearn.linear_model import LinearRegression -import mapping +from .analytical_util import * +from globals import OPTS +import debug -reference_dir = "data" +relative_data_path = "/sim_data" +data_filename = "data.csv" +tech_path = os.environ.get('OPENRAM_TECH') +data_dir = tech_path+'/'+OPTS.tech_name+relative_data_path -def run_model(x,y,test_x,test_y): - mp = mapping.mapping() - model = LinearRegression() - model.fit(x, y) - print(model.coef_) - print(model.intercept_) +class linear_regression(): - pred = model.predict(test_x) + def get_prediction(self): - #print(pred) - unscaled_labels = mp.unscale_data(test_y.tolist(), reference_dir) - unscaled_preds = mp.unscale_data(pred.tolist(), reference_dir) - unscaled_labels, unscaled_preds = (list(t) for t in zip(*sorted(zip(unscaled_labels, unscaled_preds)))) - avg_error = mp.abs_error(unscaled_labels, unscaled_preds) - max_error = mp.max_error(unscaled_labels, unscaled_preds) - min_error = mp.min_error(unscaled_labels, unscaled_preds) + train_sets = [] + test_sets = [] + + file_path = data_dir +'/'+data_filename + num_points_train = 5 - errors = {"avg_error": avg_error, "max_error":max_error, "min_error":min_error} - return errors \ No newline at end of file + non_ip_samples, unused_samples = sample_from_file(num_points_train, file_path, data_dir) + nip_features_subset, nip_labels_subset = non_ip_samples[:, :-1], non_ip_samples[:,-1:] + nip_test_feature_subset, nip_test_labels_subset = unused_samples[:, :-1], unused_samples[:,-1:] + + train_sets = [(nip_features_subset, nip_labels_subset)] + test_sets = [(nip_test_feature_subset, nip_test_labels_subset)] + + runs_per_model = 1 + + for train_tuple, test_tuple in zip(train_sets, test_sets): + train_x, train_y = train_tuple + test_x, test_y = test_tuple + + errors = {} + min_train_set = None + for _ in range(runs_per_model): + new_error = self.run_model(train_x, train_y, test_x, test_y, data_dir) + debug.info(1, "Model Error: {}".format(new_error)) + + def run_model(x,y,test_x,test_y, reference_dir): + model = LinearRegression() + model.fit(x, y) + + pred = model.predict(test_x) + + #print(pred) + unscaled_labels = unscale_data(test_y.tolist(), reference_dir) + unscaled_preds = unscale_data(pred.tolist(), reference_dir) + unscaled_labels, unscaled_preds = (list(t) for t in zip(*sorted(zip(unscaled_labels, unscaled_preds)))) + avg_error = abs_error(unscaled_labels, unscaled_preds) + max_error = max_error(unscaled_labels, unscaled_preds) + min_error = min_error(unscaled_labels, unscaled_preds) + + errors = {"avg_error": avg_error, "max_error":max_error, "min_error":min_error} + return errors \ No newline at end of file From 5f4a2f0231838604233987bacae7ce70c2ee7816 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Mon, 7 Dec 2020 13:11:04 -0800 Subject: [PATCH 06/59] Added function to get all data and scale vs just a portion --- compiler/characterizer/analytical_util.py | 44 ++++++++++++++++++--- compiler/characterizer/lib.py | 6 ++- compiler/characterizer/linear_regression.py | 19 +++++---- 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/compiler/characterizer/analytical_util.py b/compiler/characterizer/analytical_util.py index dacc1f94..b93f463d 100644 --- a/compiler/characterizer/analytical_util.py +++ b/compiler/characterizer/analytical_util.py @@ -1,4 +1,12 @@ -#import diversipy as dp +# +# 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 @@ -131,6 +139,11 @@ def rescale_data(data, old_maxs, old_mins, new_maxs, new_mins): 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: @@ -142,10 +155,7 @@ def sample_from_file(num_samples, file_name, sample_dir=None): # Get algorithms sample points, assuming hypercube for now num_labels = 1 inp_dims = len(all_data) - num_labels - #samples = dp.hycusampling.lhd_matrix(num_samples, inp_dims)/num_samples - #samples = dp.hycusampling.halton(num_samples, inp_dims) - #samples = dp.hycusampling.random_uniform(num_samples, inp_dims) - samples = None + samples = np.random.rand(num_samples, inp_dims) # Scale data from file @@ -169,6 +179,30 @@ def sample_from_file(num_samples, file_name, sample_dir=None): return np.asarray(sampled_data), np.asarray(unused_new_scaling) +def get_scaled_data(file_name, sample_dir=None): + """Get data from CSV file and scale it based on max/min of dataset""" + + 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) + + # Data is scaled by max/min and data format is changed to points vs feature lists + 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)) + + return np.asarray(self_scaled_data) + def unscale_data(data, ref_dir, pos=None): if ref_dir: maxs,mins,avgs = get_max_min_from_datasets(ref_dir) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index e6f3f305..0204de62 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -587,7 +587,11 @@ class lib: if self.use_model: #FIXME: ML models only designed for delay. Cannot produce all values for Lib d = linear_regression() - char_results = d.get_prediction() + model_inputs = [OPTS.num_words, + OPTS.word_size, + OPTS.words_per_row, + self.sram.width * self.sram.height] + char_results = d.get_prediction(model_inputs) #self.d = elmore(self.sram, self.sp_file, self.corner) # char_results = self.d.analytical_delay(self.slews,self.loads) diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index b0d0ab8f..8c69f6be 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -19,13 +19,18 @@ data_dir = tech_path+'/'+OPTS.tech_name+relative_data_path class linear_regression(): - def get_prediction(self): + def __init__(self): + self.model = None + + def get_prediction(self, model_inputs): train_sets = [] test_sets = [] file_path = data_dir +'/'+data_filename - num_points_train = 5 + num_points_train = 7 + + samples = get_scaled_data(file_path, data_dir) non_ip_samples, unused_samples = sample_from_file(num_points_train, file_path, data_dir) nip_features_subset, nip_labels_subset = non_ip_samples[:, :-1], non_ip_samples[:,-1:] @@ -46,7 +51,7 @@ class linear_regression(): new_error = self.run_model(train_x, train_y, test_x, test_y, data_dir) debug.info(1, "Model Error: {}".format(new_error)) - def run_model(x,y,test_x,test_y, reference_dir): + def train_model(self, x,y,test_x,test_y, reference_dir): model = LinearRegression() model.fit(x, y) @@ -56,9 +61,9 @@ class linear_regression(): unscaled_labels = unscale_data(test_y.tolist(), reference_dir) unscaled_preds = unscale_data(pred.tolist(), reference_dir) unscaled_labels, unscaled_preds = (list(t) for t in zip(*sorted(zip(unscaled_labels, unscaled_preds)))) - avg_error = abs_error(unscaled_labels, unscaled_preds) - max_error = max_error(unscaled_labels, unscaled_preds) - min_error = min_error(unscaled_labels, unscaled_preds) + avg_err = abs_error(unscaled_labels, unscaled_preds) + max_err = max_error(unscaled_labels, unscaled_preds) + min_err = min_error(unscaled_labels, unscaled_preds) - errors = {"avg_error": avg_error, "max_error":max_error, "min_error":min_error} + errors = {"avg_error": avg_err, "max_error":max_err, "min_error":min_err} return errors \ No newline at end of file From 6e7d1695b51087c793905d1569fc7cecb1c7dbfe Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Mon, 7 Dec 2020 14:22:53 -0800 Subject: [PATCH 07/59] Cleaned code to remove validation during training. --- compiler/characterizer/analytical_util.py | 42 ++++++++++---- compiler/characterizer/linear_regression.py | 62 +++++++++------------ 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/compiler/characterizer/analytical_util.py b/compiler/characterizer/analytical_util.py index b93f463d..e09a1bba 100644 --- a/compiler/characterizer/analytical_util.py +++ b/compiler/characterizer/analytical_util.py @@ -191,17 +191,39 @@ def get_scaled_data(file_name, sample_dir=None): 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 = [[] 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)) + self_scaled_data = scale_data_and_transform(all_data) - return np.asarray(self_scaled_data) + samples = np.asarray(self_scaled_data) + features, labels = samples[:, :-1], samples[:,-1:] + return features, labels + +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)): + scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val)) + return scaled_data + +def scale_input_datapoint(point, data_dir): + """ + Input data has no output and needs to be scaled like the model inputs during + training. + """ + maxs, mins, avgs = get_max_min_from_datasets(data_dir) + debug.info(1, "maxs={}".format(maxs)) + debug.info(1, "mins={}".format(mins)) + debug.info(1, "point={}".format(point)) + + scaled_point = [] + for feature, mx, mn in zip(point, maxs, mins): + scaled_point.append((feature-mn)/(mx-mn)) + return scaled_point def unscale_data(data, ref_dir, pos=None): if ref_dir: diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index 8c69f6be..897ad54b 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -28,42 +28,30 @@ class linear_regression(): test_sets = [] file_path = data_dir +'/'+data_filename - num_points_train = 7 + scaled_inputs = np.asarray(scale_input_datapoint(model_inputs, data_dir)) - samples = get_scaled_data(file_path, data_dir) + features, labels = get_scaled_data(file_path, data_dir) + self.train_model(features, labels) + scaled_pred = model_prediction(model_inputs) + pred = unscale_data(scaled_pred.tolist(), data_dir) + debug.info(1,"Unscaled Prediction = {}".format(pred)) + return pred - non_ip_samples, unused_samples = sample_from_file(num_points_train, file_path, data_dir) - nip_features_subset, nip_labels_subset = non_ip_samples[:, :-1], non_ip_samples[:,-1:] - nip_test_feature_subset, nip_test_labels_subset = unused_samples[:, :-1], unused_samples[:,-1:] - - train_sets = [(nip_features_subset, nip_labels_subset)] - test_sets = [(nip_test_feature_subset, nip_test_labels_subset)] - - runs_per_model = 1 - - for train_tuple, test_tuple in zip(train_sets, test_sets): - train_x, train_y = train_tuple - test_x, test_y = test_tuple - - errors = {} - min_train_set = None - for _ in range(runs_per_model): - new_error = self.run_model(train_x, train_y, test_x, test_y, data_dir) - debug.info(1, "Model Error: {}".format(new_error)) - - def train_model(self, x,y,test_x,test_y, reference_dir): - model = LinearRegression() - model.fit(x, y) - - pred = model.predict(test_x) - - #print(pred) - unscaled_labels = unscale_data(test_y.tolist(), reference_dir) - unscaled_preds = unscale_data(pred.tolist(), reference_dir) - unscaled_labels, unscaled_preds = (list(t) for t in zip(*sorted(zip(unscaled_labels, unscaled_preds)))) - avg_err = abs_error(unscaled_labels, unscaled_preds) - max_err = max_error(unscaled_labels, unscaled_preds) - min_err = min_error(unscaled_labels, unscaled_preds) - - errors = {"avg_error": avg_err, "max_error":max_err, "min_error":min_err} - return errors \ No newline at end of file + def train_model(self, features, labels): + """ + Supervised training of model. + """ + + self.model = LinearRegression() + self.model.fit(features, labels) + + def model_prediction(self, features): + """ + Have the model perform a prediction and unscale the prediction + as the model is trained with scaled values. + """ + + pred = self.model.predict(features) + debug.info(1, "pred={}".format(pred)) + return pred + \ No newline at end of file From 8a75b838897f6b172c90d716fecd2dc400035661 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Mon, 7 Dec 2020 14:36:01 -0800 Subject: [PATCH 08/59] Fixed input scaling bugs delay prediction model --- compiler/characterizer/lib.py | 7 +++++-- compiler/characterizer/linear_regression.py | 9 +++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 0204de62..948f8e47 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -585,11 +585,14 @@ class lib: def compute_delay(self): """Compute SRAM delays for current corner""" if self.use_model: + import math #FIXME: ML models only designed for delay. Cannot produce all values for Lib d = linear_regression() - model_inputs = [OPTS.num_words, + temp_wpr = 2.0 #OPTS not working right now + log_num_words = math.log(OPTS.num_words, 2) + model_inputs = [log_num_words, OPTS.word_size, - OPTS.words_per_row, + temp_wpr, self.sram.width * self.sram.height] char_results = d.get_prediction(model_inputs) diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index 897ad54b..511cd378 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -22,17 +22,14 @@ class linear_regression(): def __init__(self): self.model = None - def get_prediction(self, model_inputs): - - train_sets = [] - test_sets = [] + def get_prediction(self, model_inputs): file_path = data_dir +'/'+data_filename - scaled_inputs = np.asarray(scale_input_datapoint(model_inputs, data_dir)) + scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, data_dir)]) features, labels = get_scaled_data(file_path, data_dir) self.train_model(features, labels) - scaled_pred = model_prediction(model_inputs) + scaled_pred = self.model_prediction(scaled_inputs) pred = unscale_data(scaled_pred.tolist(), data_dir) debug.info(1,"Unscaled Prediction = {}".format(pred)) return pred From fc55cd194d1699f913f8e5992282bc8e6e1c3b30 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 9 Dec 2020 12:54:11 -0800 Subject: [PATCH 09/59] Added model selection option. --- compiler/characterizer/lib.py | 18 ++++++++++++------ compiler/options.py | 10 ++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 948f8e47..ad1f5e1c 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -11,9 +11,7 @@ import math import datetime from .setup_hold import * from .delay import * -from .elmore import * from .charutils import * -from .linear_regression import * import tech import numpy as np from globals import OPTS @@ -585,16 +583,24 @@ class lib: def compute_delay(self): """Compute SRAM delays for current corner""" if self.use_model: + model_name_lc = OPTS.model_name.lower() + if model_name_lc == "linear_regression": + from .linear_regression import linear_regression as model + elif model_name_lc == "elmore": + from .elmore import elmore as model + else: + debug.error("{} model not recognized. See options.py for available models.".format(OPTS.model_name)) import math #FIXME: ML models only designed for delay. Cannot produce all values for Lib - d = linear_regression() - temp_wpr = 2.0 #OPTS not working right now + m = model() + #temp_wpr = 2.0 #OPTS not working right now log_num_words = math.log(OPTS.num_words, 2) + debug.info(1, "OPTS.words_per_row={}".format(OPTS.words_per_row)) model_inputs = [log_num_words, OPTS.word_size, - temp_wpr, + OPTS.words_per_row, self.sram.width * self.sram.height] - char_results = d.get_prediction(model_inputs) + char_results = m.get_prediction(model_inputs) #self.d = elmore(self.sram, self.sp_file, self.corner) # char_results = self.d.analytical_delay(self.slews,self.loads) diff --git a/compiler/options.py b/compiler/options.py index e4a61052..f6915035 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -103,7 +103,9 @@ class options(optparse.Values): use_pex = False # Output config with all options output_extended_config = True - + # Determines which analytical model to use. + # Available Models: elmore, linear_regression + model_name = "linear_regression" ################### # Tool options @@ -152,8 +154,6 @@ class options(optparse.Values): bitcell = "bitcell" buf_dec = "pbuf" column_mux_array = "column_mux_array" - col_cap = "col_cap" - col_cap_array = "col_cap_array" control_logic = "control_logic" decoder = "hierarchical_decoder" delay_chain = "delay_chain" @@ -162,12 +162,10 @@ class options(optparse.Values): inv_dec = "pinv" nand2_dec = "pnand2" nand3_dec = "pnand3" - nand4_dec = "pnand4" + nand4_dec = "pnand4" # Not available right now precharge_array = "precharge_array" ptx = "ptx" replica_bitline = "replica_bitline" - row_cap = "row_cap" - row_cap_array = "row_cap_array" sense_amp_array = "sense_amp_array" sense_amp = "sense_amp" tri_gate_array = "tri_gate_array" From 393a9ca0d83110870701135b3d07f96a2541eb85 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 9 Dec 2020 15:03:04 -0800 Subject: [PATCH 10/59] Data scaling is only dependent on a single file rather than a directory now. --- compiler/characterizer/analytical_util.py | 34 ++++++++++++++++----- compiler/characterizer/linear_regression.py | 6 ++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/compiler/characterizer/analytical_util.py b/compiler/characterizer/analytical_util.py index e09a1bba..26b9d23f 100644 --- a/compiler/characterizer/analytical_util.py +++ b/compiler/characterizer/analytical_util.py @@ -110,6 +110,24 @@ def get_max_min_from_datasets(dir): 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) @@ -179,11 +197,11 @@ def sample_from_file(num_samples, file_name, sample_dir=None): return np.asarray(sampled_data), np.asarray(unused_new_scaling) -def get_scaled_data(file_name, sample_dir=None): +def get_scaled_data(file_name): """Get data from CSV file and scale it based on max/min of dataset""" - if sample_dir: - maxs,mins,avgs = get_max_min_from_datasets(sample_dir) + if file_name: + maxs,mins,avgs = get_max_min_from_file(file_name) else: maxs,mins,avgs = [], [], [] @@ -210,12 +228,12 @@ def scale_data_and_transform(data): scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val)) return scaled_data -def scale_input_datapoint(point, data_dir): +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_datasets(data_dir) + maxs, mins, avgs = get_max_min_from_file(file_path) debug.info(1, "maxs={}".format(maxs)) debug.info(1, "mins={}".format(mins)) debug.info(1, "point={}".format(point)) @@ -225,9 +243,9 @@ def scale_input_datapoint(point, data_dir): scaled_point.append((feature-mn)/(mx-mn)) return scaled_point -def unscale_data(data, ref_dir, pos=None): - if ref_dir: - maxs,mins,avgs = get_max_min_from_datasets(ref_dir) +def unscale_data(data, file_path, pos=None): + if file_path: + maxs,mins,avgs = get_max_min_from_file(file_path) else: print("Must provide reference data to unscale") return None diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index 511cd378..267ccebc 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -25,12 +25,12 @@ class linear_regression(): def get_prediction(self, model_inputs): file_path = data_dir +'/'+data_filename - scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, data_dir)]) + scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, file_path)]) - features, labels = get_scaled_data(file_path, data_dir) + features, labels = get_scaled_data(file_path) self.train_model(features, labels) scaled_pred = self.model_prediction(scaled_inputs) - pred = unscale_data(scaled_pred.tolist(), data_dir) + pred = unscale_data(scaled_pred.tolist(), file_path) debug.info(1,"Unscaled Prediction = {}".format(pred)) return pred From b1a7e0e55bab8e731a60fa9eee8af7e28e40b1e0 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 9 Dec 2020 15:21:22 -0800 Subject: [PATCH 11/59] Added power data --- technology/scn4m_subm/sim_data/power_data.csv | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 technology/scn4m_subm/sim_data/power_data.csv diff --git a/technology/scn4m_subm/sim_data/power_data.csv b/technology/scn4m_subm/sim_data/power_data.csv new file mode 100644 index 00000000..301d5f9f --- /dev/null +++ b/technology/scn4m_subm/sim_data/power_data.csv @@ -0,0 +1,9 @@ +num_words,word_size,words_per_row,area,read_power +16,2,1,88873,8.0252 +64,2,4,108116,9.8616 +16,1,1,86004,7.4911 +32,3,2,101618,9.0957 +32,2,2,95878,8.7143 +16,3,1,93009,8.5708 +64,1,4,95878,8.5750 +32,1,2,90139,7.9725 From 0adcf8935f0775dd9d5936efe30e442be0e745b7 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 9 Dec 2020 15:31:43 -0800 Subject: [PATCH 12/59] Added linear regression model for power. --- compiler/characterizer/lib.py | 2 +- compiler/characterizer/linear_regression.py | 40 ++++++++++++--------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index ad1f5e1c..6fdcb1bf 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -600,7 +600,7 @@ class lib: OPTS.word_size, OPTS.words_per_row, self.sram.width * self.sram.height] - char_results = m.get_prediction(model_inputs) + char_results = m.get_predictions(model_inputs) #self.d = elmore(self.sram, self.sp_file, self.corner) # char_results = self.d.analytical_delay(self.slews,self.loads) diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index 267ccebc..6f63c3c4 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -13,42 +13,48 @@ from globals import OPTS import debug relative_data_path = "/sim_data" -data_filename = "data.csv" +delay_data_filename = "data.csv" +power_data_filename = "power_data.csv" tech_path = os.environ.get('OPENRAM_TECH') data_dir = tech_path+'/'+OPTS.tech_name+relative_data_path class linear_regression(): def __init__(self): - self.model = None + self.delay_model = None + self.power_model = None - def get_prediction(self, model_inputs): + def get_predictions(self, model_inputs): - file_path = data_dir +'/'+data_filename - scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, file_path)]) + delay_file_path = data_dir +'/'+delay_data_filename + power_file_path = data_dir +'/'+power_data_filename + scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, delay_file_path)]) - features, labels = get_scaled_data(file_path) - self.train_model(features, labels) - scaled_pred = self.model_prediction(scaled_inputs) - pred = unscale_data(scaled_pred.tolist(), file_path) - debug.info(1,"Unscaled Prediction = {}".format(pred)) - return pred + predictions = [] + for path, model in zip([delay_file_path, power_file_path], [self.delay_model, self.power_model]): + features, labels = get_scaled_data(path) + model = self.generate_model(features, labels) + scaled_pred = self.model_prediction(model, scaled_inputs) + pred = unscale_data(scaled_pred.tolist(), path) + debug.info(1,"Unscaled Prediction = {}".format(pred)) + predictions.append(pred) + return predictions - def train_model(self, features, labels): + def generate_model(self, features, labels): """ Supervised training of model. """ - self.model = LinearRegression() - self.model.fit(features, labels) + model = LinearRegression() + model.fit(features, labels) + return model - def model_prediction(self, 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 = self.model.predict(features) - debug.info(1, "pred={}".format(pred)) + pred = model.predict(features) return pred \ No newline at end of file From 25544c3974f41762a7f593470c511ceb91859b09 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Mon, 14 Dec 2020 13:59:31 -0800 Subject: [PATCH 13/59] Added similar interface to linear regression as elmore --- compiler/characterizer/delay.py | 76 +--------------- compiler/characterizer/elmore.py | 2 +- compiler/characterizer/lib.py | 20 ++--- compiler/characterizer/linear_regression.py | 88 ++++++++++++++++--- .../sim_data/{data.csv => delay_data.csv} | 0 5 files changed, 85 insertions(+), 101 deletions(-) rename technology/scn4m_subm/sim_data/{data.csv => delay_data.csv} (100%) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 1535ebc5..49e68af7 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -510,7 +510,7 @@ class delay(simulation): elif delay_obj.meta_str == sram_op.READ_ONE: meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]] else: - debug.error("Unrecognised delay Index={}".format(delay_obj.meta_str),1) + debug.error("Unrecognized delay Index={}".format(delay_obj.meta_str),1) # These measurements have there time further delayed to the neg. edge of the clock. if delay_obj.meta_add_delay: @@ -1270,80 +1270,6 @@ class delay(simulation): # 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) - def analytical_delay(self, slews, loads): - """ - 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.create_graph() - self.set_internal_spice_names() - self.create_measurement_names() - - port = self.read_ports[0] - self.graph.get_all_paths('{}{}'.format("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() - power = self.analytical_power(slews, loads) - debug.info(1, 'Slew, Load, Delay(ns), Slew(ns)') - max_delay = 0.0 - for slew in slews: - for load in loads: - # Calculate delay based on slew and 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 mname in self.delay_meas_names + self.power_meas_names: - if "power" in mname: - port_data[port][mname].append(power.dynamic) - elif "delay" in mname and port in self.read_ports: - port_data[port][mname].append(total_delay.delay / 1e3) - elif "slew" in mname and port in self.read_ports: - port_data[port][mname].append(total_delay.slew / 1e3) - else: - debug.error("Measurement name not recognized: {}".format(mname), 1) - - # Estimate the period as double the delay with margin - period_margin = 0.1 - sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin, - "leakage_power": power.leakage} - - debug.info(2, "SRAM Data:\n{}".format(sram_data)) - debug.info(2, "Port Data:\n{}".format(port_data)) - - return (sram_data, port_data) - - def analytical_power(self, slews, loads): - """Get the dynamic and leakage power from the SRAM""" - - # slews unused, only last load is used - load = loads[-1] - power = self.sram.analytical_power(self.corner, load) - # convert from nW to mW - power.dynamic /= 1e6 - 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 - def gen_data(self): """ Generates the PWL data inputs for a simulation timing test. """ diff --git a/compiler/characterizer/elmore.py b/compiler/characterizer/elmore.py index 80ced306..7dbb4abd 100644 --- a/compiler/characterizer/elmore.py +++ b/compiler/characterizer/elmore.py @@ -30,7 +30,7 @@ class elmore(simulation): self.create_signal_names() self.add_graph_exclusions() - def analytical_delay(self, slews, loads): + def get_lib_values(self, slews, loads): """ Return the analytical model results for the SRAM. """ diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 6fdcb1bf..679010fe 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -591,20 +591,10 @@ class lib: else: debug.error("{} model not recognized. See options.py for available models.".format(OPTS.model_name)) import math - #FIXME: ML models only designed for delay. Cannot produce all values for Lib - m = model() - #temp_wpr = 2.0 #OPTS not working right now - log_num_words = math.log(OPTS.num_words, 2) - debug.info(1, "OPTS.words_per_row={}".format(OPTS.words_per_row)) - model_inputs = [log_num_words, - OPTS.word_size, - OPTS.words_per_row, - self.sram.width * self.sram.height] - char_results = m.get_predictions(model_inputs) - - #self.d = elmore(self.sram, self.sp_file, self.corner) - # char_results = self.d.analytical_delay(self.slews,self.loads) - # self.char_sram_results, self.char_port_results = char_results + + m = model(self.sram, self.sp_file, self.corner) + char_results = m.get_lib_values(self.slews,self.loads) + else: self.d = delay(self.sram, self.sp_file, self.corner) if (self.sram.num_spare_rows == 0): @@ -613,7 +603,7 @@ class lib: probe_address = "0" + "1" * (self.sram.addr_size - 1) probe_data = self.sram.word_size - 1 char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads) - self.char_sram_results, self.char_port_results = char_results + self.char_sram_results, self.char_port_results = char_results def compute_setup_hold(self): """ Do the analysis if we haven't characterized a FF yet """ diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index 6f63c3c4..2e02bd7b 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -6,32 +6,100 @@ # All rights reserved. # -import os -from sklearn.linear_model import LinearRegression from .analytical_util import * +from .simulation import simulation from globals import OPTS import debug +import os +from sklearn.linear_model import LinearRegression +import math + relative_data_path = "/sim_data" -delay_data_filename = "data.csv" -power_data_filename = "power_data.csv" +data_fnames = ["delay_data.csv", + "power_data.csv", + "leakage_data.csv", + "slew_data.csv"] tech_path = os.environ.get('OPENRAM_TECH') data_dir = tech_path+'/'+OPTS.tech_name+relative_data_path +data_paths = [data_dir +'/'+fname for fname in data_fnames] -class linear_regression(): +class linear_regression(simulation): - def __init__(self): + def __init__(self, sram, spfile, corner): + super().__init__(sram, spfile, corner) + self.set_corner(corner) + self.create_signal_names() + self.add_graph_exclusions() self.delay_model = None + self.slew_model = None self.power_model = None + self.leakage_model = None + + def get_lib_values(self, slews, loads): + """ + A model and prediction is created for each output needed for the LIB + """ + + log_num_words = math.log(OPTS.num_words, 2) + debug.info(1, "OPTS.words_per_row={}".format(OPTS.words_per_row)) + model_inputs = [log_num_words, + OPTS.word_size, + OPTS.words_per_row, + self.sram.width * self.sram.height] + + # List returned with value order being delay, power, leakage, slew + # FIXME: make order less hard coded + sram_vals = self.get_predictions(model_inputs) + + self.create_measurement_names() + + + # Set delay/power for slews and loads + port_data = self.get_empty_measure_data_dict() + debug.info(1, 'Slew, Load, Delay(ns), Slew(ns)') + max_delay = 0.0 + for slew in slews: + for load in loads: + # Calculate delay based on slew and load + 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 mname in self.delay_meas_names + self.power_meas_names: + #FIXME: fix magic for indexing the data + if "power" in mname: + port_data[port][mname].append(sram_vals[1]) + elif "delay" in mname and port in self.read_ports: + port_data[port][mname].append(sram_vals[0]) + elif "slew" in mname and port in self.read_ports: + port_data[port][mname].append(sram_vals[3]) + else: + debug.error("Measurement name not recognized: {}".format(mname), 1) + + # Estimate the period as double the delay with margin + period_margin = 0.1 + sram_data = {"min_period": sram_vals[0] * 2, + "leakage_power": sram_vals[2]} + + debug.info(2, "SRAM Data:\n{}".format(sram_data)) + debug.info(2, "Port Data:\n{}".format(port_data)) + + return (sram_data, port_data) def get_predictions(self, model_inputs): + """ + Generate a model and prediction for LIB output + """ - delay_file_path = data_dir +'/'+delay_data_filename - power_file_path = data_dir +'/'+power_data_filename - scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, delay_file_path)]) + scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, data_paths[0])]) predictions = [] - for path, model in zip([delay_file_path, power_file_path], [self.delay_model, self.power_model]): + for path in data_paths: features, labels = get_scaled_data(path) model = self.generate_model(features, labels) scaled_pred = self.model_prediction(model, scaled_inputs) diff --git a/technology/scn4m_subm/sim_data/data.csv b/technology/scn4m_subm/sim_data/delay_data.csv similarity index 100% rename from technology/scn4m_subm/sim_data/data.csv rename to technology/scn4m_subm/sim_data/delay_data.csv From 06232dee8f71a5c8f93392bff2932978762dce96 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Mon, 14 Dec 2020 14:32:10 -0800 Subject: [PATCH 14/59] Added leakage and slew data. Added temporary fix to model output format. --- compiler/characterizer/linear_regression.py | 17 ++++++----------- technology/scn4m_subm/sim_data/leakage_data.csv | 9 +++++++++ technology/scn4m_subm/sim_data/slew_data.csv | 9 +++++++++ 3 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 technology/scn4m_subm/sim_data/leakage_data.csv create mode 100644 technology/scn4m_subm/sim_data/slew_data.csv diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index 2e02bd7b..65b5c9c9 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -61,30 +61,25 @@ class linear_regression(simulation): max_delay = 0.0 for slew in slews: for load in loads: - # Calculate delay based on slew and load - 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 mname in self.delay_meas_names + self.power_meas_names: #FIXME: fix magic for indexing the data + #FIXME: model output is double list. Simply this if "power" in mname: - port_data[port][mname].append(sram_vals[1]) + port_data[port][mname].append(sram_vals[1][0][0]) elif "delay" in mname and port in self.read_ports: - port_data[port][mname].append(sram_vals[0]) + port_data[port][mname].append(sram_vals[0][0][0]) elif "slew" in mname and port in self.read_ports: - port_data[port][mname].append(sram_vals[3]) + port_data[port][mname].append(sram_vals[3][0][0]) else: debug.error("Measurement name not recognized: {}".format(mname), 1) # Estimate the period as double the delay with margin period_margin = 0.1 - sram_data = {"min_period": sram_vals[0] * 2, - "leakage_power": sram_vals[2]} + sram_data = {"min_period": sram_vals[0][0][0] * 2, + "leakage_power": sram_vals[2][0][0]} debug.info(2, "SRAM Data:\n{}".format(sram_data)) debug.info(2, "Port Data:\n{}".format(port_data)) diff --git a/technology/scn4m_subm/sim_data/leakage_data.csv b/technology/scn4m_subm/sim_data/leakage_data.csv new file mode 100644 index 00000000..f3e8afd3 --- /dev/null +++ b/technology/scn4m_subm/sim_data/leakage_data.csv @@ -0,0 +1,9 @@ +num_words,word_size,words_per_row,area,leakage_power +16,2,1,88873,0.0009381791 +64,2,4,108116,0.0011511999999999998 +16,1,1,86004,0.0005252088 +32,3,2,101618,0.0012168 +32,2,2,95878,0.000771978 +16,3,1,93009,0.0009978024 +64,1,4,95878,0.0006975546000000001 +32,1,2,90139,0.0006437493 diff --git a/technology/scn4m_subm/sim_data/slew_data.csv b/technology/scn4m_subm/sim_data/slew_data.csv new file mode 100644 index 00000000..7bf1cbf9 --- /dev/null +++ b/technology/scn4m_subm/sim_data/slew_data.csv @@ -0,0 +1,9 @@ +num_words,word_size,words_per_row,area,output_slew +16,2,1,88873,1.81 +64,2,4,108116,1.69 +16,1,1,86004,1.786 +32,3,2,101618,1.725 +32,2,2,95878,1.71 +16,3,1,93009,1.835 +64,1,4,95878,1.662 +32,1,2,90139,1.69 From 942675051a98e332beaa941915b648b1a9fa5635 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Mon, 14 Dec 2020 14:37:53 -0800 Subject: [PATCH 15/59] Added test for linear regression model. --- compiler/options.py | 2 +- .../23_lib_sram_linear_regression_test.py | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100755 compiler/tests/23_lib_sram_linear_regression_test.py diff --git a/compiler/options.py b/compiler/options.py index f6915035..df547991 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -105,7 +105,7 @@ class options(optparse.Values): output_extended_config = True # Determines which analytical model to use. # Available Models: elmore, linear_regression - model_name = "linear_regression" + model_name = "elmore" ################### # Tool options diff --git a/compiler/tests/23_lib_sram_linear_regression_test.py b/compiler/tests/23_lib_sram_linear_regression_test.py new file mode 100755 index 00000000..bdca8fdd --- /dev/null +++ b/compiler/tests/23_lib_sram_linear_regression_test.py @@ -0,0 +1,70 @@ +#!/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,re +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +import debug + +#@unittest.skip("SKIPPING 23_lib_sram_linear_regression_test") +class lib_sram_linear_regression_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.nominal_corner_only = False + OPTS.netlist_only = True + OPTS.model_name = "linear_regression" + + from characterizer import lib + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=2, + num_words=16, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") + + # This doesn't have to use the factory since worst case + # it will just replaece the top-level module of the same name + s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name)) + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + lib(out_dir=OPTS.openram_temp, sram=s.s, sp_file=tempspice, use_model=True) + + # get all of the .lib files generated + files = os.listdir(OPTS.openram_temp) + nametest = re.compile("\.lib$", re.IGNORECASE) + lib_files = filter(nametest.search, files) + + # and compare them with the golden model + for filename in lib_files: + newname = filename.replace(".lib","_analytical.lib") + libname = "{0}/{1}".format(OPTS.openram_temp,filename) + golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname) + self.assertTrue(self.isapproxdiff(libname,golden,0.15)) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) + + + + + + From f1f6a1a520f75aabb64afc45dd6ff7d25632753d Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 15 Dec 2020 12:08:31 -0800 Subject: [PATCH 16/59] Removed windows end of line characters. --- compiler/characterizer/linear_regression.py | 238 +++++++++--------- technology/scn4m_subm/sim_data/delay_data.csv | 18 +- .../scn4m_subm/sim_data/leakage_data.csv | 18 +- technology/scn4m_subm/sim_data/power_data.csv | 18 +- technology/scn4m_subm/sim_data/slew_data.csv | 18 +- 5 files changed, 152 insertions(+), 158 deletions(-) diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index 65b5c9c9..ef6cf4f6 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -1,123 +1,117 @@ -# 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. -# - -from .analytical_util import * -from .simulation import simulation -from globals import OPTS -import debug - -import os -from sklearn.linear_model import LinearRegression -import math - -relative_data_path = "/sim_data" -data_fnames = ["delay_data.csv", - "power_data.csv", - "leakage_data.csv", - "slew_data.csv"] -tech_path = os.environ.get('OPENRAM_TECH') -data_dir = tech_path+'/'+OPTS.tech_name+relative_data_path -data_paths = [data_dir +'/'+fname for fname in data_fnames] - -class linear_regression(simulation): - - def __init__(self, sram, spfile, corner): - super().__init__(sram, spfile, corner) - self.set_corner(corner) - self.create_signal_names() - self.add_graph_exclusions() - self.delay_model = None - self.slew_model = None - self.power_model = None - self.leakage_model = None - - def get_lib_values(self, slews, loads): - """ - A model and prediction is created for each output needed for the LIB - """ - - log_num_words = math.log(OPTS.num_words, 2) - debug.info(1, "OPTS.words_per_row={}".format(OPTS.words_per_row)) - model_inputs = [log_num_words, - OPTS.word_size, - OPTS.words_per_row, - self.sram.width * self.sram.height] - - # List returned with value order being delay, power, leakage, slew - # FIXME: make order less hard coded - sram_vals = self.get_predictions(model_inputs) - - self.create_measurement_names() - - - # Set delay/power for slews and loads - port_data = self.get_empty_measure_data_dict() - debug.info(1, 'Slew, Load, Delay(ns), Slew(ns)') - max_delay = 0.0 - for slew in slews: - for load in loads: - - # Delay is only calculated on a single port and replicated for now. - for port in self.all_ports: - for mname in self.delay_meas_names + self.power_meas_names: - #FIXME: fix magic for indexing the data - #FIXME: model output is double list. Simply this - if "power" in mname: - port_data[port][mname].append(sram_vals[1][0][0]) - elif "delay" in mname and port in self.read_ports: - port_data[port][mname].append(sram_vals[0][0][0]) - elif "slew" in mname and port in self.read_ports: - port_data[port][mname].append(sram_vals[3][0][0]) - else: - debug.error("Measurement name not recognized: {}".format(mname), 1) - - # Estimate the period as double the delay with margin - period_margin = 0.1 - sram_data = {"min_period": sram_vals[0][0][0] * 2, - "leakage_power": sram_vals[2][0][0]} - - debug.info(2, "SRAM Data:\n{}".format(sram_data)) - debug.info(2, "Port Data:\n{}".format(port_data)) - - return (sram_data, port_data) - - def get_predictions(self, model_inputs): - """ - Generate a model and prediction for LIB output - """ - - scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, data_paths[0])]) - - predictions = [] - for path in data_paths: - features, labels = get_scaled_data(path) - model = self.generate_model(features, labels) - scaled_pred = self.model_prediction(model, scaled_inputs) - pred = unscale_data(scaled_pred.tolist(), path) - debug.info(1,"Unscaled Prediction = {}".format(pred)) - predictions.append(pred) - return predictions - - def generate_model(self, features, labels): - """ - Supervised training of model. - """ - - model = LinearRegression() - model.fit(features, labels) - return model - - 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 +# 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. +# + +from .analytical_util import * +from .simulation import simulation +from globals import OPTS +import debug + +import os +from sklearn.linear_model import LinearRegression +import math + +relative_data_path = "/sim_data" +data_fnames = ["delay_data.csv", + "power_data.csv", + "leakage_data.csv", + "slew_data.csv"] +tech_path = os.environ.get('OPENRAM_TECH') +data_dir = tech_path+'/'+OPTS.tech_name+relative_data_path +data_paths = [data_dir +'/'+fname for fname in data_fnames] + +class linear_regression(simulation): + + def __init__(self, sram, spfile, corner): + super().__init__(sram, spfile, corner) + self.set_corner(corner) + + def get_lib_values(self, slews, loads): + """ + A model and prediction is created for each output needed for the LIB + """ + + log_num_words = math.log(OPTS.num_words, 2) + debug.info(1, "OPTS.words_per_row={}".format(OPTS.words_per_row)) + model_inputs = [log_num_words, + OPTS.word_size, + OPTS.words_per_row, + self.sram.width * self.sram.height] + + # List returned with value order being delay, power, leakage, slew + # FIXME: make order less hard coded + sram_vals = self.get_predictions(model_inputs) + + self.create_measurement_names() + + + # Set delay/power for slews and loads + port_data = self.get_empty_measure_data_dict() + debug.info(1, 'Slew, Load, Delay(ns), Slew(ns)') + max_delay = 0.0 + for slew in slews: + for load in loads: + + # Delay is only calculated on a single port and replicated for now. + for port in self.all_ports: + for mname in self.delay_meas_names + self.power_meas_names: + #FIXME: fix magic for indexing the data + #FIXME: model output is double list. Simply this + if "power" in mname: + port_data[port][mname].append(sram_vals[1][0][0]) + elif "delay" in mname and port in self.read_ports: + port_data[port][mname].append(sram_vals[0][0][0]) + elif "slew" in mname and port in self.read_ports: + port_data[port][mname].append(sram_vals[3][0][0]) + else: + debug.error("Measurement name not recognized: {}".format(mname), 1) + + # Estimate the period as double the delay with margin + period_margin = 0.1 + sram_data = {"min_period": sram_vals[0][0][0] * 2, + "leakage_power": sram_vals[2][0][0]} + + debug.info(2, "SRAM Data:\n{}".format(sram_data)) + debug.info(2, "Port Data:\n{}".format(port_data)) + + return (sram_data, port_data) + + def get_predictions(self, model_inputs): + """ + Generate a model and prediction for LIB output + """ + + scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, data_paths[0])]) + + predictions = [] + for path in data_paths: + features, labels = get_scaled_data(path) + model = self.generate_model(features, labels) + scaled_pred = self.model_prediction(model, scaled_inputs) + pred = unscale_data(scaled_pred.tolist(), path) + debug.info(1,"Unscaled Prediction = {}".format(pred)) + predictions.append(pred) + return predictions + + def generate_model(self, features, labels): + """ + Supervised training of model. + """ + + model = LinearRegression() + model.fit(features, labels) + return model + + 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 \ No newline at end of file diff --git a/technology/scn4m_subm/sim_data/delay_data.csv b/technology/scn4m_subm/sim_data/delay_data.csv index 043403e8..46a88c5f 100644 --- a/technology/scn4m_subm/sim_data/delay_data.csv +++ b/technology/scn4m_subm/sim_data/delay_data.csv @@ -1,9 +1,9 @@ -num_words,word_size,words_per_row,area,max_delay -16,2,1,88873,2.272 -64,2,4,108116,2.721 -16,1,1,86004,2.267 -32,3,2,101618,2.634 -32,2,2,95878,2.594 -16,3,1,93009,2.292 -64,1,4,95878,2.705 -32,1,2,90139,2.552 +num_words,word_size,words_per_row,area,max_delay +16,2,1,88873,2.272 +64,2,4,108116,2.721 +16,1,1,86004,2.267 +32,3,2,101618,2.634 +32,2,2,95878,2.594 +16,3,1,93009,2.292 +64,1,4,95878,2.705 +32,1,2,90139,2.552 diff --git a/technology/scn4m_subm/sim_data/leakage_data.csv b/technology/scn4m_subm/sim_data/leakage_data.csv index f3e8afd3..5f748e04 100644 --- a/technology/scn4m_subm/sim_data/leakage_data.csv +++ b/technology/scn4m_subm/sim_data/leakage_data.csv @@ -1,9 +1,9 @@ -num_words,word_size,words_per_row,area,leakage_power -16,2,1,88873,0.0009381791 -64,2,4,108116,0.0011511999999999998 -16,1,1,86004,0.0005252088 -32,3,2,101618,0.0012168 -32,2,2,95878,0.000771978 -16,3,1,93009,0.0009978024 -64,1,4,95878,0.0006975546000000001 -32,1,2,90139,0.0006437493 +num_words,word_size,words_per_row,area,leakage_power +16,2,1,88873,0.0009381791 +64,2,4,108116,0.0011511999999999998 +16,1,1,86004,0.0005252088 +32,3,2,101618,0.0012168 +32,2,2,95878,0.000771978 +16,3,1,93009,0.0009978024 +64,1,4,95878,0.0006975546000000001 +32,1,2,90139,0.0006437493 diff --git a/technology/scn4m_subm/sim_data/power_data.csv b/technology/scn4m_subm/sim_data/power_data.csv index 301d5f9f..8e3d3de2 100644 --- a/technology/scn4m_subm/sim_data/power_data.csv +++ b/technology/scn4m_subm/sim_data/power_data.csv @@ -1,9 +1,9 @@ -num_words,word_size,words_per_row,area,read_power -16,2,1,88873,8.0252 -64,2,4,108116,9.8616 -16,1,1,86004,7.4911 -32,3,2,101618,9.0957 -32,2,2,95878,8.7143 -16,3,1,93009,8.5708 -64,1,4,95878,8.5750 -32,1,2,90139,7.9725 +num_words,word_size,words_per_row,area,read_power +16,2,1,88873,8.0252 +64,2,4,108116,9.8616 +16,1,1,86004,7.4911 +32,3,2,101618,9.0957 +32,2,2,95878,8.7143 +16,3,1,93009,8.5708 +64,1,4,95878,8.5750 +32,1,2,90139,7.9725 diff --git a/technology/scn4m_subm/sim_data/slew_data.csv b/technology/scn4m_subm/sim_data/slew_data.csv index 7bf1cbf9..87d0e196 100644 --- a/technology/scn4m_subm/sim_data/slew_data.csv +++ b/technology/scn4m_subm/sim_data/slew_data.csv @@ -1,9 +1,9 @@ -num_words,word_size,words_per_row,area,output_slew -16,2,1,88873,1.81 -64,2,4,108116,1.69 -16,1,1,86004,1.786 -32,3,2,101618,1.725 -32,2,2,95878,1.71 -16,3,1,93009,1.835 -64,1,4,95878,1.662 -32,1,2,90139,1.69 +num_words,word_size,words_per_row,area,output_slew +16,2,1,88873,1.81 +64,2,4,108116,1.69 +16,1,1,86004,1.786 +32,3,2,101618,1.725 +32,2,2,95878,1.71 +16,3,1,93009,1.835 +64,1,4,95878,1.662 +32,1,2,90139,1.69 From d6177b34f02df9d471c0231a6d424b82cee0a467 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 17 Dec 2020 12:59:06 -0800 Subject: [PATCH 17/59] Added data which includes corner as an input feature --- technology/scn4m_subm/sim_data/delay_data.csv | 31 +++++++++++++------ .../scn4m_subm/sim_data/leakage_data.csv | 31 +++++++++++++------ technology/scn4m_subm/sim_data/power_data.csv | 31 +++++++++++++------ technology/scn4m_subm/sim_data/slew_data.csv | 31 +++++++++++++------ 4 files changed, 88 insertions(+), 36 deletions(-) diff --git a/technology/scn4m_subm/sim_data/delay_data.csv b/technology/scn4m_subm/sim_data/delay_data.csv index 46a88c5f..b395af15 100644 --- a/technology/scn4m_subm/sim_data/delay_data.csv +++ b/technology/scn4m_subm/sim_data/delay_data.csv @@ -1,9 +1,22 @@ -num_words,word_size,words_per_row,area,max_delay -16,2,1,88873,2.272 -64,2,4,108116,2.721 -16,1,1,86004,2.267 -32,3,2,101618,2.634 -32,2,2,95878,2.594 -16,3,1,93009,2.292 -64,1,4,95878,2.705 -32,1,2,90139,2.552 +num_words,word_size,words_per_row,area,process,voltage,temperature,max_delay +16,2,1,88873,FF,5.0,25,1.738 +16,2,1,88873,SS,5.0,25,1.8 +16,2,1,88873,TT,3.6,25,2.131 +64,2,4,108116,FF,5.0,25,2.071 +64,2,4,108116,SS,5.0,25,2.137 +64,2,4,108116,TT,3.6,25,2.552 +16,1,1,86004,FF,5.0,25,1.733 +16,1,1,86004,SS,5.0,25,1.795 +16,1,1,86004,TT,3.6,25,2.126 +32,3,2,101618,FF,5.0,25,2.009 +32,3,2,101618,SS,5.0,25,2.077 +32,3,2,101618,TT,3.6,25,2.471 +16,3,1,93009,FF,5.0,25,1.753 +16,3,1,93009,SS,5.0,25,1.815 +16,3,1,93009,TT,3.6,25,2.15 +64,1,4,95878,FF,5.0,25,2.064 +64,1,4,95878,SS,5.0,25,2.13 +64,1,4,95878,TT,3.6,25,2.538 +32,1,2,90139,FF,5.0,25,1.949 +32,1,2,90139,SS,5.0,25,2.013 +32,1,2,90139,TT,3.6,25,2.395 diff --git a/technology/scn4m_subm/sim_data/leakage_data.csv b/technology/scn4m_subm/sim_data/leakage_data.csv index 5f748e04..123d5a43 100644 --- a/technology/scn4m_subm/sim_data/leakage_data.csv +++ b/technology/scn4m_subm/sim_data/leakage_data.csv @@ -1,9 +1,22 @@ -num_words,word_size,words_per_row,area,leakage_power -16,2,1,88873,0.0009381791 -64,2,4,108116,0.0011511999999999998 -16,1,1,86004,0.0005252088 -32,3,2,101618,0.0012168 -32,2,2,95878,0.000771978 -16,3,1,93009,0.0009978024 -64,1,4,95878,0.0006975546000000001 -32,1,2,90139,0.0006437493 +num_words,word_size,words_per_row,area,process,voltage,temperature,leakage_power +16,2,1,88873,FF,5.0,25,0.0009381791 +16,2,1,88873,SS,5.0,25,0.0009381791 +16,2,1,88873,TT,3.6,25,0.0009381791 +64,2,4,108116,FF,5.0,25,0.0011511999999999998 +64,2,4,108116,SS,5.0,25,0.0011511999999999998 +64,2,4,108116,TT,3.6,25,0.0011511999999999998 +16,1,1,86004,FF,5.0,25,0.0005504517 +16,1,1,86004,SS,5.0,25,0.0005504517 +16,1,1,86004,TT,3.6,25,0.0005504517 +32,3,2,101618,FF,5.0,25,0.0012168 +32,3,2,101618,SS,5.0,25,0.0012168 +32,3,2,101618,TT,3.6,25,0.0012168 +16,3,1,93009,FF,5.0,25,0.0009978024 +16,3,1,93009,SS,5.0,25,0.0009978024 +16,3,1,93009,TT,3.6,25,0.0009978024 +64,1,4,95878,FF,5.0,25,0.0007272557000000001 +64,1,4,95878,SS,5.0,25,0.0007272557000000001 +64,1,4,95878,TT,3.6,25,0.0007272557000000001 +32,1,2,90139,FF,5.0,25,0.0006715376 +32,1,2,90139,SS,5.0,25,0.0006715376 +32,1,2,90139,TT,3.6,25,0.0006715376 diff --git a/technology/scn4m_subm/sim_data/power_data.csv b/technology/scn4m_subm/sim_data/power_data.csv index 8e3d3de2..85063fc3 100644 --- a/technology/scn4m_subm/sim_data/power_data.csv +++ b/technology/scn4m_subm/sim_data/power_data.csv @@ -1,9 +1,22 @@ -num_words,word_size,words_per_row,area,read_power -16,2,1,88873,8.0252 -64,2,4,108116,9.8616 -16,1,1,86004,7.4911 -32,3,2,101618,9.0957 -32,2,2,95878,8.7143 -16,3,1,93009,8.5708 -64,1,4,95878,8.5750 -32,1,2,90139,7.9725 +num_words,word_size,words_per_row,area,process,voltage,temperature,read_power +16,2,1,88873,FF,5.0,25,8.0252 +16,2,1,88873,SS,5.0,25,8.0252 +16,2,1,88873,TT,3.6,25,8.0252 +64,2,4,108116,FF,5.0,25,9.8616 +64,2,4,108116,SS,5.0,25,9.8616 +64,2,4,108116,TT,3.6,25,9.8616 +16,1,1,86004,FF,5.0,25,7.4911 +16,1,1,86004,SS,5.0,25,7.4911 +16,1,1,86004,TT,3.6,25,7.4911 +32,3,2,101618,FF,5.0,25,9.0957 +32,3,2,101618,SS,5.0,25,9.0957 +32,3,2,101618,TT,3.6,25,9.0957 +16,3,1,93009,FF,5.0,25,8.5708 +16,3,1,93009,SS,5.0,25,8.5708 +16,3,1,93009,TT,3.6,25,8.5708 +64,1,4,95878,FF,5.0,25,8.5750 +64,1,4,95878,SS,5.0,25,8.5750 +64,1,4,95878,TT,3.6,25,8.5750 +32,1,2,90139,FF,5.0,25,7.9725 +32,1,2,90139,SS,5.0,25,7.9725 +32,1,2,90139,TT,3.6,25,7.9725 diff --git a/technology/scn4m_subm/sim_data/slew_data.csv b/technology/scn4m_subm/sim_data/slew_data.csv index 87d0e196..a9359b61 100644 --- a/technology/scn4m_subm/sim_data/slew_data.csv +++ b/technology/scn4m_subm/sim_data/slew_data.csv @@ -1,9 +1,22 @@ -num_words,word_size,words_per_row,area,output_slew -16,2,1,88873,1.81 -64,2,4,108116,1.69 -16,1,1,86004,1.786 -32,3,2,101618,1.725 -32,2,2,95878,1.71 -16,3,1,93009,1.835 -64,1,4,95878,1.662 -32,1,2,90139,1.69 +num_words,word_size,words_per_row,area,process,voltage,temperature,output_slew +16,2,1,88873,FF,5.0,25,1.81 +16,2,1,88873,SS,5.0,25,1.81 +16,2,1,88873,TT,3.6,25,1.81 +64,2,4,108116,FF,5.0,25,1.69 +64,2,4,108116,SS,5.0,25,1.69 +64,2,4,108116,TT,3.6,25,1.69 +16,1,1,86004,FF,5.0,25,1.786 +16,1,1,86004,SS,5.0,25,1.786 +16,1,1,86004,TT,3.6,25,1.786 +32,3,2,101618,FF,5.0,25,1.725 +32,3,2,101618,SS,5.0,25,1.725 +32,3,2,101618,TT,3.6,25,1.725 +16,3,1,93009,FF,5.0,25,1.835 +16,3,1,93009,SS,5.0,25,1.835 +16,3,1,93009,TT,3.6,25,1.835 +64,1,4,95878,FF,5.0,25,1.662 +64,1,4,95878,SS,5.0,25,1.662 +64,1,4,95878,TT,3.6,25,1.662 +32,1,2,90139,FF,5.0,25,1.69 +32,1,2,90139,SS,5.0,25,1.69 +32,1,2,90139,TT,3.6,25,1.69 From b760656572a042a18db50b0c745c175f6a485ed2 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 17 Dec 2020 14:08:45 -0800 Subject: [PATCH 18/59] Made process a required feature. Fixed issue with features that have the same max and min --- compiler/characterizer/analytical_util.py | 47 +++++++++++++++++------ 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/compiler/characterizer/analytical_util.py b/compiler/characterizer/analytical_util.py index 26b9d23f..a838761c 100644 --- a/compiler/characterizer/analytical_util.py +++ b/compiler/characterizer/analytical_util.py @@ -12,7 +12,13 @@ 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): + """ + 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 @@ -22,26 +28,39 @@ def get_data_names(file_name): return row[0].split(',') 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 for row in csv_reader: - #data = [int(csv_str) for csv_str in ', '.join(row)] row_iter += 1 if row_iter == 1: feature_names = row[0].split(',') input_list = [[] for _ in feature_names] scaled_list = [[] for _ in feature_names] - #label_list = [] + + try: + process_ind = feature_names.index('process') + except: + debug.error('Process not included as a feature.') continue - #print(row[0]) - data = [float(csv_str) for csv_str in row[0].split(',')] + + data = [] + split_str = row[0].split(',') + for i in range(len(split_str)): + if i == process_ind: + data.append(process_transform[split_str[i]]) + else: + data.append(float(split_str[i])) + data[0] = math.log(data[0], 2) - #input_list.append(data) + for i in range(len(data)): input_list[i].append(data[i]) - #label_list.append([data[-1]]) - #print(data) + return input_list def apply_samples_to_data(all_data, algo_samples): @@ -81,7 +100,7 @@ def squared_error(list_a, list_b): def get_max_min_from_datasets(dir): if not os.path.isdir(dir): - print("Input Directory not found:",dir) + debug.warning("Input Directory not found:{}".format(dir)) return [], [], [] # Assuming all files are CSV @@ -210,8 +229,10 @@ def get_scaled_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) - + + print(self_scaled_data) samples = np.asarray(self_scaled_data) + print(samples) features, labels = samples[:, :-1], samples[:,-1:] return features, labels @@ -224,8 +245,12 @@ def scale_data_and_transform(data): for feature_list in data: max_val = max(feature_list) min_val = min(feature_list) - for i in range(len(feature_list)): - scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val)) + + if max_val == min_val: + scaled_data[i] = [0.0 for _ in range(len(feature_list))] + else: + for i in range(len(feature_list)): + scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val)) return scaled_data def scale_input_datapoint(point, file_path): From 240dc784af7ce4daece083ea0aa9abff9be11d9d Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 17 Dec 2020 14:54:43 -0800 Subject: [PATCH 19/59] Fixed issue with static inputs causing errors. Added corners to linear regression inputs. --- compiler/characterizer/analytical_util.py | 23 +++++++++------------ compiler/characterizer/linear_regression.py | 5 ++++- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/characterizer/analytical_util.py b/compiler/characterizer/analytical_util.py index a838761c..79cb1294 100644 --- a/compiler/characterizer/analytical_util.py +++ b/compiler/characterizer/analytical_util.py @@ -91,7 +91,6 @@ def find_sample_position_with_min_error(data, sampled_vals): return sample_pos def squared_error(list_a, list_b): - #print('a:',list_a, 'b:', list_b) error_sum = 0; for a,b in zip(list_a, list_b): error_sum+=(a-b)**2 @@ -207,7 +206,6 @@ def sample_from_file(num_samples, file_name, sample_dir=None): 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) - #print(sampled_data) #unscale values and rescale using all available data (both sampled and unused points rescaled) if len(maxs)!=0 and len(mins)!=0: @@ -229,10 +227,7 @@ def get_scaled_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) - - print(self_scaled_data) samples = np.asarray(self_scaled_data) - print(samples) features, labels = samples[:, :-1], samples[:,-1:] return features, labels @@ -245,11 +240,11 @@ def scale_data_and_transform(data): for feature_list in data: max_val = max(feature_list) min_val = min(feature_list) - - if max_val == min_val: - scaled_data[i] = [0.0 for _ in range(len(feature_list))] - else: - for i in range(len(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 @@ -265,14 +260,17 @@ def scale_input_datapoint(point, file_path): scaled_point = [] for feature, mx, mn in zip(point, maxs, mins): - scaled_point.append((feature-mn)/(mx-mn)) + 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: - print("Must provide reference data to unscale") + 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) @@ -294,7 +292,6 @@ 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] - # print(cur_error) total_error += cur_error return total_error/len(labels) diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index ef6cf4f6..12d19a16 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -40,7 +40,10 @@ class linear_regression(simulation): model_inputs = [log_num_words, OPTS.word_size, OPTS.words_per_row, - self.sram.width * self.sram.height] + self.sram.width * self.sram.height, + process_transform[self.process], + self.vdd_voltage, + self.temperature] # List returned with value order being delay, power, leakage, slew # FIXME: make order less hard coded From 732404b3304fcbd01487d0a9649439a31dd7ecaa Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 17 Dec 2020 15:32:15 -0800 Subject: [PATCH 20/59] Added an option that prevents lib.py from generating corners and only uses corners in config file. --- compiler/characterizer/lib.py | 33 +++++++++++++++++++++------------ compiler/options.py | 2 ++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 679010fe..3b6c1364 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -77,18 +77,27 @@ class lib: # Nominal corner corner_set = set() - nom_corner = (nom_process, nom_supply, nom_temperature) - corner_set.add(nom_corner) - if not OPTS.nominal_corner_only: - # Temperature corners - corner_set.add((nom_process, nom_supply, min_temperature)) - corner_set.add((nom_process, nom_supply, max_temperature)) - # Supply corners - corner_set.add((nom_process, min_supply, nom_temperature)) - corner_set.add((nom_process, max_supply, nom_temperature)) - # Process corners - corner_set.add((min_process, nom_supply, nom_temperature)) - corner_set.add((max_process, nom_supply, nom_temperature)) + if OPTS.only_use_config_corners: + if OPTS.nominal_corner_only: + debug.warning("Nominal corner only option ignored if use only config corners is set.") + # Generate a powerset of input PVT lists + for p in self.process_corners: + for v in self.supply_voltages: + for t in self.temperatures: + corner_set.add((p, v, t)) + else: + nom_corner = (nom_process, nom_supply, nom_temperature) + corner_set.add(nom_corner) + if not OPTS.nominal_corner_only: + # Temperature corners + corner_set.add((nom_process, nom_supply, min_temperature)) + corner_set.add((nom_process, nom_supply, max_temperature)) + # Supply corners + corner_set.add((nom_process, min_supply, nom_temperature)) + corner_set.add((nom_process, max_supply, nom_temperature)) + # Process corners + corner_set.add((min_process, nom_supply, nom_temperature)) + corner_set.add((max_process, nom_supply, nom_temperature)) # Enforce that nominal corner is the first to be characterized self.add_corner(*nom_corner) diff --git a/compiler/options.py b/compiler/options.py index df547991..e740f88b 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -82,6 +82,8 @@ class options(optparse.Values): verbose_level = 0 # Drop to pdb on failure? debug = False + # Only use corners in config file. Disables generated corners + only_use_config_corners = False ################### From 6eac0530a103ebbaf5a1a1ec848d457c5f38854a Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 22 Dec 2020 15:00:11 -0800 Subject: [PATCH 21/59] Added words per row to datasheet --- compiler/characterizer/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 3b6c1364..8e16abbb 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -813,6 +813,6 @@ class lib: datasheet.write("{0},{1},{2},".format('leak', control_str, self.char_sram_results["leakage_power"])) - + datasheet.write("{0},{1},".format('words_per_row', OPTS.words_per_row)) datasheet.write("END\n") datasheet.close() From 32ad436153df32e9a04a96a46fff3b60e1827d4e Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 22 Dec 2020 15:19:31 -0800 Subject: [PATCH 22/59] Added freepdk45 data for linear regression --- technology/freepdk45/sim_data/delay_data.csv | 25 +++++++++++++++++++ .../freepdk45/sim_data/leakage_data.csv | 25 +++++++++++++++++++ technology/freepdk45/sim_data/power_data.csv | 25 +++++++++++++++++++ technology/freepdk45/sim_data/slew_data.csv | 25 +++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 technology/freepdk45/sim_data/delay_data.csv create mode 100644 technology/freepdk45/sim_data/leakage_data.csv create mode 100644 technology/freepdk45/sim_data/power_data.csv create mode 100644 technology/freepdk45/sim_data/slew_data.csv diff --git a/technology/freepdk45/sim_data/delay_data.csv b/technology/freepdk45/sim_data/delay_data.csv new file mode 100644 index 00000000..1ed5f98d --- /dev/null +++ b/technology/freepdk45/sim_data/delay_data.csv @@ -0,0 +1,25 @@ +num_words,word_size,words_per_row,area,process,voltage,temperature,max_delay +16,2,1,1113,FF,1.0,25,0.219 +16,2,1,1113,SS,1.0,25,0.266 +16,2,1,1113,TT,1.0,25,0.24 +64,2,4,1362,FF,1.0,25,0.265 +64,2,4,1362,SS,1.0,25,0.331 +64,2,4,1362,TT,1.0,25,0.293 +16,1,1,1066,FF,1.0,25,0.222 +16,1,1,1066,SS,1.0,25,0.27 +16,1,1,1066,TT,1.0,25,0.243 +32,3,2,1270,FF,1.0,25,0.256 +32,3,2,1270,SS,1.0,25,0.319 +32,3,2,1270,TT,1.0,25,0.282 +32,2,2,1190,FF,1.0,25,0.252 +32,2,2,1190,SS,1.0,25,0.314 +32,2,2,1190,TT,1.0,25,0.278 +16,3,1,1146,FF,1.0,25,0.221 +16,3,1,1146,SS,1.0,25,0.269 +16,3,1,1146,TT,1.0,25,0.242 +32,1,2,1113,FF,1.0,25,0.249 +32,1,2,1113,SS,1.0,25,0.31 +32,1,2,1113,TT,1.0,25,0.275 +64,3,4,1506,FF,1.0,25,0.267 +64,3,4,1506,SS,1.0,25,0.333 +64,3,4,1506,TT,1.0,25,0.295 diff --git a/technology/freepdk45/sim_data/leakage_data.csv b/technology/freepdk45/sim_data/leakage_data.csv new file mode 100644 index 00000000..c69b26c6 --- /dev/null +++ b/technology/freepdk45/sim_data/leakage_data.csv @@ -0,0 +1,25 @@ +num_words,word_size,words_per_row,area,process,voltage,temperature,leakage_power +16,2,1,1113,FF,1.0,25,0.0032716999999999998 +16,2,1,1113,SS,1.0,25,0.0032716999999999998 +16,2,1,1113,TT,1.0,25,0.0032716999999999998 +64,2,4,1362,FF,1.0,25,0.0047589 +64,2,4,1362,SS,1.0,25,0.0047589 +64,2,4,1362,TT,1.0,25,0.0047589 +16,1,1,1066,FF,1.0,25,0.0028373 +16,1,1,1066,SS,1.0,25,0.0028373 +16,1,1,1066,TT,1.0,25,0.0028373 +32,3,2,1270,FF,1.0,25,0.0043715 +32,3,2,1270,SS,1.0,25,0.0043715 +32,3,2,1270,TT,1.0,25,0.0043715 +32,2,2,1190,FF,1.0,25,0.0038058000000000002 +32,2,2,1190,SS,1.0,25,0.0038058000000000002 +32,2,2,1190,TT,1.0,25,0.0038058000000000002 +16,3,1,1146,FF,1.0,25,0.0036628 +16,3,1,1146,SS,1.0,25,0.0036628 +16,3,1,1146,TT,1.0,25,0.0036628 +32,1,2,1113,FF,1.0,25,0.0032621 +32,1,2,1113,SS,1.0,25,0.0032621 +32,1,2,1113,TT,1.0,25,0.0032621 +64,3,4,1506,FF,1.0,25,0.0057736 +64,3,4,1506,SS,1.0,25,0.0057736 +64,3,4,1506,TT,1.0,25,0.0057736 diff --git a/technology/freepdk45/sim_data/power_data.csv b/technology/freepdk45/sim_data/power_data.csv new file mode 100644 index 00000000..3f933e6e --- /dev/null +++ b/technology/freepdk45/sim_data/power_data.csv @@ -0,0 +1,25 @@ +num_words,word_size,words_per_row,area,process,voltage,temperature,read_power +16,2,1,1113,FF,1.0,25,0.1725 +16,2,1,1113,SS,1.0,25,0.1725 +16,2,1,1113,TT,1.0,25,0.1725 +64,2,4,1362,FF,1.0,25,0.2171 +64,2,4,1362,SS,1.0,25,0.2171 +64,2,4,1362,TT,1.0,25,0.2171 +16,1,1,1066,FF,1.0,25,0.1581 +16,1,1,1066,SS,1.0,25,0.1581 +16,1,1,1066,TT,1.0,25,0.1581 +32,3,2,1270,FF,1.0,25,0.2117 +32,3,2,1270,SS,1.0,25,0.2117 +32,3,2,1270,TT,1.0,25,0.2117 +32,2,2,1190,FF,1.0,25,0.1889 +32,2,2,1190,SS,1.0,25,0.1889 +32,2,2,1190,TT,1.0,25,0.1889 +16,3,1,1146,FF,1.0,25,0.1862 +16,3,1,1146,SS,1.0,25,0.1862 +16,3,1,1146,TT,1.0,25,0.1862 +32,1,2,1113,FF,1.0,25,0.1792 +32,1,2,1113,SS,1.0,25,0.1792 +32,1,2,1113,TT,1.0,25,0.1792 +64,3,4,1506,FF,1.0,25,0.2412 +64,3,4,1506,SS,1.0,25,0.2412 +64,3,4,1506,TT,1.0,25,0.2412 diff --git a/technology/freepdk45/sim_data/slew_data.csv b/technology/freepdk45/sim_data/slew_data.csv new file mode 100644 index 00000000..0b6d7b90 --- /dev/null +++ b/technology/freepdk45/sim_data/slew_data.csv @@ -0,0 +1,25 @@ +num_words,word_size,words_per_row,area,process,voltage,temperature,output_slew +16,2,1,1113,FF,1.0,25,0.268 +16,2,1,1113,SS,1.0,25,0.268 +16,2,1,1113,TT,1.0,25,0.268 +64,2,4,1362,FF,1.0,25,0.244 +64,2,4,1362,SS,1.0,25,0.244 +64,2,4,1362,TT,1.0,25,0.244 +16,1,1,1066,FF,1.0,25,0.264 +16,1,1,1066,SS,1.0,25,0.264 +16,1,1,1066,TT,1.0,25,0.264 +32,3,2,1270,FF,1.0,25,0.25 +32,3,2,1270,SS,1.0,25,0.25 +32,3,2,1270,TT,1.0,25,0.25 +32,2,2,1190,FF,1.0,25,0.246 +32,2,2,1190,SS,1.0,25,0.246 +32,2,2,1190,TT,1.0,25,0.246 +16,3,1,1146,FF,1.0,25,0.272 +16,3,1,1146,SS,1.0,25,0.272 +16,3,1,1146,TT,1.0,25,0.272 +32,1,2,1113,FF,1.0,25,0.241 +32,1,2,1113,SS,1.0,25,0.241 +32,1,2,1113,TT,1.0,25,0.241 +64,3,4,1506,FF,1.0,25,0.248 +64,3,4,1506,SS,1.0,25,0.248 +64,3,4,1506,TT,1.0,25,0.248 From 9edaca0616c71f8b8431bc7507c7186c43474eaa Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 22 Dec 2020 16:45:04 -0800 Subject: [PATCH 23/59] Changed tech path in linear regression to use openram_tech option. --- compiler/characterizer/linear_regression.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/characterizer/linear_regression.py b/compiler/characterizer/linear_regression.py index 12d19a16..8f2d8cba 100644 --- a/compiler/characterizer/linear_regression.py +++ b/compiler/characterizer/linear_regression.py @@ -20,8 +20,8 @@ data_fnames = ["delay_data.csv", "power_data.csv", "leakage_data.csv", "slew_data.csv"] -tech_path = os.environ.get('OPENRAM_TECH') -data_dir = tech_path+'/'+OPTS.tech_name+relative_data_path + +data_dir = OPTS.openram_tech+relative_data_path data_paths = [data_dir +'/'+fname for fname in data_fnames] class linear_regression(simulation): From 48baf3ab4e1620fa3a3216d64a34b40651512d96 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 6 Jan 2021 01:34:44 -0800 Subject: [PATCH 24/59] Updated test to use new analytical class --- compiler/tests/21_model_delay_test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/tests/21_model_delay_test.py b/compiler/tests/21_model_delay_test.py index e711c7ca..b14f96ed 100755 --- a/compiler/tests/21_model_delay_test.py +++ b/compiler/tests/21_model_delay_test.py @@ -30,6 +30,7 @@ class model_delay_test(openram_test): import characterizer reload(characterizer) from characterizer import delay + from characterizer import elmore from sram import sram from sram_config import sram_config c = sram_config(word_size=1, @@ -48,7 +49,9 @@ class model_delay_test(openram_test): debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data)) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + d = delay(s.s, tempspice, corner) + m = elmore(s.s, tempspice, corner) import tech loads = [tech.spice["dff_in_cap"]*4] slews = [tech.spice["rise_time"]*2] @@ -58,7 +61,7 @@ class model_delay_test(openram_test): spice_data.update(port_data[0]) # Run analytical characterization - model_data, port_data = d.analytical_delay(slews, loads) + model_data, port_data = m.get_lib_values(slews, loads) model_data.update(port_data[0]) # Only compare the delays From bb841fc84db5269c369410424138d4227ee30d84 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 6 Jan 2021 12:45:34 -0800 Subject: [PATCH 25/59] Added option to output the datasheet.info file. --- compiler/characterizer/lib.py | 7 +++++-- compiler/datasheet/datasheet.py | 6 +++++- compiler/datasheet/datasheet_gen.py | 11 +++++++---- compiler/options.py | 4 +++- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 8e16abbb..6f1ec3ba 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -645,8 +645,11 @@ class lib: if len(git_id) != 40: debug.warning("Failed to retrieve git id") git_id = 'Failed to retruieve' - - datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+') + if OPTS.output_datasheet_info: + datasheet_path = OPTS.output_path + else: + datasheet_path = OPTS.openram_temp + datasheet = open(datasheet_path +'/datasheet.info', 'a+') current_time = datetime.date.today() # write static information to be parser later diff --git a/compiler/datasheet/datasheet.py b/compiler/datasheet/datasheet.py index 35091d36..845f542f 100644 --- a/compiler/datasheet/datasheet.py +++ b/compiler/datasheet/datasheet.py @@ -28,7 +28,11 @@ class datasheet(): # css styling is kept in a seperate file self.html += datasheet_css.read() - with open(OPTS.openram_temp + "/datasheet.info") as info: + if OPTS.output_datasheet_info: + datasheet_path = OPTS.output_path + else: + datasheet_path = OPTS.openram_temp + with open(datasheet_path + "/datasheet.info") as info: self.html += '