2022-11-30 23:50:43 +01:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2024-01-03 23:32:44 +01:00
|
|
|
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
2022-11-30 23:50:43 +01:00
|
|
|
# All rights reserved.
|
|
|
|
|
#
|
2021-05-17 23:04:20 +02:00
|
|
|
import os
|
|
|
|
|
import csv
|
|
|
|
|
import re
|
|
|
|
|
import sys
|
|
|
|
|
import csv
|
2021-05-19 08:41:16 +02:00
|
|
|
import importlib
|
2021-05-17 23:04:20 +02:00
|
|
|
|
|
|
|
|
# Use the HTML file to extra the data. Easier to do than LIB
|
|
|
|
|
data_file_ext = ".html"
|
|
|
|
|
extended_name = "_extended" # Name addon of extended config file
|
2021-05-24 19:44:46 +02:00
|
|
|
DEFAULT_LAS = 0
|
2021-05-17 23:04:20 +02:00
|
|
|
|
|
|
|
|
def gen_regex_float_group(num, separator):
|
|
|
|
|
if num <= 0:
|
|
|
|
|
return ''
|
|
|
|
|
float_regex = '([-+]?[0-9]*\.?[0-9]*)'
|
|
|
|
|
full_regex = float_regex
|
|
|
|
|
for i in range(num-1):
|
|
|
|
|
full_regex+=separator+float_regex
|
|
|
|
|
return full_regex
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
def import_module(mod_name, mod_path):
|
|
|
|
|
spec = importlib.util.spec_from_file_location(mod_name, mod_path)
|
|
|
|
|
mod = importlib.util.module_from_spec(spec)
|
|
|
|
|
spec.loader.exec_module(mod)
|
|
|
|
|
return mod
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
def get_config_mods(openram_dir):
|
|
|
|
|
# Get dataset name used by all the files e.g. sram_1b_16
|
|
|
|
|
files_names = [name for name in os.listdir(openram_dir) if os.path.isfile(openram_dir+'/'+name)]
|
|
|
|
|
log = [name for name in files_names if '.log' in name][0]
|
|
|
|
|
dataset_name = log[:-4]
|
2021-09-21 01:35:16 +02:00
|
|
|
sys.path.append(openram_dir)
|
2021-05-17 23:04:20 +02:00
|
|
|
print("Extracting dataset:{}".format(dataset_name))
|
2022-07-22 18:52:38 +02:00
|
|
|
|
|
|
|
|
# Check that the config files exist (including special extended config)
|
2021-05-17 23:04:20 +02:00
|
|
|
dir_path = openram_dir+"/"
|
|
|
|
|
#sys.path.append(dir_path)
|
2021-09-21 01:35:16 +02:00
|
|
|
#imp_mod = None
|
2021-05-17 23:04:20 +02:00
|
|
|
imp_mod_extended = None
|
2021-09-21 01:35:16 +02:00
|
|
|
# if not os.path.exists(openram_dir+'/'+dataset_name+".py"):
|
|
|
|
|
# print("Python module for {} not found.".format(dataset_name))
|
|
|
|
|
# imp_mod = None
|
|
|
|
|
# else:
|
|
|
|
|
# imp_mod = import_module(dataset_name, openram_dir+"/"+dataset_name+".py")
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
if not os.path.exists(openram_dir+'/'+dataset_name+extended_name+".py"):
|
2021-06-04 22:37:21 +02:00
|
|
|
print("Extended Python module for {} not found.".format(dataset_name))
|
|
|
|
|
imp_mod_extended = None
|
2021-05-17 23:04:20 +02:00
|
|
|
else:
|
2021-05-19 08:41:16 +02:00
|
|
|
imp_mod_extended = import_module(dataset_name+extended_name, openram_dir+"/"+dataset_name+extended_name+".py")
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-19 08:41:16 +02:00
|
|
|
datasheet_fname = openram_dir+"/"+dataset_name+data_file_ext
|
2022-07-22 18:52:38 +02:00
|
|
|
|
|
|
|
|
return dataset_name, imp_mod_extended, datasheet_fname
|
2021-05-19 08:41:16 +02:00
|
|
|
|
|
|
|
|
def get_corners(datafile_contents, dataset_name, tech):
|
|
|
|
|
"""Search through given datasheet to find all corners available"""
|
|
|
|
|
|
|
|
|
|
corner_regex = r"{}.*{},([-+]?[0-9]*\.?[0-9]*),([-+]?[0-9]*\.?[0-9]*),([tsfTSF][tsfTSF]),".format(dataset_name, tech)
|
|
|
|
|
corners = re.findall(corner_regex,datafile_contents)
|
|
|
|
|
return corners # List of corner tuples in order (T, V, P)
|
2022-07-22 18:52:38 +02:00
|
|
|
|
|
|
|
|
feature_names = ['num_words',
|
|
|
|
|
'word_size',
|
2021-05-19 08:41:16 +02:00
|
|
|
'words_per_row',
|
|
|
|
|
'local_array_size',
|
2022-07-22 18:52:38 +02:00
|
|
|
'area',
|
|
|
|
|
'process',
|
|
|
|
|
'voltage',
|
2021-05-19 08:41:16 +02:00
|
|
|
'temperature',
|
|
|
|
|
'slew',
|
|
|
|
|
'load']
|
2022-07-22 18:52:38 +02:00
|
|
|
output_names = ['rise_delay',
|
2021-05-19 08:41:16 +02:00
|
|
|
'fall_delay',
|
|
|
|
|
'rise_slew',
|
|
|
|
|
'fall_slew',
|
|
|
|
|
'write1_power',
|
|
|
|
|
'write0_power',
|
|
|
|
|
'read1_power',
|
|
|
|
|
'read0_power',
|
2022-07-22 18:52:38 +02:00
|
|
|
'leakage_power']
|
|
|
|
|
|
|
|
|
|
multivalue_names = ['cell_rise_0',
|
2021-05-19 08:41:16 +02:00
|
|
|
'cell_fall_0',
|
|
|
|
|
'rise_transition_0',
|
|
|
|
|
'fall_transition_0']
|
|
|
|
|
singlevalue_names = ['write_rise_power_0',
|
|
|
|
|
'write_fall_power_0',
|
|
|
|
|
'read_rise_power_0',
|
2022-07-22 18:52:38 +02:00
|
|
|
'read_fall_power_0']
|
|
|
|
|
|
|
|
|
|
def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
|
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
|
2021-06-01 23:49:08 +02:00
|
|
|
writer = csv.writer(csv_file,lineterminator='\n')
|
2021-05-19 08:41:16 +02:00
|
|
|
# If the file was opened to write and not append then we write the header
|
|
|
|
|
if mode == 'w':
|
|
|
|
|
writer.writerow(feature_names+output_names)
|
2021-05-17 23:04:20 +02:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
load_slews = imp_mod.use_specified_load_slew
|
|
|
|
|
except:
|
|
|
|
|
load_slews = None
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
if load_slews != None:
|
|
|
|
|
num_items = len(load_slews)
|
|
|
|
|
num_loads_or_slews = len(load_slews)
|
|
|
|
|
else:
|
|
|
|
|
# These are the defaults for openram
|
|
|
|
|
num_items = 9
|
|
|
|
|
num_loads_or_slews = 3
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
try:
|
2021-05-19 08:41:16 +02:00
|
|
|
f = open(datasheet_fname, "r")
|
2021-05-17 23:04:20 +02:00
|
|
|
except IOError:
|
2021-05-19 08:41:16 +02:00
|
|
|
print("Unable to open spice output file: {0}".format(datasheet_fname))
|
2021-05-17 23:04:20 +02:00
|
|
|
return None
|
2021-05-19 08:41:16 +02:00
|
|
|
print("Opened file",datasheet_fname)
|
2021-05-17 23:04:20 +02:00
|
|
|
contents = f.read()
|
2022-07-22 18:52:38 +02:00
|
|
|
f.close()
|
|
|
|
|
|
2021-09-21 01:35:16 +02:00
|
|
|
available_corners = get_corners(contents, dataset_name, imp_mod.tech_name)
|
2022-07-22 18:52:38 +02:00
|
|
|
|
|
|
|
|
# Loop through corners, adding data for each corner
|
|
|
|
|
for (temp, voltage, process) in available_corners:
|
|
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
# Create a regex to search the datasheet for specified outputs
|
|
|
|
|
voltage_str = "".join(['\\'+i if i=='.' else i for i in str(voltage)])
|
|
|
|
|
area_regex = r"Area \(µm<sup>2<\/sup>\)<\/td><td>(\d+)"
|
|
|
|
|
|
|
|
|
|
leakage_regex = r"leakage<\/td><td>([-+]?[0-9]*\.?[0-9]*)"
|
|
|
|
|
slew_regex = r"rise transition<\/td><td>([-+]?[0-9]*\.?[0-9]*)"
|
|
|
|
|
|
|
|
|
|
if load_slews == None:
|
|
|
|
|
float_regex = gen_regex_float_group(num_loads_or_slews, ', ')
|
|
|
|
|
inp_slews_regex = r"{},{}.*{},{},{},.*slews,\[{}".format(
|
|
|
|
|
dataset_name,
|
|
|
|
|
imp_mod.num_words,
|
|
|
|
|
str(temp),
|
|
|
|
|
voltage_str,
|
|
|
|
|
process,
|
|
|
|
|
float_regex)
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
loads_regex = r"{},{}.*{},{},{},.*loads,\[{}".format(
|
|
|
|
|
dataset_name,
|
|
|
|
|
imp_mod.num_words,
|
|
|
|
|
str(temp),
|
|
|
|
|
voltage_str,
|
|
|
|
|
process,
|
2022-07-22 18:52:38 +02:00
|
|
|
float_regex)
|
2021-05-17 23:04:20 +02:00
|
|
|
|
|
|
|
|
float_regex = gen_regex_float_group(num_items, ', ')
|
|
|
|
|
multivalue_regexs = []
|
|
|
|
|
for value_identifier in multivalue_names:
|
|
|
|
|
regex_str = r"{},{}.*{},{},{},.*{},\[{}".format(
|
|
|
|
|
dataset_name,
|
|
|
|
|
imp_mod.num_words,
|
|
|
|
|
str(temp),
|
|
|
|
|
voltage_str,
|
|
|
|
|
process,
|
|
|
|
|
value_identifier,
|
2022-07-22 18:52:38 +02:00
|
|
|
float_regex)
|
|
|
|
|
multivalue_regexs.append(regex_str)
|
|
|
|
|
|
|
|
|
|
singlevalue_regexs = []
|
2021-05-17 23:04:20 +02:00
|
|
|
for value_identifier in singlevalue_names:
|
|
|
|
|
regex_str = r"{},{}.*{},{},{},.*{},([-+]?[0-9]*\.?[0-9]*)".format(
|
|
|
|
|
dataset_name,
|
|
|
|
|
imp_mod.num_words,
|
|
|
|
|
str(temp),
|
|
|
|
|
voltage_str,
|
|
|
|
|
process,
|
|
|
|
|
value_identifier,
|
2022-07-22 18:52:38 +02:00
|
|
|
float_regex)
|
|
|
|
|
singlevalue_regexs.append(regex_str)
|
|
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
area_vals = re.search(area_regex,contents)
|
|
|
|
|
leakage_vals = re.search(leakage_regex,contents)
|
|
|
|
|
if load_slews == None:
|
|
|
|
|
inp_slew_vals = re.search(inp_slews_regex,contents)
|
|
|
|
|
load_vals = re.search(loads_regex,contents)
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
datasheet_multivalues = [re.search(r,contents) for r in multivalue_regexs]
|
|
|
|
|
datasheet_singlevalues = [re.search(r,contents) for r in singlevalue_regexs]
|
|
|
|
|
for dval in datasheet_multivalues+datasheet_singlevalues:
|
|
|
|
|
if dval == None:
|
2021-05-19 08:41:16 +02:00
|
|
|
print("Error occurred while searching through datasheet: {}".format(datasheet_fname))
|
2021-09-21 01:35:16 +02:00
|
|
|
print("datasheet_multivalues",datasheet_multivalues)
|
|
|
|
|
print("datasheet_singlevalues",datasheet_singlevalues)
|
|
|
|
|
print("multivalue_regexs",multivalue_regexs[0])
|
|
|
|
|
sys.exit()
|
2021-05-17 23:04:20 +02:00
|
|
|
return None
|
|
|
|
|
|
2021-05-24 19:44:46 +02:00
|
|
|
try:
|
|
|
|
|
las = imp_mod.local_array_size
|
|
|
|
|
except:
|
|
|
|
|
las = DEFAULT_LAS
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
# All the extracted values are delays but val[2] is the max delay
|
2022-07-22 18:52:38 +02:00
|
|
|
feature_vals = [imp_mod.num_words,
|
2021-05-17 23:04:20 +02:00
|
|
|
imp_mod.word_size,
|
2021-09-21 01:35:16 +02:00
|
|
|
imp_mod.words_per_row,
|
2021-05-24 19:44:46 +02:00
|
|
|
las,
|
2022-07-22 18:52:38 +02:00
|
|
|
area_vals[1],
|
|
|
|
|
process,
|
|
|
|
|
voltage,
|
2021-05-17 23:04:20 +02:00
|
|
|
temp]
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
if load_slews == None:
|
2022-07-22 18:52:38 +02:00
|
|
|
c = 1
|
2021-05-17 23:04:20 +02:00
|
|
|
for i in range(num_loads_or_slews):
|
|
|
|
|
for j in range(num_loads_or_slews):
|
|
|
|
|
multi_values = [val[i+j+c] for val in datasheet_multivalues]
|
|
|
|
|
single_values = [val[1] for val in datasheet_singlevalues]
|
2022-07-22 18:52:38 +02:00
|
|
|
writer.writerow(feature_vals+[inp_slew_vals[i+1], load_vals[j+1]]+multi_values+single_values+[leakage_vals[1]])
|
2021-05-17 23:04:20 +02:00
|
|
|
c+=2
|
|
|
|
|
else:
|
|
|
|
|
# if num loads and num slews are not equal then this might break because of how OpenRAM formats
|
|
|
|
|
# the outputs
|
|
|
|
|
c = 1
|
|
|
|
|
for load,slew in load_slews:
|
|
|
|
|
multi_values = [val[c] for val in datasheet_multivalues]
|
|
|
|
|
single_values = [val[1] for val in datasheet_singlevalues]
|
2022-07-22 18:52:38 +02:00
|
|
|
writer.writerow(feature_vals+[slew, load]+multi_values+single_values+[leakage_vals[1]])
|
2021-05-17 23:04:20 +02:00
|
|
|
c+=1
|
|
|
|
|
|
2021-05-19 08:41:16 +02:00
|
|
|
|
|
|
|
|
def extract_data(openram_dir, out_dir, is_first):
|
2021-05-17 23:04:20 +02:00
|
|
|
"""Given an OpenRAM output dir, searches through datasheet files and ouputs
|
|
|
|
|
a CSV files with data used in model."""
|
|
|
|
|
|
|
|
|
|
# Get dataset name used by all the files e.g. sram_1b_16
|
2021-09-21 01:35:16 +02:00
|
|
|
dataset_name, inp_mod, datasheet_fname = get_config_mods(openram_dir)
|
|
|
|
|
if inp_mod == None:
|
2021-06-04 22:37:21 +02:00
|
|
|
print("Config file(s) for this run not found. Skipping...")
|
|
|
|
|
return
|
2021-05-17 23:04:20 +02:00
|
|
|
|
2021-05-19 08:41:16 +02:00
|
|
|
if is_first:
|
|
|
|
|
mode = 'w'
|
|
|
|
|
else:
|
|
|
|
|
mode = 'a+'
|
2021-06-01 23:49:08 +02:00
|
|
|
with open("{}/sim_data.csv".format(out_dir), mode, newline='\n') as data_file:
|
2021-09-21 01:35:16 +02:00
|
|
|
write_to_csv(dataset_name, data_file, datasheet_fname, inp_mod, mode)
|
2021-05-17 23:04:20 +02:00
|
|
|
|
|
|
|
|
return out_dir
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-19 08:41:16 +02:00
|
|
|
def gen_model_csv(openram_dir_path, out_dir):
|
|
|
|
|
if not os.path.isdir(input_dir_path):
|
|
|
|
|
print("Path does not exist: {}".format(input_dir_path))
|
|
|
|
|
return
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-19 08:41:16 +02:00
|
|
|
if not os.path.isdir(out_path):
|
|
|
|
|
print("Path does not exist: {}".format(out_path))
|
2022-07-22 18:52:38 +02:00
|
|
|
return
|
|
|
|
|
|
2021-05-19 08:41:16 +02:00
|
|
|
is_first = True
|
|
|
|
|
oram_dirs = [openram_dir_path+'/'+name for name in os.listdir(openram_dir_path) if os.path.isdir(openram_dir_path+'/'+name)]
|
|
|
|
|
for dir in oram_dirs:
|
|
|
|
|
extract_data(dir, out_dir, is_first)
|
|
|
|
|
is_first = False
|
2022-07-22 18:52:38 +02:00
|
|
|
|
2021-05-17 23:04:20 +02:00
|
|
|
if __name__ == "__main__":
|
2021-05-19 08:41:16 +02:00
|
|
|
if len(sys.argv) < 3:
|
|
|
|
|
print("Usage: python model_data_util.py path_to_openram_dirs out_dir_path")
|
|
|
|
|
else:
|
2022-07-22 18:52:38 +02:00
|
|
|
input_dir_path = sys.argv[1]
|
|
|
|
|
out_path = sys.argv[2]
|
2022-11-30 23:50:43 +01:00
|
|
|
gen_model_csv(input_dir_path, out_path)
|