mirror of https://github.com/VLSIDA/OpenRAM.git
Revert "Fix whitespace"
This commit is contained in:
parent
f1e452c8e3
commit
5d6e56763f
|
|
@ -359,7 +359,7 @@ class instance(geometry):
|
||||||
for offset in range(len(normalized_br_offsets)):
|
for offset in range(len(normalized_br_offsets)):
|
||||||
for port in range(len(br_names)):
|
for port in range(len(br_names)):
|
||||||
cell_br_meta.append([br_names[offset], row, col, port])
|
cell_br_meta.append([br_names[offset], row, col, port])
|
||||||
|
|
||||||
if normalized_storage_nets == []:
|
if normalized_storage_nets == []:
|
||||||
debug.error("normalized storage nets should not be empty! Check if the GDS labels Q and Q_bar are correctly set on M1 of the cell",1)
|
debug.error("normalized storage nets should not be empty! Check if the GDS labels Q and Q_bar are correctly set on M1 of the cell",1)
|
||||||
Q_x = normalized_storage_nets[0][0]
|
Q_x = normalized_storage_nets[0][0]
|
||||||
|
|
|
||||||
|
|
@ -1317,7 +1317,7 @@ class layout():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
intermediate_layers = self.get_metal_layers(from_layer, to_layer)
|
intermediate_layers = self.get_metal_layers(from_layer, to_layer)
|
||||||
|
|
||||||
via = None
|
via = None
|
||||||
cur_layer = from_layer
|
cur_layer = from_layer
|
||||||
while cur_layer != to_layer:
|
while cur_layer != to_layer:
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ class lef:
|
||||||
# return
|
# return
|
||||||
|
|
||||||
# To maintain the indent level easily
|
# To maintain the indent level easily
|
||||||
self.indent = ""
|
self.indent = ""
|
||||||
|
|
||||||
if OPTS.detailed_lef:
|
if OPTS.detailed_lef:
|
||||||
debug.info(3, "Writing detailed LEF to {0}".format(lef_name))
|
debug.info(3, "Writing detailed LEF to {0}".format(lef_name))
|
||||||
|
|
@ -88,7 +88,7 @@ class lef:
|
||||||
|
|
||||||
for pin_name in self.pins:
|
for pin_name in self.pins:
|
||||||
self.lef_write_pin(pin_name)
|
self.lef_write_pin(pin_name)
|
||||||
|
|
||||||
self.lef_write_obstructions(OPTS.detailed_lef)
|
self.lef_write_obstructions(OPTS.detailed_lef)
|
||||||
self.lef_write_footer()
|
self.lef_write_footer()
|
||||||
self.lef.close()
|
self.lef.close()
|
||||||
|
|
@ -220,3 +220,4 @@ class lef:
|
||||||
round(item[1],
|
round(item[1],
|
||||||
self.round_grid)))
|
self.round_grid)))
|
||||||
self.lef.write(" ;\n")
|
self.lef.write(" ;\n")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ class pin_layout:
|
||||||
if self.same_lpp(layer_name_pp, lpp):
|
if self.same_lpp(layer_name_pp, lpp):
|
||||||
self._layer = layer_name
|
self._layer = layer_name
|
||||||
break
|
break
|
||||||
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
from tech import layer_override
|
from tech import layer_override
|
||||||
|
|
@ -57,7 +57,7 @@ class pin_layout:
|
||||||
return
|
return
|
||||||
except:
|
except:
|
||||||
debug.error("Layer {} is not a valid routing layer in the tech file.".format(layer_name_pp), -1)
|
debug.error("Layer {} is not a valid routing layer in the tech file.".format(layer_name_pp), -1)
|
||||||
|
|
||||||
self.lpp = layer[self.layer]
|
self.lpp = layer[self.layer]
|
||||||
self._recompute_hash()
|
self._recompute_hash()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ class timing_graph():
|
||||||
# If at the last output, include the final output load
|
# If at the last output, include the final output load
|
||||||
if i == len(path) - 2:
|
if i == len(path) - 2:
|
||||||
cout += load
|
cout += load
|
||||||
|
|
||||||
if params["model_name"] == "cacti":
|
if params["model_name"] == "cacti":
|
||||||
delays.append(path_edge_mod.cacti_delay(corner, cur_slew, cout, params))
|
delays.append(path_edge_mod.cacti_delay(corner, cur_slew, cout, params))
|
||||||
cur_slew = delays[-1].slew
|
cur_slew = delays[-1].slew
|
||||||
|
|
@ -130,14 +130,14 @@ class timing_graph():
|
||||||
return_value=1)
|
return_value=1)
|
||||||
|
|
||||||
return delays
|
return delays
|
||||||
|
|
||||||
def get_edge_mods(self, path):
|
def get_edge_mods(self, path):
|
||||||
"""Return all edge mods associated with path"""
|
"""Return all edge mods associated with path"""
|
||||||
|
|
||||||
if len(path) == 0:
|
if len(path) == 0:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
return [self.edge_mods[(path[i], path[i+1])] for i in range(len(path)-1)]
|
return [self.edge_mods[(path[i], path[i+1])] for i in range(len(path)-1)]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
|
|
@ -153,3 +153,4 @@ class timing_graph():
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
|
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ def get_gds_pins(pin_names, name, gds_filename, units):
|
||||||
if layer_override[pin_name]:
|
if layer_override[pin_name]:
|
||||||
lpp = layer_override[pin_name.textString]
|
lpp = layer_override[pin_name.textString]
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
lpp = (lpp[0], None)
|
lpp = (lpp[0], None)
|
||||||
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
|
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ class wire(wire_path):
|
||||||
This is contact direction independent pitch,
|
This is contact direction independent pitch,
|
||||||
i.e. we take the maximum contact dimension
|
i.e. we take the maximum contact dimension
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# This is here for the unit tests which may not have
|
# This is here for the unit tests which may not have
|
||||||
# initialized the static parts of the layout class yet.
|
# initialized the static parts of the layout class yet.
|
||||||
from base import layout
|
from base import layout
|
||||||
|
|
|
||||||
|
|
@ -1,325 +1,325 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
||||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
import math
|
import math
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import os
|
import os
|
||||||
|
|
||||||
process_transform = {'SS':0.0, 'TT': 0.5, 'FF':1.0}
|
process_transform = {'SS':0.0, 'TT': 0.5, 'FF':1.0}
|
||||||
|
|
||||||
def get_data_names(file_name, exclude_area=True):
|
def get_data_names(file_name, exclude_area=True):
|
||||||
"""
|
"""
|
||||||
Returns just the data names in the first row of the CSV
|
Returns just the data names in the first row of the CSV
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with open(file_name, newline='') as csvfile:
|
with open(file_name, newline='') as csvfile:
|
||||||
csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|')
|
csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|')
|
||||||
row_iter = 0
|
row_iter = 0
|
||||||
# reader is iterable not a list, probably a better way to do this
|
# reader is iterable not a list, probably a better way to do this
|
||||||
for row in csv_reader:
|
for row in csv_reader:
|
||||||
# Return names from first row
|
# Return names from first row
|
||||||
names = row[0].split(',')
|
names = row[0].split(',')
|
||||||
break
|
break
|
||||||
if exclude_area:
|
if exclude_area:
|
||||||
try:
|
try:
|
||||||
area_ind = names.index('area')
|
area_ind = names.index('area')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
area_ind = -1
|
area_ind = -1
|
||||||
|
|
||||||
if area_ind != -1:
|
if area_ind != -1:
|
||||||
names = names[:area_ind] + names[area_ind+1:]
|
names = names[:area_ind] + names[area_ind+1:]
|
||||||
return names
|
return names
|
||||||
|
|
||||||
def get_data(file_name):
|
def get_data(file_name):
|
||||||
"""
|
"""
|
||||||
Returns data in CSV as lists of features
|
Returns data in CSV as lists of features
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with open(file_name, newline='') as csvfile:
|
with open(file_name, newline='') as csvfile:
|
||||||
csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|')
|
csv_reader = csv.reader(csvfile, delimiter=' ', quotechar='|')
|
||||||
row_iter = 0
|
row_iter = 0
|
||||||
removed_items = 1
|
removed_items = 1
|
||||||
for row in csv_reader:
|
for row in csv_reader:
|
||||||
row_iter += 1
|
row_iter += 1
|
||||||
if row_iter == 1:
|
if row_iter == 1:
|
||||||
feature_names = row[0].split(',')
|
feature_names = row[0].split(',')
|
||||||
input_list = [[] for _ in range(len(feature_names)-removed_items)]
|
input_list = [[] for _ in range(len(feature_names)-removed_items)]
|
||||||
try:
|
try:
|
||||||
# Save to remove area
|
# Save to remove area
|
||||||
area_ind = feature_names.index('area')
|
area_ind = feature_names.index('area')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
area_ind = -1
|
area_ind = -1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
process_ind = feature_names.index('process')
|
process_ind = feature_names.index('process')
|
||||||
except:
|
except:
|
||||||
debug.error('Process not included as a feature.')
|
debug.error('Process not included as a feature.')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
split_str = row[0].split(',')
|
split_str = row[0].split(',')
|
||||||
for i in range(len(split_str)):
|
for i in range(len(split_str)):
|
||||||
if i == process_ind:
|
if i == process_ind:
|
||||||
data.append(process_transform[split_str[i]])
|
data.append(process_transform[split_str[i]])
|
||||||
elif i == area_ind:
|
elif i == area_ind:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
data.append(float(split_str[i]))
|
data.append(float(split_str[i]))
|
||||||
|
|
||||||
data[0] = math.log(data[0], 2)
|
data[0] = math.log(data[0], 2)
|
||||||
|
|
||||||
for i in range(len(data)):
|
for i in range(len(data)):
|
||||||
input_list[i].append(data[i])
|
input_list[i].append(data[i])
|
||||||
|
|
||||||
return input_list
|
return input_list
|
||||||
|
|
||||||
def apply_samples_to_data(all_data, algo_samples):
|
def apply_samples_to_data(all_data, algo_samples):
|
||||||
# Take samples from algorithm and match them to samples in data
|
# Take samples from algorithm and match them to samples in data
|
||||||
data_samples, unused_data = [], []
|
data_samples, unused_data = [], []
|
||||||
sample_positions = set()
|
sample_positions = set()
|
||||||
for sample in algo_samples:
|
for sample in algo_samples:
|
||||||
sample_positions.add(find_sample_position_with_min_error(all_data, sample))
|
sample_positions.add(find_sample_position_with_min_error(all_data, sample))
|
||||||
|
|
||||||
for i in range(len(all_data)):
|
for i in range(len(all_data)):
|
||||||
if i in sample_positions:
|
if i in sample_positions:
|
||||||
data_samples.append(all_data[i])
|
data_samples.append(all_data[i])
|
||||||
else:
|
else:
|
||||||
unused_data.append(all_data[i])
|
unused_data.append(all_data[i])
|
||||||
|
|
||||||
return data_samples, unused_data
|
return data_samples, unused_data
|
||||||
|
|
||||||
def find_sample_position_with_min_error(data, sampled_vals):
|
def find_sample_position_with_min_error(data, sampled_vals):
|
||||||
min_error = 0
|
min_error = 0
|
||||||
sample_pos = 0
|
sample_pos = 0
|
||||||
count = 0
|
count = 0
|
||||||
for data_slice in data:
|
for data_slice in data:
|
||||||
error = squared_error(data_slice, sampled_vals)
|
error = squared_error(data_slice, sampled_vals)
|
||||||
if min_error == 0 or error < min_error:
|
if min_error == 0 or error < min_error:
|
||||||
min_error = error
|
min_error = error
|
||||||
sample_pos = count
|
sample_pos = count
|
||||||
count += 1
|
count += 1
|
||||||
return sample_pos
|
return sample_pos
|
||||||
|
|
||||||
def squared_error(list_a, list_b):
|
def squared_error(list_a, list_b):
|
||||||
error_sum = 0;
|
error_sum = 0;
|
||||||
for a,b in zip(list_a, list_b):
|
for a,b in zip(list_a, list_b):
|
||||||
error_sum+=(a-b)**2
|
error_sum+=(a-b)**2
|
||||||
return error_sum
|
return error_sum
|
||||||
|
|
||||||
|
|
||||||
def get_max_min_from_datasets(dir):
|
def get_max_min_from_datasets(dir):
|
||||||
if not os.path.isdir(dir):
|
if not os.path.isdir(dir):
|
||||||
debug.warning("Input Directory not found:{}".format(dir))
|
debug.warning("Input Directory not found:{}".format(dir))
|
||||||
return [], [], []
|
return [], [], []
|
||||||
|
|
||||||
# Assuming all files are CSV
|
# Assuming all files are CSV
|
||||||
data_files = [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))]
|
data_files = [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))]
|
||||||
maxs,mins,sums,total_count = [],[],[],0
|
maxs,mins,sums,total_count = [],[],[],0
|
||||||
for file in data_files:
|
for file in data_files:
|
||||||
data = get_data(os.path.join(dir, file))
|
data = get_data(os.path.join(dir, file))
|
||||||
# Get max, min, sum, and count from every file
|
# Get max, min, sum, and count from every file
|
||||||
data_max, data_min, data_sum, count = [],[],[], 0
|
data_max, data_min, data_sum, count = [],[],[], 0
|
||||||
for feature_list in data:
|
for feature_list in data:
|
||||||
data_max.append(max(feature_list))
|
data_max.append(max(feature_list))
|
||||||
data_min.append(min(feature_list))
|
data_min.append(min(feature_list))
|
||||||
data_sum.append(sum(feature_list))
|
data_sum.append(sum(feature_list))
|
||||||
count = len(feature_list)
|
count = len(feature_list)
|
||||||
|
|
||||||
# Aggregate the data
|
# Aggregate the data
|
||||||
if not maxs or not mins or not sums:
|
if not maxs or not mins or not sums:
|
||||||
maxs,mins,sums,total_count = data_max,data_min,data_sum,count
|
maxs,mins,sums,total_count = data_max,data_min,data_sum,count
|
||||||
else:
|
else:
|
||||||
for i in range(len(maxs)):
|
for i in range(len(maxs)):
|
||||||
maxs[i] = max(data_max[i], maxs[i])
|
maxs[i] = max(data_max[i], maxs[i])
|
||||||
mins[i] = min(data_min[i], mins[i])
|
mins[i] = min(data_min[i], mins[i])
|
||||||
sums[i] = data_sum[i]+sums[i]
|
sums[i] = data_sum[i]+sums[i]
|
||||||
total_count+=count
|
total_count+=count
|
||||||
|
|
||||||
avgs = [s/total_count for s in sums]
|
avgs = [s/total_count for s in sums]
|
||||||
return maxs,mins,avgs
|
return maxs,mins,avgs
|
||||||
|
|
||||||
def get_max_min_from_file(path):
|
def get_max_min_from_file(path):
|
||||||
if not os.path.isfile(path):
|
if not os.path.isfile(path):
|
||||||
debug.warning("Input file not found: {}".format(path))
|
debug.warning("Input file not found: {}".format(path))
|
||||||
return [], [], []
|
return [], [], []
|
||||||
|
|
||||||
|
|
||||||
data = get_data(path)
|
data = get_data(path)
|
||||||
# Get max, min, sum, and count from every file
|
# Get max, min, sum, and count from every file
|
||||||
data_max, data_min, data_sum, count = [],[],[], 0
|
data_max, data_min, data_sum, count = [],[],[], 0
|
||||||
for feature_list in data:
|
for feature_list in data:
|
||||||
data_max.append(max(feature_list))
|
data_max.append(max(feature_list))
|
||||||
data_min.append(min(feature_list))
|
data_min.append(min(feature_list))
|
||||||
data_sum.append(sum(feature_list))
|
data_sum.append(sum(feature_list))
|
||||||
count = len(feature_list)
|
count = len(feature_list)
|
||||||
|
|
||||||
avgs = [s/count for s in data_sum]
|
avgs = [s/count for s in data_sum]
|
||||||
return data_max, data_min, avgs
|
return data_max, data_min, avgs
|
||||||
|
|
||||||
def get_data_and_scale(file_name, sample_dir):
|
def get_data_and_scale(file_name, sample_dir):
|
||||||
maxs,mins,avgs = get_max_min_from_datasets(sample_dir)
|
maxs,mins,avgs = get_max_min_from_datasets(sample_dir)
|
||||||
|
|
||||||
# Get data
|
# Get data
|
||||||
all_data = get_data(file_name)
|
all_data = get_data(file_name)
|
||||||
|
|
||||||
# Scale data from file
|
# Scale data from file
|
||||||
self_scaled_data = [[] for _ in range(len(all_data[0]))]
|
self_scaled_data = [[] for _ in range(len(all_data[0]))]
|
||||||
self_maxs,self_mins = [],[]
|
self_maxs,self_mins = [],[]
|
||||||
for feature_list, cur_max, cur_min in zip(all_data,maxs, mins):
|
for feature_list, cur_max, cur_min in zip(all_data,maxs, mins):
|
||||||
for i in range(len(feature_list)):
|
for i in range(len(feature_list)):
|
||||||
self_scaled_data[i].append((feature_list[i]-cur_min)/(cur_max-cur_min))
|
self_scaled_data[i].append((feature_list[i]-cur_min)/(cur_max-cur_min))
|
||||||
|
|
||||||
return np.asarray(self_scaled_data)
|
return np.asarray(self_scaled_data)
|
||||||
|
|
||||||
def rescale_data(data, old_maxs, old_mins, new_maxs, new_mins):
|
def rescale_data(data, old_maxs, old_mins, new_maxs, new_mins):
|
||||||
# unscale from old values, rescale by new values
|
# unscale from old values, rescale by new values
|
||||||
data_new_scaling = []
|
data_new_scaling = []
|
||||||
for data_row in data:
|
for data_row in data:
|
||||||
scaled_row = []
|
scaled_row = []
|
||||||
for val, old_max,old_min, cur_max, cur_min in zip(data_row, old_maxs,old_mins, new_maxs, new_mins):
|
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
|
unscaled_data = val*(old_max-old_min) + old_min
|
||||||
scaled_row.append((unscaled_data-cur_min)/(cur_max-cur_min))
|
scaled_row.append((unscaled_data-cur_min)/(cur_max-cur_min))
|
||||||
|
|
||||||
data_new_scaling.append(scaled_row)
|
data_new_scaling.append(scaled_row)
|
||||||
|
|
||||||
return data_new_scaling
|
return data_new_scaling
|
||||||
|
|
||||||
def sample_from_file(num_samples, file_name, sample_dir=None):
|
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.
|
Get a portion of the data from CSV file and scale it based on max/min of dataset.
|
||||||
Duplicate samples are trimmed.
|
Duplicate samples are trimmed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if sample_dir:
|
if sample_dir:
|
||||||
maxs,mins,avgs = get_max_min_from_datasets(sample_dir)
|
maxs,mins,avgs = get_max_min_from_datasets(sample_dir)
|
||||||
else:
|
else:
|
||||||
maxs,mins,avgs = [], [], []
|
maxs,mins,avgs = [], [], []
|
||||||
|
|
||||||
# Get data
|
# Get data
|
||||||
all_data = get_data(file_name)
|
all_data = get_data(file_name)
|
||||||
|
|
||||||
# Get algorithms sample points, assuming hypercube for now
|
# Get algorithms sample points, assuming hypercube for now
|
||||||
num_labels = 1
|
num_labels = 1
|
||||||
inp_dims = len(all_data) - num_labels
|
inp_dims = len(all_data) - num_labels
|
||||||
samples = np.random.rand(num_samples, inp_dims)
|
samples = np.random.rand(num_samples, inp_dims)
|
||||||
|
|
||||||
|
|
||||||
# Scale data from file
|
# Scale data from file
|
||||||
self_scaled_data = [[] for _ in range(len(all_data[0]))]
|
self_scaled_data = [[] for _ in range(len(all_data[0]))]
|
||||||
self_maxs,self_mins = [],[]
|
self_maxs,self_mins = [],[]
|
||||||
for feature_list in all_data:
|
for feature_list in all_data:
|
||||||
max_val = max(feature_list)
|
max_val = max(feature_list)
|
||||||
self_maxs.append(max_val)
|
self_maxs.append(max_val)
|
||||||
min_val = min(feature_list)
|
min_val = min(feature_list)
|
||||||
self_mins.append(min_val)
|
self_mins.append(min_val)
|
||||||
for i in range(len(feature_list)):
|
for i in range(len(feature_list)):
|
||||||
self_scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val))
|
self_scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val))
|
||||||
# Apply algorithm sampling points to available data
|
# Apply algorithm sampling points to available data
|
||||||
sampled_data, unused_data = apply_samples_to_data(self_scaled_data,samples)
|
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)
|
#unscale values and rescale using all available data (both sampled and unused points rescaled)
|
||||||
if len(maxs)!=0 and len(mins)!=0:
|
if len(maxs)!=0 and len(mins)!=0:
|
||||||
sampled_data = rescale_data(sampled_data, self_maxs,self_mins, maxs, mins)
|
sampled_data = rescale_data(sampled_data, self_maxs,self_mins, maxs, mins)
|
||||||
unused_new_scaling = rescale_data(unused_data, self_maxs,self_mins, maxs, mins)
|
unused_new_scaling = rescale_data(unused_data, self_maxs,self_mins, maxs, mins)
|
||||||
|
|
||||||
return np.asarray(sampled_data), np.asarray(unused_new_scaling)
|
return np.asarray(sampled_data), np.asarray(unused_new_scaling)
|
||||||
|
|
||||||
def get_scaled_data(file_name):
|
def get_scaled_data(file_name):
|
||||||
"""Get data from CSV file and scale it based on max/min of dataset"""
|
"""Get data from CSV file and scale it based on max/min of dataset"""
|
||||||
|
|
||||||
if file_name:
|
if file_name:
|
||||||
maxs,mins,avgs = get_max_min_from_file(file_name)
|
maxs,mins,avgs = get_max_min_from_file(file_name)
|
||||||
else:
|
else:
|
||||||
maxs,mins,avgs = [], [], []
|
maxs,mins,avgs = [], [], []
|
||||||
|
|
||||||
# Get data
|
# Get data
|
||||||
all_data = get_data(file_name)
|
all_data = get_data(file_name)
|
||||||
|
|
||||||
# Data is scaled by max/min and data format is changed to points vs feature lists
|
# 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)
|
self_scaled_data = scale_data_and_transform(all_data)
|
||||||
data_np = np.asarray(self_scaled_data)
|
data_np = np.asarray(self_scaled_data)
|
||||||
return data_np
|
return data_np
|
||||||
|
|
||||||
def scale_data_and_transform(data):
|
def scale_data_and_transform(data):
|
||||||
"""
|
"""
|
||||||
Assume data is a list of features, change to a list of points and max/min scale
|
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]))]
|
scaled_data = [[] for _ in range(len(data[0]))]
|
||||||
for feature_list in data:
|
for feature_list in data:
|
||||||
max_val = max(feature_list)
|
max_val = max(feature_list)
|
||||||
min_val = min(feature_list)
|
min_val = min(feature_list)
|
||||||
|
|
||||||
for i in range(len(feature_list)):
|
for i in range(len(feature_list)):
|
||||||
if max_val == min_val:
|
if max_val == min_val:
|
||||||
scaled_data[i].append(0.0)
|
scaled_data[i].append(0.0)
|
||||||
else:
|
else:
|
||||||
scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val))
|
scaled_data[i].append((feature_list[i]-min_val)/(max_val-min_val))
|
||||||
return scaled_data
|
return scaled_data
|
||||||
|
|
||||||
def scale_input_datapoint(point, file_path):
|
def scale_input_datapoint(point, file_path):
|
||||||
"""
|
"""
|
||||||
Input data has no output and needs to be scaled like the model inputs during
|
Input data has no output and needs to be scaled like the model inputs during
|
||||||
training.
|
training.
|
||||||
"""
|
"""
|
||||||
maxs, mins, avgs = get_max_min_from_file(file_path)
|
maxs, mins, avgs = get_max_min_from_file(file_path)
|
||||||
debug.info(3, "maxs={}".format(maxs))
|
debug.info(3, "maxs={}".format(maxs))
|
||||||
debug.info(3, "mins={}".format(mins))
|
debug.info(3, "mins={}".format(mins))
|
||||||
debug.info(3, "point={}".format(point))
|
debug.info(3, "point={}".format(point))
|
||||||
|
|
||||||
scaled_point = []
|
scaled_point = []
|
||||||
for feature, mx, mn in zip(point, maxs, mins):
|
for feature, mx, mn in zip(point, maxs, mins):
|
||||||
if mx == mn:
|
if mx == mn:
|
||||||
scaled_point.append(0.0)
|
scaled_point.append(0.0)
|
||||||
else:
|
else:
|
||||||
scaled_point.append((feature-mn)/(mx-mn))
|
scaled_point.append((feature-mn)/(mx-mn))
|
||||||
return scaled_point
|
return scaled_point
|
||||||
|
|
||||||
def unscale_data(data, file_path, pos=None):
|
def unscale_data(data, file_path, pos=None):
|
||||||
if file_path:
|
if file_path:
|
||||||
maxs,mins,avgs = get_max_min_from_file(file_path)
|
maxs,mins,avgs = get_max_min_from_file(file_path)
|
||||||
else:
|
else:
|
||||||
debug.error("Must provide reference data to unscale")
|
debug.error("Must provide reference data to unscale")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Hard coded to only convert the last max/min (i.e. the label of the data)
|
# Hard coded to only convert the last max/min (i.e. the label of the data)
|
||||||
if pos == None:
|
if pos == None:
|
||||||
maxs,mins,avgs = maxs[-1],mins[-1],avgs[-1]
|
maxs,mins,avgs = maxs[-1],mins[-1],avgs[-1]
|
||||||
else:
|
else:
|
||||||
maxs,mins,avgs = maxs[pos],mins[pos],avgs[pos]
|
maxs,mins,avgs = maxs[pos],mins[pos],avgs[pos]
|
||||||
unscaled_data = []
|
unscaled_data = []
|
||||||
for data_row in data:
|
for data_row in data:
|
||||||
unscaled_val = data_row*(maxs-mins) + mins
|
unscaled_val = data_row*(maxs-mins) + mins
|
||||||
unscaled_data.append(unscaled_val)
|
unscaled_data.append(unscaled_val)
|
||||||
|
|
||||||
return unscaled_data
|
return unscaled_data
|
||||||
|
|
||||||
def abs_error(labels, preds):
|
def abs_error(labels, preds):
|
||||||
total_error = 0
|
total_error = 0
|
||||||
for label_i, pred_i in zip(labels, preds):
|
for label_i, pred_i in zip(labels, preds):
|
||||||
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
|
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
|
||||||
total_error += cur_error
|
total_error += cur_error
|
||||||
return total_error/len(labels)
|
return total_error/len(labels)
|
||||||
|
|
||||||
def max_error(labels, preds):
|
def max_error(labels, preds):
|
||||||
mx_error = 0
|
mx_error = 0
|
||||||
for label_i, pred_i in zip(labels, preds):
|
for label_i, pred_i in zip(labels, preds):
|
||||||
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
|
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
|
||||||
mx_error = max(cur_error, mx_error)
|
mx_error = max(cur_error, mx_error)
|
||||||
return mx_error
|
return mx_error
|
||||||
|
|
||||||
def min_error(labels, preds):
|
def min_error(labels, preds):
|
||||||
mn_error = 1
|
mn_error = 1
|
||||||
for label_i, pred_i in zip(labels, preds):
|
for label_i, pred_i in zip(labels, preds):
|
||||||
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
|
cur_error = abs(label_i[0]-pred_i[0])/label_i[0]
|
||||||
mn_error = min(cur_error, mn_error)
|
mn_error = min(cur_error, mn_error)
|
||||||
return mn_error
|
return mn_error
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,15 @@
|
||||||
from .simulation import simulation
|
from .simulation import simulation
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
import tech
|
import tech
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
|
||||||
class cacti(simulation):
|
class cacti(simulation):
|
||||||
"""
|
"""
|
||||||
Delay model for the SRAM which which
|
Delay model for the SRAM which which
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, sram, spfile, corner):
|
def __init__(self, sram, spfile, corner):
|
||||||
super().__init__(sram, spfile, corner)
|
super().__init__(sram, spfile, corner)
|
||||||
|
|
||||||
|
|
@ -33,8 +33,8 @@ class cacti(simulation):
|
||||||
self.create_signal_names()
|
self.create_signal_names()
|
||||||
self.add_graph_exclusions()
|
self.add_graph_exclusions()
|
||||||
self.set_params()
|
self.set_params()
|
||||||
|
|
||||||
def set_params(self):
|
def set_params(self):
|
||||||
"""Set parameters specific to the corner being simulated"""
|
"""Set parameters specific to the corner being simulated"""
|
||||||
self.params = {}
|
self.params = {}
|
||||||
# Set the specific functions to use for timing defined in the SRAM module
|
# Set the specific functions to use for timing defined in the SRAM module
|
||||||
|
|
@ -42,14 +42,14 @@ class cacti(simulation):
|
||||||
# Only parameter right now is r_on which is dependent on Vdd
|
# 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_nch_on"] = self.vdd_voltage / tech.spice["i_on_n"]
|
||||||
self.params["r_pch_on"] = self.vdd_voltage / tech.spice["i_on_p"]
|
self.params["r_pch_on"] = self.vdd_voltage / tech.spice["i_on_p"]
|
||||||
|
|
||||||
def get_lib_values(self, load_slews):
|
def get_lib_values(self, load_slews):
|
||||||
"""
|
"""
|
||||||
Return the analytical model results for the SRAM.
|
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:
|
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.")
|
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.
|
# Probe set to 0th bit, does not matter for analytical delay.
|
||||||
self.set_probe('0' * self.addr_size, 0)
|
self.set_probe('0' * self.addr_size, 0)
|
||||||
self.create_graph()
|
self.create_graph()
|
||||||
|
|
@ -77,7 +77,7 @@ class cacti(simulation):
|
||||||
slew = 0
|
slew = 0
|
||||||
path_delays = self.graph.get_timing(bl_path, self.corner, slew, load_farad, self.params)
|
path_delays = self.graph.get_timing(bl_path, self.corner, slew, load_farad, self.params)
|
||||||
total_delay = self.sum_delays(path_delays)
|
total_delay = self.sum_delays(path_delays)
|
||||||
|
|
||||||
delay_ns = total_delay.delay/1e-9
|
delay_ns = total_delay.delay/1e-9
|
||||||
slew_ns = total_delay.slew/1e-9
|
slew_ns = total_delay.slew/1e-9
|
||||||
max_delay = max(max_delay, total_delay.delay)
|
max_delay = max(max_delay, total_delay.delay)
|
||||||
|
|
@ -95,7 +95,7 @@ class cacti(simulation):
|
||||||
elif "slew" in mname and port in self.read_ports:
|
elif "slew" in mname and port in self.read_ports:
|
||||||
port_data[port][mname].append(total_delay.slew / 1e-9)
|
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.
|
# memory. FIXME: margin is quite large, should be looked into.
|
||||||
period_margin = 1.85
|
period_margin = 1.85
|
||||||
sram_data = {"min_period": (max_delay / 1e-9) * 2 * period_margin,
|
sram_data = {"min_period": (max_delay / 1e-9) * 2 * period_margin,
|
||||||
|
|
@ -118,3 +118,5 @@ class cacti(simulation):
|
||||||
debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
|
debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
|
||||||
debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
|
debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
|
||||||
return power
|
return power
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ def parse_spice_list(filename, key):
|
||||||
except IOError:
|
except IOError:
|
||||||
debug.error("Unable to open spice output file: {0}".format(full_filename),1)
|
debug.error("Unable to open spice output file: {0}".format(full_filename),1)
|
||||||
debug.archive()
|
debug.archive()
|
||||||
|
|
||||||
contents = f.read().lower()
|
contents = f.read().lower()
|
||||||
f.close()
|
f.close()
|
||||||
# val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents)
|
# val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents)
|
||||||
|
|
|
||||||
|
|
@ -235,10 +235,10 @@ class delay(simulation):
|
||||||
qbar_meas = voltage_at_measure("v_qbar_{0}".format(meas_tag), qbar_name)
|
qbar_meas = voltage_at_measure("v_qbar_{0}".format(meas_tag), qbar_name)
|
||||||
|
|
||||||
return {bit_polarity.NONINVERTING: q_meas, bit_polarity.INVERTING: qbar_meas}
|
return {bit_polarity.NONINVERTING: q_meas, bit_polarity.INVERTING: qbar_meas}
|
||||||
|
|
||||||
def create_sen_and_bitline_path_measures(self):
|
def create_sen_and_bitline_path_measures(self):
|
||||||
"""Create measurements for the s_en and bitline paths for individual delays per stage."""
|
"""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
|
# FIXME: There should be a default_read_port variable in this case, pathing is done with this
|
||||||
# but is never mentioned otherwise
|
# but is never mentioned otherwise
|
||||||
port = self.read_ports[0]
|
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)))
|
debug.check(len(bl_paths)==1, 'Found {0} paths which contain the bitline net.'.format(len(bl_paths)))
|
||||||
sen_path = sen_paths[0]
|
sen_path = sen_paths[0]
|
||||||
bitline_path = bl_paths[0]
|
bitline_path = bl_paths[0]
|
||||||
|
|
||||||
# Get the measures
|
# Get the measures
|
||||||
self.sen_path_meas = self.create_delay_path_measures(sen_path)
|
self.sen_path_meas = self.create_delay_path_measures(sen_path)
|
||||||
self.bl_path_meas = self.create_delay_path_measures(bitline_path)
|
self.bl_path_meas = self.create_delay_path_measures(bitline_path)
|
||||||
all_meas = self.sen_path_meas + self.bl_path_meas
|
all_meas = self.sen_path_meas + self.bl_path_meas
|
||||||
|
|
||||||
# Paths could have duplicate measurements, remove them before they go to the stim file
|
# Paths could have duplicate measurements, remove them before they go to the stim file
|
||||||
all_meas = self.remove_duplicate_meas_names(all_meas)
|
all_meas = self.remove_duplicate_meas_names(all_meas)
|
||||||
# FIXME: duplicate measurements still exist in the member variables, since they have the same
|
# 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.
|
# 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):
|
def remove_duplicate_meas_names(self, measures):
|
||||||
"""Returns new list of measurements without duplicate names"""
|
"""Returns new list of measurements without duplicate names"""
|
||||||
|
|
||||||
name_set = set()
|
name_set = set()
|
||||||
unique_measures = []
|
unique_measures = []
|
||||||
for meas in measures:
|
for meas in measures:
|
||||||
if meas.name not in name_set:
|
if meas.name not in name_set:
|
||||||
name_set.add(meas.name)
|
name_set.add(meas.name)
|
||||||
unique_measures.append(meas)
|
unique_measures.append(meas)
|
||||||
|
|
||||||
return unique_measures
|
return unique_measures
|
||||||
|
|
||||||
def create_delay_path_measures(self, path):
|
def create_delay_path_measures(self, path):
|
||||||
"""Creates measurements for each net along given path."""
|
"""Creates measurements for each net along given path."""
|
||||||
|
|
||||||
# Determine the directions (RISE/FALL) of signals
|
# Determine the directions (RISE/FALL) of signals
|
||||||
path_dirs = self.get_meas_directions(path)
|
path_dirs = self.get_meas_directions(path)
|
||||||
|
|
||||||
# Create the measurements
|
# Create the measurements
|
||||||
path_meas = []
|
path_meas = []
|
||||||
for i in range(len(path) - 1):
|
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.
|
# 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_str = sram_op.READ_ZERO
|
||||||
path_meas[-1].meta_add_delay = True
|
path_meas[-1].meta_add_delay = True
|
||||||
|
|
||||||
return path_meas
|
return path_meas
|
||||||
|
|
||||||
def get_meas_directions(self, path):
|
def get_meas_directions(self, path):
|
||||||
"""Returns SPICE measurements directions based on path."""
|
"""Returns SPICE measurements directions based on path."""
|
||||||
|
|
||||||
# Get the edges modules which define the path
|
# Get the edges modules which define the path
|
||||||
edge_mods = self.graph.get_edge_mods(path)
|
edge_mods = self.graph.get_edge_mods(path)
|
||||||
|
|
||||||
# Convert to booleans based on function of modules (inverting/non-inverting)
|
# Convert to booleans based on function of modules (inverting/non-inverting)
|
||||||
mod_type_bools = [mod.is_non_inverting() for mod in edge_mods]
|
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
|
# FIXME: obtuse hack to differentiate s_en input from bitline in sense amps
|
||||||
if self.sen_name in path:
|
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.
|
# bitline->DOUT is non-inverting, but the module cannot differentiate inputs.
|
||||||
s_en_index = path.index(self.sen_name)
|
s_en_index = path.index(self.sen_name)
|
||||||
mod_type_bools[s_en_index] = False
|
mod_type_bools[s_en_index] = False
|
||||||
debug.info(2, 'Forcing sen->dout to be inverting.')
|
debug.info(2, 'Forcing sen->dout to be inverting.')
|
||||||
|
|
||||||
# Use these to determine direction list assuming delay start on neg. edge of clock (FALL)
|
# 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
|
# Also, use shorthand that 'FALL' == False, 'RISE' == True to simplify logic
|
||||||
bool_dirs = [False]
|
bool_dirs = [False]
|
||||||
|
|
@ -324,9 +324,9 @@ class delay(simulation):
|
||||||
for mod_bool in mod_type_bools:
|
for mod_bool in mod_type_bools:
|
||||||
cur_dir = (cur_dir == mod_bool)
|
cur_dir = (cur_dir == mod_bool)
|
||||||
bool_dirs.append(cur_dir)
|
bool_dirs.append(cur_dir)
|
||||||
|
|
||||||
# Convert from boolean to string
|
# 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):
|
def set_load_slew(self, load, slew):
|
||||||
""" Set the load and slew """
|
""" Set the load and slew """
|
||||||
|
|
@ -827,7 +827,7 @@ class delay(simulation):
|
||||||
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict), 1)
|
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict), 1)
|
||||||
|
|
||||||
result[port].update(read_port_dict)
|
result[port].update(read_port_dict)
|
||||||
|
|
||||||
self.path_delays = self.check_path_measures()
|
self.path_delays = self.check_path_measures()
|
||||||
|
|
||||||
return (True, result)
|
return (True, result)
|
||||||
|
|
@ -932,7 +932,7 @@ class delay(simulation):
|
||||||
|
|
||||||
def check_path_measures(self):
|
def check_path_measures(self):
|
||||||
"""Get and check all the delays along the sen and bitline paths"""
|
"""Get and check all the delays along the sen and bitline paths"""
|
||||||
|
|
||||||
# Get and set measurement, no error checking done other than prints.
|
# Get and set measurement, no error checking done other than prints.
|
||||||
debug.info(2, "Checking measures in Delay Path")
|
debug.info(2, "Checking measures in Delay Path")
|
||||||
value_dict = {}
|
value_dict = {}
|
||||||
|
|
@ -1179,7 +1179,7 @@ class delay(simulation):
|
||||||
#char_sram_data["sen_path_names"] = sen_names
|
#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.
|
# 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)
|
self.alter_lh_char_data(char_port_data)
|
||||||
|
|
||||||
return (char_sram_data, char_port_data)
|
return (char_sram_data, char_port_data)
|
||||||
|
|
||||||
def alter_lh_char_data(self, 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:
|
for meas in self.sen_path_meas:
|
||||||
sen_name_list.append(meas.name)
|
sen_name_list.append(meas.name)
|
||||||
sen_delay_list.append(value_dict[meas.name])
|
sen_delay_list.append(value_dict[meas.name])
|
||||||
|
|
||||||
bl_name_list = []
|
bl_name_list = []
|
||||||
bl_delay_list = []
|
bl_delay_list = []
|
||||||
for meas in self.bl_path_meas:
|
for meas in self.bl_path_meas:
|
||||||
bl_name_list.append(meas.name)
|
bl_name_list.append(meas.name)
|
||||||
bl_delay_list.append(value_dict[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):
|
def calculate_inverse_address(self):
|
||||||
"""Determine dummy test address based on probe address and column mux size."""
|
"""Determine dummy test address based on probe address and column mux size."""
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@ from .simulation import simulation
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
class elmore(simulation):
|
class elmore(simulation):
|
||||||
"""
|
"""
|
||||||
Delay model for the SRAM which calculates Elmore delays along the SRAM critical path.
|
Delay model for the SRAM which calculates Elmore delays along the SRAM critical path.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, sram, spfile, corner):
|
def __init__(self, sram, spfile, corner):
|
||||||
super().__init__(sram, spfile, corner)
|
super().__init__(sram, spfile, corner)
|
||||||
|
|
||||||
|
|
@ -30,13 +30,13 @@ class elmore(simulation):
|
||||||
self.set_corner(corner)
|
self.set_corner(corner)
|
||||||
self.create_signal_names()
|
self.create_signal_names()
|
||||||
self.add_graph_exclusions()
|
self.add_graph_exclusions()
|
||||||
|
|
||||||
def set_params(self):
|
def set_params(self):
|
||||||
"""Set parameters specific to the corner being simulated"""
|
"""Set parameters specific to the corner being simulated"""
|
||||||
self.params = {}
|
self.params = {}
|
||||||
# Set the specific functions to use for timing defined in the SRAM module
|
# Set the specific functions to use for timing defined in the SRAM module
|
||||||
self.params["model_name"] = OPTS.model_name
|
self.params["model_name"] = OPTS.model_name
|
||||||
|
|
||||||
def get_lib_values(self, load_slews):
|
def get_lib_values(self, load_slews):
|
||||||
"""
|
"""
|
||||||
Return the analytical model results for the SRAM.
|
Return the analytical model results for the SRAM.
|
||||||
|
|
@ -66,7 +66,7 @@ class elmore(simulation):
|
||||||
for load,slew in load_slews:
|
for load,slew in load_slews:
|
||||||
# Calculate delay based on slew and load
|
# Calculate delay based on slew and load
|
||||||
path_delays = self.graph.get_timing(bl_path, self.corner, slew, load, self.params)
|
path_delays = self.graph.get_timing(bl_path, self.corner, slew, load, self.params)
|
||||||
|
|
||||||
total_delay = self.sum_delays(path_delays)
|
total_delay = self.sum_delays(path_delays)
|
||||||
max_delay = max(max_delay, total_delay.delay)
|
max_delay = max(max_delay, total_delay.delay)
|
||||||
debug.info(1,
|
debug.info(1,
|
||||||
|
|
@ -84,7 +84,7 @@ class elmore(simulation):
|
||||||
elif "slew" in mname and port in self.read_ports:
|
elif "slew" in mname and port in self.read_ports:
|
||||||
port_data[port][mname].append(total_delay.slew / 1e3)
|
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.
|
# memory. FIXME: margin is quite large, should be looked into.
|
||||||
period_margin = 1.85
|
period_margin = 1.85
|
||||||
sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin,
|
sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin,
|
||||||
|
|
|
||||||
|
|
@ -26,17 +26,18 @@ class linear_regression(regression_model):
|
||||||
"""
|
"""
|
||||||
Supervised training of model.
|
Supervised training of model.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#model = LinearRegression()
|
#model = LinearRegression()
|
||||||
model = self.get_model()
|
model = self.get_model()
|
||||||
model.fit(features, labels)
|
model.fit(features, labels)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
def model_prediction(self, model, features):
|
def model_prediction(self, model, features):
|
||||||
"""
|
"""
|
||||||
Have the model perform a prediction and unscale the prediction
|
Have the model perform a prediction and unscale the prediction
|
||||||
as the model is trained with scaled values.
|
as the model is trained with scaled values.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pred = model.predict(features)
|
pred = model.predict(features)
|
||||||
return pred
|
return pred
|
||||||
|
|
||||||
|
|
@ -184,7 +184,7 @@ class voltage_when_measure(spice_measurement):
|
||||||
trig_voltage = self.trig_val_of_vdd * vdd_voltage
|
trig_voltage = self.trig_val_of_vdd * vdd_voltage
|
||||||
return (meas_name, trig_name, targ_name, trig_voltage, self.trig_dir_str, trig_td)
|
return (meas_name, trig_name, targ_name, trig_voltage, self.trig_dir_str, trig_td)
|
||||||
|
|
||||||
|
|
||||||
class voltage_at_measure(spice_measurement):
|
class voltage_at_measure(spice_measurement):
|
||||||
"""Generates a spice measurement to measure the voltage at a specific time.
|
"""Generates a spice measurement to measure the voltage at a specific time.
|
||||||
The time is considered variant with different periods."""
|
The time is considered variant with different periods."""
|
||||||
|
|
@ -211,3 +211,4 @@ class voltage_at_measure(spice_measurement):
|
||||||
meas_name = self.name
|
meas_name = self.name
|
||||||
targ_name = self.targ_name_no_port
|
targ_name = self.targ_name_no_port
|
||||||
return (meas_name, targ_name, time_at)
|
return (meas_name, targ_name, time_at)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class model_check(delay):
|
||||||
replicated here.
|
replicated here.
|
||||||
"""
|
"""
|
||||||
delay.create_signal_names(self)
|
delay.create_signal_names(self)
|
||||||
|
|
||||||
# Signal names are all hardcoded, need to update to make it work for probe address and different configurations.
|
# 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_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())]
|
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,3 +448,6 @@ class model_check(delay):
|
||||||
name_dict[self.sae_model_name] = name_dict["sae_measures"]
|
name_dict[self.sae_model_name] = name_dict["sae_measures"]
|
||||||
|
|
||||||
return name_dict
|
return name_dict
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,19 +25,20 @@ class neural_network(regression_model):
|
||||||
"""
|
"""
|
||||||
Training multilayer model
|
Training multilayer model
|
||||||
"""
|
"""
|
||||||
|
|
||||||
flat_labels = np.ravel(labels)
|
flat_labels = np.ravel(labels)
|
||||||
model = self.get_model()
|
model = self.get_model()
|
||||||
model.fit(features, flat_labels)
|
model.fit(features, flat_labels)
|
||||||
|
|
||||||
return model
|
return model
|
||||||
|
|
||||||
def model_prediction(self, model, features):
|
def model_prediction(self, model, features):
|
||||||
"""
|
"""
|
||||||
Have the model perform a prediction and unscale the prediction
|
Have the model perform a prediction and unscale the prediction
|
||||||
as the model is trained with scaled values.
|
as the model is trained with scaled values.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pred = model.predict(features)
|
pred = model.predict(features)
|
||||||
reshape_pred = np.reshape(pred, (len(pred),1))
|
reshape_pred = np.reshape(pred, (len(pred),1))
|
||||||
return reshape_pred
|
return reshape_pred
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ data_fnames = ["rise_delay.csv",
|
||||||
"read0_power.csv",
|
"read0_power.csv",
|
||||||
"leakage_data.csv",
|
"leakage_data.csv",
|
||||||
"sim_time.csv"]
|
"sim_time.csv"]
|
||||||
# Positions must correspond to data_fname list
|
# Positions must correspond to data_fname list
|
||||||
lib_dnames = ["delay_lh",
|
lib_dnames = ["delay_lh",
|
||||||
"delay_hl",
|
"delay_hl",
|
||||||
"slew_lh",
|
"slew_lh",
|
||||||
|
|
@ -35,13 +35,13 @@ lib_dnames = ["delay_lh",
|
||||||
"read1_power",
|
"read1_power",
|
||||||
"read0_power",
|
"read0_power",
|
||||||
"leakage_power",
|
"leakage_power",
|
||||||
"sim_time"]
|
"sim_time"]
|
||||||
# Check if another data dir was specified
|
# 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
|
data_dir = OPTS.openram_tech+relative_data_path
|
||||||
else:
|
else:
|
||||||
data_dir = OPTS.sim_data_path
|
data_dir = OPTS.sim_data_path
|
||||||
|
|
||||||
data_path = data_dir + '/' + data_file
|
data_path = data_dir + '/' + data_file
|
||||||
|
|
||||||
class regression_model(simulation):
|
class regression_model(simulation):
|
||||||
|
|
@ -52,23 +52,23 @@ class regression_model(simulation):
|
||||||
|
|
||||||
def get_lib_values(self, load_slews):
|
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.")
|
debug.info(1, "Characterizing SRAM using regression models.")
|
||||||
log_num_words = math.log(OPTS.num_words, 2)
|
log_num_words = math.log(OPTS.num_words, 2)
|
||||||
model_inputs = [log_num_words,
|
model_inputs = [log_num_words,
|
||||||
OPTS.word_size,
|
OPTS.word_size,
|
||||||
OPTS.words_per_row,
|
OPTS.words_per_row,
|
||||||
OPTS.local_array_size,
|
OPTS.local_array_size,
|
||||||
process_transform[self.process],
|
process_transform[self.process],
|
||||||
self.vdd_voltage,
|
self.vdd_voltage,
|
||||||
self.temperature]
|
self.temperature]
|
||||||
# Area removed for now
|
# Area removed for now
|
||||||
# self.sram.width * self.sram.height,
|
# self.sram.width * self.sram.height,
|
||||||
# Include above inputs, plus load and slew which are added below
|
# Include above inputs, plus load and slew which are added below
|
||||||
self.num_inputs = len(model_inputs)+2
|
self.num_inputs = len(model_inputs)+2
|
||||||
|
|
||||||
self.create_measurement_names()
|
self.create_measurement_names()
|
||||||
models = self.train_models()
|
models = self.train_models()
|
||||||
|
|
||||||
|
|
@ -85,22 +85,22 @@ class regression_model(simulation):
|
||||||
port_data[port]['delay_hl'].append(sram_vals['fall_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_lh'].append(sram_vals['rise_slew'])
|
||||||
port_data[port]['slew_hl'].append(sram_vals['fall_slew'])
|
port_data[port]['slew_hl'].append(sram_vals['fall_slew'])
|
||||||
|
|
||||||
port_data[port]['write1_power'].append(sram_vals['write1_power'])
|
port_data[port]['write1_power'].append(sram_vals['write1_power'])
|
||||||
port_data[port]['write0_power'].append(sram_vals['write0_power'])
|
port_data[port]['write0_power'].append(sram_vals['write0_power'])
|
||||||
port_data[port]['read1_power'].append(sram_vals['read1_power'])
|
port_data[port]['read1_power'].append(sram_vals['read1_power'])
|
||||||
port_data[port]['read0_power'].append(sram_vals['read0_power'])
|
port_data[port]['read0_power'].append(sram_vals['read0_power'])
|
||||||
|
|
||||||
# Disabled power not modeled. Copied from other power predictions
|
# Disabled power not modeled. Copied from other power predictions
|
||||||
port_data[port]['disabled_write1_power'].append(sram_vals['write1_power'])
|
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_write0_power'].append(sram_vals['write0_power'])
|
||||||
port_data[port]['disabled_read1_power'].append(sram_vals['read1_power'])
|
port_data[port]['disabled_read1_power'].append(sram_vals['read1_power'])
|
||||||
port_data[port]['disabled_read0_power'].append(sram_vals['read0_power'])
|
port_data[port]['disabled_read0_power'].append(sram_vals['read0_power'])
|
||||||
|
|
||||||
debug.info(1, '{}, {}, {}, {}, {}'.format(slew,
|
debug.info(1, '{}, {}, {}, {}, {}'.format(slew,
|
||||||
load,
|
load,
|
||||||
port,
|
port,
|
||||||
sram_vals['rise_delay'],
|
sram_vals['rise_delay'],
|
||||||
sram_vals['rise_slew']))
|
sram_vals['rise_slew']))
|
||||||
# Estimate the period as double the delay with margin
|
# Estimate the period as double the delay with margin
|
||||||
period_margin = 0.1
|
period_margin = 0.1
|
||||||
|
|
@ -112,19 +112,19 @@ class regression_model(simulation):
|
||||||
|
|
||||||
return (sram_data, port_data)
|
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
|
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)])
|
scaled_inputs = np.asarray([scale_input_datapoint(model_inputs, data_path)])
|
||||||
|
|
||||||
predictions = {}
|
predictions = {}
|
||||||
out_pos = 0
|
out_pos = 0
|
||||||
for dname in self.output_names:
|
for dname in self.output_names:
|
||||||
m = models[dname]
|
m = models[dname]
|
||||||
|
|
||||||
scaled_pred = self.model_prediction(m, scaled_inputs)
|
scaled_pred = self.model_prediction(m, scaled_inputs)
|
||||||
pred = unscale_data(scaled_pred.tolist(), data_path, pos=self.num_inputs+out_pos)
|
pred = unscale_data(scaled_pred.tolist(), data_path, pos=self.num_inputs+out_pos)
|
||||||
debug.info(2,"Unscaled Prediction = {}".format(pred))
|
debug.info(2,"Unscaled Prediction = {}".format(pred))
|
||||||
|
|
@ -149,7 +149,7 @@ class regression_model(simulation):
|
||||||
output_num+=1
|
output_num+=1
|
||||||
|
|
||||||
return models
|
return models
|
||||||
|
|
||||||
def score_model(self):
|
def score_model(self):
|
||||||
num_inputs = 9 #FIXME - should be defined somewhere else
|
num_inputs = 9 #FIXME - should be defined somewhere else
|
||||||
self.output_names = get_data_names(data_path)[num_inputs:]
|
self.output_names = get_data_names(data_path)[num_inputs:]
|
||||||
|
|
@ -165,15 +165,15 @@ class regression_model(simulation):
|
||||||
scr = model.score(features, output_label)
|
scr = model.score(features, output_label)
|
||||||
debug.info(1, "{}, {}".format(o_name, scr))
|
debug.info(1, "{}, {}".format(o_name, scr))
|
||||||
output_num+=1
|
output_num+=1
|
||||||
|
|
||||||
|
|
||||||
def cross_validation(self, test_only=None):
|
def cross_validation(self, test_only=None):
|
||||||
"""Wrapper for sklean cross validation function for OpenRAM regression models.
|
"""Wrapper for sklean cross validation function for OpenRAM regression models.
|
||||||
Returns the mean accuracy for each model/output."""
|
Returns the mean accuracy for each model/output."""
|
||||||
|
|
||||||
from sklearn.model_selection import cross_val_score
|
from sklearn.model_selection import cross_val_score
|
||||||
untrained_model = self.get_model()
|
untrained_model = self.get_model()
|
||||||
|
|
||||||
num_inputs = 9 #FIXME - should be defined somewhere else
|
num_inputs = 9 #FIXME - should be defined somewhere else
|
||||||
self.output_names = get_data_names(data_path)[num_inputs:]
|
self.output_names = get_data_names(data_path)[num_inputs:]
|
||||||
data = get_scaled_data(data_path)
|
data = get_scaled_data(data_path)
|
||||||
|
|
@ -193,9 +193,9 @@ class regression_model(simulation):
|
||||||
debug.info(1, "{}, {}, {}".format(o_name, scores.mean(), scores.std()))
|
debug.info(1, "{}, {}, {}".format(o_name, scores.mean(), scores.std()))
|
||||||
model_scores[o_name] = scores.mean()
|
model_scores[o_name] = scores.mean()
|
||||||
output_num+=1
|
output_num+=1
|
||||||
|
|
||||||
return model_scores
|
return model_scores
|
||||||
|
|
||||||
# Fixme - only will work for sklearn regression models
|
# Fixme - only will work for sklearn regression models
|
||||||
def save_model(self, model_name, model):
|
def save_model(self, model_name, model):
|
||||||
try:
|
try:
|
||||||
|
|
@ -205,3 +205,4 @@ class regression_model(simulation):
|
||||||
OPTS.model_dict[model_name+"_coef"] = list(model.coef_[0])
|
OPTS.model_dict[model_name+"_coef"] = list(model.coef_[0])
|
||||||
debug.info(1,"Coefs of {}:{}".format(model_name,OPTS.model_dict[model_name+"_coef"]))
|
debug.info(1,"Coefs of {}:{}".format(model_name,OPTS.model_dict[model_name+"_coef"]))
|
||||||
OPTS.model_dict[model_name+"_intercept"] = float(model.intercept_)
|
OPTS.model_dict[model_name+"_intercept"] = float(model.intercept_)
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ class setup_hold():
|
||||||
def __init__(self, corner):
|
def __init__(self, corner):
|
||||||
# This must match the spice model order
|
# This must match the spice model order
|
||||||
self.dff = factory.create(module_type=OPTS.dff)
|
self.dff = factory.create(module_type=OPTS.dff)
|
||||||
|
|
||||||
self.period = tech.spice["feasible_period"]
|
self.period = tech.spice["feasible_period"]
|
||||||
|
|
||||||
debug.info(2, "Feasible period from technology file: {0} ".format(self.period))
|
debug.info(2, "Feasible period from technology file: {0} ".format(self.period))
|
||||||
|
|
@ -106,8 +106,8 @@ class setup_hold():
|
||||||
setup=0)
|
setup=0)
|
||||||
|
|
||||||
def write_clock(self):
|
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
|
First period initializes the FF
|
||||||
while the second is used for characterization.
|
while the second is used for characterization.
|
||||||
"""
|
"""
|
||||||
|
|
@ -206,7 +206,7 @@ class setup_hold():
|
||||||
|
|
||||||
self.stim.run_sim(self.stim_sp)
|
self.stim.run_sim(self.stim_sp)
|
||||||
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
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)
|
setuphold_time = (target_time - 2 * self.period)
|
||||||
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
||||||
passing_setuphold_time = -1 * setuphold_time
|
passing_setuphold_time = -1 * setuphold_time
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,9 @@ class trim_spice():
|
||||||
self.col_addr_size = int(log(self.words_per_row, 2))
|
self.col_addr_size = int(log(self.words_per_row, 2))
|
||||||
self.bank_addr_size = self.col_addr_size + self.row_addr_size
|
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))
|
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
|
||||||
|
|
||||||
def trim(self, address, data_bit):
|
def trim(self, address, data_bit):
|
||||||
"""
|
"""
|
||||||
Reduce the spice netlist but KEEP the given bits at the
|
Reduce the spice netlist but KEEP the given bits at the
|
||||||
address (and things that will add capacitive load!)
|
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)
|
col_address = int(address[0:self.col_addr_size], 2)
|
||||||
else:
|
else:
|
||||||
col_address = 0
|
col_address = 0
|
||||||
|
|
||||||
# 1. Keep cells in the bitcell array based on WL and BL
|
# 1. Keep cells in the bitcell array based on WL and BL
|
||||||
wl_name = "wl_{}".format(wl_address)
|
wl_name = "wl_{}".format(wl_address)
|
||||||
bl_name = "bl_{}".format(int(self.words_per_row*data_bit + col_address))
|
bl_name = "bl_{}".format(int(self.words_per_row*data_bit + col_address))
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class datasheet():
|
||||||
if OPTS.output_datasheet_info:
|
if OPTS.output_datasheet_info:
|
||||||
datasheet_path = OPTS.output_path
|
datasheet_path = OPTS.output_path
|
||||||
else:
|
else:
|
||||||
datasheet_path = OPTS.openram_temp
|
datasheet_path = OPTS.openram_temp
|
||||||
with open(datasheet_path + "/datasheet.info") as info:
|
with open(datasheet_path + "/datasheet.info") as info:
|
||||||
self.html += '<!--'
|
self.html += '<!--'
|
||||||
for row in info:
|
for row in info:
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ def check(check, str):
|
||||||
|
|
||||||
if globals.OPTS.debug:
|
if globals.OPTS.debug:
|
||||||
pdb.set_trace()
|
pdb.set_trace()
|
||||||
|
|
||||||
assert 0
|
assert 0
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -108,7 +108,7 @@ def info(lev, str):
|
||||||
print_raw("[{0}/{1}]: {2}".format(class_name,
|
print_raw("[{0}/{1}]: {2}".format(class_name,
|
||||||
frm[0].f_code.co_name, str))
|
frm[0].f_code.co_name, str))
|
||||||
|
|
||||||
|
|
||||||
def archive():
|
def archive():
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
try:
|
try:
|
||||||
|
|
@ -121,7 +121,7 @@ def archive():
|
||||||
info(0, "Archiving failed files to {}.zip".format(zip_file))
|
info(0, "Archiving failed files to {}.zip".format(zip_file))
|
||||||
shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
|
shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
|
||||||
|
|
||||||
|
|
||||||
def bp():
|
def bp():
|
||||||
"""
|
"""
|
||||||
An empty function so you can set soft breakpoints in pdb.
|
An empty function so you can set soft breakpoints in pdb.
|
||||||
|
|
@ -130,7 +130,8 @@ def bp():
|
||||||
2) Run "python3 -m pdb openram.py config.py" or "python3 -m pdb 05_bitcell_array.test" (for example)
|
2) Run "python3 -m pdb openram.py config.py" or "python3 -m pdb 05_bitcell_array.test" (for example)
|
||||||
3) When pdb starts, run "break debug.bp" to set a SOFT breakpoint. (Or you can add this to your ~/.pdbrc)
|
3) When pdb starts, run "break debug.bp" to set a SOFT breakpoint. (Or you can add this to your ~/.pdbrc)
|
||||||
4) Then run "cont" to continue.
|
4) Then run "cont" to continue.
|
||||||
5) You can now set additional breakpoints or display commands
|
5) You can now set additional breakpoints or display commands
|
||||||
and whenever you encounter the debug.bp() they won't be "reset".
|
and whenever you encounter the debug.bp() they won't be "reset".
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,14 @@ class cell:
|
||||||
|
|
||||||
# Some cells may have body bias (well taps) exposed as ports
|
# Some cells may have body bias (well taps) exposed as ports
|
||||||
self._body_bias = body_bias
|
self._body_bias = body_bias
|
||||||
|
|
||||||
# Specifies if this is a hard (i.e. GDS) cell
|
# Specifies if this is a hard (i.e. GDS) cell
|
||||||
self._hard_cell = hard_cell
|
self._hard_cell = hard_cell
|
||||||
self._boundary_layer = boundary_layer
|
self._boundary_layer = boundary_layer
|
||||||
|
|
||||||
# Specifies the port directions
|
# Specifies the port directions
|
||||||
self._port_types_map = {x: y for (x, y) in zip(port_order, port_types)}
|
self._port_types_map = {x: y for (x, y) in zip(port_order, port_types)}
|
||||||
|
|
||||||
# Specifies a map from OpenRAM names to cell names
|
# Specifies a map from OpenRAM names to cell names
|
||||||
# by default it is 1:1
|
# by default it is 1:1
|
||||||
if not port_map:
|
if not port_map:
|
||||||
|
|
@ -31,13 +31,13 @@ class cell:
|
||||||
|
|
||||||
# Create an index array
|
# Create an index array
|
||||||
self._port_indices = [self._port_order.index(x) for x in self._original_port_order]
|
self._port_indices = [self._port_order.index(x) for x in self._original_port_order]
|
||||||
|
|
||||||
# Update ordered name list
|
# Update ordered name list
|
||||||
self._port_names = [self._port_map[x] for x in self._port_order]
|
self._port_names = [self._port_map[x] for x in self._port_order]
|
||||||
|
|
||||||
# Update ordered type list
|
# Update ordered type list
|
||||||
self._port_types = [self._port_types_map[x] for x in self._port_order]
|
self._port_types = [self._port_types_map[x] for x in self._port_order]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hard_cell(self):
|
def hard_cell(self):
|
||||||
return self._hard_cell
|
return self._hard_cell
|
||||||
|
|
@ -73,21 +73,21 @@ class cell:
|
||||||
@property
|
@property
|
||||||
def port_indices(self):
|
def port_indices(self):
|
||||||
return self._port_indices
|
return self._port_indices
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def port_map(self):
|
def port_map(self):
|
||||||
return self._port_map
|
return self._port_map
|
||||||
|
|
||||||
@port_map.setter
|
@port_map.setter
|
||||||
def port_map(self, port_map):
|
def port_map(self, port_map):
|
||||||
self._port_map = port_map
|
self._port_map = port_map
|
||||||
# Update ordered name list to use the new names
|
# Update ordered name list to use the new names
|
||||||
self._port_names = [self._port_map[x] for x in self._port_order]
|
self._port_names = [self._port_map[x] for x in self._port_order]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def body_bias(self):
|
def body_bias(self):
|
||||||
return self._body_bias
|
return self._body_bias
|
||||||
|
|
||||||
@body_bias.setter
|
@body_bias.setter
|
||||||
def body_bias(self, body_bias):
|
def body_bias(self, body_bias):
|
||||||
# It is assumed it is [nwell, pwell]
|
# It is assumed it is [nwell, pwell]
|
||||||
|
|
@ -96,7 +96,7 @@ class cell:
|
||||||
self._port_types['vnb'] = "GROUND"
|
self._port_types['vnb'] = "GROUND"
|
||||||
self._port_map['vpb'] = body_bias[1]
|
self._port_map['vpb'] = body_bias[1]
|
||||||
self._port_types['vpb'] = "POWER"
|
self._port_types['vpb'] = "POWER"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def port_types(self):
|
def port_types(self):
|
||||||
return self._port_types
|
return self._port_types
|
||||||
|
|
@ -108,7 +108,7 @@ class cell:
|
||||||
self._port_types_map = {x: y for (x, y) in zip(self._port_order, self._port_types)}
|
self._port_types_map = {x: y for (x, y) in zip(self._port_order, self._port_types)}
|
||||||
# Update ordered type list
|
# Update ordered type list
|
||||||
self._port_types = [self._port_types_map[x] for x in self._port_order]
|
self._port_types = [self._port_types_map[x] for x in self._port_order]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def boundary_layer(self):
|
def boundary_layer(self):
|
||||||
return self._boundary_layer
|
return self._boundary_layer
|
||||||
|
|
@ -116,8 +116,8 @@ class cell:
|
||||||
@boundary_layer.setter
|
@boundary_layer.setter
|
||||||
def boundary_layer(self, x):
|
def boundary_layer(self, x):
|
||||||
self._boundary_layer = x
|
self._boundary_layer = x
|
||||||
|
|
||||||
|
|
||||||
class _pins:
|
class _pins:
|
||||||
def __init__(self, pin_dict):
|
def __init__(self, pin_dict):
|
||||||
# make the pins elements of the class to allow "." access.
|
# make the pins elements of the class to allow "." access.
|
||||||
|
|
@ -148,7 +148,7 @@ class bitcell(cell):
|
||||||
super().__init__(port_order, port_types, port_map)
|
super().__init__(port_order, port_types, port_map)
|
||||||
|
|
||||||
self.end_caps = end_caps
|
self.end_caps = end_caps
|
||||||
|
|
||||||
if not mirror:
|
if not mirror:
|
||||||
self.mirror = _mirror_axis(True, False)
|
self.mirror = _mirror_axis(True, False)
|
||||||
else:
|
else:
|
||||||
|
|
@ -166,7 +166,7 @@ class bitcell(cell):
|
||||||
self.gnd_layer = "m1"
|
self.gnd_layer = "m1"
|
||||||
self.gnd_dir = "H"
|
self.gnd_dir = "H"
|
||||||
|
|
||||||
|
|
||||||
class cell_properties():
|
class cell_properties():
|
||||||
"""
|
"""
|
||||||
This contains meta information about the custom designed cells. For
|
This contains meta information about the custom designed cells. For
|
||||||
|
|
@ -194,16 +194,16 @@ class cell_properties():
|
||||||
|
|
||||||
self._inv_dec = cell(["A", "Z", "vdd", "gnd"],
|
self._inv_dec = cell(["A", "Z", "vdd", "gnd"],
|
||||||
["INPUT", "OUTPUT", "POWER", "GROUND"])
|
["INPUT", "OUTPUT", "POWER", "GROUND"])
|
||||||
|
|
||||||
self._nand2_dec = cell(["A", "B", "Z", "vdd", "gnd"],
|
self._nand2_dec = cell(["A", "B", "Z", "vdd", "gnd"],
|
||||||
["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"])
|
["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"])
|
||||||
|
|
||||||
self._nand3_dec = cell(["A", "B", "C", "Z", "vdd", "gnd"],
|
self._nand3_dec = cell(["A", "B", "C", "Z", "vdd", "gnd"],
|
||||||
["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"])
|
["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"])
|
||||||
|
|
||||||
self._nand4_dec = cell(["A", "B", "C", "D", "Z", "vdd", "gnd"],
|
self._nand4_dec = cell(["A", "B", "C", "D", "Z", "vdd", "gnd"],
|
||||||
["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"])
|
["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"])
|
||||||
|
|
||||||
self._dff = cell(["D", "Q", "clk", "vdd", "gnd"],
|
self._dff = cell(["D", "Q", "clk", "vdd", "gnd"],
|
||||||
["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"])
|
["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"])
|
||||||
|
|
||||||
|
|
@ -230,7 +230,7 @@ class cell_properties():
|
||||||
|
|
||||||
self._row_cap_2port = bitcell(["wl0", "wl1", "gnd"],
|
self._row_cap_2port = bitcell(["wl0", "wl1", "gnd"],
|
||||||
["INPUT", "INPUT", "POWER", "GROUND"])
|
["INPUT", "INPUT", "POWER", "GROUND"])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ptx(self):
|
def ptx(self):
|
||||||
return self._ptx
|
return self._ptx
|
||||||
|
|
@ -246,15 +246,15 @@ class cell_properties():
|
||||||
@property
|
@property
|
||||||
def nand2_dec(self):
|
def nand2_dec(self):
|
||||||
return self._nand2_dec
|
return self._nand2_dec
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nand3_dec(self):
|
def nand3_dec(self):
|
||||||
return self._nand3_dec
|
return self._nand3_dec
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nand4_dec(self):
|
def nand4_dec(self):
|
||||||
return self._nand4_dec
|
return self._nand4_dec
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dff(self):
|
def dff(self):
|
||||||
return self._dff
|
return self._dff
|
||||||
|
|
@ -270,7 +270,7 @@ class cell_properties():
|
||||||
@property
|
@property
|
||||||
def bitcell_1port(self):
|
def bitcell_1port(self):
|
||||||
return self._bitcell_1port
|
return self._bitcell_1port
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bitcell_2port(self):
|
def bitcell_2port(self):
|
||||||
return self._bitcell_2port
|
return self._bitcell_2port
|
||||||
|
|
@ -282,7 +282,7 @@ class cell_properties():
|
||||||
@property
|
@property
|
||||||
def row_cap_1port(self):
|
def row_cap_1port(self):
|
||||||
return self._row_cap_1port
|
return self._row_cap_1port
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def col_cap_2port(self):
|
def col_cap_2port(self):
|
||||||
return self._col_cap_2port
|
return self._col_cap_2port
|
||||||
|
|
@ -290,3 +290,4 @@ class cell_properties():
|
||||||
@property
|
@property
|
||||||
def row_cap_2port(self):
|
def row_cap_2port(self):
|
||||||
return self._row_cap_2port
|
return self._row_cap_2port
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,7 @@ def check_versions():
|
||||||
else:
|
else:
|
||||||
OPTS.coverage_exe = ""
|
OPTS.coverage_exe = ""
|
||||||
debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage")
|
debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import coverage
|
import coverage
|
||||||
OPTS.coverage = 1
|
OPTS.coverage = 1
|
||||||
|
|
@ -249,7 +249,7 @@ def setup_bitcell():
|
||||||
OPTS.bitcell = "bitcell_{}port".format(OPTS.num_ports)
|
OPTS.bitcell = "bitcell_{}port".format(OPTS.num_ports)
|
||||||
OPTS.dummy_bitcell = "dummy_" + OPTS.bitcell
|
OPTS.dummy_bitcell = "dummy_" + OPTS.bitcell
|
||||||
OPTS.replica_bitcell = "replica_" + OPTS.bitcell
|
OPTS.replica_bitcell = "replica_" + OPTS.bitcell
|
||||||
|
|
||||||
# See if bitcell exists
|
# See if bitcell exists
|
||||||
try:
|
try:
|
||||||
c = importlib.import_module("modules." + OPTS.bitcell)
|
c = importlib.import_module("modules." + OPTS.bitcell)
|
||||||
|
|
@ -388,7 +388,7 @@ def end_openram():
|
||||||
verify.print_lvs_stats()
|
verify.print_lvs_stats()
|
||||||
verify.print_pex_stats()
|
verify.print_pex_stats()
|
||||||
|
|
||||||
|
|
||||||
def purge_temp():
|
def purge_temp():
|
||||||
""" Remove the temp directory. """
|
""" Remove the temp directory. """
|
||||||
debug.info(1,
|
debug.info(1,
|
||||||
|
|
@ -406,7 +406,7 @@ def purge_temp():
|
||||||
os.remove(i)
|
os.remove(i)
|
||||||
else:
|
else:
|
||||||
shutil.rmtree(i)
|
shutil.rmtree(i)
|
||||||
|
|
||||||
|
|
||||||
def cleanup_paths():
|
def cleanup_paths():
|
||||||
"""
|
"""
|
||||||
|
|
@ -420,7 +420,7 @@ def cleanup_paths():
|
||||||
elif os.path.exists(OPTS.openram_temp):
|
elif os.path.exists(OPTS.openram_temp):
|
||||||
purge_temp()
|
purge_temp()
|
||||||
|
|
||||||
|
|
||||||
def setup_paths():
|
def setup_paths():
|
||||||
""" Set up the non-tech related paths. """
|
""" Set up the non-tech related paths. """
|
||||||
debug.info(2, "Setting up paths...")
|
debug.info(2, "Setting up paths...")
|
||||||
|
|
@ -447,12 +447,12 @@ def setup_paths():
|
||||||
# Only add the unique subdir one time
|
# Only add the unique subdir one time
|
||||||
if tempdir not in OPTS.openram_temp:
|
if tempdir not in OPTS.openram_temp:
|
||||||
OPTS.openram_temp += tempdir
|
OPTS.openram_temp += tempdir
|
||||||
|
|
||||||
if not OPTS.openram_temp.endswith('/'):
|
if not OPTS.openram_temp.endswith('/'):
|
||||||
OPTS.openram_temp += "/"
|
OPTS.openram_temp += "/"
|
||||||
debug.info(1, "Temporary files saved in " + OPTS.openram_temp)
|
debug.info(1, "Temporary files saved in " + OPTS.openram_temp)
|
||||||
|
|
||||||
|
|
||||||
def is_exe(fpath):
|
def is_exe(fpath):
|
||||||
""" Return true if the given is an executable file that exists. """
|
""" Return true if the given is an executable file that exists. """
|
||||||
return os.path.exists(fpath) and os.access(fpath, os.X_OK)
|
return os.path.exists(fpath) and os.access(fpath, os.X_OK)
|
||||||
|
|
@ -490,7 +490,7 @@ def init_paths():
|
||||||
#from pprint import pprint
|
#from pprint import pprint
|
||||||
#pprint(s)
|
#pprint(s)
|
||||||
#print("Test {0} in dir {1}".format(s[2].filename, OPTS.openram_temp))
|
#print("Test {0} in dir {1}".format(s[2].filename, OPTS.openram_temp))
|
||||||
|
|
||||||
|
|
||||||
# Don't delete the output dir, it may have other files!
|
# Don't delete the output dir, it may have other files!
|
||||||
# make the directory if it doesn't exist
|
# make the directory if it doesn't exist
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,13 @@ def gen_regex_float_group(num, separator):
|
||||||
for i in range(num-1):
|
for i in range(num-1):
|
||||||
full_regex+=separator+float_regex
|
full_regex+=separator+float_regex
|
||||||
return full_regex
|
return full_regex
|
||||||
|
|
||||||
def import_module(mod_name, mod_path):
|
def import_module(mod_name, mod_path):
|
||||||
spec = importlib.util.spec_from_file_location(mod_name, mod_path)
|
spec = importlib.util.spec_from_file_location(mod_name, mod_path)
|
||||||
mod = importlib.util.module_from_spec(spec)
|
mod = importlib.util.module_from_spec(spec)
|
||||||
spec.loader.exec_module(mod)
|
spec.loader.exec_module(mod)
|
||||||
return mod
|
return mod
|
||||||
|
|
||||||
def get_config_mods(openram_dir):
|
def get_config_mods(openram_dir):
|
||||||
# Get dataset name used by all the files e.g. sram_1b_16
|
# 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)]
|
files_names = [name for name in os.listdir(openram_dir) if os.path.isfile(openram_dir+'/'+name)]
|
||||||
|
|
@ -32,8 +32,8 @@ def get_config_mods(openram_dir):
|
||||||
dataset_name = log[:-4]
|
dataset_name = log[:-4]
|
||||||
sys.path.append(openram_dir)
|
sys.path.append(openram_dir)
|
||||||
print("Extracting dataset:{}".format(dataset_name))
|
print("Extracting dataset:{}".format(dataset_name))
|
||||||
|
|
||||||
# Check that the config files exist (including special extended config)
|
# Check that the config files exist (including special extended config)
|
||||||
dir_path = openram_dir+"/"
|
dir_path = openram_dir+"/"
|
||||||
#sys.path.append(dir_path)
|
#sys.path.append(dir_path)
|
||||||
#imp_mod = None
|
#imp_mod = None
|
||||||
|
|
@ -43,16 +43,16 @@ def get_config_mods(openram_dir):
|
||||||
# imp_mod = None
|
# imp_mod = None
|
||||||
# else:
|
# else:
|
||||||
# imp_mod = import_module(dataset_name, openram_dir+"/"+dataset_name+".py")
|
# imp_mod = import_module(dataset_name, openram_dir+"/"+dataset_name+".py")
|
||||||
|
|
||||||
if not os.path.exists(openram_dir+'/'+dataset_name+extended_name+".py"):
|
if not os.path.exists(openram_dir+'/'+dataset_name+extended_name+".py"):
|
||||||
print("Extended Python module for {} not found.".format(dataset_name))
|
print("Extended Python module for {} not found.".format(dataset_name))
|
||||||
imp_mod_extended = None
|
imp_mod_extended = None
|
||||||
else:
|
else:
|
||||||
imp_mod_extended = import_module(dataset_name+extended_name, openram_dir+"/"+dataset_name+extended_name+".py")
|
imp_mod_extended = import_module(dataset_name+extended_name, openram_dir+"/"+dataset_name+extended_name+".py")
|
||||||
|
|
||||||
datasheet_fname = openram_dir+"/"+dataset_name+data_file_ext
|
datasheet_fname = openram_dir+"/"+dataset_name+data_file_ext
|
||||||
|
|
||||||
return dataset_name, imp_mod_extended, datasheet_fname
|
return dataset_name, imp_mod_extended, datasheet_fname
|
||||||
|
|
||||||
def get_corners(datafile_contents, dataset_name, tech):
|
def get_corners(datafile_contents, dataset_name, tech):
|
||||||
"""Search through given datasheet to find all corners available"""
|
"""Search through given datasheet to find all corners available"""
|
||||||
|
|
@ -60,18 +60,18 @@ def get_corners(datafile_contents, dataset_name, tech):
|
||||||
corner_regex = r"{}.*{},([-+]?[0-9]*\.?[0-9]*),([-+]?[0-9]*\.?[0-9]*),([tsfTSF][tsfTSF]),".format(dataset_name, tech)
|
corner_regex = r"{}.*{},([-+]?[0-9]*\.?[0-9]*),([-+]?[0-9]*\.?[0-9]*),([tsfTSF][tsfTSF]),".format(dataset_name, tech)
|
||||||
corners = re.findall(corner_regex,datafile_contents)
|
corners = re.findall(corner_regex,datafile_contents)
|
||||||
return corners # List of corner tuples in order (T, V, P)
|
return corners # List of corner tuples in order (T, V, P)
|
||||||
|
|
||||||
feature_names = ['num_words',
|
feature_names = ['num_words',
|
||||||
'word_size',
|
'word_size',
|
||||||
'words_per_row',
|
'words_per_row',
|
||||||
'local_array_size',
|
'local_array_size',
|
||||||
'area',
|
'area',
|
||||||
'process',
|
'process',
|
||||||
'voltage',
|
'voltage',
|
||||||
'temperature',
|
'temperature',
|
||||||
'slew',
|
'slew',
|
||||||
'load']
|
'load']
|
||||||
output_names = ['rise_delay',
|
output_names = ['rise_delay',
|
||||||
'fall_delay',
|
'fall_delay',
|
||||||
'rise_slew',
|
'rise_slew',
|
||||||
'fall_slew',
|
'fall_slew',
|
||||||
|
|
@ -79,20 +79,20 @@ output_names = ['rise_delay',
|
||||||
'write0_power',
|
'write0_power',
|
||||||
'read1_power',
|
'read1_power',
|
||||||
'read0_power',
|
'read0_power',
|
||||||
'leakage_power']
|
'leakage_power']
|
||||||
|
|
||||||
multivalue_names = ['cell_rise_0',
|
multivalue_names = ['cell_rise_0',
|
||||||
'cell_fall_0',
|
'cell_fall_0',
|
||||||
'rise_transition_0',
|
'rise_transition_0',
|
||||||
'fall_transition_0']
|
'fall_transition_0']
|
||||||
singlevalue_names = ['write_rise_power_0',
|
singlevalue_names = ['write_rise_power_0',
|
||||||
'write_fall_power_0',
|
'write_fall_power_0',
|
||||||
'read_rise_power_0',
|
'read_rise_power_0',
|
||||||
'read_fall_power_0']
|
'read_fall_power_0']
|
||||||
|
|
||||||
def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
writer = csv.writer(csv_file,lineterminator='\n')
|
writer = csv.writer(csv_file,lineterminator='\n')
|
||||||
# If the file was opened to write and not append then we write the header
|
# If the file was opened to write and not append then we write the header
|
||||||
if mode == 'w':
|
if mode == 'w':
|
||||||
|
|
@ -102,7 +102,7 @@ def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
||||||
load_slews = imp_mod.use_specified_load_slew
|
load_slews = imp_mod.use_specified_load_slew
|
||||||
except:
|
except:
|
||||||
load_slews = None
|
load_slews = None
|
||||||
|
|
||||||
if load_slews != None:
|
if load_slews != None:
|
||||||
num_items = len(load_slews)
|
num_items = len(load_slews)
|
||||||
num_loads_or_slews = len(load_slews)
|
num_loads_or_slews = len(load_slews)
|
||||||
|
|
@ -110,7 +110,7 @@ def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
||||||
# These are the defaults for openram
|
# These are the defaults for openram
|
||||||
num_items = 9
|
num_items = 9
|
||||||
num_loads_or_slews = 3
|
num_loads_or_slews = 3
|
||||||
|
|
||||||
try:
|
try:
|
||||||
f = open(datasheet_fname, "r")
|
f = open(datasheet_fname, "r")
|
||||||
except IOError:
|
except IOError:
|
||||||
|
|
@ -118,13 +118,13 @@ def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
||||||
return None
|
return None
|
||||||
print("Opened file",datasheet_fname)
|
print("Opened file",datasheet_fname)
|
||||||
contents = f.read()
|
contents = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
available_corners = get_corners(contents, dataset_name, imp_mod.tech_name)
|
available_corners = get_corners(contents, dataset_name, imp_mod.tech_name)
|
||||||
|
|
||||||
# Loop through corners, adding data for each corner
|
# Loop through corners, adding data for each corner
|
||||||
for (temp, voltage, process) in available_corners:
|
for (temp, voltage, process) in available_corners:
|
||||||
|
|
||||||
# Create a regex to search the datasheet for specified outputs
|
# Create a regex to search the datasheet for specified outputs
|
||||||
voltage_str = "".join(['\\'+i if i=='.' else i for i in str(voltage)])
|
voltage_str = "".join(['\\'+i if i=='.' else i for i in str(voltage)])
|
||||||
area_regex = r"Area \(µm<sup>2<\/sup>\)<\/td><td>(\d+)"
|
area_regex = r"Area \(µm<sup>2<\/sup>\)<\/td><td>(\d+)"
|
||||||
|
|
@ -141,14 +141,14 @@ def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
||||||
voltage_str,
|
voltage_str,
|
||||||
process,
|
process,
|
||||||
float_regex)
|
float_regex)
|
||||||
|
|
||||||
loads_regex = r"{},{}.*{},{},{},.*loads,\[{}".format(
|
loads_regex = r"{},{}.*{},{},{},.*loads,\[{}".format(
|
||||||
dataset_name,
|
dataset_name,
|
||||||
imp_mod.num_words,
|
imp_mod.num_words,
|
||||||
str(temp),
|
str(temp),
|
||||||
voltage_str,
|
voltage_str,
|
||||||
process,
|
process,
|
||||||
float_regex)
|
float_regex)
|
||||||
|
|
||||||
float_regex = gen_regex_float_group(num_items, ', ')
|
float_regex = gen_regex_float_group(num_items, ', ')
|
||||||
multivalue_regexs = []
|
multivalue_regexs = []
|
||||||
|
|
@ -160,10 +160,10 @@ def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
||||||
voltage_str,
|
voltage_str,
|
||||||
process,
|
process,
|
||||||
value_identifier,
|
value_identifier,
|
||||||
float_regex)
|
float_regex)
|
||||||
multivalue_regexs.append(regex_str)
|
multivalue_regexs.append(regex_str)
|
||||||
|
|
||||||
singlevalue_regexs = []
|
singlevalue_regexs = []
|
||||||
for value_identifier in singlevalue_names:
|
for value_identifier in singlevalue_names:
|
||||||
regex_str = r"{},{}.*{},{},{},.*{},([-+]?[0-9]*\.?[0-9]*)".format(
|
regex_str = r"{},{}.*{},{},{},.*{},([-+]?[0-9]*\.?[0-9]*)".format(
|
||||||
dataset_name,
|
dataset_name,
|
||||||
|
|
@ -172,15 +172,15 @@ def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
||||||
voltage_str,
|
voltage_str,
|
||||||
process,
|
process,
|
||||||
value_identifier,
|
value_identifier,
|
||||||
float_regex)
|
float_regex)
|
||||||
singlevalue_regexs.append(regex_str)
|
singlevalue_regexs.append(regex_str)
|
||||||
|
|
||||||
area_vals = re.search(area_regex,contents)
|
area_vals = re.search(area_regex,contents)
|
||||||
leakage_vals = re.search(leakage_regex,contents)
|
leakage_vals = re.search(leakage_regex,contents)
|
||||||
if load_slews == None:
|
if load_slews == None:
|
||||||
inp_slew_vals = re.search(inp_slews_regex,contents)
|
inp_slew_vals = re.search(inp_slews_regex,contents)
|
||||||
load_vals = re.search(loads_regex,contents)
|
load_vals = re.search(loads_regex,contents)
|
||||||
|
|
||||||
datasheet_multivalues = [re.search(r,contents) for r in multivalue_regexs]
|
datasheet_multivalues = [re.search(r,contents) for r in multivalue_regexs]
|
||||||
datasheet_singlevalues = [re.search(r,contents) for r in singlevalue_regexs]
|
datasheet_singlevalues = [re.search(r,contents) for r in singlevalue_regexs]
|
||||||
for dval in datasheet_multivalues+datasheet_singlevalues:
|
for dval in datasheet_multivalues+datasheet_singlevalues:
|
||||||
|
|
@ -196,24 +196,24 @@ def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
||||||
las = imp_mod.local_array_size
|
las = imp_mod.local_array_size
|
||||||
except:
|
except:
|
||||||
las = DEFAULT_LAS
|
las = DEFAULT_LAS
|
||||||
|
|
||||||
# All the extracted values are delays but val[2] is the max delay
|
# All the extracted values are delays but val[2] is the max delay
|
||||||
feature_vals = [imp_mod.num_words,
|
feature_vals = [imp_mod.num_words,
|
||||||
imp_mod.word_size,
|
imp_mod.word_size,
|
||||||
imp_mod.words_per_row,
|
imp_mod.words_per_row,
|
||||||
las,
|
las,
|
||||||
area_vals[1],
|
area_vals[1],
|
||||||
process,
|
process,
|
||||||
voltage,
|
voltage,
|
||||||
temp]
|
temp]
|
||||||
|
|
||||||
if load_slews == None:
|
if load_slews == None:
|
||||||
c = 1
|
c = 1
|
||||||
for i in range(num_loads_or_slews):
|
for i in range(num_loads_or_slews):
|
||||||
for j 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]
|
multi_values = [val[i+j+c] for val in datasheet_multivalues]
|
||||||
single_values = [val[1] for val in datasheet_singlevalues]
|
single_values = [val[1] for val in datasheet_singlevalues]
|
||||||
writer.writerow(feature_vals+[inp_slew_vals[i+1], load_vals[j+1]]+multi_values+single_values+[leakage_vals[1]])
|
writer.writerow(feature_vals+[inp_slew_vals[i+1], load_vals[j+1]]+multi_values+single_values+[leakage_vals[1]])
|
||||||
c+=2
|
c+=2
|
||||||
else:
|
else:
|
||||||
# if num loads and num slews are not equal then this might break because of how OpenRAM formats
|
# if num loads and num slews are not equal then this might break because of how OpenRAM formats
|
||||||
|
|
@ -222,7 +222,7 @@ def write_to_csv(dataset_name, csv_file, datasheet_fname, imp_mod, mode):
|
||||||
for load,slew in load_slews:
|
for load,slew in load_slews:
|
||||||
multi_values = [val[c] for val in datasheet_multivalues]
|
multi_values = [val[c] for val in datasheet_multivalues]
|
||||||
single_values = [val[1] for val in datasheet_singlevalues]
|
single_values = [val[1] for val in datasheet_singlevalues]
|
||||||
writer.writerow(feature_vals+[slew, load]+multi_values+single_values+[leakage_vals[1]])
|
writer.writerow(feature_vals+[slew, load]+multi_values+single_values+[leakage_vals[1]])
|
||||||
c+=1
|
c+=1
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -244,26 +244,50 @@ def extract_data(openram_dir, out_dir, is_first):
|
||||||
write_to_csv(dataset_name, data_file, datasheet_fname, inp_mod, mode)
|
write_to_csv(dataset_name, data_file, datasheet_fname, inp_mod, mode)
|
||||||
|
|
||||||
return out_dir
|
return out_dir
|
||||||
|
|
||||||
def gen_model_csv(openram_dir_path, out_dir):
|
def gen_model_csv(openram_dir_path, out_dir):
|
||||||
if not os.path.isdir(input_dir_path):
|
if not os.path.isdir(input_dir_path):
|
||||||
print("Path does not exist: {}".format(input_dir_path))
|
print("Path does not exist: {}".format(input_dir_path))
|
||||||
return
|
return
|
||||||
|
|
||||||
if not os.path.isdir(out_path):
|
if not os.path.isdir(out_path):
|
||||||
print("Path does not exist: {}".format(out_path))
|
print("Path does not exist: {}".format(out_path))
|
||||||
return
|
return
|
||||||
|
|
||||||
is_first = True
|
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)]
|
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:
|
for dir in oram_dirs:
|
||||||
extract_data(dir, out_dir, is_first)
|
extract_data(dir, out_dir, is_first)
|
||||||
is_first = False
|
is_first = False
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) < 3:
|
if len(sys.argv) < 3:
|
||||||
print("Usage: python model_data_util.py path_to_openram_dirs out_dir_path")
|
print("Usage: python model_data_util.py path_to_openram_dirs out_dir_path")
|
||||||
else:
|
else:
|
||||||
input_dir_path = sys.argv[1]
|
input_dir_path = sys.argv[1]
|
||||||
out_path = sys.argv[2]
|
out_path = sys.argv[2]
|
||||||
gen_model_csv(input_dir_path, out_path)
|
gen_model_csv(input_dir_path, out_path)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,5 +31,5 @@ class bitcell_1port(bitcell_base):
|
||||||
|
|
||||||
def is_non_inverting(self):
|
def is_non_inverting(self):
|
||||||
"""Return input to output polarity for module"""
|
"""Return input to output polarity for module"""
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -102,5 +102,5 @@ class bitcell_2port(bitcell_base):
|
||||||
|
|
||||||
def is_non_inverting(self):
|
def is_non_inverting(self):
|
||||||
"""Return input to output polarity for module"""
|
"""Return input to output polarity for module"""
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ class bitcell_base(design):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return
|
return
|
||||||
|
|
||||||
def get_all_wl_names(self):
|
def get_all_wl_names(self):
|
||||||
""" Creates a list of all wordline pin names """
|
""" Creates a list of all wordline pin names """
|
||||||
row_pins = ["wl"]
|
row_pins = ["wl"]
|
||||||
|
|
@ -207,39 +207,39 @@ class bitcell_base(design):
|
||||||
is_nchannel = True
|
is_nchannel = True
|
||||||
stack = 2 # for access and inv tx
|
stack = 2 # for access and inv tx
|
||||||
is_cell = False
|
is_cell = False
|
||||||
return self.tr_r_on(drc["minwidth_tx"], is_nchannel, stack, is_cell)
|
return self.tr_r_on(drc["minwidth_tx"], is_nchannel, stack, is_cell)
|
||||||
|
|
||||||
def get_input_capacitance(self):
|
def get_input_capacitance(self):
|
||||||
"""Input cap of input, passes width of gates to gate cap function"""
|
"""Input cap of input, passes width of gates to gate cap function"""
|
||||||
# Input cap of both access TX connected to the wordline
|
# Input cap of both access TX connected to the wordline
|
||||||
return self.gate_c(2*parameter["6T_access_size"])
|
return self.gate_c(2*parameter["6T_access_size"])
|
||||||
|
|
||||||
def get_intrinsic_capacitance(self):
|
def get_intrinsic_capacitance(self):
|
||||||
"""Get the drain capacitances of the TXs in the gate."""
|
"""Get the drain capacitances of the TXs in the gate."""
|
||||||
stack = 1
|
stack = 1
|
||||||
mult = 1
|
mult = 1
|
||||||
# FIXME: Need to define TX sizes of bitcell storage node. Using
|
# FIXME: Need to define TX sizes of bitcell storage node. Using
|
||||||
# min_width as a temp value
|
# min_width as a temp value
|
||||||
|
|
||||||
# Add the inverter drain Cap and the bitline TX drain Cap
|
# Add the inverter drain Cap and the bitline TX drain Cap
|
||||||
nmos_drain_c = self.drain_c_(drc["minwidth_tx"]*mult,
|
nmos_drain_c = self.drain_c_(drc["minwidth_tx"]*mult,
|
||||||
stack,
|
stack,
|
||||||
mult)
|
mult)
|
||||||
pmos_drain_c = self.drain_c_(drc["minwidth_tx"]*mult,
|
pmos_drain_c = self.drain_c_(drc["minwidth_tx"]*mult,
|
||||||
stack,
|
stack,
|
||||||
mult)
|
mult)
|
||||||
|
|
||||||
bl_nmos_drain_c = self.drain_c_(parameter["6T_access_size"],
|
bl_nmos_drain_c = self.drain_c_(parameter["6T_access_size"],
|
||||||
stack,
|
stack,
|
||||||
mult)
|
mult)
|
||||||
|
|
||||||
return nmos_drain_c + pmos_drain_c + bl_nmos_drain_c
|
|
||||||
|
|
||||||
|
return nmos_drain_c + pmos_drain_c + bl_nmos_drain_c
|
||||||
|
|
||||||
def module_wire_c(self):
|
def module_wire_c(self):
|
||||||
"""Capacitance of bitline"""
|
"""Capacitance of bitline"""
|
||||||
# FIXME: entire bitline cap is calculated here because of the current
|
# FIXME: entire bitline cap is calculated here because of the current
|
||||||
# graph implementation so array dims are all re-calculated here. May
|
# graph implementation so array dims are all re-calculated here. May
|
||||||
# be incorrect if dim calculations change
|
# be incorrect if dim calculations change
|
||||||
cells_in_col = OPTS.num_words/OPTS.words_per_row
|
cells_in_col = OPTS.num_words/OPTS.words_per_row
|
||||||
return cells_in_col*self.height*spice["wire_c_per_um"]
|
return cells_in_col*self.height*spice["wire_c_per_um"]
|
||||||
|
|
||||||
|
|
@ -247,15 +247,15 @@ class bitcell_base(design):
|
||||||
"""Resistance of bitline"""
|
"""Resistance of bitline"""
|
||||||
# FIXME: entire bitline r is calculated here because of the current
|
# FIXME: entire bitline r is calculated here because of the current
|
||||||
# graph implementation so array dims are all re-calculated. May
|
# graph implementation so array dims are all re-calculated. May
|
||||||
# be incorrect if dim calculations change
|
# be incorrect if dim calculations change
|
||||||
cells_in_col = OPTS.num_words/OPTS.words_per_row
|
cells_in_col = OPTS.num_words/OPTS.words_per_row
|
||||||
return cells_in_col*self.height*spice["wire_r_per_um"]
|
return cells_in_col*self.height*spice["wire_r_per_um"]
|
||||||
|
|
||||||
def cacti_rc_delay(self, inputramptime, tf, vs1, vs2, rise, extra_param_dict):
|
def cacti_rc_delay(self, inputramptime, tf, vs1, vs2, rise, extra_param_dict):
|
||||||
""" Special RC delay function used by CACTI for bitline delay
|
""" Special RC delay function used by CACTI for bitline delay
|
||||||
"""
|
"""
|
||||||
import math
|
import math
|
||||||
vdd = extra_param_dict['vdd']
|
vdd = extra_param_dict['vdd']
|
||||||
m = vdd / inputramptime #v_wl = vdd for OpenRAM
|
m = vdd / inputramptime #v_wl = vdd for OpenRAM
|
||||||
# vdd == V_b_pre in OpenRAM. Bitline swing is assumed 10% of vdd
|
# vdd == V_b_pre in OpenRAM. Bitline swing is assumed 10% of vdd
|
||||||
tstep = tf * math.log(vdd/(vdd - 0.1*vdd))
|
tstep = tf * math.log(vdd/(vdd - 0.1*vdd))
|
||||||
|
|
@ -264,4 +264,4 @@ class bitcell_base(design):
|
||||||
else:
|
else:
|
||||||
delay = math.sqrt(2*tstep*(vdd-spice["nom_threshold"])/m)
|
delay = math.sqrt(2*tstep*(vdd-spice["nom_threshold"])/m)
|
||||||
|
|
||||||
return delay
|
return delay
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ class bitcell_base_array(design):
|
||||||
if len(self.all_ports) > 1:
|
if len(self.all_ports) > 1:
|
||||||
temp.extend(self.get_rbl_wordline_names(1))
|
temp.extend(self.get_rbl_wordline_names(1))
|
||||||
return temp
|
return temp
|
||||||
|
|
||||||
def add_bitline_pins(self):
|
def add_bitline_pins(self):
|
||||||
bitline_names = self.cell.get_all_bitline_names()
|
bitline_names = self.cell.get_all_bitline_names()
|
||||||
for col in range(self.column_size):
|
for col in range(self.column_size):
|
||||||
|
|
@ -165,7 +165,7 @@ class bitcell_base_array(design):
|
||||||
""" Add the layout pins """
|
""" Add the layout pins """
|
||||||
self.add_bitline_pins()
|
self.add_bitline_pins()
|
||||||
self.add_wl_pins()
|
self.add_wl_pins()
|
||||||
|
|
||||||
def _adjust_x_offset(self, xoffset, col, col_offset):
|
def _adjust_x_offset(self, xoffset, col, col_offset):
|
||||||
tempx = xoffset
|
tempx = xoffset
|
||||||
dir_y = False
|
dir_y = False
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from .bitcell_base import bitcell_base
|
||||||
|
|
||||||
class col_cap_bitcell_1port(bitcell_base):
|
class col_cap_bitcell_1port(bitcell_base):
|
||||||
"""
|
"""
|
||||||
Column end cap cell.
|
Column end cap cell.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name="col_cap_bitcell_1port"):
|
def __init__(self, name="col_cap_bitcell_1port"):
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from .bitcell_base import bitcell_base
|
||||||
|
|
||||||
class col_cap_bitcell_2port(bitcell_base):
|
class col_cap_bitcell_2port(bitcell_base):
|
||||||
"""
|
"""
|
||||||
Column end cap cell.
|
Column end cap cell.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name="col_cap_bitcell_2port"):
|
def __init__(self, name="col_cap_bitcell_2port"):
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ class column_mux_array(design):
|
||||||
# Add the column x offset to find the right select bit
|
# Add the column x offset to find the right select bit
|
||||||
gate_offset = self.mux_inst[col].get_pin("sel").bc()
|
gate_offset = self.mux_inst[col].get_pin("sel").bc()
|
||||||
# use the y offset from the sel pin and the x offset from the gate
|
# use the y offset from the sel pin and the x offset from the gate
|
||||||
|
|
||||||
offset = vector(gate_offset.x,
|
offset = vector(gate_offset.x,
|
||||||
self.get_pin("sel_{}".format(sel_index)).cy())
|
self.get_pin("sel_{}".format(sel_index)).cy())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -757,7 +757,7 @@ class control_logic(design):
|
||||||
else:
|
else:
|
||||||
via_height=None
|
via_height=None
|
||||||
via_width=0
|
via_width=0
|
||||||
|
|
||||||
min_y = min([x.y for x in vdd_pin_locs])
|
min_y = min([x.y for x in vdd_pin_locs])
|
||||||
max_y = max([x.y for x in vdd_pin_locs])
|
max_y = max([x.y for x in vdd_pin_locs])
|
||||||
bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height)
|
bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height)
|
||||||
|
|
|
||||||
|
|
@ -597,7 +597,7 @@ class hierarchical_decoder(design):
|
||||||
for inst in all_insts:
|
for inst in all_insts:
|
||||||
self.copy_layout_pin(inst, "vdd")
|
self.copy_layout_pin(inst, "vdd")
|
||||||
self.copy_layout_pin(inst, "gnd")
|
self.copy_layout_pin(inst, "gnd")
|
||||||
|
|
||||||
self.route_vertical_pins("vdd", self.and_inst, xside="rx",)
|
self.route_vertical_pins("vdd", self.and_inst, xside="rx",)
|
||||||
self.route_vertical_pins("gnd", self.and_inst, xside="lx",)
|
self.route_vertical_pins("gnd", self.and_inst, xside="lx",)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ class local_bitcell_array(bitcell_base_array):
|
||||||
if len(self.all_ports) > 1:
|
if len(self.all_ports) > 1:
|
||||||
wl_offset = vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing,
|
wl_offset = vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing,
|
||||||
self.bitcell_array.get_replica_bottom() + self.wl_array.height + self.cell.height)
|
self.bitcell_array.get_replica_bottom() + self.wl_array.height + self.cell.height)
|
||||||
self.wl_insts[1].place(wl_offset,
|
self.wl_insts[1].place(wl_offset,
|
||||||
mirror="XY")
|
mirror="XY")
|
||||||
|
|
||||||
self.height = self.bitcell_array.height
|
self.height = self.bitcell_array.height
|
||||||
|
|
|
||||||
|
|
@ -80,20 +80,20 @@ class nand2_dec(design):
|
||||||
is_nchannel = True
|
is_nchannel = True
|
||||||
stack = 2
|
stack = 2
|
||||||
is_cell = False
|
is_cell = False
|
||||||
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
||||||
|
|
||||||
def get_input_capacitance(self):
|
def get_input_capacitance(self):
|
||||||
"""Input cap of input, passes width of gates to gate cap function"""
|
"""Input cap of input, passes width of gates to gate cap function"""
|
||||||
return self.gate_c(self.nmos_width+self.pmos_width)
|
return self.gate_c(self.nmos_width+self.pmos_width)
|
||||||
|
|
||||||
def get_intrinsic_capacitance(self):
|
def get_intrinsic_capacitance(self):
|
||||||
"""Get the drain capacitances of the TXs in the gate."""
|
"""Get the drain capacitances of the TXs in the gate."""
|
||||||
nmos_stack = 2
|
nmos_stack = 2
|
||||||
mult = 1
|
mult = 1
|
||||||
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
|
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
|
||||||
nmos_stack,
|
nmos_stack,
|
||||||
mult)
|
mult)
|
||||||
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
|
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
|
||||||
1,
|
1,
|
||||||
mult)
|
mult)
|
||||||
return nmos_drain_c + pmos_drain_c
|
return nmos_drain_c + pmos_drain_c
|
||||||
|
|
|
||||||
|
|
@ -80,20 +80,20 @@ class nand3_dec(design):
|
||||||
is_nchannel = True
|
is_nchannel = True
|
||||||
stack = 3
|
stack = 3
|
||||||
is_cell = False
|
is_cell = False
|
||||||
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
||||||
|
|
||||||
def get_input_capacitance(self):
|
def get_input_capacitance(self):
|
||||||
"""Input cap of input, passes width of gates to gate cap function"""
|
"""Input cap of input, passes width of gates to gate cap function"""
|
||||||
return self.gate_c(self.nmos_width+self.pmos_width)
|
return self.gate_c(self.nmos_width+self.pmos_width)
|
||||||
|
|
||||||
def get_intrinsic_capacitance(self):
|
def get_intrinsic_capacitance(self):
|
||||||
"""Get the drain capacitances of the TXs in the gate."""
|
"""Get the drain capacitances of the TXs in the gate."""
|
||||||
nmos_stack = 3
|
nmos_stack = 3
|
||||||
mult = 1
|
mult = 1
|
||||||
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
|
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
|
||||||
nmos_stack,
|
nmos_stack,
|
||||||
mult)
|
mult)
|
||||||
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
|
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
|
||||||
1,
|
1,
|
||||||
mult)
|
mult)
|
||||||
return nmos_drain_c + pmos_drain_c
|
return nmos_drain_c + pmos_drain_c
|
||||||
|
|
|
||||||
|
|
@ -80,20 +80,20 @@ class nand4_dec(design):
|
||||||
is_nchannel = True
|
is_nchannel = True
|
||||||
stack = 4
|
stack = 4
|
||||||
is_cell = False
|
is_cell = False
|
||||||
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
||||||
|
|
||||||
def get_input_capacitance(self):
|
def get_input_capacitance(self):
|
||||||
"""Input cap of input, passes width of gates to gate cap function"""
|
"""Input cap of input, passes width of gates to gate cap function"""
|
||||||
return self.gate_c(self.nmos_width+self.pmos_width)
|
return self.gate_c(self.nmos_width+self.pmos_width)
|
||||||
|
|
||||||
def get_intrinsic_capacitance(self):
|
def get_intrinsic_capacitance(self):
|
||||||
"""Get the drain capacitances of the TXs in the gate."""
|
"""Get the drain capacitances of the TXs in the gate."""
|
||||||
nmos_stack = 4
|
nmos_stack = 4
|
||||||
mult = 1
|
mult = 1
|
||||||
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
|
nmos_drain_c = self.drain_c_(self.nmos_width*mult,
|
||||||
nmos_stack,
|
nmos_stack,
|
||||||
mult)
|
mult)
|
||||||
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
|
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
|
||||||
1,
|
1,
|
||||||
mult)
|
mult)
|
||||||
return nmos_drain_c + pmos_drain_c
|
return nmos_drain_c + pmos_drain_c
|
||||||
|
|
|
||||||
|
|
@ -317,7 +317,7 @@ class pgate(design):
|
||||||
contact_xoffset = nmos_pos.x + nmos.active_width \
|
contact_xoffset = nmos_pos.x + nmos.active_width \
|
||||||
+ self.active_space
|
+ self.active_space
|
||||||
# Allow an nimplant below it under the rail
|
# Allow an nimplant below it under the rail
|
||||||
contact_yoffset = max(0.5 * self.implant_width + self.implant_enclose_active,
|
contact_yoffset = max(0.5 * self.implant_width + self.implant_enclose_active,
|
||||||
self.get_tx_insts("nmos")[0].by())
|
self.get_tx_insts("nmos")[0].by())
|
||||||
contact_offset = vector(contact_xoffset, contact_yoffset)
|
contact_offset = vector(contact_xoffset, contact_yoffset)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -368,19 +368,19 @@ class pnand4(pgate):
|
||||||
is_nchannel = True
|
is_nchannel = True
|
||||||
stack = 4
|
stack = 4
|
||||||
is_cell = False
|
is_cell = False
|
||||||
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
||||||
|
|
||||||
def get_input_capacitance(self):
|
def get_input_capacitance(self):
|
||||||
"""Input cap of input, passes width of gates to gate cap function"""
|
"""Input cap of input, passes width of gates to gate cap function"""
|
||||||
return self.gate_c(self.nmos_width+self.pmos_width)
|
return self.gate_c(self.nmos_width+self.pmos_width)
|
||||||
|
|
||||||
def get_intrinsic_capacitance(self):
|
def get_intrinsic_capacitance(self):
|
||||||
"""Get the drain capacitances of the TXs in the gate."""
|
"""Get the drain capacitances of the TXs in the gate."""
|
||||||
nmos_stack = 4
|
nmos_stack = 4
|
||||||
nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults,
|
nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults,
|
||||||
nmos_stack,
|
nmos_stack,
|
||||||
self.tx_mults)
|
self.tx_mults)
|
||||||
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
|
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
|
||||||
1,
|
1,
|
||||||
self.tx_mults)
|
self.tx_mults)
|
||||||
return nmos_drain_c + pmos_drain_c
|
return nmos_drain_c + pmos_drain_c
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ class port_address(design):
|
||||||
|
|
||||||
self.copy_layout_pin(self.row_decoder_inst, "vdd")
|
self.copy_layout_pin(self.row_decoder_inst, "vdd")
|
||||||
self.copy_layout_pin(self.row_decoder_inst, "gnd")
|
self.copy_layout_pin(self.row_decoder_inst, "gnd")
|
||||||
|
|
||||||
# Also connect the B input of the RBL and_dec to vdd
|
# Also connect the B input of the RBL and_dec to vdd
|
||||||
if OPTS.local_array_size == 0:
|
if OPTS.local_array_size == 0:
|
||||||
rbl_b_pin = self.rbl_driver_inst.get_pin("B")
|
rbl_b_pin = self.rbl_driver_inst.get_pin("B")
|
||||||
|
|
|
||||||
|
|
@ -51,5 +51,5 @@ class replica_bitcell_1port(bitcell_base):
|
||||||
|
|
||||||
def is_non_inverting(self):
|
def is_non_inverting(self):
|
||||||
"""Return input to output polarity for module"""
|
"""Return input to output polarity for module"""
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -52,5 +52,5 @@ class replica_bitcell_2port(bitcell_base):
|
||||||
|
|
||||||
def is_non_inverting(self):
|
def is_non_inverting(self):
|
||||||
"""Return input to output polarity for module"""
|
"""Return input to output polarity for module"""
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from .bitcell_base import bitcell_base
|
||||||
|
|
||||||
class row_cap_bitcell_1port(bitcell_base):
|
class row_cap_bitcell_1port(bitcell_base):
|
||||||
"""
|
"""
|
||||||
Row end cap cell.
|
Row end cap cell.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name="row_cap_bitcell_1port"):
|
def __init__(self, name="row_cap_bitcell_1port"):
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from .bitcell_base import bitcell_base
|
||||||
|
|
||||||
class row_cap_bitcell_2port(bitcell_base):
|
class row_cap_bitcell_2port(bitcell_base):
|
||||||
"""
|
"""
|
||||||
Row end cap cell.
|
Row end cap cell.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name="row_cap_bitcell_2port"):
|
def __init__(self, name="row_cap_bitcell_2port"):
|
||||||
|
|
|
||||||
|
|
@ -73,48 +73,48 @@ class sense_amp(design):
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
||||||
def is_non_inverting(self):
|
def is_non_inverting(self):
|
||||||
"""Return input to output polarity for module"""
|
"""Return input to output polarity for module"""
|
||||||
|
|
||||||
#FIXME: This only applied to bl/br -> dout and not s_en->dout
|
#FIXME: This only applied to bl/br -> dout and not s_en->dout
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_on_resistance(self):
|
def get_on_resistance(self):
|
||||||
"""On resistance of pinv, defined by single nmos"""
|
"""On resistance of pinv, defined by single nmos"""
|
||||||
is_nchannel = True
|
is_nchannel = True
|
||||||
stack = 1
|
stack = 1
|
||||||
is_cell = False
|
is_cell = False
|
||||||
return self.tr_r_on(parameter["sa_inv_nmos_size"], is_nchannel, stack, is_cell)
|
return self.tr_r_on(parameter["sa_inv_nmos_size"], is_nchannel, stack, is_cell)
|
||||||
|
|
||||||
def get_input_capacitance(self):
|
def get_input_capacitance(self):
|
||||||
"""Input cap of input, passes width of gates to gate cap function"""
|
"""Input cap of input, passes width of gates to gate cap function"""
|
||||||
return self.gate_c(parameter["sa_inv_nmos_size"])
|
return self.gate_c(parameter["sa_inv_nmos_size"])
|
||||||
|
|
||||||
def get_intrinsic_capacitance(self):
|
def get_intrinsic_capacitance(self):
|
||||||
"""Get the drain capacitances of the TXs in the gate."""
|
"""Get the drain capacitances of the TXs in the gate."""
|
||||||
stack = 1
|
stack = 1
|
||||||
mult = 1
|
mult = 1
|
||||||
# Add the inverter drain Cap and the bitline TX drain Cap
|
# Add the inverter drain Cap and the bitline TX drain Cap
|
||||||
nmos_drain_c = self.drain_c_(parameter["sa_inv_nmos_size"]*mult,
|
nmos_drain_c = self.drain_c_(parameter["sa_inv_nmos_size"]*mult,
|
||||||
stack,
|
stack,
|
||||||
mult)
|
mult)
|
||||||
pmos_drain_c = self.drain_c_(parameter["sa_inv_pmos_size"]*mult,
|
pmos_drain_c = self.drain_c_(parameter["sa_inv_pmos_size"]*mult,
|
||||||
stack,
|
stack,
|
||||||
mult)
|
mult)
|
||||||
|
|
||||||
bitline_pmos_size = 8
|
bitline_pmos_size = 8
|
||||||
bl_pmos_drain_c = self.drain_c_(drc("minwidth_tx")*bitline_pmos_size,
|
bl_pmos_drain_c = self.drain_c_(drc("minwidth_tx")*bitline_pmos_size,
|
||||||
stack,
|
stack,
|
||||||
mult)
|
mult)
|
||||||
return nmos_drain_c + pmos_drain_c + bl_pmos_drain_c
|
return nmos_drain_c + pmos_drain_c + bl_pmos_drain_c
|
||||||
|
|
||||||
def cacti_rc_delay(self, inputramptime, tf, vs1, vs2, rise, extra_param_dict):
|
def cacti_rc_delay(self, inputramptime, tf, vs1, vs2, rise, extra_param_dict):
|
||||||
""" Special RC delay function used by CACTI for sense amp delay
|
""" Special RC delay function used by CACTI for sense amp delay
|
||||||
"""
|
"""
|
||||||
import math
|
import math
|
||||||
|
|
||||||
c_senseamp = extra_param_dict['load']
|
c_senseamp = extra_param_dict['load']
|
||||||
vdd = extra_param_dict['vdd']
|
vdd = extra_param_dict['vdd']
|
||||||
tau = c_senseamp/spice["sa_transconductance"]
|
tau = c_senseamp/spice["sa_transconductance"]
|
||||||
return tau*math.log(vdd/(0.1*vdd))
|
return tau*math.log(vdd/(0.1*vdd))
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class sram():
|
||||||
def save(self):
|
def save(self):
|
||||||
""" Save all the output files while reporting time to do it as well. """
|
""" Save all the output files while reporting time to do it as well. """
|
||||||
|
|
||||||
# Import this at the last minute so that the proper tech file
|
# Import this at the last minute so that the proper tech file
|
||||||
# is loaded and the right tools are selected
|
# is loaded and the right tools are selected
|
||||||
import verify
|
import verify
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,7 @@ class sram_1bank(sram_base):
|
||||||
self.add_dnwell(inflate=2.5)
|
self.add_dnwell(inflate=2.5)
|
||||||
|
|
||||||
# Route the supplies together and/or to the ring/stripes.
|
# Route the supplies together and/or to the ring/stripes.
|
||||||
# This is done with the original bbox since the escape routes need to
|
# This is done with the original bbox since the escape routes need to
|
||||||
# be outside of the ring for OpenLane
|
# be outside of the ring for OpenLane
|
||||||
rt = router_tech(self.supply_stack, 1)
|
rt = router_tech(self.supply_stack, 1)
|
||||||
init_bbox = self.get_bbox(side="ring",
|
init_bbox = self.get_bbox(side="ring",
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ class wordline_driver_array(design):
|
||||||
en_pin = self.add_layout_pin_segment_center(text="en",
|
en_pin = self.add_layout_pin_segment_center(text="en",
|
||||||
layer="m2",
|
layer="m2",
|
||||||
start=en_bottom_pos,
|
start=en_bottom_pos,
|
||||||
end=en_top_pos)
|
end=en_top_pos)
|
||||||
|
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
and_inst = self.wld_inst[row]
|
and_inst = self.wld_inst[row]
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ if len(sys.argv) < 2:
|
||||||
print("Usage: {0} file.gds".format(sys.argv[0]))
|
print("Usage: {0} file.gds".format(sys.argv[0]))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
gds_file = sys.argv[1]
|
gds_file = sys.argv[1]
|
||||||
arrayCellLayout = gdsMill.VlsiLayout()
|
arrayCellLayout = gdsMill.VlsiLayout()
|
||||||
reader = gdsMill.Gds2reader(arrayCellLayout,debugToTerminal = 1)
|
reader = gdsMill.Gds2reader(arrayCellLayout,debugToTerminal = 1)
|
||||||
reader.loadFromFile(gds_file)
|
reader.loadFromFile(gds_file)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ struct = layout.structures[layout.rootStructureName]
|
||||||
for text in struct.texts:
|
for text in struct.texts:
|
||||||
print(text.textString)
|
print(text.textString)
|
||||||
text.magFactor=""
|
text.magFactor=""
|
||||||
|
|
||||||
writer = gdsMill.Gds2writer(layout)
|
writer = gdsMill.Gds2writer(layout)
|
||||||
writer.writeToFile(out_gds_file)
|
writer.writeToFile(out_gds_file)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ class grid:
|
||||||
for k in self.map:
|
for k in self.map:
|
||||||
self.map[k].target=False
|
self.map[k].target=False
|
||||||
self.target = set()
|
self.target = set()
|
||||||
|
|
||||||
def set_target(self, n):
|
def set_target(self, n):
|
||||||
if not isinstance(n, vector3d):
|
if not isinstance(n, vector3d):
|
||||||
for item in n:
|
for item in n:
|
||||||
|
|
@ -119,7 +119,7 @@ class grid:
|
||||||
self.map[n].target=True
|
self.map[n].target=True
|
||||||
self.map[n].blocked=False
|
self.map[n].blocked=False
|
||||||
self.target.add(n)
|
self.target.add(n)
|
||||||
|
|
||||||
def add_source(self, track_list):
|
def add_source(self, track_list):
|
||||||
debug.info(3, "Adding source list={0}".format(str(track_list)))
|
debug.info(3, "Adding source list={0}".format(str(track_list)))
|
||||||
for n in track_list:
|
for n in track_list:
|
||||||
|
|
@ -158,7 +158,7 @@ class grid:
|
||||||
for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1):
|
for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1):
|
||||||
for layer in layers:
|
for layer in layers:
|
||||||
perimeter_list.append(vector3d(x, y, layer))
|
perimeter_list.append(vector3d(x, y, layer))
|
||||||
|
|
||||||
if side=="all" or "right" in side:
|
if side=="all" or "right" in side:
|
||||||
for x in range(self.ur.x + offset, self.ur.x + width + offset, 1):
|
for x in range(self.ur.x + offset, self.ur.x + width + offset, 1):
|
||||||
for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1):
|
for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1):
|
||||||
|
|
@ -181,14 +181,14 @@ class grid:
|
||||||
self.add_map(perimeter_list)
|
self.add_map(perimeter_list)
|
||||||
|
|
||||||
return perimeter_list
|
return perimeter_list
|
||||||
|
|
||||||
def add_perimeter_target(self, side="all", layers=[0, 1]):
|
def add_perimeter_target(self, side="all", layers=[0, 1]):
|
||||||
debug.info(3, "Adding perimeter target")
|
debug.info(3, "Adding perimeter target")
|
||||||
|
|
||||||
perimeter_list = self.get_perimeter_list(side, layers)
|
perimeter_list = self.get_perimeter_list(side, layers)
|
||||||
|
|
||||||
self.set_target(perimeter_list)
|
self.set_target(perimeter_list)
|
||||||
|
|
||||||
def is_target(self, point):
|
def is_target(self, point):
|
||||||
"""
|
"""
|
||||||
Point is in the target set, so we are done.
|
Point is in the target set, so we are done.
|
||||||
|
|
@ -213,3 +213,8 @@ class grid:
|
||||||
"""
|
"""
|
||||||
path.set_path(False)
|
path.set_path(False)
|
||||||
path.set_blocked(True)
|
path.set_blocked(True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class grid_cell:
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""
|
"""
|
||||||
Reset the dynamic info about routing.
|
Reset the dynamic info about routing.
|
||||||
"""
|
"""
|
||||||
self.min_cost=-1
|
self.min_cost=-1
|
||||||
self.min_path=None
|
self.min_path=None
|
||||||
|
|
@ -49,3 +49,4 @@ class grid_cell:
|
||||||
type_string += "P"
|
type_string += "P"
|
||||||
|
|
||||||
return type_string
|
return type_string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ class pin_group:
|
||||||
def add_pin(self, pin):
|
def add_pin(self, pin):
|
||||||
self.pins.add(pin)
|
self.pins.add(pin)
|
||||||
self.remove_redundant_pins()
|
self.remove_redundant_pins()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
""" override repr function output """
|
""" override repr function output """
|
||||||
return str(self)
|
return str(self)
|
||||||
|
|
@ -641,13 +641,13 @@ class pin_group:
|
||||||
# way than blockages.
|
# way than blockages.
|
||||||
blockages = sufficient | insufficient | blockage_in_tracks
|
blockages = sufficient | insufficient | blockage_in_tracks
|
||||||
self.blockages.update(blockages)
|
self.blockages.update(blockages)
|
||||||
|
|
||||||
# If we have a blockage, we must remove the grids
|
# If we have a blockage, we must remove the grids
|
||||||
# Remember, this excludes the pin blockages already
|
# Remember, this excludes the pin blockages already
|
||||||
blocked_grids = self.router.get_blocked_grids()
|
blocked_grids = self.router.get_blocked_grids()
|
||||||
pin_set.difference_update(blocked_grids)
|
pin_set.difference_update(blocked_grids)
|
||||||
partial_set.difference_update(blocked_grids)
|
partial_set.difference_update(blocked_grids)
|
||||||
|
|
||||||
# At least one of the groups must have some valid tracks
|
# At least one of the groups must have some valid tracks
|
||||||
if (len(pin_set) == 0 and len(partial_set) == 0):
|
if (len(pin_set) == 0 and len(partial_set) == 0):
|
||||||
# debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
|
# debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class router(router_tech):
|
||||||
route on top of this. The blockages from the gds/module will be
|
route on top of this. The blockages from the gds/module will be
|
||||||
considered.
|
considered.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
router_tech.__init__(self, layers, route_track_width)
|
router_tech.__init__(self, layers, route_track_width)
|
||||||
|
|
||||||
self.cell = design
|
self.cell = design
|
||||||
|
|
@ -91,7 +91,7 @@ class router(router_tech):
|
||||||
|
|
||||||
def get_bbox(self):
|
def get_bbox(self):
|
||||||
return self.bbox
|
return self.bbox
|
||||||
|
|
||||||
def create_routing_grid(self, router_type=None):
|
def create_routing_grid(self, router_type=None):
|
||||||
"""
|
"""
|
||||||
Create (or recreate) a sprase routing grid with A* expansion functions.
|
Create (or recreate) a sprase routing grid with A* expansion functions.
|
||||||
|
|
@ -178,7 +178,7 @@ class router(router_tech):
|
||||||
self.reader.loadFromFile(self.gds_filename)
|
self.reader.loadFromFile(self.gds_filename)
|
||||||
self.top_name = self.layout.rootStructureName
|
self.top_name = self.layout.rootStructureName
|
||||||
# print_time("GDS read",datetime.now(), start_time)
|
# print_time("GDS read",datetime.now(), start_time)
|
||||||
|
|
||||||
# This finds the pin shapes and sorts them into "groups" that
|
# This finds the pin shapes and sorts them into "groups" that
|
||||||
# are connected. This must come before the blockages, so we
|
# are connected. This must come before the blockages, so we
|
||||||
# can not count the pins themselves
|
# can not count the pins themselves
|
||||||
|
|
@ -374,7 +374,7 @@ class router(router_tech):
|
||||||
def set_supply_rail_blocked(self, value):
|
def set_supply_rail_blocked(self, value):
|
||||||
# This is just a virtual function
|
# This is just a virtual function
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def prepare_blockages(self, src=None, dest=None):
|
def prepare_blockages(self, src=None, dest=None):
|
||||||
"""
|
"""
|
||||||
Reset and add all of the blockages in the design.
|
Reset and add all of the blockages in the design.
|
||||||
|
|
@ -384,7 +384,7 @@ class router(router_tech):
|
||||||
|
|
||||||
# Start fresh. Not the best for run-time, but simpler.
|
# Start fresh. Not the best for run-time, but simpler.
|
||||||
self.clear_all_blockages()
|
self.clear_all_blockages()
|
||||||
|
|
||||||
# This adds the initial blockges of the design
|
# This adds the initial blockges of the design
|
||||||
# which includes all blockages due to non-pin shapes
|
# which includes all blockages due to non-pin shapes
|
||||||
# print("BLOCKING:", self.blocked_grids)
|
# print("BLOCKING:", self.blocked_grids)
|
||||||
|
|
@ -457,7 +457,7 @@ class router(router_tech):
|
||||||
"""
|
"""
|
||||||
blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.blockages}
|
blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.blockages}
|
||||||
self.set_blockages(blockage_grids, False)
|
self.set_blockages(blockage_grids, False)
|
||||||
|
|
||||||
def clear_all_blockages(self):
|
def clear_all_blockages(self):
|
||||||
"""
|
"""
|
||||||
Clear all blockages on the grid.
|
Clear all blockages on the grid.
|
||||||
|
|
@ -498,7 +498,7 @@ class router(router_tech):
|
||||||
self.blocked_grids.update(blockage_list)
|
self.blocked_grids.update(blockage_list)
|
||||||
|
|
||||||
def get_blocked_grids(self):
|
def get_blocked_grids(self):
|
||||||
"""
|
"""
|
||||||
Return the blocked grids with their flag set
|
Return the blocked grids with their flag set
|
||||||
"""
|
"""
|
||||||
#return set([x for x in self.blocked_grids if self.rg.is_blocked(x)])
|
#return set([x for x in self.blocked_grids if self.rg.is_blocked(x)])
|
||||||
|
|
@ -518,7 +518,7 @@ class router(router_tech):
|
||||||
new_shape = pin_layout("blockage{}".format(len(self.blockages)),
|
new_shape = pin_layout("blockage{}".format(len(self.blockages)),
|
||||||
rect,
|
rect,
|
||||||
lpp)
|
lpp)
|
||||||
|
|
||||||
# If there is a rectangle that is the same in the pins,
|
# If there is a rectangle that is the same in the pins,
|
||||||
# it isn't a blockage!
|
# it isn't a blockage!
|
||||||
if new_shape not in self.all_pins and not self.pin_contains(new_shape):
|
if new_shape not in self.all_pins and not self.pin_contains(new_shape):
|
||||||
|
|
@ -529,7 +529,7 @@ class router(router_tech):
|
||||||
if pin.contains(shape):
|
if pin.contains(shape):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def convert_point_to_units(self, p):
|
def convert_point_to_units(self, p):
|
||||||
"""
|
"""
|
||||||
Convert a path set of tracks to center line path.
|
Convert a path set of tracks to center line path.
|
||||||
|
|
@ -543,7 +543,7 @@ class router(router_tech):
|
||||||
Convert a wave to a set of center points
|
Convert a wave to a set of center points
|
||||||
"""
|
"""
|
||||||
return [self.convert_point_to_units(i) for i in wave]
|
return [self.convert_point_to_units(i) for i in wave]
|
||||||
|
|
||||||
def convert_shape_to_tracks(self, shape):
|
def convert_shape_to_tracks(self, shape):
|
||||||
"""
|
"""
|
||||||
Convert a rectangular shape into track units.
|
Convert a rectangular shape into track units.
|
||||||
|
|
@ -767,7 +767,7 @@ class router(router_tech):
|
||||||
"""
|
"""
|
||||||
for t in tracks:
|
for t in tracks:
|
||||||
debug.check(t[2] == tracks[0][2], "Different layers used.")
|
debug.check(t[2] == tracks[0][2], "Different layers used.")
|
||||||
|
|
||||||
# For each shape, convert it to a pin
|
# For each shape, convert it to a pin
|
||||||
pins = [self.convert_track_to_pin(t) for t in tracks]
|
pins = [self.convert_track_to_pin(t) for t in tracks]
|
||||||
# Now find the bounding box
|
# Now find the bounding box
|
||||||
|
|
@ -777,10 +777,10 @@ class router(router_tech):
|
||||||
maxy = max([p.uy() for p in pins])
|
maxy = max([p.uy() for p in pins])
|
||||||
ll = vector(minx, miny)
|
ll = vector(minx, miny)
|
||||||
ur = vector(maxx, maxy)
|
ur = vector(maxx, maxy)
|
||||||
|
|
||||||
p = pin_layout("", [ll, ur], self.get_layer(tracks[0][2]))
|
p = pin_layout("", [ll, ur], self.get_layer(tracks[0][2]))
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def convert_track_to_shape_pin(self, track):
|
def convert_track_to_shape_pin(self, track):
|
||||||
"""
|
"""
|
||||||
Convert a grid point into a rectangle shape
|
Convert a grid point into a rectangle shape
|
||||||
|
|
@ -977,7 +977,7 @@ class router(router_tech):
|
||||||
self.pin_groups[name].append(pg)
|
self.pin_groups[name].append(pg)
|
||||||
|
|
||||||
self.new_pins[name] = pg.pins
|
self.new_pins[name] = pg.pins
|
||||||
|
|
||||||
def add_ring_supply_pin(self, name, width=3, space=3):
|
def add_ring_supply_pin(self, name, width=3, space=3):
|
||||||
"""
|
"""
|
||||||
Adds a ring supply pin that goes outside the given bbox.
|
Adds a ring supply pin that goes outside the given bbox.
|
||||||
|
|
@ -1011,7 +1011,7 @@ class router(router_tech):
|
||||||
layers=[0]))
|
layers=[0]))
|
||||||
|
|
||||||
horizontal_layer_grids = left_grids | right_grids
|
horizontal_layer_grids = left_grids | right_grids
|
||||||
|
|
||||||
# Must move to the same layer to find layer 1 corner grids
|
# Must move to the same layer to find layer 1 corner grids
|
||||||
vertical_layer_grids = set()
|
vertical_layer_grids = set()
|
||||||
for x in top_grids | bottom_grids:
|
for x in top_grids | bottom_grids:
|
||||||
|
|
@ -1027,7 +1027,7 @@ class router(router_tech):
|
||||||
pg.grids = (left_grids | right_grids | top_grids | bottom_grids)
|
pg.grids = (left_grids | right_grids | top_grids | bottom_grids)
|
||||||
pg.enclosures = pg.compute_enclosures()
|
pg.enclosures = pg.compute_enclosures()
|
||||||
pg.pins = set(pg.enclosures)
|
pg.pins = set(pg.enclosures)
|
||||||
|
|
||||||
self.cell.pin_map[name].update(pg.pins)
|
self.cell.pin_map[name].update(pg.pins)
|
||||||
self.pin_groups[name].append(pg)
|
self.pin_groups[name].append(pg)
|
||||||
self.new_pins[name] = pg.pins
|
self.new_pins[name] = pg.pins
|
||||||
|
|
@ -1043,7 +1043,7 @@ class router(router_tech):
|
||||||
|
|
||||||
def get_new_pins(self, name):
|
def get_new_pins(self, name):
|
||||||
return self.new_pins[name]
|
return self.new_pins[name]
|
||||||
|
|
||||||
def add_perimeter_target(self, side="all"):
|
def add_perimeter_target(self, side="all"):
|
||||||
"""
|
"""
|
||||||
This will mark all the cells on the perimeter of the original layout as a target.
|
This will mark all the cells on the perimeter of the original layout as a target.
|
||||||
|
|
@ -1206,7 +1206,7 @@ class router(router_tech):
|
||||||
closest_track_pin, closest_part_pin = self.find_closest_pin(track_pins, offgrid_pin_parts)
|
closest_track_pin, closest_part_pin = self.find_closest_pin(track_pins, offgrid_pin_parts)
|
||||||
|
|
||||||
debug.check(closest_track_pin and closest_part_pin, "Found no closest pins.")
|
debug.check(closest_track_pin and closest_part_pin, "Found no closest pins.")
|
||||||
|
|
||||||
# Find the bbox of the on-grid track and the off-grid pin part
|
# Find the bbox of the on-grid track and the off-grid pin part
|
||||||
closest_track_pin.bbox([closest_part_pin])
|
closest_track_pin.bbox([closest_part_pin])
|
||||||
|
|
||||||
|
|
@ -1313,10 +1313,10 @@ class router(router_tech):
|
||||||
|
|
||||||
self.paths.append(grid_utils.flatten_set(path))
|
self.paths.append(grid_utils.flatten_set(path))
|
||||||
self.add_route(path)
|
self.add_route(path)
|
||||||
self.create_route_connector(path,
|
self.create_route_connector(path,
|
||||||
self.source_name,
|
self.source_name,
|
||||||
self.source_components)
|
self.source_components)
|
||||||
self.create_route_connector(path,
|
self.create_route_connector(path,
|
||||||
self.target_name,
|
self.target_name,
|
||||||
self.target_components)
|
self.target_components)
|
||||||
self.path_blockages.append(self.paths[-1])
|
self.path_blockages.append(self.paths[-1])
|
||||||
|
|
@ -1404,7 +1404,7 @@ class router(router_tech):
|
||||||
self.cell.add_label(text="{0},{1}".format(g[0], g[1]),
|
self.cell.add_label(text="{0},{1}".format(g[0], g[1]),
|
||||||
layer="text",
|
layer="text",
|
||||||
offset=shape[0])
|
offset=shape[0])
|
||||||
|
|
||||||
def del_router_info(self):
|
def del_router_info(self):
|
||||||
"""
|
"""
|
||||||
Erase all of the comments on the current level.
|
Erase all of the comments on the current level.
|
||||||
|
|
@ -1489,7 +1489,7 @@ class router(router_tech):
|
||||||
# Else if we came from a different layer, we can only add
|
# Else if we came from a different layer, we can only add
|
||||||
# a signle grid
|
# a signle grid
|
||||||
return self.convert_track_to_pin(v)
|
return self.convert_track_to_pin(v)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_ll_pin(self, pin_name):
|
def get_ll_pin(self, pin_name):
|
||||||
|
|
@ -1503,9 +1503,9 @@ class router(router_tech):
|
||||||
else:
|
else:
|
||||||
if pin.lx() <= keep_pin.lx() and pin.by() <= keep_pin.by():
|
if pin.lx() <= keep_pin.lx() and pin.by() <= keep_pin.by():
|
||||||
keep_pin = pin
|
keep_pin = pin
|
||||||
|
|
||||||
return keep_pin
|
return keep_pin
|
||||||
|
|
||||||
def check_all_routed(self, pin_name):
|
def check_all_routed(self, pin_name):
|
||||||
"""
|
"""
|
||||||
Check that all pin groups are routed.
|
Check that all pin groups are routed.
|
||||||
|
|
@ -1513,8 +1513,8 @@ class router(router_tech):
|
||||||
for pg in self.pin_groups[pin_name]:
|
for pg in self.pin_groups[pin_name]:
|
||||||
if not pg.is_routed():
|
if not pg.is_routed():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
# FIXME: This should be replaced with vector.snap_to_grid at some point
|
# FIXME: This should be replaced with vector.snap_to_grid at some point
|
||||||
def snap_to_grid(offset):
|
def snap_to_grid(offset):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class signal_escape_router(router):
|
||||||
y_dist = min(loc.y - self.ll.y, self.ur.y - loc.y)
|
y_dist = min(loc.y - self.ll.y, self.ur.y - loc.y)
|
||||||
|
|
||||||
return min(x_dist, y_dist)
|
return min(x_dist, y_dist)
|
||||||
|
|
||||||
def escape_route(self, pin_names):
|
def escape_route(self, pin_names):
|
||||||
"""
|
"""
|
||||||
Takes a list of tuples (name, side) and routes them. After routing,
|
Takes a list of tuples (name, side) and routes them. After routing,
|
||||||
|
|
@ -52,7 +52,7 @@ class signal_escape_router(router):
|
||||||
# Order the routes by closest to the perimeter first
|
# Order the routes by closest to the perimeter first
|
||||||
# This prevents some pins near the perimeter from being blocked by other pins
|
# This prevents some pins near the perimeter from being blocked by other pins
|
||||||
ordered_pin_names = sorted(pin_names, key=lambda x: self.perimeter_dist(x))
|
ordered_pin_names = sorted(pin_names, key=lambda x: self.perimeter_dist(x))
|
||||||
|
|
||||||
# Route the supply pins to the supply rails
|
# Route the supply pins to the supply rails
|
||||||
# Route vdd first since we want it to be shorter
|
# Route vdd first since we want it to be shorter
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
|
|
@ -60,18 +60,18 @@ class signal_escape_router(router):
|
||||||
self.route_signal(pin_name)
|
self.route_signal(pin_name)
|
||||||
# if pin_name == "dout0[1]":
|
# if pin_name == "dout0[1]":
|
||||||
# self.write_debug_gds("postroute.gds", True)
|
# self.write_debug_gds("postroute.gds", True)
|
||||||
|
|
||||||
print_time("Maze routing pins",datetime.now(), start_time, 3)
|
print_time("Maze routing pins",datetime.now(), start_time, 3)
|
||||||
|
|
||||||
#self.write_debug_gds("final_escape_router.gds",False)
|
#self.write_debug_gds("final_escape_router.gds",False)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def route_signal(self, pin_name, side="all"):
|
def route_signal(self, pin_name, side="all"):
|
||||||
|
|
||||||
for detour_scale in [5 * pow(2, x) for x in range(5)]:
|
for detour_scale in [5 * pow(2, x) for x in range(5)]:
|
||||||
debug.info(1, "Escape routing {0} with scale {1}".format(pin_name, detour_scale))
|
debug.info(1, "Escape routing {0} with scale {1}".format(pin_name, detour_scale))
|
||||||
|
|
||||||
# Clear everything in the routing grid.
|
# Clear everything in the routing grid.
|
||||||
self.rg.reinit()
|
self.rg.reinit()
|
||||||
|
|
||||||
|
|
@ -86,11 +86,11 @@ class signal_escape_router(router):
|
||||||
|
|
||||||
# Marks the grid cells all along the perimeter as a target
|
# Marks the grid cells all along the perimeter as a target
|
||||||
self.add_perimeter_target(side)
|
self.add_perimeter_target(side)
|
||||||
|
|
||||||
# if pin_name == "dout0[3]":
|
# if pin_name == "dout0[3]":
|
||||||
# self.write_debug_gds("pre_route.gds", False)
|
# self.write_debug_gds("pre_route.gds", False)
|
||||||
# breakpoint()
|
# breakpoint()
|
||||||
|
|
||||||
# Actually run the A* router
|
# Actually run the A* router
|
||||||
if self.run_router(detour_scale=detour_scale):
|
if self.run_router(detour_scale=detour_scale):
|
||||||
new_pin = self.get_perimeter_pin()
|
new_pin = self.get_perimeter_pin()
|
||||||
|
|
@ -100,5 +100,7 @@ class signal_escape_router(router):
|
||||||
# if pin_name == "dout0[3]":
|
# if pin_name == "dout0[3]":
|
||||||
# self.write_debug_gds("pre_route.gds", False)
|
# self.write_debug_gds("pre_route.gds", False)
|
||||||
# breakpoint()
|
# breakpoint()
|
||||||
|
|
||||||
self.write_debug_gds("debug_route.gds", True)
|
self.write_debug_gds("debug_route.gds", True)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,10 @@ class supply_grid(signal_grid):
|
||||||
|
|
||||||
def reinit(self):
|
def reinit(self):
|
||||||
""" Reinitialize everything for a new route. """
|
""" Reinitialize everything for a new route. """
|
||||||
|
|
||||||
self.source = set()
|
self.source = set()
|
||||||
self.target = set()
|
self.target = set()
|
||||||
|
|
||||||
# Reset all the cells in the map
|
# Reset all the cells in the map
|
||||||
for p in self.map.values():
|
for p in self.map.values():
|
||||||
p.reset()
|
p.reset()
|
||||||
|
|
@ -77,3 +77,5 @@ class supply_grid(signal_grid):
|
||||||
wave = wave_path.neighbor(direct)
|
wave = wave_path.neighbor(direct)
|
||||||
|
|
||||||
return wave_path
|
return wave_path
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,14 +66,14 @@ class supply_grid_router(router):
|
||||||
# Block everything
|
# Block everything
|
||||||
self.prepare_blockages()
|
self.prepare_blockages()
|
||||||
self.clear_blockages(self.gnd_name)
|
self.clear_blockages(self.gnd_name)
|
||||||
|
|
||||||
|
|
||||||
# Determine the rail locations
|
# Determine the rail locations
|
||||||
self.route_supply_rails(self.gnd_name, 0)
|
self.route_supply_rails(self.gnd_name, 0)
|
||||||
|
|
||||||
# Block everything
|
# Block everything
|
||||||
self.prepare_blockages()
|
self.prepare_blockages()
|
||||||
self.clear_blockages(self.vdd_name)
|
self.clear_blockages(self.vdd_name)
|
||||||
# Determine the rail locations
|
# Determine the rail locations
|
||||||
self.route_supply_rails(self.vdd_name, 1)
|
self.route_supply_rails(self.vdd_name, 1)
|
||||||
print_time("Routing supply rails", datetime.now(), start_time, 3)
|
print_time("Routing supply rails", datetime.now(), start_time, 3)
|
||||||
|
|
@ -359,7 +359,7 @@ class supply_grid_router(router):
|
||||||
# easier to debug.
|
# easier to debug.
|
||||||
self.prepare_blockages()
|
self.prepare_blockages()
|
||||||
self.clear_blockages(self.vdd_name)
|
self.clear_blockages(self.vdd_name)
|
||||||
|
|
||||||
# Add the single component of the pin as the source
|
# Add the single component of the pin as the source
|
||||||
# which unmarks it as a blockage too
|
# which unmarks it as a blockage too
|
||||||
self.add_pin_component_source(pin_name, index)
|
self.add_pin_component_source(pin_name, index)
|
||||||
|
|
@ -392,3 +392,4 @@ class supply_grid_router(router):
|
||||||
debug.info(4, "Blocking supply rail")
|
debug.info(4, "Blocking supply rail")
|
||||||
for rail_name in self.supply_rail_tracks:
|
for rail_name in self.supply_rail_tracks:
|
||||||
self.rg.set_blocked(self.supply_rail_tracks[rail_name])
|
self.rg.set_blocked(self.supply_rail_tracks[rail_name])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ class code_format_test(openram_test):
|
||||||
continue
|
continue
|
||||||
errors += check_file_format_tab(code)
|
errors += check_file_format_tab(code)
|
||||||
errors += check_file_format_carriage(code)
|
errors += check_file_format_carriage(code)
|
||||||
errors += check_file_format_whitespace(code)
|
|
||||||
|
|
||||||
for code in source_codes:
|
for code in source_codes:
|
||||||
if re.search("gdsMill", code):
|
if re.search("gdsMill", code):
|
||||||
|
|
@ -52,7 +51,7 @@ def setup_files(path):
|
||||||
files = []
|
files = []
|
||||||
for (dir, _, current_files) in os.walk(path):
|
for (dir, _, current_files) in os.walk(path):
|
||||||
for f in current_files:
|
for f in current_files:
|
||||||
files.append(os.path.join(dir, f))
|
files.append(os.getenv("OPENRAM_HOME"))
|
||||||
nametest = re.compile("\.py$", re.IGNORECASE)
|
nametest = re.compile("\.py$", re.IGNORECASE)
|
||||||
select_files = list(filter(nametest.search, files))
|
select_files = list(filter(nametest.search, files))
|
||||||
return select_files
|
return select_files
|
||||||
|
|
@ -93,35 +92,13 @@ def check_file_format_carriage(file_name):
|
||||||
if len(key_positions)>10:
|
if len(key_positions)>10:
|
||||||
line_numbers = key_positions[:10] + [" ..."]
|
line_numbers = key_positions[:10] + [" ..."]
|
||||||
else:
|
else:
|
||||||
line_numbers = key_positions
|
line_numbers = key_positoins
|
||||||
debug.info(0, '\nFound ' + str(len(key_positions)) + ' carriage returns in ' +
|
debug.info(0, '\nFound ' + str(len(key_positions)) + ' carriage returns in ' +
|
||||||
str(file_name) + ' (lines ' + ",".join(str(x) for x in line_numbers) + ')')
|
str(file_name) + ' (lines ' + ",".join(str(x) for x in line_numbers) + ')')
|
||||||
f.close()
|
f.close()
|
||||||
return len(key_positions)
|
return len(key_positions)
|
||||||
|
|
||||||
|
|
||||||
def check_file_format_whitespace(file_name):
|
|
||||||
"""
|
|
||||||
Check if file contains a line with whitespace at the end
|
|
||||||
and return the number of these lines.
|
|
||||||
"""
|
|
||||||
|
|
||||||
f = open(file_name, "r")
|
|
||||||
key_positions = []
|
|
||||||
for num, line in enumerate(f.readlines()):
|
|
||||||
if re.match(r".*[ \t]$", line):
|
|
||||||
key_positions.append(num)
|
|
||||||
if len(key_positions) > 0:
|
|
||||||
if len(key_positions) > 10:
|
|
||||||
line_numbers = key_positions[:10] + [" ..."]
|
|
||||||
else:
|
|
||||||
line_numbers = key_positions
|
|
||||||
debug.info(0, "\nFound " + str(len(key_positions)) + " ending whitespace in " +
|
|
||||||
str(file_name) + " (lines " + ",".join(str(x) for x in line_numbers) + ")")
|
|
||||||
f.close()
|
|
||||||
return len(key_positions)
|
|
||||||
|
|
||||||
|
|
||||||
def check_print_output(file_name):
|
def check_print_output(file_name):
|
||||||
"""Check if any files (except debug.py) call the _print_ function. We should
|
"""Check if any files (except debug.py) call the _print_ function. We should
|
||||||
use the debug output with verbosity instead!"""
|
use the debug output with verbosity instead!"""
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class library_lvs_test(openram_test):
|
||||||
self.assertEqual(drc_errors + lvs_errors, 0)
|
self.assertEqual(drc_errors + lvs_errors, 0)
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
|
|
||||||
def setup_files():
|
def setup_files():
|
||||||
gds_dir = OPTS.openram_tech + "/gds_lib"
|
gds_dir = OPTS.openram_tech + "/gds_lib"
|
||||||
sp_dir = OPTS.openram_tech + "/lvs_lib"
|
sp_dir = OPTS.openram_tech + "/lvs_lib"
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class replica_column_test(openram_test):
|
||||||
debug.info(2, "Testing one right replica column for dual port")
|
debug.info(2, "Testing one right replica column for dual port")
|
||||||
a = factory.create(module_type="replica_column", rows=4, rbl=[0, 1], replica_bit=5)
|
a = factory.create(module_type="replica_column", rows=4, rbl=[0, 1], replica_bit=5)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
debug.info(2, "Testing two (left, right) replica columns for dual port")
|
debug.info(2, "Testing two (left, right) replica columns for dual port")
|
||||||
a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=1)
|
a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=1)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
@ -40,7 +40,7 @@ class replica_column_test(openram_test):
|
||||||
debug.info(2, "Testing two (left, right) replica columns for dual port")
|
debug.info(2, "Testing two (left, right) replica columns for dual port")
|
||||||
a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=6)
|
a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=6)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
# run the test from the command line
|
# run the test from the command line
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ class port_data_spare_cols_test(openram_test):
|
||||||
OPTS.num_r_ports = 1
|
OPTS.num_r_ports = 1
|
||||||
OPTS.num_w_ports = 1
|
OPTS.num_w_ports = 1
|
||||||
globals.setup_bitcell()
|
globals.setup_bitcell()
|
||||||
|
|
||||||
c.num_words=16
|
c.num_words=16
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
factory.reset()
|
factory.reset()
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class psingle_bank_test(openram_test):
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
OPTS.num_r_ports = 0
|
OPTS.num_r_ports = 0
|
||||||
globals.setup_bitcell()
|
globals.setup_bitcell()
|
||||||
|
|
||||||
c = sram_config(word_size=4,
|
c = sram_config(word_size=4,
|
||||||
num_words=16)
|
num_words=16)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
|
|
||||||
class psram_1bank_2mux_1rw_1w_test(openram_test):
|
class psram_1bank_2mux_1rw_1w_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ class psram_1bank_2mux_1rw_1w_wmask_test(openram_test):
|
||||||
OPTS.num_w_ports = 1
|
OPTS.num_w_ports = 1
|
||||||
OPTS.num_r_ports = 0
|
OPTS.num_r_ports = 0
|
||||||
globals.setup_bitcell()
|
globals.setup_bitcell()
|
||||||
|
|
||||||
c = sram_config(word_size=8,
|
c = sram_config(word_size=8,
|
||||||
write_size=4,
|
write_size=4,
|
||||||
num_words=32,
|
num_words=32,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class sram_1bank_4mux_1rw_1r_test(openram_test):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
from modules import sram_config
|
from modules import sram_config
|
||||||
|
|
||||||
OPTS.num_rw_ports = 1
|
OPTS.num_rw_ports = 1
|
||||||
OPTS.num_r_ports = 1
|
OPTS.num_r_ports = 1
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ class timing_sram_test(openram_test):
|
||||||
'slew_hl': [2.039655],
|
'slew_hl': [2.039655],
|
||||||
'slew_lh': [2.039655],
|
'slew_lh': [2.039655],
|
||||||
'write0_power': [19.31883],
|
'write0_power': [19.31883],
|
||||||
'write1_power': [15.297369999999999]}
|
'write1_power': [15.297369999999999]}
|
||||||
else:
|
else:
|
||||||
self.assertTrue(False) # other techs fail
|
self.assertTrue(False) # other techs fail
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class regression_model_test(openram_test):
|
||||||
debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data))
|
debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data))
|
||||||
|
|
||||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||||
|
|
||||||
#m = linear_regression(s.s, tempspice, corner)
|
#m = linear_regression(s.s, tempspice, corner)
|
||||||
m = neural_network(s.s, tempspice, corner)
|
m = neural_network(s.s, tempspice, corner)
|
||||||
only_test = ['rise_delay']
|
only_test = ['rise_delay']
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ class psram_1bank_2mux_func_test(openram_test):
|
||||||
OPTS.analytical_delay = False
|
OPTS.analytical_delay = False
|
||||||
OPTS.netlist_only = True
|
OPTS.netlist_only = True
|
||||||
OPTS.trim_netlist = False
|
OPTS.trim_netlist = False
|
||||||
|
|
||||||
OPTS.bitcell = "pbitcell"
|
OPTS.bitcell = "pbitcell"
|
||||||
OPTS.replica_bitcell="replica_pbitcell"
|
OPTS.replica_bitcell="replica_pbitcell"
|
||||||
OPTS.dummy_bitcell="dummy_pbitcell"
|
OPTS.dummy_bitcell="dummy_pbitcell"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class psram_1bank_4mux_func_test(openram_test):
|
||||||
OPTS.analytical_delay = False
|
OPTS.analytical_delay = False
|
||||||
OPTS.netlist_only = True
|
OPTS.netlist_only = True
|
||||||
OPTS.trim_netlist = False
|
OPTS.trim_netlist = False
|
||||||
|
|
||||||
OPTS.bitcell = "pbitcell"
|
OPTS.bitcell = "pbitcell"
|
||||||
OPTS.replica_bitcell="replica_pbitcell"
|
OPTS.replica_bitcell="replica_pbitcell"
|
||||||
OPTS.dummy_bitcell="dummy_pbitcell"
|
OPTS.dummy_bitcell="dummy_pbitcell"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class psram_1bank_8mux_func_test(openram_test):
|
||||||
OPTS.analytical_delay = False
|
OPTS.analytical_delay = False
|
||||||
OPTS.netlist_only = True
|
OPTS.netlist_only = True
|
||||||
OPTS.trim_netlist = False
|
OPTS.trim_netlist = False
|
||||||
|
|
||||||
OPTS.bitcell = "pbitcell"
|
OPTS.bitcell = "pbitcell"
|
||||||
OPTS.replica_bitcell="replica_pbitcell"
|
OPTS.replica_bitcell="replica_pbitcell"
|
||||||
OPTS.dummy_bitcell="dummy_pbitcell"
|
OPTS.dummy_bitcell="dummy_pbitcell"
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ class psram_1bank_nomux_func_test(openram_test):
|
||||||
OPTS.analytical_delay = False
|
OPTS.analytical_delay = False
|
||||||
OPTS.netlist_only = True
|
OPTS.netlist_only = True
|
||||||
OPTS.trim_netlist = False
|
OPTS.trim_netlist = False
|
||||||
|
|
||||||
OPTS.bitcell = "pbitcell"
|
OPTS.bitcell = "pbitcell"
|
||||||
OPTS.replica_bitcell="replica_pbitcell"
|
OPTS.replica_bitcell="replica_pbitcell"
|
||||||
OPTS.dummy_bitcell="dummy_pbitcell"
|
OPTS.dummy_bitcell="dummy_pbitcell"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class sram_1bank_2mux_func_test(openram_test):
|
||||||
OPTS.analytical_delay = False
|
OPTS.analytical_delay = False
|
||||||
OPTS.netlist_only = True
|
OPTS.netlist_only = True
|
||||||
OPTS.trim_netlist = False
|
OPTS.trim_netlist = False
|
||||||
|
|
||||||
# This is a hack to reload the characterizer __init__ with the spice version
|
# This is a hack to reload the characterizer __init__ with the spice version
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
import characterizer
|
import characterizer
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class sram_1bank_2mux_func_test(openram_test):
|
||||||
OPTS.analytical_delay = False
|
OPTS.analytical_delay = False
|
||||||
OPTS.netlist_only = True
|
OPTS.netlist_only = True
|
||||||
OPTS.trim_netlist = False
|
OPTS.trim_netlist = False
|
||||||
|
|
||||||
# This is a hack to reload the characterizer __init__ with the spice version
|
# This is a hack to reload the characterizer __init__ with the spice version
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
import characterizer
|
import characterizer
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class sram_1bank_4mux_func_test(openram_test):
|
||||||
OPTS.analytical_delay = False
|
OPTS.analytical_delay = False
|
||||||
OPTS.netlist_only = True
|
OPTS.netlist_only = True
|
||||||
OPTS.trim_netlist = False
|
OPTS.trim_netlist = False
|
||||||
|
|
||||||
# This is a hack to reload the characterizer __init__ with the spice version
|
# This is a hack to reload the characterizer __init__ with the spice version
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
import characterizer
|
import characterizer
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class sram_1bank_8mux_func_test(openram_test):
|
||||||
OPTS.analytical_delay = False
|
OPTS.analytical_delay = False
|
||||||
OPTS.netlist_only = True
|
OPTS.netlist_only = True
|
||||||
OPTS.trim_netlist = False
|
OPTS.trim_netlist = False
|
||||||
|
|
||||||
# This is a hack to reload the characterizer __init__ with the spice version
|
# This is a hack to reload the characterizer __init__ with the spice version
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
import characterizer
|
import characterizer
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,8 @@ class ngspice_pex_pinv_test(openram_test):
|
||||||
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
|
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
|
||||||
test_sim.run_sim("stim.sp")
|
test_sim.run_sim("stim.sp")
|
||||||
delay = parse_spice_list(log_file_name, "pinv_delay")
|
delay = parse_spice_list(log_file_name, "pinv_delay")
|
||||||
|
|
||||||
os.chdir(cwd)
|
os.chdir(cwd)
|
||||||
return delay
|
return delay
|
||||||
|
|
||||||
def write_simulation(self, sim_file, cir_file, top_module_name):
|
def write_simulation(self, sim_file, cir_file, top_module_name):
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class openram_back_end_test(openram_test):
|
||||||
filename = "{0}{1}".format(out_path, out_file)
|
filename = "{0}{1}".format(out_path, out_file)
|
||||||
debug.info(1, "Checking for file: " + filename)
|
debug.info(1, "Checking for file: " + filename)
|
||||||
self.assertEqual(os.path.exists(filename), True)
|
self.assertEqual(os.path.exists(filename), True)
|
||||||
|
|
||||||
# Make sure there is any .lib file
|
# Make sure there is any .lib file
|
||||||
import glob
|
import glob
|
||||||
files = glob.glob('{0}/*.lib'.format(out_path))
|
files = glob.glob('{0}/*.lib'.format(out_path))
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,9 @@ def fork_tests(num_threads):
|
||||||
results = []
|
results = []
|
||||||
test_partitions = partition_unit_tests(suite, num_threads)
|
test_partitions = partition_unit_tests(suite, num_threads)
|
||||||
suite._tests[:] = []
|
suite._tests[:] = []
|
||||||
|
|
||||||
def do_fork(suite):
|
def do_fork(suite):
|
||||||
|
|
||||||
for test_partition in test_partitions:
|
for test_partition in test_partitions:
|
||||||
test_suite = unittest.TestSuite(test_partition)
|
test_suite = unittest.TestSuite(test_partition)
|
||||||
test_partition[:] = []
|
test_partition[:] = []
|
||||||
|
|
@ -103,9 +103,9 @@ if num_threads == 1:
|
||||||
final_suite = suite
|
final_suite = suite
|
||||||
else:
|
else:
|
||||||
final_suite = ConcurrentTestSuite(suite, fork_tests(num_threads))
|
final_suite = ConcurrentTestSuite(suite, fork_tests(num_threads))
|
||||||
|
|
||||||
test_result = test_runner.run(final_suite)
|
test_result = test_runner.run(final_suite)
|
||||||
|
|
||||||
# import verify
|
# import verify
|
||||||
# verify.print_drc_stats()
|
# verify.print_drc_stats()
|
||||||
# verify.print_lvs_stats()
|
# verify.print_lvs_stats()
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,8 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
|
||||||
f.write("#!/bin/sh\n")
|
f.write("#!/bin/sh\n")
|
||||||
f.write("assura {0} 2> {1} 1> {2}\n".format(drc_runset, drc_log_file, drc_log_file))
|
f.write("assura {0} 2> {1} 1> {2}\n".format(drc_runset, drc_log_file, drc_log_file))
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def run_drc(name, gds_name, final_verification=False):
|
def run_drc(name, gds_name, final_verification=False):
|
||||||
"""Run DRC check on a given top-level name which is
|
"""Run DRC check on a given top-level name which is
|
||||||
implemented in gds_name."""
|
implemented in gds_name."""
|
||||||
|
|
@ -85,7 +85,7 @@ def run_drc(name, gds_name, final_verification=False):
|
||||||
write_drc_script(name, gds_name, True, final_verification, OPTS.openram_temp)
|
write_drc_script(name, gds_name, True, final_verification, OPTS.openram_temp)
|
||||||
|
|
||||||
(outfile, errfile, resultsfile) = run_script(name, "drc")
|
(outfile, errfile, resultsfile) = run_script(name, "drc")
|
||||||
|
|
||||||
# count and report errors
|
# count and report errors
|
||||||
errors = 0
|
errors = 0
|
||||||
try:
|
try:
|
||||||
|
|
@ -168,7 +168,7 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification, output_pa
|
||||||
f.write("assura {0} 2> {1} 1> {2}\n".format(lvs_runset, lvs_log_file, lvs_log_file))
|
f.write("assura {0} 2> {1} 1> {2}\n".format(lvs_runset, lvs_log_file, lvs_log_file))
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def run_lvs(name, gds_name, sp_name, final_verification=False):
|
def run_lvs(name, gds_name, sp_name, final_verification=False):
|
||||||
"""Run LVS check on a given top-level name which is
|
"""Run LVS check on a given top-level name which is
|
||||||
implemented in gds_name and sp_name. """
|
implemented in gds_name and sp_name. """
|
||||||
|
|
@ -179,7 +179,7 @@ def run_lvs(name, gds_name, sp_name, final_verification=False):
|
||||||
write_lvs_script(name, gds_name, sp_name, final_verification, OPTS.openram_temp)
|
write_lvs_script(name, gds_name, sp_name, final_verification, OPTS.openram_temp)
|
||||||
|
|
||||||
(outfile, errfile, resultsfile) = run_script(name, "drc")
|
(outfile, errfile, resultsfile) = run_script(name, "drc")
|
||||||
|
|
||||||
errors = 0
|
errors = 0
|
||||||
try:
|
try:
|
||||||
f = open(OPTS.openram_temp + name + ".csm", "r")
|
f = open(OPTS.openram_temp + name + ".csm", "r")
|
||||||
|
|
@ -205,14 +205,14 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
|
||||||
global num_pex_runs
|
global num_pex_runs
|
||||||
num_pex_runs += 1
|
num_pex_runs += 1
|
||||||
|
|
||||||
|
|
||||||
def print_drc_stats():
|
def print_drc_stats():
|
||||||
debug.info(1, "DRC runs: {0}".format(num_drc_runs))
|
debug.info(1, "DRC runs: {0}".format(num_drc_runs))
|
||||||
|
|
||||||
|
|
||||||
def print_lvs_stats():
|
def print_lvs_stats():
|
||||||
debug.info(1, "LVS runs: {0}".format(num_lvs_runs))
|
debug.info(1, "LVS runs: {0}".format(num_lvs_runs))
|
||||||
|
|
||||||
|
|
||||||
def print_pex_stats():
|
def print_pex_stats():
|
||||||
debug.info(1, "PEX runs: {0}".format(num_pex_runs))
|
debug.info(1, "PEX runs: {0}".format(num_pex_runs))
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ def write_drc_script(cell_name, gds_name, extract, final_verification=False, out
|
||||||
|
|
||||||
if not output_path:
|
if not output_path:
|
||||||
output_path = OPTS.openram_temp
|
output_path = OPTS.openram_temp
|
||||||
|
|
||||||
from tech import drc
|
from tech import drc
|
||||||
drc_rules = drc["drc_rules"]
|
drc_rules = drc["drc_rules"]
|
||||||
|
|
||||||
|
|
@ -125,7 +125,7 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out
|
||||||
f = open(run_file, "w")
|
f = open(run_file, "w")
|
||||||
f.write("#!/bin/sh\n")
|
f.write("#!/bin/sh\n")
|
||||||
cmd = "{0} -gui -lvs lvs_runset -batch".format(OPTS.lvs_exe[1])
|
cmd = "{0} -gui -lvs lvs_runset -batch".format(OPTS.lvs_exe[1])
|
||||||
|
|
||||||
f.write(cmd)
|
f.write(cmd)
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
f.close()
|
f.close()
|
||||||
|
|
@ -139,7 +139,7 @@ def write_pex_script(cell_name, extract, output, final_verification=False, outpu
|
||||||
|
|
||||||
if not output_path:
|
if not output_path:
|
||||||
output_path = OPTS.openram_temp
|
output_path = OPTS.openram_temp
|
||||||
|
|
||||||
if not output:
|
if not output:
|
||||||
output = cell_name + ".pex.sp"
|
output = cell_name + ".pex.sp"
|
||||||
|
|
||||||
|
|
@ -443,14 +443,14 @@ def correct_port(name, output_file_name, ref_file_name):
|
||||||
output_file.write(part2)
|
output_file.write(part2)
|
||||||
output_file.close()
|
output_file.close()
|
||||||
|
|
||||||
|
|
||||||
def print_drc_stats():
|
def print_drc_stats():
|
||||||
debug.info(1, "DRC runs: {0}".format(num_drc_runs))
|
debug.info(1, "DRC runs: {0}".format(num_drc_runs))
|
||||||
|
|
||||||
|
|
||||||
def print_lvs_stats():
|
def print_lvs_stats():
|
||||||
debug.info(1, "LVS runs: {0}".format(num_lvs_runs))
|
debug.info(1, "LVS runs: {0}".format(num_lvs_runs))
|
||||||
|
|
||||||
|
|
||||||
def print_pex_stats():
|
def print_pex_stats():
|
||||||
debug.info(1, "PEX runs: {0}".format(num_pex_runs))
|
debug.info(1, "PEX runs: {0}".format(num_pex_runs))
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out
|
||||||
if os.path.exists(full_setup_file):
|
if os.path.exists(full_setup_file):
|
||||||
# Copy setup.tcl file into temp dir
|
# Copy setup.tcl file into temp dir
|
||||||
shutil.copy(full_setup_file, output_path)
|
shutil.copy(full_setup_file, output_path)
|
||||||
|
|
||||||
setup_file_object = open(output_path + "/setup.tcl", 'a')
|
setup_file_object = open(output_path + "/setup.tcl", 'a')
|
||||||
setup_file_object.write("# Increase the column sizes for ease of reading long names\n")
|
setup_file_object.write("# Increase the column sizes for ease of reading long names\n")
|
||||||
setup_file_object.write("::netgen::format 120\n")
|
setup_file_object.write("::netgen::format 120\n")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue