OpenRAM/compiler/characterizer/regression_model.py

204 lines
8.1 KiB
Python

# 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 math
relative_data_path = "sim_data"
data_file = "sim_data.csv"
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",
"leakage_data.csv",
"sim_time.csv"]
# Positions must correspond to data_fname list
lib_dnames = ["delay_lh",
"delay_hl",
"slew_lh",
"slew_hl",
"write1_power",
"write0_power",
"read1_power",
"read0_power",
"leakage_power",
"sim_time"]
# Check if another data dir was specified
if OPTS.sim_data_path == None:
data_dir = OPTS.openram_tech+relative_data_path
else:
data_dir = OPTS.sim_data_path
data_path = data_dir + '/' + data_file
class regression_model(simulation):
def __init__(self, sram, spfile, corner):
super().__init__(sram, spfile, corner)
self.set_corner(corner)
def get_lib_values(self, load_slews):
"""
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,
OPTS.words_per_row,
OPTS.local_array_size,
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()
# 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
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:
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'])
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'],
sram_vals['rise_slew']))
# Estimate the period as double the delay with margin
period_margin = 0.1
sram_data = {"min_period": sram_vals['rise_delay'] * 2,
"leakage_power": sram_vals["leakage_power"]}
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, models):
"""
Generate a model and prediction for LIB output
"""
#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))
predictions[dname] = pred[0]
out_pos+=1
return predictions
def train_models(self):
"""
Generate and return models
"""
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
models = {}
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
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:]
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
def cross_validation(self):
"""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)
features, labels = data[:, :num_inputs], data[:,num_inputs:]
output_num = 0
models = {}
debug.info(1, "Output name, mean_accuracy, std_dev")
model_scores = {}
for o_name in self.output_names:
output_label = labels[:,output_num]
scores = cross_val_score(untrained_model, features, output_label, cv=10)
debug.info(1, "{}, {}, {}".format(o_name, scores.mean(), scores.std()))
model_scores[o_name] = scores.mean()
output_num+=1
return model_scores
# 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_)