import math
import numpy as np
from tqdm import tqdm
from scalesim.topology_utils import topologies as topoutil
from scalesim.scale_config import scale_config as cfg
# This class defines data types for operand matrices
[docs]
class operand_matrix(object):
"""
Class which creates the ifmap, filter and ofmap operand matrices to be used in compute simulation
"""
def __init__(self):
"""
The constructor method for the class
"""
# Objects from outer container classes
self.config = cfg()
self.topoutil = topoutil()
# Layer hyper parameters
self.layer_id = 0
self.ifmap_rows, self.ifmap_cols = 1, 1
self.filter_rows, self.filter_cols = 1, 1
self.num_input_channels, self.num_filters = 1, 1
self.row_stride, self.col_stride = 1, 1
self.batch_size = 1
# Derived hyper parameters
self.ofmap_px_per_filt, self.conv_window_size = 1, 1
self.ofmap_rows, self.ofmap_cols = 1, 1
# Offsets
self.ifmap_offset, self.filter_offset, self.ofmap_offset = 0, 10000000, 20000000
self.matrix_offset_arr = [0, 10000000, 20000000]
# Address matrices
self.ifmap_addr_matrix = np.ones((self.ofmap_px_per_filt, self.conv_window_size), dtype=int)
self.filter_addr_matrix = np.ones((self.conv_window_size, self.num_filters), dtype=int)
self.ofmap_addr_matrix = np.ones((self.ofmap_px_per_filt, self.num_filters), dtype=int)
# Flags
self.params_set_flag = False
self.matrices_ready_flag = False
#
[docs]
def set_params(self,
config_obj,
topoutil_obj,
layer_id=0,
):
"""
Method to set the operand matrix parameters for housekeeping.
:param config_obj: Object of scalesim.scale_config which is used to lookup the architecture and \
run parameters set by the user
:param topoutil_obj: Object of scalesim.topology_utils which is used to preprocess the workload dimensions
:param layer_id: Layer number of the workload
:return: None
"""
self.config = config_obj
self.topoutil = topoutil_obj
self.layer_id = layer_id
# TODO: Marked for cleanup
#my_name = 'operand_matrix.set_params(): '
#err_prefix = 'Error: ' + my_name
#
#if (not len(layer_hyper_param_arr) == 7 and not len(layer_hyper_param_arr) == 8
# and not len(layer_hyper_param_arr) == 9) or (not len(layer_calc_hyper_param_arr) == 4) \
# or (not len(self.matrix_offset_arr) == 3):
# message = err_prefix + 'Invalid arguments. Exiting.'
# print(message)
# return -1
self.ifmap_rows, self.ifmap_cols = self.topoutil.get_layer_ifmap_dims(self.layer_id)
self.filter_rows, self.filter_cols = self.topoutil.get_layer_filter_dims(self.layer_id)
self.num_input_channels = self.topoutil.get_layer_num_channels(self.layer_id)
self.num_filters = self.topoutil.get_layer_num_filters(self.layer_id)
self.row_stride, self.col_stride = self.topoutil.get_layer_strides(self.layer_id)
# TODO: Marked for cleanup
#self.row_stride = layer_hyper_param_arr[6]
#if len(layer_hyper_param_arr) == 8:
# self.col_stride = layer_hyper_param_arr[7]
# TODO: Anand
# TODO: Next release
# TODO: Add an option for batching
self.batch_size = 1
# TODO: Marked for cleanup
#if len(layer_hyper_param_arr) == 9:
# self.batch_size = layer_hyper_param_arr[8]
# Assign the calculated hyper parameters
self.ofmap_rows, self.ofmap_cols = self.topoutil.get_layer_ofmap_dims(self.layer_id)
self.ofmap_rows = int(self.ofmap_rows)
self.ofmap_cols = int(self.ofmap_cols)
self.ofmap_px_per_filt = int(self.ofmap_rows * self.ofmap_cols)
self.conv_window_size = int(self.topoutil.get_layer_window_size(self.layer_id))
# Assign the offsets
self.ifmap_offset, self.filter_offset, self.ofmap_offset \
= self.config.get_offsets()
# Address matrices: This is needed to take into account the updated dimensions
self.ifmap_addr_matrix = np.ones((self.ofmap_px_per_filt * self.batch_size, self.conv_window_size), dtype='>i4')
self.filter_addr_matrix = np.ones((self.conv_window_size, self.num_filters), dtype='>i4')
self.ofmap_addr_matrix = np.ones((self.ofmap_px_per_filt, self.num_filters), dtype='>i4')
self.params_set_flag = True
# TODO: This should be called from top level
# TODO: Implement get() function for getting the matrix
# TODO: Marked for cleanup
# Return 0 if operand matrix generation is successful
#self.create_operand_matrices()
#if self.matrices_ready_flag:
# return True, self.ifmap_addr_matrix, self.filter_addr_matrix, self.ofmap_addr_matrix
#else:
# message = err_prefix + 'Address Matrices not created. Exiting!'
# print(message)
# return False, None, None, None
# top level function to create the operand matrices
[docs]
def create_operand_matrices(self):
"""
Method to create ifmap, filter and ofmap operand matrices.
:return: None
"""
my_name = 'operand_matrix.create_operand_matrices(): '
err_prefix = 'Error: ' + my_name
if not self.params_set_flag:
message = err_prefix + 'Parameters not set yet. Run set_params(). Exiting'
print(message)
return -1
retcode_1 = self.create_ifmap_matrix()
retcode_2 = self.create_filter_matrix()
retcode_3 = self.create_ofmap_matrix()
retcode = retcode_1 + retcode_2 + retcode_3
if retcode == 0:
self.matrices_ready_flag = True
return retcode
# creates the ifmap operand
[docs]
def create_ifmap_matrix(self):
"""
Method to create ifmap operand matrix.
:return: None
"""
my_name = 'operand_matrix.create_ifmap_matrix(): '
err_prefix = 'Error: ' + my_name
if not self.params_set_flag:
message = err_prefix + 'Parameters not set yet. Run set_params(). Exiting'
print(message)
return -1
# for row_idx in tqdm(range(self.batch_size*self.ofmap_px_per_filt)):
for row_idx in range(self.batch_size*self.ofmap_px_per_filt):
for col_idx in range(self.conv_window_size):
self.ifmap_addr_matrix[row_idx][col_idx] = self.calc_ifmap_elem_addr(i=row_idx, j=col_idx)
return 0
# logic to translate ifmap into matrix fed into systolic array MACs
[docs]
def calc_ifmap_elem_addr(self, i, j):
"""
Method to calculate the address of an ifmap element.
:return: Ifmap pixel address
"""
offset = self.ifmap_offset
ifmap_cols = self.ifmap_cols
filter_col = self.filter_cols
r_stride = self.row_stride
c_stride = self.col_stride
Ew = self.ofmap_cols
channel = self.num_input_channels
# Calculate the row and col in the Eh X Ew mat
ofmap_row = int(math.floor(i / Ew))
ofmap_col = int(i % Ew)
# Change this to corresponding ifmap row col for the start of the conv window
i_row = ofmap_row * r_stride
i_col = ofmap_col * c_stride
# Starting address of the convolution window
window_addr = i_row * ifmap_cols * channel + i_col * channel
# Calculate the row and col in the conv window
c_row = int(math.floor(j / (filter_col * channel)))
k = int(j % (filter_col * channel))
c_col = int(math.floor(k / channel))
c_ch = int(k % channel)
if c_row + i_row >= self.ifmap_rows or c_col + i_col >= self.ifmap_cols: # for padded address
ifmap_px_addr = -1
else:
internal_address = c_row * (ifmap_cols * channel) + c_col * channel + c_ch # Address inside conv window
ifmap_px_addr = internal_address + window_addr + offset # Global address
return ifmap_px_addr
# creates the ofmap operand
[docs]
def create_ofmap_matrix(self):
"""
Method to create ofmap operand matrix.
:return: None
"""
my_name = 'operand_matrix.create_ofmap_matrix(): '
err_prefix = 'Error: ' + my_name
if not self.params_set_flag:
message = err_prefix + 'Parameters not set yet. Run set_params(). Exiting'
print(message)
return -1
# for row_idx in tqdm(range(self.ofmap_px_per_filt)):
for row_idx in range(self.ofmap_px_per_filt):
for col_idx in range(self.num_filters):
self.ofmap_addr_matrix[row_idx][col_idx] = self.calc_ofmap_elem_addr(i=row_idx, j=col_idx)
return 0
# logic to translate ofmap into matrix resulting systolic array MACs
[docs]
def calc_ofmap_elem_addr(self, i, j):
"""
Method to calculate the address of an ofmap element.
:return: Ifmap pixel address
"""
offset = self.ofmap_offset
num_filt = self.num_filters
internal_address = num_filt * i + j
ofmap_px_addr = internal_address + offset
return ofmap_px_addr
# creates the filter operand
[docs]
def create_filter_matrix(self):
"""
Method to create filter operand matrix.
:return: None
"""
my_name = 'operand_matrix.create_filter_matrix(): '
err_prefix = 'Error: ' + my_name
if not self.params_set_flag:
message = err_prefix + 'Parameters not set yet. Run set_params(). Exiting'
print(message)
return -1
# for row_idx in tqdm(range(self.conv_window_size)):
for row_idx in range(self.conv_window_size):
for col_idx in range(self.num_filters):
self.filter_addr_matrix[row_idx][col_idx] = self.calc_filter_elem_addr(i=row_idx, j=col_idx)
return 0
# logic to translate filter into matrix fed into systolic array MACs
[docs]
def calc_filter_elem_addr(self, i, j):
"""
Method to calculate the address of a filter element.
:return: Filter pixel address
"""
offset = self.filter_offset
filter_row = self.filter_rows
filter_col = self.filter_cols
channel = self.num_input_channels
internal_address = j * filter_row * filter_col * channel + i
filter_px_addr = internal_address + offset
return filter_px_addr
# function to get a part or the full ifmap operand
[docs]
def get_ifmap_matrix_part(self, start_row=0, num_rows=-1, start_col=0,
num_cols=-1):
"""
Method to get a part or full ifmap operand matrix if no error. If error, return the error code.
:param start_row: Start row index
:param num_rows: Number of rows in the output matrix
:param start_col: Start col index
:param num_cols: Number of columns in the output matrix
:return: Error code, A part or full ifmap operand matrix
"""
if num_rows == -1:
num_rows = self.ofmap_px_per_filt
if num_cols == -1:
num_cols = self.conv_window_size
my_name = 'operand_matrix.get_ifmap_matrix_part(): '
err_prefix = 'Error: ' + my_name
if not self.matrices_ready_flag:
if self.params_set_flag:
self.create_operand_matrices()
else:
message = err_prefix + ": Parameters not set yet. Run set_params(). Exiting!"
print(message)
return -1, np.zeros((1, 1))
if (start_row + num_rows) > self.ofmap_px_per_filt or (start_col + num_cols) > self.conv_window_size:
message = err_prefix + ": Illegal arguments. Exiting!"
print(message)
return -2, np.zeros((1, 1))
# Anand: ISSUE #3. Patch
#end_row = start_row + num_rows + 1
#end_col = start_col + num_cols + 1
#ret_mat = self.ifmap_addr_matrix[start_row: end_row][start_col: end_col]
end_row = start_row + num_rows
end_col = start_col + num_cols
ret_mat = self.ifmap_addr_matrix[start_row: end_row, start_col: end_col]
return 0, ret_mat
[docs]
def get_ifmap_matrix(self):
"""
Method to get ifmap operand matrix.
:return: Ifmap operand matrix
"""
return self.get_ifmap_matrix_part()
# function to get a part or the full filter operand
[docs]
def get_filter_matrix_part(self, start_row=0, num_rows=-1, start_col=0,
num_cols=-1):
"""
Method to get a part or full filter operand matrix if no error. If error, return the error code.
:param start_row: Start row index
:param num_rows: Number of rows in the output matrix
:param start_col: Start col index
:param num_cols: Number of columns in the output matrix
:return: Error code, A part or full filter operand matrix
"""
if num_rows == -1:
num_rows = self.conv_window_size
if num_cols == -1:
num_cols = self.num_filters
my_name = 'operand_matrix.get_filter_matrix_part(): '
err_prefix = 'Error: ' + my_name
if not self.matrices_ready_flag:
if self.params_set_flag:
self.create_operand_matrices()
else:
message = err_prefix + ": Parameters not set yet. Run set_params(). Exiting!"
print(message)
return -1, np.zeros((1, 1))
if (start_row + num_rows) > self.conv_window_size or (start_col + num_cols) > self.num_filters:
message = err_prefix + ": Illegal arguments. Exiting!"
print(message)
return -2, np.zeros((1, 1))
# Anand: ISSUE #3. FIX
#end_row = start_row + num_rows + 1
#end_col = start_col + num_cols + 1
end_row = start_row + num_rows
end_col = start_col + num_cols
# Anand: ISSUE #3. FIX
#ret_mat = self.filter_addr_matrix[start_row: end_row][start_col: end_col]
ret_mat = self.filter_addr_matrix[start_row: end_row, start_col: end_col]
return 0, ret_mat
[docs]
def get_filter_matrix(self):
"""
Method to get filter operand matrix.
:return: Filter operand matrix
"""
return self.get_filter_matrix_part()
# function to get a part or the full ofmap operand
[docs]
def get_ofmap_matrix_part(self, start_row=0, num_rows=-1, start_col=0,
num_cols=-1):
"""
Method to get a part or full ofmap operand matrix if no error. If error, return the error code.
:param start_row: Start row index
:param num_rows: Number of rows in the output matrix
:param start_col: Start col index
:param num_cols: Number of columns in the output matrix
:return: Error code, A part or full ofmap operand matrix
"""
# Since we cannot pass self as an argument in the member functions
# This is an alternate way of making the matrix dimensions as defaults
if num_rows == -1:
num_rows = self.ofmap_px_per_filt
if num_cols == -1:
num_cols = self.num_filters
my_name = 'operand_matrix.get_ofmap_matrix_part(): '
err_prefix = 'Error: ' + my_name
if not self.matrices_ready_flag:
if self.params_set_flag:
self.create_operand_matrices()
else:
message = err_prefix + ": Parameters not set yet. Run set_params(). Exiting!"
print(message)
return -1, np.zeros((1, 1))
if (start_row + num_rows) > self.ofmap_px_per_filt or (start_col + num_cols) > self.num_filters:
message = err_prefix + ": Illegal arguments. Exiting!"
print(message)
return -2, np.zeros((1, 1))
# Anand: ISSUE #3. Patch
#end_row = start_row + num_rows + 1
#end_col = start_col + num_cols + 1
#ret_mat = self.filter_addr_matrix[start_row: end_row][start_col: end_col]
end_row = start_row + num_rows
end_col = start_col + num_cols
# Anand: ISSUE #7. Patch
#ret_mat = self.filter_addr_matrix[start_row: end_row, start_col: end_col]
ret_mat = self.ofmap_addr_matrix[start_row: end_row, start_col: end_col]
return 0, ret_mat
[docs]
def get_ofmap_matrix(self):
"""
Method to get ofmap operand matrix.
:return: Ofmap operand matrix
"""
return self.get_ofmap_matrix_part()
[docs]
def get_all_operand_matrix(self):
"""
Method to get ifmap, filter and ofmap operand matrices.
:return: Ifmap, filter and ofmap operand matrices
"""
if not self.matrices_ready_flag:
me = 'operand_matrix.' + 'get_all_operand_matrix()'
message = 'ERROR:' + me + ': Matrices not ready or matrix gen failed'
print(message)
return
return self.ifmap_addr_matrix, \
self.filter_addr_matrix, \
self.ofmap_addr_matrix
if __name__ == '__main__':
opmat = operand_matrix()
tutil = topoutil()
lid = 3
topology_file = "../../topologies/mlperf/test.csv"
tutil.load_arrays(topofile=topology_file)
for i in range(tutil.get_num_layers()):
layer_param_arr = tutil.get_layer_params(layer_id=i)
ofmap_dims = tutil.get_layer_ofmap_dims(layer_id=i)
ofmap_px_filt = tutil.get_layer_num_ofmap_px(layer_id=i) / tutil.get_layer_num_filters(layer_id=i)
conv_window_size = tutil.get_layer_window_size(layer_id=i)
layer_calc_hyper_param_arr = [ofmap_dims[0], ofmap_dims[1], ofmap_px_filt, conv_window_size]
config_arr = [512, 512, 256, 8, 8]
#[matrix_set, ifmap_addr_matrix, filter_addr_matrix, ofmap_addr_matrix] \
# = opmat.set_params(layer_hyper_param_arr=layer_param_arr[1:],
# layer_calc_hyper_param_arr=layer_calc_hyper_param_arr,
# offset_list=[0, 1000000, 2000000])