This commit reuses NPU emul to generate reference data for tvn models.
Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
ROOT_DIR:=$(shell pwd)
export NPU_TRINITY_INSTALL_PREFIX=/opt/trinity
-export LC_ALL = C.UTF-8
+export PATH=$(shell printenv PATH):${NPU_TRINITY_INSTALL_PREFIX}/bin
+export LC_ALL=C.UTF-8
%:
dh $@ --parallel
./build/tests/apptests/apptest_async_callbacks
# Gen model binaries
- # Single-layer test data
- mkdir -p $(CURDIR)/single
+ # 1) Singl-layer models from AIP/SIM_Trinity_SIM (i.e., libnpuvision)
+ # 2) Multi-layer models from AIP/NPU_SystemService
+ # 3) MUlti-layer models from AIP/NPU_Compiler
+
+ mkdir -p $(CURDIR)/npubinfmt_v1
+ mkdir -p $(CURDIR)/npubinfmt_v2
+
+ # 1) Reuse existing testdata but need to make npu models (v1)
+ find /opt/trinity/share/npuvision/testdata/* -type d -exec basename {} \; |\
+ xargs -I{} mkdir -p $(CURDIR)/npubinfmt_v1/{}
find /opt/trinity/share/npuvision/testdata/* -type d -exec basename {} \; |\
- xargs -I{} mkdir -p single/{}
+ xargs -I{} $(ROOT_DIR)/tools/gen-testdata/gen_npu_model.py -s -o $(CURDIR)/npubinfmt_v1/{} \
+ /opt/trinity/share/npuvision/testdata/{} 1
find /opt/trinity/share/npuvision/testdata/* -type d -exec basename {} \; |\
- xargs -I{} $(ROOT_DIR)/tools/gen-testdata/model_gen.py -s -o single/{} \
- /opt/trinity/share/npuvision/testdata/{} 1
- # Multi-layer test data
- mkdir -p $(CURDIR)/multi
- cd multi && $(ROOT_DIR)/build/tools/gen-testdata/gen-testdata > /dev/null
- find $(CURDIR)/multi -name 'testcase*' -type d -exec basename {} \; | \
- xargs -I{} ${NPU_TRINITY_INSTALL_PREFIX}/bin/encoder multi/{}/program.asm multi/{}/program.bin > /dev/null
- find $(CURDIR)/multi -name 'testcase*_v1' -type d -exec basename {} \; |\
- xargs -I{} $(ROOT_DIR)/tools/gen-testdata/model_gen.py multi/{} 1
- find $(CURDIR)/multi -name 'testcase*_v2' -type d -exec basename {} \; |\
- xargs -I{} $(ROOT_DIR)/tools/gen-testdata/model_gen.py multi/{} 2
- # Remove intermediate files
- find $(CURDIR)/multi -type f -name '_*' -delete
- find $(CURDIR)/multi -type f -name 'program.*' -delete
-
- # single/multi-layer model test
- ./build/tests/apptests/apptest_example_visa single > /dev/null
- ./build/tests/apptests/apptest_example_visa multi > /dev/null
+ xargs -I{} $(ROOT_DIR)/build/tools/gen-testdata/gen_ref_data -o $(CURDIR)/npubinfmt_v1/{} \
+ $(CURDIR)/npubinfmt_v1/{}/model.tvn > /dev/null
+
+ # 2) Make npu models from scratch (v1/v2)
+ $(ROOT_DIR)/build/tools/gen-testdata/gen_visa_prog $(CURDIR)/npubinfmt_v1 > /dev/null
+ find $(CURDIR)/npubinfmt_v1 -name 'testcase*' -type d -exec basename {} \; | \
+ xargs -I{} cp -r $(CURDIR)/npubinfmt_v1/{} $(CURDIR)/npubinfmt_v2/{}
+
+ find $(CURDIR)/npubinfmt_v1 -name 'testcase*' -type d -exec basename {} \; |\
+ xargs -I{} $(ROOT_DIR)/tools/gen-testdata/gen_npu_model.py -o $(CURDIR)/npubinfmt_v1/{} \
+ $(CURDIR)/npubinfmt_v1/{} 1
+ find $(CURDIR)/npubinfmt_v2 -name 'testcase*' -type d -exec basename {} \; |\
+ xargs -I{} $(ROOT_DIR)/tools/gen-testdata/gen_npu_model.py -o $(CURDIR)/npubinfmt_v2/{} \
+ $(CURDIR)/npubinfmt_v2/{} 2
+
+ find $(CURDIR)/npubinfmt_v1 -name 'testcase*' -type d -exec basename {} \; |\
+ xargs -I{} $(ROOT_DIR)/build/tools/gen-testdata/gen_ref_data -o $(CURDIR)/npubinfmt_v1/{} \
+ $(CURDIR)/npubinfmt_v1/{}/model.tvn > /dev/null
+ find $(CURDIR)/npubinfmt_v2 -name 'testcase*' -type d -exec basename {} \; |\
+ xargs -I{} $(ROOT_DIR)/build/tools/gen-testdata/gen_ref_data -o $(CURDIR)/npubinfmt_v2/{} \
+ $(CURDIR)/npubinfmt_v2/{}/model.tvn > /dev/null
+
+ # 3) Make only reference input/output data (v1 currently)
+ tar zxf $(ROOT_DIR)/tests/models/tvn_models.tar.gz
+
+ find $(CURDIR)/tvn_models/* -type d -exec basename {} \; |\
+ xargs -I{} $(ROOT_DIR)/build/tools/gen-testdata/gen_ref_data -o $(CURDIR)/tvn_models/{} \
+ $(CURDIR)/tvn_models/{}/model.tvn > /dev/null
+ find $(CURDIR)/tvn_models/* -type d -exec basename {} \; |\
+ xargs -I{} mv $(CURDIR)/tvn_models/{} $(CURDIR)/npubinfmt_v1
+
+ # Cleanup
+ rm -rf $(CURDIR)/tvn_models
+ find $(CURDIR)/npubinfmt_v1 -type f -name 'program.*' -delete
+ find $(CURDIR)/npubinfmt_v2 -type f -name 'program.*' -delete
+ find $(CURDIR)/npubinfmt_v1 -type f -name 'input_weight*' -delete
+ find $(CURDIR)/npubinfmt_v2 -type f -name 'input_weight*' -delete
+
+ # Test the models
+ ./build/tests/apptests/apptest_example_visa $(CURDIR)/npubinfmt_v1 > /dev/null
+ ./build/tests/apptests/apptest_example_visa $(CURDIR)/npubinfmt_v2 > /dev/null
override_dh_auto_install:
DESTDIR=$(CURDIR)/debian/tmp ninja -C build install
mkdir -p debian/tmp/${NPU_TRINITY_INSTALL_PREFIX}/share/npu-engine/testdata
- mv single debian/tmp/${NPU_TRINITY_INSTALL_PREFIX}/share/npu-engine/testdata
- mv multi debian/tmp/${NPU_TRINITY_INSTALL_PREFIX}/share/npu-engine/testdata
+ mv npubinfmt_v1 debian/tmp/${NPU_TRINITY_INSTALL_PREFIX}/share/npu-engine/testdata
+ mv npubinfmt_v2 debian/tmp/${NPU_TRINITY_INSTALL_PREFIX}/share/npu-engine/testdata
override_dh_install:
dh_install --sourcedir=debian/tmp --list-missing
/* npubinfmt magiccode macros */
#define NPUBIN_MAGICCODE (0x53524E5055000000ULL) /* ASCII hex for 'SRNPU' */
+#define NPUBIN_VERSION_MAX (2)
#define NPUBIN_VERSION(magiccode) ((magiccode) & 0xFFULL)
#define CHECK_NPUBIN(magiccode) (((magiccode) & ~0xFFFFFFULL) == NPUBIN_MAGICCODE)
%define neexampledir %{_libdir}/npu-engine/bin
-%define testdatadir_single_raw %{_datadir}/npuvision/testdata
-%define testdatadir_out %{_datadir}/npu-engine/testdata
+%define testdatadir_npuvision %{_datadir}/npuvision/testdata
+%define testdatadir_out %{_datadir}/npu-engine/testdata
Name: npu-engine
Summary: NPU Engine
%ifarch aarch64 x86_64
# Install Test Data
-mkdir -p %{buildroot}%{testdatadir_out}/single
-# single layer
-find %{testdatadir_single_raw} -name 'core*' -type d -exec basename {} .asm \; | \
- xargs -I{} mkdir -p %{buildroot}%{testdatadir_out}/single/{}
-find %{testdatadir_single_raw} -name 'core*' -type d -exec basename {} .asm \; | \
- xargs -I{} ./tools/gen-testdata/model_gen.py -s -o %{buildroot}%{testdatadir_out}/single/{} \
- %{testdatadir_single_raw}/{} 1
-
-# multi layer
-mkdir -p multi-intermediate
-pushd multi-intermediate
-find %{_builddir} -name gen-testdata -type f -exec {} \; > /dev/null
-find . -name 'testcase*' -type d -exec basename {} \; | \
- xargs -I{} %{_bindir}/encoder {}/program.asm {}/program.bin > /dev/null
-find . -name 'testcase*' -type d -exec basename {} \; | \
- xargs -I{} mkdir -p %{buildroot}%{testdatadir_out}/multi/{}
-popd
-find ./multi-intermediate -name 'testcase*_v1' -type d -exec basename {} \; | \
- xargs -I{} ./tools/gen-testdata/model_gen.py -o %{buildroot}%{testdatadir_out}/multi/{} \
- ./multi-intermediate/{} 1
-find ./multi-intermediate -name 'testcase*_v2' -type d -exec basename {} \; | \
- xargs -I{} ./tools/gen-testdata/model_gen.py -o %{buildroot}%{testdatadir_out}/multi/{} \
- ./multi-intermediate/{} 2
-
-# clean up
-find %{buildroot} -name gen-testdata -delete
-find %{buildroot} -name model_gen.py -delete
-find %{buildroot} -name multi.tar.gz -delete
+
+# Gen model binaries
+# 1) Singl-layer models from AIP/SIM_Trinity_SIM (i.e., libnpuvision)
+# 2) Multi-layer models from AIP/NPU_SystemService
+# 3) MUlti-layer models from AIP/NPU_Compiler
+
+mkdir -p npubinfmt_v1
+mkdir -p npubinfmt_v2
+
+# 1) Reuse existing testdata but need to make npu models (v1)
+find %{testdatadir_npuvision} -name 'core*' -type d -exec basename {} \; |\
+ xargs -I{} mkdir -p npubinfmt_v1/{}
+
+find %{testdatadir_npuvision} -name 'core*' -type d -exec basename {} \; |\
+ xargs -I{} ./tools/gen-testdata/gen_npu_model.py -s -o npubinfmt_v1/{} \
+ %{testdatadir_npuvision}/{} 1
+
+find %{testdatadir_npuvision} -name 'core*' -type d -exec basename {} \; |\
+ xargs -I{} ./build/tools/gen-testdata/gen_ref_data -o npubinfmt_v1/{} \
+ npubinfmt_v1/{}/model.tvn > /dev/null
+
+# 2) Make npu models from scratch (v1/v2)
+./build/tools/gen-testdata/gen_visa_prog npubinfmt_v1 > /dev/null
+find npubinfmt_v1 -name 'testcase*' -type d -exec basename {} \; |\
+ xargs -I{} cp -r npubinfmt_v1/{} npubinfmt_v2/{}
+
+find npubinfmt_v1 -name 'testcase*' -type d -exec basename {} \; |\
+ xargs -I{} ./tools/gen-testdata/gen_npu_model.py -o npubinfmt_v1/{} npubinfmt_v1/{} 1
+find npubinfmt_v2 -name 'testcase*' -type d -exec basename {} \; |\
+ xargs -I{} ./tools/gen-testdata/gen_npu_model.py -o npubinfmt_v2/{} npubinfmt_v2/{} 2
+
+find npubinfmt_v1 -name 'testcase*' -type d -exec basename {} \; |\
+ xargs -I{} ./build/tools/gen-testdata/gen_ref_data -o npubinfmt_v1/{} \
+ npubinfmt_v1/{}/model.tvn > /dev/null
+
+find npubinfmt_v2 -name 'testcase*' -type d -exec basename {} \; |\
+ xargs -I{} ./build/tools/gen-testdata/gen_ref_data -o npubinfmt_v2/{} \
+ npubinfmt_v2/{}/model.tvn > /dev/null
+
+# 3) Make only reference input/output data (v1)
+tar zxf tests/models/tvn_models.tar.gz
+find tvn_models/* -type d -exec basename {} \; | xargs -I{} mkdir -p npubinfmt_v1/{}
+find tvn_models/* -type d -exec basename {} \; |\
+ xargs -I{} ./build/tools/gen-testdata/gen_ref_data -o npubinfmt_v1/{} \
+ tvn_models/{}/model.tvn > /dev/null
+find tvn_models/* -type d -exec basename {} \; |\
+ xargs -I{} mv tvn_models/{}/model.tvn npubinfmt_v1/{}/
+
+# Cleanup
+rm -rf tvn_models
+find npubinfmt_v1 -type f -name 'program.*' -delete
+find npubinfmt_v2 -type f -name 'program.*' -delete
+find npubinfmt_v1 -type f -name 'input_weight*' -delete
+find npubinfmt_v2 -type f -name 'input_weight*' -delete
+
+mkdir -p %{buildroot}%{testdatadir_out}
+mv npubinfmt_v1 %{buildroot}%{testdatadir_out}
+mv npubinfmt_v2 %{buildroot}%{testdatadir_out}
%endif
%endif
done
- # Run example_visa
+ # Run apptests using actual model files.
%ifarch x86_64
- ./apptests/apptest_example_visa %{buildroot}%{testdatadir_out}/single > /dev/null
- ./apptests/apptest_example_visa %{buildroot}%{testdatadir_out}/multi > /dev/null
+ ./apptests/apptest_example_visa %{buildroot}%{testdatadir_out}/npubinfmt_v1 > /dev/null
+ ./apptests/apptest_example_visa %{buildroot}%{testdatadir_out}/npubinfmt_v2 > /dev/null
%endif
popd
Note that the npu-example package (especailly, the application tests) requires this package.
%files testdata
%%defattr(-,root,root,-)
-%{_datadir}/npu-engine/testdata/single/*
-%{_datadir}/npu-engine/testdata/multi/*
+%{_datadir}/npu-engine/testdata/npubinfmt_v1/*
+%{_datadir}/npu-engine/testdata/npubinfmt_v2/*
%endif
%package unittest-coverage
#include <ne_test_utils.h>
#define MAX_FILE_LEN 256
+#define NPU_MODEL_NAME "model.tvn"
/** @brief compare output tensor with the golden data */
static int
/** 1: setup model (not dmabuf) */
memset (model_path, '\x00', MAX_FILE_LEN);
- snprintf (model_path, MAX_FILE_LEN, "%s/%s/npu_model.bin",
- base_path, target);
+ snprintf (model_path, MAX_FILE_LEN, "%s/%s/%s",
+ base_path, target, NPU_MODEL_NAME);
if ((err = get_model_meta (model_path, &meta)) != 0) {
fprintf (stderr, "Fail to get the metadata of %s\n", model_path);
return err;
#!/usr/bin/env python
##
-# @file model_gen.py
-# @brief generate NPU model binary with existing VISA binary and necessary data
+# @file gen_npu_model.py
+# @brief generate NPU model binary with VISA prog binary and weight data
# @author Dongju Chae <dongju.chae@samsung.com>
#
# Usage:
-# $ python model_gen.py [testdata directory]
+# $ python gen_npu_model.py [testdata directory]
##
import argparse
META_SIZE=4096
SIZE_ALIGN=4096
SIGNATURE='SRNPU'
+NPU_MODEL_NAME='model.tvn'
MAX_TENSORS=16
MAX_RANK=4
## @brief class for NPU tensor
class Tensor:
- def __init__ (self, asm, dma_en, data):
+ def __init__ (self, asm, dma_en):
opcode = asm["opcode"]
self.asm = asm
self.dma_en = dma_en
- self.data = data
- self.size = len(data)
self.dims = []
self.elem_size = ELEM_SIZE # do not consider other types yet
self.emod_y = asm[dma_en + "_emod_y"]
self.emod_z = asm[dma_en + "_emod_z"]
+ def size (self):
+ size = self.elem_size
+ for dim in self.dims:
+ size *= dim
+ return size
+
## @brief class for NPU weight
class Weight:
def __init__ (self, asm, data):
self.path_prog_asm = self.dir + "/program.asm"
self.path_prog_binary = self.dir + "/program.bin"
- self.path_model = self.outdir + "/npu_model.bin"
+ self.path_model = self.outdir + "/" + NPU_MODEL_NAME
self.size_prog_binary = os.path.getsize(self.path_prog_binary)
self.size_all_weights = 0
max_size = 0
for tensor in self.input_tensors:
- if max_size < tensor.eaddr + tensor.size:
- max_size = tensor.eaddr + tensor.size
+ if max_size < tensor.eaddr + tensor.size():
+ max_size = tensor.eaddr + tensor.size()
for tensor in self.output_tensors:
- if max_size < tensor.eaddr + tensor.size:
- max_size = tensor.eaddr + tensor.size
+ if max_size < tensor.eaddr + tensor.size():
+ max_size = tensor.eaddr + tensor.size()
return max_size
## @brief get binary data; return empty string if it does not exist
def get_bin_data (self, name, idx):
if self.single_mode:
- path = self.dir + "/" + name[1:] + ".bin"
+ path = self.dir + "/" + name + ".bin"
else:
path = self.dir + "/" + name + "_" + str(idx) + ".bin"
return open(path).read() if os.path.isfile(path) else ""
self.fill_meta()
self.fill_program()
self.fill_weight()
- self.fill_tensors()
self.finalize()
## @brief parse program assembly to retreive necessary info.
# Note that this model generator supports only HWA's layer operations, not a generic model.
# So, each layer has 2 input tensors and 1 output tensor at the most.
if asm["dma_in0_en"] == 1:
- data = self.get_bin_data ("_input_fmap", idx)
- if data:
- tensor = Tensor(asm, "in0", data)
- self.input_tensors.append(tensor)
+ tensor = Tensor(asm, "in0")
+ self.input_tensors.append(tensor)
if asm["dma_in1_en"] == 1:
- data = self.get_bin_data ("_input_esum", idx)
- if data:
- tensor = Tensor(asm, "in1", data)
- self.input_tensors.append(tensor)
+ tensor = Tensor(asm, "in1")
+ self.input_tensors.append(tensor)
if asm["dma_out_en"] == 1:
- data = self.get_bin_data ("_output_fmap", idx)
- if data:
- tensor = Tensor(asm, "out", data)
- self.output_tensors.append(tensor)
+ tensor = Tensor(asm, "out")
+ self.output_tensors.append(tensor)
if asm["dma_wgt_en"] == 1:
- data = self.get_bin_data ("_input_weight", idx)
+ data = self.get_bin_data ("input_weight", idx)
if data:
weight = Weight(asm, data)
self.size_all_weights += weight.size
# input size (first layer) including esum (all layers)
input_tensors_size = 0
for tensor in self.input_tensors:
- input_tensors_size += tensor.size
+ input_tensors_size += tensor.size()
self.file_model.write(struct.pack('<Q', input_tensors_size))
# output offset (last lasyer)
self.file_model.write(struct.pack('<Q', self.output_tensors[-1].eaddr))
# output size (last layer)
- self.file_model.write(struct.pack('<Q', self.output_tensors[-1].size))
+ self.file_model.write(struct.pack('<Q', self.output_tensors[-1].size()))
else:
# fill dummy data for backward campatibility
self.file_model.write(struct.pack('<Q', 0))
for weight in self.input_weights:
self.file_model.write(weight.data)
- ## @brief fill the content of tensors
- def fill_tensors (self):
- if self.version == 1:
- # input tensors including esum
- with open(self.outdir + "/input_fmap.bin", "wb") as file_tensor:
- for tensor in self.input_tensors:
- file_tensor.seek(tensor.eaddr - self.input_tensors[0].eaddr)
- file_tensor.write(tensor.data)
- # output tensors of the last layer
- with open(self.outdir + "/output_fmap.bin", "wb") as file_tensor:
- file_tensor.write(self.output_tensors[-1].data)
- elif self.version == 2:
- # input tensors including esum
- for idx, tensor in enumerate(self.input_tensors):
- with open(self.outdir + "/input_fmap_" + str(idx) + ".bin", "wb") as file_tensor:
- file_tensor.write(tensor.data)
- # output tensors of the last layer
- with open(self.outdir + "/output_fmap_0.bin", "wb") as file_tensor:
- file_tensor.write(self.output_tensors[-1].data)
- else:
- raise Exception("Invalid version")
-
## @brief close all files
def finalize (self):
self.file_model.close()
help='A path of directory where the test data are located')
parser.add_argument('version', metavar='VER', type=int, nargs=1,
help='A version of NPU model binary (only support 1 or 2)')
- parser.add_argument('--debug', '-d', action='store_true', dest='debug_mode', default=False,
- help='Turn on the debug mode (optional)', required=False)
parser.add_argument('--single', '-s', action='store_true', dest='single_mode', default=False,
help='Turn on the single layer mode (optional)', required=False)
+ parser.add_argument('--debug', '-d', action='store_true', dest='debug_mode', default=False,
+ help='Turn on the debug mode (optional)', required=False)
parser.add_argument('--outdir', '-o', dest='outdir', type=str,
help='A path of directory where the generated model would be placed (optional)', required=False)
args = parser.parse_args()
--- /dev/null
+/**
+ * Proprietary
+ * Copyright (C) 2019 Samsung Electronics
+ * Copyright (C) 2019 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file gen_ref_data.cpp
+ * @date 13 Feb 2020
+ * @brief generate the reference input/output/weight data for NPU models
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @note This requires the libnpuvision package to be compiled.
+ */
+
+#include <iostream>
+#include <fstream>
+#include <unistd.h>
+#include <string.h>
+
+#include <NPUemul.h>
+
+using namespace std;
+
+/** @brief a wrapper for TrinityCore/DataGen */
+class RefDataGen: public NPUCoreEmul {
+ public:
+ /** @brief constructor */
+ RefDataGen(const char *_output_dir): output_dir(_output_dir) {
+ model = buffer = NULL;
+ }
+ /** @brief destructor */
+ ~RefDataGen();
+ /** @brief load and verify the model binary */
+ int loadModel(const string target_path);
+ /** @brief emit function to generate reference data for the model */
+ int emit();
+
+ protected:
+ /** @brief dump input or output data */
+ void dumpData(const string name, const char *data, uint32_t size);
+ /** @brief generate random input data */
+ void genRandomInput();
+ /** @brief generate golden output data */
+ void genGoldenOutput();
+
+ private:
+ const string output_dir;
+ char *model;
+ char *buffer;
+ npubin_meta meta;
+};
+
+/** @brief destructor */
+RefDataGen::~RefDataGen()
+{
+ if (model)
+ delete model;
+ if (buffer)
+ delete buffer;
+}
+
+/** @brief load and verify the model binary */
+int RefDataGen::loadModel(const string target_path)
+{
+ ifstream ifs(target_path, ios::binary);
+ size_t model_size;
+
+ if (!ifs.is_open()) {
+ cerr << "Cannot find the target model " << target_path << endl;
+ return -1;
+ }
+
+ ifs.seekg (0, ios::end);
+ model_size = ifs.tellg();
+ if (model_size <= 0) {
+ cerr << "Invalid model binary" << endl;
+ goto exit_error;
+ }
+ ifs.seekg (0, ios::beg);
+
+ this->model = new char[model_size];
+ ifs.read(this->model, model_size);
+
+ memcpy(&this->meta, this->model, NPUBIN_META_SIZE);
+ if (this->meta.size != model_size) {
+ cerr << "The model size is not matched" << endl;
+ goto exit_error;
+ }
+
+ if (this->meta.buffer_size == 0) {
+ cerr << "The buffer size should be larger than 0" << endl;
+ goto exit_error;
+ }
+
+ if (!CHECK_NPUBIN(this->meta.magiccode)) {
+ cerr << "This model binary is not compatible to our design" << endl;
+ cerr << "Please refer to ./include/common/npubinfmt.h" << endl;
+ goto exit_error;
+ }
+
+ if (NPUBIN_VERSION(this->meta.magiccode) > NPUBIN_VERSION_MAX) {
+ cerr << "Unsupported npubinfmt version" << endl;
+ goto exit_error;
+ }
+
+ this->buffer = new char[this->meta.buffer_size];
+
+ ifs.close();
+ return 0;
+
+exit_error:
+ if (this->model)
+ delete this->model;
+ ifs.close();
+ return -1;
+}
+
+/** @brief dump input or output data */
+void RefDataGen::dumpData(const string name, const char *data, uint32_t size)
+{
+ ofstream ofs(output_dir + '/' + name, ios::binary);
+
+ if (!ofs.is_open()) {
+ cerr << "Cannot create input data" << endl;
+ return;
+ }
+
+ ofs.write(data, size);
+ ofs.close();
+}
+
+/** @brief generate random input data */
+void RefDataGen::genRandomInput()
+{
+ srand (time(NULL));
+
+ switch (NPUBIN_VERSION(this->meta.magiccode)) {
+ case 0: /* regarded as version 1 */
+ case 1:
+ for (uint64_t i = 0; i < this->meta.input_size; i++)
+ this->buffer[this->meta.input_offset + i] = (uint8_t) rand();
+ dumpData("input_fmap.bin",
+ this->buffer + this->meta.input_offset, this->meta.input_size);
+ break;
+ case 2:
+ for (uint32_t i = 0; i < this->meta.input_num; i++) {
+ /* it may not be the exact size; but enough for testing */
+ uint32_t input_size = this->meta.input_elem_size[i];
+
+ for (uint32_t j = 0; j < MAX_RANK; j++)
+ input_size *= this->meta.input_dims[i][j];
+
+ for (uint32_t j = 0; j < input_size; j++)
+ this->buffer[this->meta.input_offsets[i] + j] = (uint8_t) rand();
+
+ dumpData("input_fmap_" + to_string(i) + ".bin",
+ this->buffer + this->meta.input_offsets[i], input_size);
+ }
+ break;
+ default:
+ abort (); /* already checked */
+ }
+}
+
+/** @brief generate golden output data */
+void RefDataGen::genGoldenOutput()
+{
+ switch (NPUBIN_VERSION(this->meta.magiccode)) {
+ case 0: /* regarded as version 1 */
+ case 1:
+ dumpData("output_fmap.bin",
+ this->buffer + this->meta.output_offset, this->meta.output_size);
+ break;
+ case 2:
+ for (uint32_t i = 0; i < this->meta.output_num; i++) {
+ /* it may not be the exact size; but enough for testing */
+ uint32_t output_size = this->meta.output_elem_size[i];
+
+ for (uint32_t j = 0; j < MAX_RANK; j++)
+ output_size *= this->meta.output_dims[i][j];
+
+ dumpData("output_fmap_" + to_string(i) + ".bin",
+ this->buffer + this->meta.output_offsets[i], output_size);
+ }
+ break;
+ default:
+ abort (); /* already checked */
+ }
+}
+
+/** @brief emit function to generate reference data for the model */
+int RefDataGen::emit()
+{
+ genRandomInput();
+ int ret;
+
+ ret = run(this->model, this->buffer);
+ if (ret == 0)
+ genGoldenOutput();
+
+ return ret;
+}
+
+static void print_usage(const char *name)
+{
+ cerr << "Usage: " << name << " [options] binary" << endl;
+ cerr << "Options: " << endl;
+ cerr << " -o <arg> \t Specify output directory path" << endl;
+}
+
+static char default_output_dir[] = ".";
+
+/** @brief main function */
+int main(int argc, char **argv)
+{
+ char *target_path = NULL;
+ char *output_dir = NULL;
+ int i, c;
+
+ /* parse option arguments */
+ opterr = 0;
+ while ((c = getopt (argc, argv, "o:")) != -1) {
+ switch (c)
+ {
+ case 'o':
+ output_dir = optarg;
+ break;
+ case '?':
+ if (optopt == 'o')
+ cerr << "Option -o requires an argument" << endl;
+ else
+ cerr << "Unknown option" << endl;
+ cerr << endl;
+
+ print_usage(argv[0]);
+ return -1;
+ default:
+ abort(); /* impossible */
+ }
+ }
+
+ if (output_dir == NULL)
+ output_dir = default_output_dir;
+
+ /* parse non-option arguments */
+ for (i = optind; i < argc; i++)
+ target_path = argv[i];
+
+ if (target_path == NULL) {
+ print_usage(argv[0]);
+ return -1;
+ }
+
+ /* generate reference data */
+ RefDataGen gen(output_dir);
+
+ if (gen.loadModel(target_path) != 0)
+ return -1;
+
+ return gen.emit();
+}
* Copyright (C) 2019 Dongju Chae <dongju.chae@samsung.com>
*/
/**
- * @file testdata_gen.cpp
+ * @file gen_visa_prog.cpp
* @date 07 Nov 2019
- * @brief geneate the testdata for multi-layer models
+ * @brief generate VISA program binary for multi-layer NPU models
* @author Dongju Chae <dongju.chae@samsung.com>
* @note This requires the libnpuvision package to be compiled.
*/
#include <cstdlib>
#include <ctime>
#include <vector>
+
#include <TrinityCore.h>
-#include <DataGen.h>
#define INPUT_ADDR_START 0x1000
#define OUTPUT_ADDR_START 0x100000
static int avgp_mults[17] = {0,0,1073741824,1431655765,1073741824,0,1431655765,0,1073741824,1908874354,0,0,1431655765,0,0,0,1073741824};
/** @brief a wrapper for TrinityCore/DataGen */
-class TestdataGen: public TrinityCore<64>, public DataGen {
+class ProgGen: public TrinityCore<64> {
public:
/** @brief constructor */
- TestdataGen(const char* _dir, uint32_t _max_ops, uint32_t _version)
- : dir(_dir), version(_version) {
- ops.reserve(_max_ops);
+ ProgGen(const string _dir): dir(_dir) {
addr_in = addr_wgt = 0;
addr_out = OUTPUT_ADDR_START;
addr_out_prev = INPUT_ADDR_START + get_random_offset(); /* any value is fine */
}
/** @brief emit function to generate the testdata for multi-layer models */
- void emit();
+ int emit();
/** @brief append an trinity operation to the operation list */
void append(TRINITY_CORE_PARA_OP& op) { ops.push_back(op); }
/** @brief convert the operation to VISA assembly codes */
private:
const string dir;
- uint32_t version;
uint32_t addr_in, addr_wgt;
uint32_t addr_out, addr_out_prev;
vector<TRINITY_CORE_PARA_OP> ops;
};
/** @brief get random offset aligned to 0x1000 */
-uint32_t TestdataGen::get_random_offset()
+uint32_t ProgGen::get_random_offset()
{
- /** consider non-contiguous tensors */
- if (version == 2)
- return (std::rand() % 9 + 1) * 0x1000;
- else
- return 0;
+ return (std::rand() % 9 + 1) * 0x1000;
}
/** @brief convert the operation to VISA assembly codes */
-void TestdataGen::write_asm_file(size_t idx, TRINITY_FMAP_PARA& in,
+void ProgGen::write_asm_file(size_t idx, TRINITY_FMAP_PARA& in,
TRINITY_FMAP_PARA& out, WGT_PARA& weight)
{
TRINITY_CORE_PARA_OP& op = ops[idx];
}
/** @brief emit function to generate the testdata for multi-layer models */
-void TestdataGen::emit()
+int ProgGen::emit()
{
- TRINITY_FMAP_PARA para_fmap_out_prev;
- bool remove_pad = true;
- bool depth_first = true;
- bool bin_file_only = true;
const string mkdir_cmd = "mkdir -p " + dir;
- int ret = 0;
+ const string encode_cmd = "encoder " + dir + "/program.asm " + dir + "/program.bin";
- ret = system(mkdir_cmd.c_str());
- if (ret != 0) {
- cerr << "Fail to create a directory, " << mkdir_cmd << ", where the test data would be placed" << endl;
- TR_ASSERT(false);
+ if (system(mkdir_cmd.c_str()) != 0) {
+ cerr << "Fail to create a directory, " << mkdir_cmd
+ << ", where the result would be placed" << endl;
+ return -1;
}
ofs_asm.open(dir + "/program.asm");
TRINITY_FMAP_PARA para_fmap_out;
WGT_PARA para_weight;
- /** parse the op and set its variables */
- if (!calc_tensor_data_size(ops[idx], para_fmap_in, para_weight, para_fmap_out)) {
- cerr << "Fail to parse a trinity op" << endl;
- TR_ASSERT(false);
- }
-
- /** compare dimensions of this layer's input and the previous layer's output */
- if (idx != 0 &&
- !(para_fmap_in.height == para_fmap_out_prev.height &&
- para_fmap_in.width == para_fmap_out_prev.width &&
- para_fmap_in.depth == para_fmap_out_prev.depth)) {
- cerr << "Mismatch detected for dimensions between layers" << endl;
- TR_ASSERT(false);
- }
- para_fmap_out_prev = para_fmap_out;
-
- WGT_BIN_DATA wmod = WBIN_NORMAL;
-
- /** input (== in0) fmap (random generation for the first layer) */
- SIZE3D size3d_in;
- size3d_in.depth = para_fmap_in.depth;
- size3d_in.height = para_fmap_in.height;
- size3d_in.width = para_fmap_in.width;
-
- TR_FMAP trinity_fmap_in;
- trinity_fmap_in.alloc(size3d_in);
- if (idx == 0) {
- trinity_fmap_in.gen_rand(9, 0);
- trinity_fmap_in.write_data_file(dir, "_input_fmap_" + to_string(idx),
- remove_pad, depth_first, bin_file_only);
- } else {
- Point1D<int8_t, 8> data_mem;
- data_mem.clean();
- TR_ASSERT(read_data_file(dir, "_output_fmap_" + to_string(idx-1), data_mem));
- trinity_fmap_in.set_data(data_mem);
+ /** parse the operation and assign its parameters */
+ if (!calc_tensor_data_size(ops[idx],
+ para_fmap_in, para_weight, para_fmap_out)) {
+ cerr << "Fail to parse a trinity operation" << endl;
+ ofs_asm.close();
+ return -1;
}
- /** output fmap (SW emulation) */
- SIZE3D size3d_out;
- size3d_out.depth = para_fmap_out.depth;
- size3d_out.height = para_fmap_out.height;
- size3d_out.width = para_fmap_out.width;
-
- TR_FMAP trinity_fmap_out;
- trinity_fmap_out.alloc(size3d_out);
-
- /** esum (== in1) fmap (if exist) */
- TR_FMAP trinity_esum_in;
- if (ops[idx].info.OPCODE == TRINITY_OPCODE_ESUM || ops[idx].info.CNV_ESUM_EN) {
- if (ops[idx].info.OPCODE == TRINITY_OPCODE_ESUM) {
- trinity_esum_in.alloc(size3d_in);
- } else {
- trinity_esum_in.alloc(size3d_out);
- }
- trinity_esum_in.gen_rand(9, 0);
- trinity_esum_in.write_data_file(dir, "_input_esum_" + to_string(idx),
- remove_pad, depth_first, bin_file_only);
- }
-
- /** input weight (random generation) */
- TrinityWgt trinity_weight;
+ /** generate weight data */
if (ops[idx].info.OPCODE < TRINITY_OPCODE_MAXP) {
+ TrinityWgt trinity_weight;
+
trinity_weight.alloc(para_weight);
trinity_weight.gen_rand();
- trinity_weight.write_data_file(dir, "_input_weight_" + to_string(idx),
- wmod, bin_file_only);
+ trinity_weight.write_data_file(dir, "input_weight_" + to_string(idx),
+ WBIN_NORMAL, true);
}
- /** run emulation */
- do_main_operation(ops[idx],
- trinity_fmap_in,
- trinity_esum_in,
- trinity_weight,
- trinity_fmap_out);
-
- trinity_fmap_out.write_data_file(dir, "_output_fmap_" + to_string(idx),
- remove_pad, depth_first, bin_file_only);
-
write_asm_file(idx, para_fmap_in, para_fmap_out, para_weight);
}
ofs_asm << "SAW" << endl;
ofs_asm << "NOP" << endl;
ofs_asm.close();
+
+ if (system(encode_cmd.c_str()) != 0) {
+ cerr << "Fail to encode visa program, " << encode_cmd << endl;
+ return -1;
+ }
+
+ return 0;
}
/** @brief generate testdata for testcase1 (CONV/CONV) */
-void gen_testdata_testcase1(const char* dir, uint32_t version)
+int gen_prog_testcase1(const string base, const string name)
{
- TestdataGen gen(dir, 2, version);
+ ProgGen gen(base + '/' + name);
+
+ cout << "testcase1: CONV/CONV" << endl;
/** 1) 3x3 CONV */
TRINITY_CORE_PARA_OP conv1;
gen.append(conv2);
- gen.emit();
+ return gen.emit();
}
/** @brief generate testdata for testcase2 (CONV/ESUM/MAXP) */
-void gen_testdata_testcase2(const char* dir, uint32_t version)
+int gen_prog_testcase2(const string base, const string name)
{
- TestdataGen gen(dir, 3, version);
+ ProgGen gen(base + '/' + name);
+
+ cout << "testcase2: CONV/ESUM/MAXP" << endl;
/** 1) 3x3 CONV */
TRINITY_CORE_PARA_OP conv;
gen.append(maxp);
- gen.emit();
+ return gen.emit();
}
/** @brief generate testdata for testcase3 (CONVE/ReLU/AVGP) */
-void gen_testdata_testcase3(const char* dir, uint32_t version)
+int gen_prog_testcase3(const string base, const string name)
{
- TestdataGen gen(dir, 3, version);
+ ProgGen gen(base + '/' + name);
+
+ cout << "testcase3: CONVE/ReLU/AVGP" << endl;
/** 1) 3x3 CONVE */
TRINITY_CORE_PARA_OP conve;
gen.append(avgp);
- gen.emit();
+ return gen.emit();
}
/** @brief main function */
-int main()
+int main(int argc, char **argv)
{
- std::srand(time(NULL));
-
- cout << "testcase1: CONV/CONV" << endl;
- gen_testdata_testcase1("testcase1_v1", 1);
- gen_testdata_testcase1("testcase1_v2", 2);
+ if (argc != 2) {
+ cerr << "Usage: " << argv[0] << " result_dir" << endl;
+ return -1;
+ }
- cout << "testcase2: CONV/ESUM/MAXP" << endl;
- gen_testdata_testcase2("testcase2_v1", 1);
- gen_testdata_testcase2("testcase2_v2", 2);
+ std::srand(time(NULL));
- cout << "testcase3: CONVE/ReLU/AVGP" << endl;
- gen_testdata_testcase3("testcase3_v1", 1);
- gen_testdata_testcase3("testcase3_v2", 2);
+ TR_ASSERT(gen_prog_testcase1(argv[1], "testcase1") == 0);
+ TR_ASSERT(gen_prog_testcase2(argv[1], "testcase2") == 0);
+ TR_ASSERT(gen_prog_testcase3(argv[1], "testcase3") == 0);
- /** @todo add more test cases for multi-layer models */
+ /** @todo add more multi-layer models */
return 0;
}
# Build tools
-gen_testdata_tool = executable('gen-testdata',
- 'testdata_gen.cpp',
- dependencies : dependency('libnpuvision'),
+libnpuvision_dep = dependency('libnpuvision')
+
+gen_visa_asm = executable('gen_visa_prog',
+ 'gen_visa_prog.cpp',
+ include_directories: ne_common_inc,
+ dependencies : [libnpuvision_dep],
+ install : false)
+
+gen_ref_data = executable('gen_ref_data',
+ 'gen_ref_data.cpp',
+ include_directories: ne_common_inc,
+ dependencies : [libnpuvision_dep, ne_core_npu_emul_dep],
install : false)