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