2021-01-19 23:19:50 +01:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2023-01-29 07:56:27 +01:00
|
|
|
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
2021-01-19 23:19:50 +01:00
|
|
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
|
|
|
# (acting for and on behalf of Oklahoma State University)
|
|
|
|
|
# All rights reserved.
|
|
|
|
|
#
|
2022-11-27 22:01:20 +01:00
|
|
|
import math
|
|
|
|
|
from openram import debug
|
|
|
|
|
from openram import OPTS
|
2021-01-19 23:19:50 +01:00
|
|
|
from .analytical_util import *
|
|
|
|
|
from .simulation import simulation
|
|
|
|
|
|
|
|
|
|
|
2021-05-24 21:03:26 +02:00
|
|
|
relative_data_path = "sim_data"
|
|
|
|
|
data_file = "sim_data.csv"
|
2021-01-19 23:19:50 +01:00
|
|
|
data_fnames = ["rise_delay.csv",
|
|
|
|
|
"fall_delay.csv",
|
|
|
|
|
"rise_slew.csv",
|
|
|
|
|
"fall_slew.csv",
|
|
|
|
|
"write1_power.csv",
|
|
|
|
|
"write0_power.csv",
|
|
|
|
|
"read1_power.csv",
|
|
|
|
|
"read0_power.csv",
|
2021-03-22 20:55:29 +01:00
|
|
|
"leakage_data.csv",
|
|
|
|
|
"sim_time.csv"]
|
2022-07-22 18:52:38 +02:00
|
|
|
# Positions must correspond to data_fname list
|
2021-01-19 23:19:50 +01:00
|
|
|
lib_dnames = ["delay_lh",
|
|
|
|
|
"delay_hl",
|
|
|
|
|
"slew_lh",
|
|
|
|
|
"slew_hl",
|
|
|
|
|
"write1_power",
|
|
|
|
|
"write0_power",
|
|
|
|
|
"read1_power",
|
|
|
|
|
"read0_power",
|
2021-03-22 20:55:29 +01:00
|
|
|
"leakage_power",
|
2022-07-22 18:52:38 +02:00
|
|
|
"sim_time"]
|
2021-01-25 23:24:54 +01:00
|
|
|
# Check if another data dir was specified
|
2022-07-22 18:52:38 +02:00
|
|
|
if OPTS.sim_data_path == None:
|
2021-01-25 23:24:54 +01:00
|
|
|
data_dir = OPTS.openram_tech+relative_data_path
|
|
|
|
|
else:
|
2022-07-22 18:52:38 +02:00
|
|
|
data_dir = OPTS.sim_data_path
|
|
|
|
|
|
2021-05-24 21:03:26 +02:00
|
|
|
data_path = data_dir + '/' + data_file
|
2021-01-19 23:19:50 +01:00
|
|
|
|
|
|
|
|
class regression_model(simulation):
|
|
|
|
|
|
|
|
|
|
def __init__(self, sram, spfile, corner):
|
|
|
|
|
super().__init__(sram, spfile, corner)
|
|
|
|
|
self.set_corner(corner)
|
|
|
|
|
|
2021-02-25 01:43:34 +01:00
|
|
|
def get_lib_values(self, load_slews):
|
2021-01-19 23:19:50 +01:00
|
|
|
"""
|
2022-07-22 18:52:38 +02:00
|
|
|
A model and prediction is created for each output needed for the LIB
|
2021-01-19 23:19:50 +01:00
|
|
|
"""
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-06-04 22:37:21 +02:00
|
|
|
debug.info(1, "Characterizing SRAM using regression models.")
|
2021-01-19 23:19:50 +01:00
|
|
|
log_num_words = math.log(OPTS.num_words, 2)
|
2022-07-22 18:52:38 +02:00
|
|
|
model_inputs = [log_num_words,
|
|
|
|
|
OPTS.word_size,
|
2021-02-17 19:00:11 +01:00
|
|
|
OPTS.words_per_row,
|
|
|
|
|
OPTS.local_array_size,
|
2022-07-22 18:52:38 +02:00
|
|
|
process_transform[self.process],
|
|
|
|
|
self.vdd_voltage,
|
|
|
|
|
self.temperature]
|
2021-02-10 23:20:38 +01:00
|
|
|
# Area removed for now
|
|
|
|
|
# self.sram.width * self.sram.height,
|
2021-05-24 21:03:26 +02:00
|
|
|
# Include above inputs, plus load and slew which are added below
|
|
|
|
|
self.num_inputs = len(model_inputs)+2
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-01-19 23:19:50 +01:00
|
|
|
self.create_measurement_names()
|
|
|
|
|
models = self.train_models()
|
|
|
|
|
|
|
|
|
|
# Set delay/power for slews and loads
|
|
|
|
|
port_data = self.get_empty_measure_data_dict()
|
|
|
|
|
debug.info(1, 'Slew, Load, Port, Delay(ns), Slew(ns)')
|
|
|
|
|
max_delay = 0.0
|
2021-02-25 01:43:34 +01:00
|
|
|
for load, slew in load_slews:
|
|
|
|
|
# List returned with value order being delay, power, leakage, slew
|
|
|
|
|
sram_vals = self.get_predictions(model_inputs+[slew, load], models)
|
|
|
|
|
# Delay is only calculated on a single port and replicated for now.
|
|
|
|
|
for port in self.all_ports:
|
2021-05-24 21:53:51 +02:00
|
|
|
port_data[port]['delay_lh'].append(sram_vals['rise_delay'])
|
|
|
|
|
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'])
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-02-25 01:43:34 +01:00
|
|
|
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'])
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-02-25 01:43:34 +01:00
|
|
|
# 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'])
|
2022-07-22 18:52:38 +02:00
|
|
|
|
|
|
|
|
debug.info(1, '{}, {}, {}, {}, {}'.format(slew,
|
|
|
|
|
load,
|
|
|
|
|
port,
|
|
|
|
|
sram_vals['rise_delay'],
|
2021-05-24 21:53:51 +02:00
|
|
|
sram_vals['rise_slew']))
|
2021-01-19 23:19:50 +01:00
|
|
|
# Estimate the period as double the delay with margin
|
|
|
|
|
period_margin = 0.1
|
2021-05-24 21:53:51 +02:00
|
|
|
sram_data = {"min_period": sram_vals['rise_delay'] * 2,
|
|
|
|
|
"leakage_power": sram_vals["leakage_power"]}
|
2021-01-19 23:19:50 +01:00
|
|
|
|
|
|
|
|
debug.info(2, "SRAM Data:\n{}".format(sram_data))
|
|
|
|
|
debug.info(2, "Port Data:\n{}".format(port_data))
|
|
|
|
|
|
|
|
|
|
return (sram_data, port_data)
|
|
|
|
|
|
2022-07-22 18:52:38 +02:00
|
|
|
def get_predictions(self, model_inputs, models):
|
2021-01-19 23:19:50 +01:00
|
|
|
"""
|
|
|
|
|
Generate a model and prediction for LIB output
|
|
|
|
|
"""
|
2022-07-22 18:52:38 +02:00
|
|
|
|
|
|
|
|
#Scaled the inputs using first data file as a reference
|
2021-05-24 21:53:51 +02:00
|
|
|
scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, data_path)])
|
2021-01-19 23:19:50 +01:00
|
|
|
|
|
|
|
|
predictions = {}
|
2021-05-24 21:53:51 +02:00
|
|
|
out_pos = 0
|
|
|
|
|
for dname in self.output_names:
|
2021-01-19 23:19:50 +01:00
|
|
|
m = models[dname]
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-01-19 23:19:50 +01:00
|
|
|
scaled_pred = self.model_prediction(m, scaled_inputs)
|
2021-05-24 21:53:51 +02:00
|
|
|
pred = unscale_data(scaled_pred.tolist(), data_path, pos=self.num_inputs+out_pos)
|
2021-01-19 23:19:50 +01:00
|
|
|
debug.info(2,"Unscaled Prediction = {}".format(pred))
|
2021-05-24 21:53:51 +02:00
|
|
|
predictions[dname] = pred[0]
|
|
|
|
|
out_pos+=1
|
2021-01-19 23:19:50 +01:00
|
|
|
return predictions
|
|
|
|
|
|
|
|
|
|
def train_models(self):
|
|
|
|
|
"""
|
|
|
|
|
Generate and return models
|
|
|
|
|
"""
|
2021-05-24 21:03:26 +02:00
|
|
|
self.output_names = get_data_names(data_path)[self.num_inputs:]
|
|
|
|
|
data = get_scaled_data(data_path)
|
|
|
|
|
features, labels = data[:, :self.num_inputs], data[:,self.num_inputs:]
|
|
|
|
|
|
|
|
|
|
output_num = 0
|
2021-01-19 23:19:50 +01:00
|
|
|
models = {}
|
2021-05-24 21:03:26 +02:00
|
|
|
for o_name in self.output_names:
|
|
|
|
|
output_label = labels[:,output_num]
|
|
|
|
|
model = self.generate_model(features, output_label)
|
|
|
|
|
models[o_name] = model
|
|
|
|
|
output_num+=1
|
|
|
|
|
|
2021-01-19 23:19:50 +01:00
|
|
|
return models
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-06-05 00:04:52 +02:00
|
|
|
def score_model(self):
|
|
|
|
|
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)
|
|
|
|
|
features, labels = data[:, :num_inputs], data[:,num_inputs:]
|
|
|
|
|
|
|
|
|
|
output_num = 0
|
|
|
|
|
models = {}
|
|
|
|
|
debug.info(1, "Output name, score")
|
|
|
|
|
for o_name in self.output_names:
|
|
|
|
|
output_label = labels[:,output_num]
|
|
|
|
|
model = self.generate_model(features, output_label)
|
|
|
|
|
scr = model.score(features, output_label)
|
|
|
|
|
debug.info(1, "{}, {}".format(o_name, scr))
|
|
|
|
|
output_num+=1
|
2022-07-22 18:52:38 +02:00
|
|
|
|
|
|
|
|
|
2021-06-17 08:50:20 +02:00
|
|
|
def cross_validation(self, test_only=None):
|
2021-06-09 22:44:42 +02:00
|
|
|
"""Wrapper for sklean cross validation function for OpenRAM regression models.
|
|
|
|
|
Returns the mean accuracy for each model/output."""
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-06-05 00:04:52 +02:00
|
|
|
from sklearn.model_selection import cross_val_score
|
|
|
|
|
untrained_model = self.get_model()
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-06-05 00:04:52 +02:00
|
|
|
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)
|
|
|
|
|
features, labels = data[:, :num_inputs], data[:,num_inputs:]
|
|
|
|
|
|
|
|
|
|
output_num = 0
|
|
|
|
|
models = {}
|
|
|
|
|
debug.info(1, "Output name, mean_accuracy, std_dev")
|
2021-06-09 22:44:42 +02:00
|
|
|
model_scores = {}
|
2021-06-17 08:50:20 +02:00
|
|
|
if test_only != None:
|
|
|
|
|
test_outputs = test_only
|
|
|
|
|
else:
|
|
|
|
|
test_outputs = self.output_names
|
|
|
|
|
for o_name in test_outputs:
|
2021-06-05 00:04:52 +02:00
|
|
|
output_label = labels[:,output_num]
|
2021-06-09 22:44:42 +02:00
|
|
|
scores = cross_val_score(untrained_model, features, output_label, cv=10)
|
2021-06-05 00:04:52 +02:00
|
|
|
debug.info(1, "{}, {}, {}".format(o_name, scores.mean(), scores.std()))
|
2021-06-09 22:44:42 +02:00
|
|
|
model_scores[o_name] = scores.mean()
|
2021-06-05 00:04:52 +02:00
|
|
|
output_num+=1
|
2022-07-22 18:52:38 +02:00
|
|
|
|
|
|
|
|
return model_scores
|
|
|
|
|
|
2021-03-02 22:14:56 +01:00
|
|
|
# Fixme - only will work for sklearn regression models
|
|
|
|
|
def save_model(self, model_name, model):
|
|
|
|
|
try:
|
|
|
|
|
OPTS.model_dict
|
|
|
|
|
except AttributeError:
|
|
|
|
|
OPTS.model_dict = {}
|
|
|
|
|
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_)
|