From 46d91793965f5e577156e0d58a3a7b782b7041a4 Mon Sep 17 00:00:00 2001 From: Mateja Putic Date: Tue, 4 Apr 2017 10:09:35 -0400 Subject: [PATCH 1/3] added support for parsing a learn trace, for comparison to baseline FANN config --- tools/src/parse-learn-trace.c | 109 ++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 tools/src/parse-learn-trace.c diff --git a/tools/src/parse-learn-trace.c b/tools/src/parse-learn-trace.c new file mode 100644 index 0000000..10c9055 --- /dev/null +++ b/tools/src/parse-learn-trace.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include + +typedef struct { + int8_t decimal_point : 3; + bool error_function : 1; + int8_t binary_format : 3; + uint16_t unused : 9; + uint16_t num_weight_blocks : 16; + uint16_t num_neurons : 16; + uint16_t num_layers : 16; + uint16_t first_layer_ptr : 16; + uint16_t weights_ptr : 16; + int16_t learning_rate : 16; + int16_t lambda : 16; +} packed_info_t; + +typedef struct { + ptrdiff_t weight_offset : 16; + uint8_t num_weights : 8; + int8_t activation_function : 5; + int8_t steepness : 3; + int32_t bias : 32; +} __attribute__((packed, aligned(1))) packed_neuron_t; + +typedef struct { + uint16_t first_neuron_ptr : 12; + uint16_t num_neurons : 10; + uint16_t num_neurons_prev : 10; +} __attribute__((packed, aligned(1))) packed_layer_t; + +int main(int argc, char **argv) { + if (argc != 2) + printf("Usage: %s filename\n", argv[0]); + else { + FILE *file = fopen(argv[1], "rb"); + if (file == 0) { + printf("Could not open file\n"); + } + else { + packed_info_t info; + packed_layer_t layer; + printf("sizeof(packed_info_t): %lu\n", sizeof(packed_info_t)); + printf("sizeof(packed_layer_t): %lu\n", sizeof(packed_layer_t)); + packed_neuron_t neuron; + int32_t weight; + fread(&info, sizeof(packed_info_t), 1, file); + printf("decimal point: %d\n", info.decimal_point); + printf("error function: %d\n", info.error_function); + printf("binary format: %d\n", info.binary_format); + printf("num. weight blocks: %u\n", info.num_weight_blocks); + printf("num. neurons: %u\n", info.num_neurons); + printf("num. layers: %u\n", info.num_layers); + printf("first layer ptr: %d\n", info.first_layer_ptr); + printf("weights ptr: %d\n", info.weights_ptr); + printf("learning rate: %d\n", info.learning_rate); + printf("lambda: %d\n", info.lambda); + fseek(file, info.first_layer_ptr, SEEK_SET); + int32_t layer_ptr, neuron_ptr; + for (uint16_t i = 0; i < info.num_layers; i++) { + fread(&layer, sizeof(packed_layer_t), 1, file); + layer_ptr = ftell(file); + printf("layer %u\n", i); + printf("first neuron ptr: %u\n", layer.first_neuron_ptr); + printf("neurons in layer: %u\n", layer.num_neurons); + printf("neurons in previous layer: %u\n", layer.num_neurons_prev); + fseek(file, layer.first_neuron_ptr, SEEK_SET); + for (uint16_t j = 0; j < layer.num_neurons; j++) { + fread(&neuron, sizeof(packed_neuron_t), 1, file); + neuron_ptr = ftell(file); + printf("\tneuron %u\n", j); + printf("\tweight offset: %d\n", neuron.weight_offset); + printf("\tnum. weights: %u\n", neuron.num_weights); + printf("\tactivation function: %u\n", neuron.activation_function); + printf("\tsteepness: %d\n", neuron.steepness); + printf("\tbias: %d\n", neuron.bias); + fseek(file, neuron.weight_offset, SEEK_SET); + for (uint16_t k = 0; k < neuron.num_weights; k++) { + fread(&weight, sizeof(int32_t), 1, file); + printf("\t\tweight %u: %d\n", k, weight); + } + fseek(file, neuron_ptr, SEEK_SET); + } + fseek(file, layer_ptr, SEEK_SET); + } + return 0; + fseek(file, 6, SEEK_SET); + /*fread(&num_layers, sizeof(uint16_t), 1, file);*/ + /*fread(&first_layer_ptr, sizeof(uint16_t), 1, file);*/ + /*printf("num layers: %u\n", num_layers);*/ + /*printf("first layer ptr: %d\n", first_layer_ptr);*/ + /*packed_layer_t layer;*/ + /*fseek(file, first_layer_ptr, SEEK_SET);*/ + /*printf("file pos: %lu\n", ftell(file));*/ + /*for (uint32_t i = 0; i < num_layers; i++) {*/ + /*fread(&layer, sizeof(packed_layer_t), 1, file);*/ + /*printf("layer: %u, num. neurons: %u\n", i, layer.num_neurons);*/ + /*for(uint32_t j = 0; j < num_neurons; j++) {*/ + + /*}*/ + /*}*/ + + fclose(file); + } + } + return 0; +} From f8446bf34d3f2f03875ccbffd7d83be062143d28 Mon Sep 17 00:00:00 2001 From: Mateja Putic Date: Tue, 4 Apr 2017 10:09:59 -0400 Subject: [PATCH 2/3] added support for parsing a learn trace, for comparison to baseline FANN config --- tools/Makefile | 7 ++++++- tools/scripts/parse_emu_log.py | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/tools/Makefile b/tools/Makefile index 3f893f9..3172f62 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -17,6 +17,7 @@ TOOLS = \ fann-random \ fann-train \ generate-ant \ + parse-learn-trace \ fann-eval \ fann-eval-fixed BINS = $(addprefix $(DIR_BIN)/, $(TOOLS)) @@ -48,11 +49,15 @@ LDIRS = \ $(DIR_BIN)/fann-train.o \ $(DIR_BIN)/fann-random.o \ $(DIR_BIN)/fann-float-to-fixed.o \ - $(DIR_BIN)/generate-ant.o + $(DIR_BIN)/generate-ant.o \ + $(DIR_BIN)/parse-learn-trace.o $(DIR_BIN)/generate-ant: $(DIR_BIN)/generate-ant.o $(DIR_TOP)/tests/libs/build/$(TARGET)/libxfiles-ant.a $(libfann_dep) $(CC) $(CFLAGS) $< $(LDIRS) -lxfiles-ant -o $@ +$(DIR_BIN)/parse-learn-trace: $(DIR_BIN)/parse-learn-trace.o $(DIR_TOP)/tests/libs/build/$(TARGET)/libxfiles-ant.a $(libfann_dep) + $(CC) $(CFLAGS) $< $(LDIRS) -lxfiles-ant -o $@ + # No pattern rules as I need to be explicit about what is linking # against FANN since it's LGPLv2 diff --git a/tools/scripts/parse_emu_log.py b/tools/scripts/parse_emu_log.py index e94ff06..82fd918 100755 --- a/tools/scripts/parse_emu_log.py +++ b/tools/scripts/parse_emu_log.py @@ -7,6 +7,7 @@ import os import mmap import ipdb +import struct from collections import defaultdict re_in_out_mapping = re.compile(r'RegFile: (?:Saw TTable write|PE write element).+(?P0x[0-9a-fA-F]+)\/(?P0x[0-9a-fA-F]+)\/(?P0x[0-9a-fA-F]+)') @@ -14,6 +15,7 @@ re_in_val = re.compile(r'queueIn\[0\]\sdeq\s\[data:(0x[\da-f]+)') re_out_val = re.compile(r'queueOut\[0\]\sdeq\s\[data:(0x[\da-f]+)') re_in_out_val = re.compile(r"(?P(?:[a-f0-9]+\s)+)->(?P(?:\s[a-f0-9]+)+)") +re_store_data = re.compile(r"Received store data 0x([\da-f]+)") path_fann_eval_fixed = 'tools/bin/fann-eval-fixed' #path_emulator_bin = 'emulator/emulator-rocketchip-XFilesDanaCppPe1Epb4Config' path_emulator_bin = 'emulator/emulator-rocketchip-XFilesDanaCppPe4Epb4Config' @@ -113,6 +115,24 @@ def parse_fann_eval_fixed_trace(trace_file_path, write_file=False): return fann_fixed_outputs +def parse_learn_trace_new(learn_trace_file_path): + with open(learn_trace_file_path) as learn_trace_file: + with open('a.out', 'wb') as out_file: + for line in learn_trace_file: + res = re_store_data.search(line) + if res: + foo = res.groups()[0] + print(foo) + bar = [] + for i in range(0, len(foo), 4): + baz = foo[i+2:i+4] + foo[i:i+2] + bar.append(baz) + bar.reverse() + baz = ''.join(bar) + print(baz) + print() + out_file.write(bytes.fromhex(baz)) + @click.command() @click.option('--net_file_path', '-n', type=click.Path(exists=True), required=False) @click.option('--train_file_path', '-t', type=click.Path(exists=True), required=False) @@ -214,6 +234,8 @@ def cli(net_file_path, train_file_path, fann_trace_file_path, baremetal_bin_file print(item['data']) ipdb.set_trace() else: + parse_learn_trace_new(baremetal_trace_file_path) + sys.exit(0) print("Generating baremetal trace") baremetal_trace_file_path = baremetal_trace_file_path or generate_baremetal_trace(path_emulator_bin, baremetal_bin_file_path) print("Parsing baremetal trace") From b3c47eb3a517427f4520e55b2c0606431d4ac3d8 Mon Sep 17 00:00:00 2001 From: Mateja Putic Date: Wed, 19 Apr 2017 09:17:02 -0400 Subject: [PATCH 3/3] First checkin of tt utility --- tools/scripts/parse_emu_log.py | 41 +++-- tools/scripts/tt | 298 +++++++++++++++++++++++++++++++++ 2 files changed, 321 insertions(+), 18 deletions(-) create mode 100755 tools/scripts/tt diff --git a/tools/scripts/parse_emu_log.py b/tools/scripts/parse_emu_log.py index 82fd918..7162795 100755 --- a/tools/scripts/parse_emu_log.py +++ b/tools/scripts/parse_emu_log.py @@ -115,25 +115,8 @@ def parse_fann_eval_fixed_trace(trace_file_path, write_file=False): return fann_fixed_outputs -def parse_learn_trace_new(learn_trace_file_path): - with open(learn_trace_file_path) as learn_trace_file: - with open('a.out', 'wb') as out_file: - for line in learn_trace_file: - res = re_store_data.search(line) - if res: - foo = res.groups()[0] - print(foo) - bar = [] - for i in range(0, len(foo), 4): - baz = foo[i+2:i+4] + foo[i:i+2] - bar.append(baz) - bar.reverse() - baz = ''.join(bar) - print(baz) - print() - out_file.write(bytes.fromhex(baz)) -@click.command() +@click.group() @click.option('--net_file_path', '-n', type=click.Path(exists=True), required=False) @click.option('--train_file_path', '-t', type=click.Path(exists=True), required=False) @click.option('--fann_trace_file_path', '-ft', type=click.Path(exists=True), required=False) @@ -144,6 +127,8 @@ def parse_learn_trace_new(learn_trace_file_path): @click.option('--debug', is_flag=True, required=False) @click.option('--learn', is_flag=True, required=False) def cli(net_file_path, train_file_path, fann_trace_file_path, baremetal_bin_file_path, baremetal_trace_file_path, test_list_file_path, trace_list_file_path, debug, learn): + pass + if test_list_file_path: with open(test_list_file_path, 'r') as test_list_file: test_list = test_list_file.read() @@ -289,5 +274,25 @@ def cli(net_file_path, train_file_path, fann_trace_file_path, baremetal_bin_file j = int(j.decode("utf-8"), 16) print("{}, {}, {}".format(j, out_val, abs(j - out_val))) +@cli.command() +@click.option('--baremetal_trace_file_path', '-bt', type=click.Path(exists=True), required=False) +def parse_learn_trace_new(baremetal_trace_file_path): + with open(baremetal_trace_file_path) as learn_trace_file: + with open('a.out', 'wb') as out_file: + for line in learn_trace_file: + res = re_store_data.search(line) + if res: + foo = res.groups()[0] + print(foo) + bar = [] + for i in range(0, len(foo), 4): + baz = foo[i+2:i+4] + foo[i:i+2] + bar.append(baz) + bar.reverse() + baz = ''.join(bar) + print(baz) + print() + out_file.write(bytes.fromhex(baz)) + if __name__ == '__main__': cli() diff --git a/tools/scripts/tt b/tools/scripts/tt new file mode 100755 index 0000000..b50a9d3 --- /dev/null +++ b/tools/scripts/tt @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 +"""tt - the xfiles-dana test tool + +Provides functions for running emulator tests and inspecting memory + +Usage: tt --help +""" + +import click +from re import compile +from os import path +from subprocess import run, PIPE, DEVNULL, TimeoutExpired +# from ipdb import set_trace +from os import system + + +@click.group() +@click.option('--debug', is_flag=True) +@click.option('--rocket_chip_project_path') +@click.pass_context +def tt(ctx, debug, rocket_chip_project_path): + ctx.obj['DEBUG'] = debug + rocket_chip_project_path = rocket_chip_project_path or os.environ.get( + 'ROCKET_CHIP_PROJECT_PATH') + assert rocket_chip_project_path, "Must specify option rocket_chip_project_path or define ROCKET_CHIP_PROJECT_PATH environment variable" + ctx.obj['ROCKET_CHIP_PROJECT_PATH'] = rocket_chip_project_path + if debug: + click.echo("rocket_chip_path: {}".format(rocket_chip_project_path)) + xfiles_dana_path = path.join(rocket_chip_project_path, 'xfiles-dana') + if debug: + click.echo("xfiles_dana_path: {}".format(xfiles_dana_path)) + ctx.obj['XFILES_DANA_PATH'] = xfiles_dana_path + + +@tt.command() +@click.argument('test_names', nargs=-1) +@click.pass_context +def emu_test(ctx, test_names, timeout=300): + """Run a baremetal emulation test.""" + debug = ctx.obj['DEBUG'] + rocket_chip_path = ctx.obj['ROCKET_CHIP_PATH'] + xfiles_dana_path = ctx.obj['XFILES_DANA_PATH'] + + # Build and assert paths + emulator_bin_path = path.join( + rocket_chip_path, + 'emulator', + 'emulator-rocketchip-VelvetConfig') + assert path.exists(emulator_bin_path), "emulator_bin_path does not exist!" + ctx.obj['EMULATOR_BIN_PATH'] = emulator_bin_path + parse_learn_bin_path = path.join( + xfiles_dana_path, 'tools', 'bin', 'parse-learn-trace') + assert path.exists( + parse_learn_bin_path), "parse_learn_bin_path does not exist!" + ctx.obj['PARSE_LEARN_BIN_PATH'] = parse_learn_bin_path + baremetal_bin_dir_path = path.join( + xfiles_dana_path, 'tests', 'build', 'nets') + assert path.exists( + baremetal_bin_dir_path), "baremetal_bin_dir_path does not exist!" + ctx.obj['BAREMETAL_BIN_DIR_PATH'] = baremetal_bin_dir_path + build_nets_dir_path = path.join(xfiles_dana_path, 'build', 'nets') + assert path.exists( + build_nets_dir_path), "build_nets_dir_path does not exist!" + ctx.obj['BUILD_NETS_DIR_PATH'] = build_nets_dir_path + + # Run tests + for test_name in test_names: + if debug: + click.echo('Running emulation test {}'.format(test_name)) + # Build and assert test path + baremetal_bin_file_name = test_name + baremetal_bin_file_path = path.join( + baremetal_bin_dir_path, baremetal_bin_file_name) + baremetal_trace_file_name = baremetal_bin_file_name + '.test.trace' + baremetal_trace_file_path = baremetal_trace_file_name + if debug: + click.echo('baremetal_bin_file_path: {}'.format( + baremetal_bin_file_path)) + assert path.exists(baremetal_bin_file_path), "baremetal_bin_file_path {} does not exist!".format( + baremetal_bin_file_path) + + # Run test with timeout. If timeout expires, proceed to next test. + # Write test debug output to file + try: + emulator = run([emulator_bin_path, + '+verbose', + baremetal_bin_file_path], + stderr=PIPE, + timeout=timeout) + except TimeoutExpired as e: + click.echo(e) + continue + with open(baremetal_trace_file_path, 'wb') as baremetal_trace_file: + for line in emulator.stderr.splitlines(): + if chr(line[0]) == '[': + baremetal_trace_file.write(line) + baremetal_trace_file.write(b'\n') + + +def _diff_cache_dump( + debug, + cache_dump_ascii_file_path_0, + cache_dump_ascii_file_path_1, + out_file_path=None): + """Helper function to run diff on two cache dumps processed by parse_learn_trace. Optionally write output to file""" + if out_file_path: + cmd = ' '.join(['diff', '-y', cache_dump_ascii_file_path_0, + cache_dump_ascii_file_path_1, '>', out_file_path]) + else: + cmd = ' '.join(['diff', + '-y', + cache_dump_ascii_file_path_0, + cache_dump_ascii_file_path_1]) + if debug: + click.echo(cmd) + system(cmd) + + +def _parse_learn_trace( + ctx, + cache_dump_bin_file_path, + cache_dump_ascii_file_name): + """Helper function to convert binary cache dump to ascii cache dump""" + debug = ctx.obj['DEBUG'] + parse_learn_bin_path = ctx.obj['PARSE_LEARN_BIN_PATH'] + cmd = ' '.join([parse_learn_bin_path, + cache_dump_bin_file_path, + '>', + cache_dump_ascii_file_name]) + if debug: + click.echo(cmd) + system(cmd) + + +@tt.command() +@click.argument('emu_cache_dump_ascii_file_paths', nargs=-1) +@click.pass_context +def diff_cache_dumps(ctx, emu_cache_dump_ascii_file_paths): + """Run diff on a list of ascii-format emulator dumps. Expects xfiles-emu-nets filenames. + + Emulator cache dump file names are assumed to be xfiles-emu-nets-p-.cache.ascii + FANN cache dump file names are assumed to be -fixed.16bin.cache.ascii + + Usage: find . '*.cache.ascii' |xargs tt diff_cache_dumps + + Outputs: .cache.ascii.diff""" + + debug = ctx.obj['DEBUG'] + xfiles_dana_path = ctx.obj['XFILES_DANA_PATH'] + build_nets_dir_path = path.join(xfiles_dana_path, 'build', 'nets') + _build_parse_learn_bin_path(ctx) + for emu_cache_dump_ascii_file_path in emu_cache_dump_ascii_file_paths: + # Get the filename + foo = emu_cache_dump_ascii_file_path.split('/')[-1] + # Remove the filename extension + net_name = foo.split('.')[0][19:-6] + if debug: + click.echo(net_name) + baseline_cache_bin_file_path = path.join( + build_nets_dir_path, net_name + '-fixed.16bin') + if debug: + click.echo(baseline_cache_bin_file_path) + # parse_learn_trace of baseline cache + _parse_learn_trace( + ctx, + baseline_cache_bin_file_path, + net_name + + '-fixed.cache.ascii') + _diff_cache_dump( + ctx, + net_name + + '-fixed.cache.ascii', + emu_cache_dump_ascii_file_path, + net_name + + '.cache.ascii.diff') + + +def _build_parse_learn_bin_path(ctx): + """Helper function that adds PARSE_LEARN_BIN_PATH to the context""" + # Get xfiles-dana path from context + xfiles_dana_path = ctx.obj['XFILES_DANA_PATH'] + # Build path to parse-learn-trace, assert it exists + parse_learn_bin_path = path.join( + xfiles_dana_path, 'tools', 'bin', 'parse-learn-trace') + assert path.exists( + parse_learn_bin_path), "parse_learn_bin_path does not exist!" + ctx.obj['PARSE_LEARN_BIN_PATH'] = parse_learn_bin_path + + +@tt.command() +@click.argument('cache_dump_bin_file_paths', nargs=-1) +@click.pass_context +def dump_cache_ascii(ctx, cache_dump_bin_file_paths): + """Convert a list of binary cache dumps to ascii + + Usage: find . '*.cache.bin' |xargs tt dump_cache_ascii + + Outputs: .cache.ascii""" + debug = ctx.obj['DEBUG'] + _build_parse_learn_bin_path(ctx) + # Iterate over binary cache dump files + for cache_dump_bin_file_path in cache_dump_bin_file_paths: + # Get the filename + foo = cache_dump_bin_file_path.split('/')[-1] + # Remove the filename extension + cache_dump_bin_file_name = foo.split('.')[0] + if debug: + click.echo(cache_dump_bin_file_name) + # Run parse-learn-trace and dump ascii + _parse_learn_trace( + ctx, + cache_dump_bin_file_path, + cache_dump_bin_file_name + + '.cache.ascii') + +# @tt.command() +# @click.argument('cache_dump_names', nargs=-1) +# @click.pass_context +# def compare_cache_dumps(ctx, cache_dump_names): + # xfiles_dana_path = ctx.obj['XFILES_DANA_PATH'] + # build_nets_dir_path = path.join(xfiles_dana_path, 'build', 'nets') + # assert path.exists(build_nets_dir_path), "build_nets_dir_path does not exist!" + # parse_learn_bin_path = path.join(xfiles_dana_path, 'tools', 'bin', 'parse-learn-trace') + # assert path.exists(parse_learn_bin_path), "parse_learn_bin_path does not exist!" + # ctx.obj['PARSE_LEARN_BIN_PATH'] = parse_learn_bin_path + # for name in cache_dump_names: + # baseline_cache_file_name = name[19:-19] + '-fixed.16bin' + # baseline_cache_file_path = path.join(build_nets_dir_path, baseline_cache_file_name) + # click.echo(name) + # click.echo(baseline_cache_file_path) + # # Convert binary cache trace to ascii cache trace + # _parse_learn_trace(ctx, baseline_cache_file_path, baseline_cache_file_name + '.cache.ascii') + # click.echo() + + +def _parse_store_data_from_line(debug, res): + """_cache_dump helper function that fixes endinaness""" + foo = res.groups()[0] + # if debug: click.echo(foo) + bar = [] + for i in range(0, len(foo), 4): + baz = foo[i + 2:i + 4] + foo[i:i + 2] + bar.append(baz) + bar.reverse() + baz = ''.join(bar) + # if debug: click.echo(baz) + return baz + + +def _cache_dump(debug, baremetal_trace_file_path, cache_dump_file_path): + """Given a baremetal trace file path, find the cache lines and dump them to a binary file + + Returns: list of found cache lines""" + re_store_data = compile(r"Received store data 0x([\da-f]+)") + cache_dump_res = [] + # Search the trace file line-by-line, + # store found cache lines in list `cache_dump_res` + with open(baremetal_trace_file_path) as trace_file: + for line in trace_file: + res = re_store_data.search(line) + if res: + foo = _parse_store_data_from_line(debug, res) + cache_dump_res.append(foo) + if debug: + click.echo(cache_dump_res) + if cache_dump_res: + with open(cache_dump_file_path, 'wb') as out_file: + for line in cache_dump_res: + out_file.write(bytes.fromhex(line)) + return cache_dump_res + + +@tt.command() +@click.argument('baremetal_trace_file_paths', nargs=-1) +@click.pass_context +def dump_cache_bin(ctx, baremetal_trace_file_paths): + """Dump binary format cache lines from baremetal learn trace files. + + Usage: find . -name '*learn.test.trace' |xargs tt cache_dump_bin + + Outputs: .cache.bin""" + debug = ctx.obj['DEBUG'] + for baremetal_trace_file_path in baremetal_trace_file_paths: + # Get the filename + foo = baremetal_trace_file_path.split('/')[-1] + # Remove the filename extension + cache_dump_bin_file_name = foo.split('.')[0] + if debug: + click.echo(cache_dump_bin_file_name) + _cache_dump( + debug, + baremetal_trace_file_path, + cache_dump_bin_file_name + + '.cache.bin') + + +if __name__ == '__main__': + tt(obj={})