Skip to content

Commit

Permalink
INT8 MLP example design for RTL/SystemC co-simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
boutros6 committed Nov 16, 2023
1 parent 285822f commit 53fc99e
Show file tree
Hide file tree
Showing 47 changed files with 3,163 additions and 8 deletions.
4 changes: 2 additions & 2 deletions rad-sim/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,10 @@ def prepare_build_dir(design_name):
"noc_num_packet_types": [3],
"noc_num_nodes": [0],
"noc_max_num_router_dest_interfaces": 32,
"interfaces_max_axis_tdata_width": 1024,
"interfaces_max_axis_tdata_width": 512,
"interfaces_axis_tkeep_width": 8,
"interfaces_axis_tstrb_width": 8,
"interfaces_axis_tuser_width": 66,
"interfaces_axis_tuser_width": 75,
"interfaces_axi_id_width": 8,
"interfaces_axi_user_width": 64,
"interfaces_max_axi_data_width": 512,
Expand Down
6 changes: 6 additions & 0 deletions rad-sim/example-designs/mlp_int8/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
compiler/input_mifs/*.mif
compiler/inst_mifs/*.mif
compiler/weight_mifs/*.mif
golden_outputs.mif
layer_mvm_config
*.pdf
52 changes: 52 additions & 0 deletions rad-sim/example-designs/mlp_int8/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.16)
find_package(SystemCLanguage CONFIG REQUIRED)

include_directories(
./
modules
../../sim
../../sim/noc
../../sim/noc/booksim
../../sim/noc/booksim/networks
../../sim/noc/booksim/routers
)

set(srcfiles
modules/fifo.cpp
modules/register_file.cpp
modules/sim_utils.cpp
modules/instructions.cpp
modules/dispatcher.cpp
modules/collector.cpp
modules/datapath.cpp
modules/pipeline.cpp
modules/mvm.cpp
modules/weight_loader.cpp
modules/inst_loader.cpp
mlp_top.cpp
mlp_driver.cpp
mlp_int8_system.cpp
)

set(hdrfiles
modules/fifo.hpp
modules/register_file.hpp
modules/sim_utils.hpp
modules/instructions.hpp
modules/dispatcher.hpp
modules/collector.hpp
modules/datapath.hpp
modules/pipeline.hpp
modules/mvm.hpp
modules/weight_loader.hpp
modules/inst_loader.hpp
modules/config.hpp
mlp_top.hpp
mlp_driver.hpp
mlp_int8_system.hpp
)

add_compile_options(-Wall -Wextra -pedantic)

add_library(design STATIC ${srcfiles} ${hdrfiles})
target_link_libraries(design PUBLIC SystemC::systemc booksim noc)
2 changes: 2 additions & 0 deletions rad-sim/example-designs/mlp_int8/compiler/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.mif
layer_mvm_config
244 changes: 244 additions & 0 deletions rad-sim/example-designs/mlp_int8/compiler/gen_testcase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import sys
import math
import os
import glob
import numpy as np

# Defining constants
native_dim = 64
num_test_inputs = 1
num_noc_routers = 16

if ('-h' in sys.argv or '--help' in sys.argv):
print('python gen_testcase.py <num_layers> <input_dim> [<hidden_dims>] [<mvms_per_layer>]')
exit(1)

# Parse command line arguments
num_layers = int(sys.argv[1])
print('Number of layers = ' + str(num_layers))
input_dim = int(sys.argv[2])
print('Input dimension = ' + str(input_dim))
hidden_dims = np.zeros(shape=num_layers, dtype=int)
print('Hidden dimensions = [ ', end='')
for i in range(3, 3 + num_layers):
hidden_dims[i-3] = int(sys.argv[i])
print(str(hidden_dims[i-3]) + ' ', end='')
print(']')
num_mvms = np.ones(shape=num_layers, dtype=int)
print('Number of MVMs = [ ', end='')
for i in range(3 + num_layers, 3 + 2 * num_layers):
num_mvms[i-3-num_layers] = int(sys.argv[i])
print(str(num_mvms[i-3-num_layers]) + ' ', end='')
print(']')

# Generate placement and clock constraint files
placement_file = open('../mlp.place', 'w')
clocks_file = open('../mlp.clks', 'w')
placement_dict = {}

total_num_blocks = 0
for mvm_count in num_mvms:
total_num_blocks = total_num_blocks + mvm_count
total_num_blocks = total_num_blocks + num_mvms[0] + 3
if (total_num_blocks > num_noc_routers):
print("Number of blocks is more than NoC routers!")
exit(1)
router_ids = np.random.choice(num_noc_routers, size=total_num_blocks, replace=False)

idx = 0
for l in range(num_layers):
for m in range(num_mvms[l]):
mvm_name = 'layer' + str(l) + '_mvm' + str(m)
placement_file.write(mvm_name + ' 0 ' + str(router_ids[idx]) + ' axis\n')
placement_dict[mvm_name] = router_ids[idx]
idx = idx + 1
clocks_file.write(mvm_name + ' 0 0\n')

for m in range(num_mvms[0]):
dispatcher_name = 'input_dispatcher' + str(m)
placement_file.write(dispatcher_name + ' 0 ' + str(router_ids[idx]) + ' axis\n')
placement_dict[dispatcher_name] = router_ids[idx]
idx = idx + 1
clocks_file.write(dispatcher_name + ' 0 0\n')

placement_file.write('output_collector 0 ' + str(router_ids[idx]) + ' axis\n')
placement_dict['output_collector'] = router_ids[idx]
idx = idx + 1
clocks_file.write('output_collector 0 0\n')

placement_file.write('weight_loader 0 ' + str(router_ids[idx]) + ' axis\n')
placement_dict['weight_loader'] = router_ids[idx]
idx = idx + 1
clocks_file.write('weight_loader 0 0\n')

placement_file.write('inst_loader 0 ' + str(router_ids[idx]) + ' axis\n')
placement_dict['inst_loader'] = router_ids[idx]
idx = idx + 1
clocks_file.write('inst_loader 0 0\n')

placement_file.close()
clocks_file.close()

# Generate random padded weight matrices
padded_weights = []
for l in range(num_layers):
num_mvms_in = num_mvms[l]
if (l == num_layers-1):
num_mvms_out = 1 #num_mvms[0]
else:
num_mvms_out = num_mvms[l+1]
if (l == 0):
layer_input_dim = input_dim
else:
layer_input_dim = hidden_dims[l-1]
padded_dimx = int(math.ceil(layer_input_dim * 1.0 / native_dim / num_mvms_in) * native_dim * num_mvms_in)
padded_dimy = int(math.ceil(hidden_dims[l] * 1.0 / native_dim / num_mvms_out) * native_dim * num_mvms_out)
padded_weights.append(np.zeros(shape=(padded_dimy, padded_dimx), dtype=int))
padded_weights[l][:hidden_dims[l], :layer_input_dim] = np.random.randint(-2, 2, size=(hidden_dims[l], layer_input_dim))

# Prepare weight MIFs directory
if(not(os.path.exists('./weight_mifs'))):
os.mkdir('weight_mifs')
else:
files = glob.glob('weight_mifs/*.mif')
for file in files:
os.remove(file)

# Write weight MIFs
for l in range(num_layers):
layer_mvms = num_mvms[l]
mvm_idx = 0
limx = int(padded_weights[l].shape[1] / native_dim)
limy = int(padded_weights[l].shape[0] / native_dim)
mifs = []
for m in range(layer_mvms):
mifs.append([])
for d in range(native_dim):
mifs[m].append(open('weight_mifs/layer'+str(l)+'_mvm'+str(m)+'_dot'+str(d)+'.mif', 'w'))

for i in range(limx):
for j in range(limy):
for d in range(native_dim):
for e in range(native_dim):
mifs[mvm_idx][d].write(str(padded_weights[l][(j * native_dim) + d][(i * native_dim) + e]) + ' ')
mifs[mvm_idx][d].write('\n')
if (mvm_idx == layer_mvms-1):
mvm_idx = 0
else:
mvm_idx = mvm_idx + 1

for mvm_mifs in mifs:
for mif in mvm_mifs:
mif.close()

# Prepare instruction MIFs directory
if(not(os.path.exists('./inst_mifs'))):
os.mkdir('inst_mifs')
else:
files = glob.glob('inst_mifs/*.mif')
for file in files:
os.remove(file)

# Generate instruction MIFs
# release_op, release_dest, rf_raddr, accum_raddr, last, release, accum_en, reduce
for l in range(num_layers):
layer_mvms = num_mvms[l]
limx = int(padded_weights[l].shape[1] / native_dim / layer_mvms)
limy = int(padded_weights[l].shape[0] / native_dim)
for m in range(layer_mvms):
inst_mif = open('inst_mifs/layer'+str(l)+'_mvm'+str(m)+'.mif', 'w')
for i in range(limx):
for j in range(limy):
# release_op
if (m == layer_mvms - 1):
inst_mif.write('1 ')
else:
inst_mif.write('0 ')
# release_dest
if ((l == num_layers - 1) and (m == layer_mvms - 1)):
inst_mif.write(str(placement_dict['output_collector']) + ' ')
elif (m == layer_mvms - 1):
dest_layer = l + 1
dest_mvm = j % num_mvms[l+1]
dest_str = 'layer' + str(dest_layer) + '_mvm' + str(dest_mvm)
inst_mif.write(str(placement_dict[dest_str]) + ' ')
else:
dest_layer = l
dest_mvm = m + 1
dest_str = 'layer' + str(dest_layer) + '_mvm' + str(dest_mvm)
inst_mif.write(str(placement_dict[dest_str]) + ' ')
# rf_raddr
inst_mif.write(str(i * limy + j) + ' ')
# accum_raddr
inst_mif.write(str(j) + ' ')
# last
if (j == limy-1):
inst_mif.write('1 ')
else:
inst_mif.write('0 ')
# release
if (i == limx-1):
inst_mif.write('1 ')
else:
inst_mif.write('0 ')
# accum_en
if (i == 0):
inst_mif.write('0 ')
else:
inst_mif.write('1 ')
# reduce
if (m == 0 or i < limx-1):
inst_mif.write('0\n')
else:
inst_mif.write('1\n')
inst_mif.close()

# Prepare input MIFs directory
if(not(os.path.exists('./input_mifs'))):
os.mkdir('input_mifs')
else:
files = glob.glob('input_mifs/*.mif')
for file in files:
os.remove(file)

# Generate test input MIFs
padded_input_dim = int(math.ceil(input_dim * 1.0 / native_dim / num_mvms[0]) * native_dim * num_mvms[0])
test_inputs = np.zeros(shape=(num_test_inputs, padded_input_dim), dtype=int)
test_inputs[:, :input_dim] = np.random.randint(-2, 2, size=(num_test_inputs, input_dim))
input_files = []
for i in range(num_mvms[0]):
input_files.append(open('input_mifs/inputs_mvm' + str(i) + '.mif', 'w'))
for i in range(num_test_inputs):
for c in range(int(padded_input_dim / native_dim)):
for e in range(native_dim):
input_files[c % num_mvms[0]].write(str(test_inputs[i][(c * native_dim) + e]) + ' ')
input_files[c % num_mvms[0]].write('\n')
for file in input_files:
file.close()

# Compute test outputs
test_inputs = np.transpose(test_inputs)
test_outputs = np.dot(padded_weights[0], test_inputs)
test_outputs = test_outputs.astype(np.int8)
#test_outputs = np.maximum(test_outputs, np.zeros(shape=test_outputs.shape, dtype=int))
for l in range(1, num_layers):
test_outputs = np.dot(padded_weights[l], test_outputs)
test_outputs = test_outputs.astype(np.int8)
#test_outputs = np.maximum(test_outputs, np.zeros(shape=test_outputs.shape, dtype=int))
test_outputs = np.transpose(test_outputs)

# Generate test output MIFs
output_file = open('./golden_outputs.mif', 'w')
for o in range(test_outputs.shape[0]):
for c in range(int(test_outputs.shape[1] / native_dim)):
for e in range(native_dim):
output_file.write(str(test_outputs[o][(c * native_dim) + e]) + ' ')
output_file.write('\n')
output_file.close()

# Generate layer/MVM configuration
config_file = open('./layer_mvm_config', 'w')
config_file.write(str(num_layers) + ' ')
for mvm_count in num_mvms:
config_file.write(str(mvm_count) + ' ')
config_file.close()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.mif
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Directory for dumping input MIFs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.mif
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Directory for dumping instruction MIFs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.mif
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Directory for dumping weight MIFs
37 changes: 37 additions & 0 deletions rad-sim/example-designs/mlp_int8/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
noc:
type: ['2d']
num_nocs: 1
clk_period: [1.0]
payload_width: [166]
topology: ['mesh']
dim_x: [4]
dim_y: [4]
routing_func: ['dim_order']
vcs: [5]
vc_buffer_size: [8]
output_buffer_size: [8]
num_packet_types: [5]
router_uarch: ['iq']
vc_allocator: ['islip']
sw_allocator: ['islip']
credit_delay: [1]
routing_delay: [1]
vc_alloc_delay: [1]
sw_alloc_delay: [1]

noc_adapters:
clk_period: [1.25]
fifo_size: [16]
obuff_size: [2]
in_arbiter: ['fixed_rr']
out_arbiter: ['priority_rr']
vc_mapping: ['direct']

design:
name: 'mlp_int8'
noc_placement: ['mlp.place']
clk_periods: [5.0]

telemetry:
log_verbosity: 2
traces: []
10 changes: 10 additions & 0 deletions rad-sim/example-designs/mlp_int8/mlp.clks
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
layer0_mvm0 0 0
layer0_mvm1 0 0
layer1_mvm0 0 0
layer1_mvm1 0 0
layer2_mvm0 0 0
input_dispatcher0 0 0
input_dispatcher1 0 0
output_collector 0 0
weight_loader 0 0
inst_loader 0 0
Loading

0 comments on commit 53fc99e

Please sign in to comment.