From 925861593c9647b0d162d785ae5d0f26704df481 Mon Sep 17 00:00:00 2001 From: Dongju Chae Date: Thu, 13 Aug 2020 15:02:00 +0900 Subject: [PATCH] [UnitTest] Add new unittest for libnpuhost This patch adds new unittest for libnpuhost. Signed-off-by: Dongju Chae --- meson.build | 4 +- src/core/npu/NPUdrvAPI_emul.cc | 4 + tests/unittests/meson.build | 10 + tests/unittests/ne_libnpuhost_test.cc | 367 ++++++++++++++++++++++++++++++++++ 4 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 tests/unittests/ne_libnpuhost_test.cc diff --git a/meson.build b/meson.build index 12649d1..e4ea57f 100644 --- a/meson.build +++ b/meson.build @@ -66,9 +66,11 @@ ne_libdir = join_paths(ne_prefix, get_option('libdir')) ne_bindir = join_paths(ne_prefix, get_option('bindir')) ne_includedir = join_paths(ne_prefix, get_option('includedir')) ne_inidir = get_option('sysconfdir') -add_project_arguments('-DNE_INIDIR="' + ne_inidir + '"', language: ['c', 'cpp']) ne_datadir = join_paths(ne_prefix, join_paths(get_option('datadir'), 'npu-engine')) +add_project_arguments('-DNE_INIDIR="' + ne_inidir + '"', language: ['c', 'cpp']) +add_project_arguments('-DNE_DATADIR="' + ne_datadir + '"', language: ['c', 'cpp']) + ne_common_inc = include_directories('include/common') ne_host_inc = include_directories('include/host') diff --git a/src/core/npu/NPUdrvAPI_emul.cc b/src/core/npu/NPUdrvAPI_emul.cc index dc86957..2f0ae8f 100644 --- a/src/core/npu/NPUdrvAPI_emul.cc +++ b/src/core/npu/NPUdrvAPI_emul.cc @@ -139,6 +139,8 @@ ThreadSafeMap TrinityEmulAPI::task_map_; std::atomic EmulElement::global_id_ (0); /** @brief element's global id */ std::atomic TrinityEmulAPI::global_fd_ (0); +/** @brief global lock for run */ +static std::mutex global_lock; /** * @brief constructor of emulation API driver @@ -390,6 +392,8 @@ TrinityEmulAPI::runInput (input_config_t *input_config) const if ((dev_type_ & DEVICETYPE_MASK) == DEVICETYPE_TRIA) return 0; + std::unique_lock lock (global_lock); + /** either buffer or segment table */ EmulElement *elem_input = elem_map_.find (input_config->dbuf_fd); if (elem_input == nullptr || elem_input->getAddr () == nullptr) diff --git a/tests/unittests/meson.build b/tests/unittests/meson.build index b8d0ce2..122359e 100644 --- a/tests/unittests/meson.build +++ b/tests/unittests/meson.build @@ -121,4 +121,14 @@ if gtest_dep.found() install_dir : join_paths(ne_bindir, 'unittests') ) test('unittest_ne_core_inputservice', unittest_ne_core_inputservice) + + unittest_ne_libnpuhost = executable('unittest_ne_libnpuhost', + ['ne_libnpuhost_test.cc'], + include_directories: [ne_host_inc, ne_common_inc], + dependencies: [gtest_dep, ne_core_dep, ne_test_utils_dep], + install : true, + install_rpath : ne_libdir, + install_dir : join_paths(ne_bindir, 'unittests') + ) + test('unittest_ne_libnpuhost', unittest_ne_libnpuhost) endif diff --git a/tests/unittests/ne_libnpuhost_test.cc b/tests/unittests/ne_libnpuhost_test.cc new file mode 100644 index 0000000..2dd4367 --- /dev/null +++ b/tests/unittests/ne_libnpuhost_test.cc @@ -0,0 +1,367 @@ +/** + * Proprietary + * Copyright (C) 2020 Samsung Electronics + * Copyright (C) 2020 Dongju Chae + */ +/** + * @file ne_libnpuhost_test.cc + * @date 12 Aug 2020 + * @brief UnitTests to test functionality of libnpuhost APIs + * @author Dongju Chae + * @bug No known bugs except for NYI items + */ + +#include +#include +#include +#include + +#include +#include + +#include "ne_unittest_utils.h" + +/** + * @brief test APIs to check number of devices + */ +TEST (ne_libnpuhost_test, get_num_devices) +{ + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIV_CONN_SOCIP), 0); + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIV2_CONN_SOCIP), 0); + + /* other aliases also work */ + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_CONN_UNKNOWN), 0); + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_CONN_USB), 0); + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_CONN_SOCIP), 0); + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_CONN_PCIE), 0); + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_CONN_FILESYS), 0); + + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIV_CONN_UNKNOWN), 0); + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIV2_CONN_UNKNOWN), 0); + + /* TRIA is not supported in FastModel */ +#if defined(ENABLE_EMUL) + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIA_CONN_SOCIP), 0); + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_TRIA_CONN_UNKNOWN), 0); + + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_ASR_CONN_SOCIP), 0); + EXPECT_GT (getnumNPUdeviceByType (NPUCOND_ASR_CONN_UNKNOWN), 0); +#endif +} + +/** + * @brief test APIs to check number of devices (negative cases) + */ +TEST (ne_libnpuhost_test, get_num_devices_n) +{ + EXPECT_EQ (getnumNPUdeviceByType ((dev_type) 0), 0); + EXPECT_EQ (getnumNPUdeviceByType ((dev_type) 0x1234), 0); + EXPECT_EQ (getnumNPUdeviceByType ((dev_type) 0xdeadbeef), 0); + EXPECT_EQ (getnumNPUdeviceByType ((dev_type) 0xffffffff), 0); +} + +static dev_type test_types[] = { + NPUCOND_TRIV_CONN_SOCIP, +#if defined(ENABLE_EMUL) + NPUCOND_TRIA_CONN_SOCIP, +#endif + NPUCOND_TRIV2_CONN_SOCIP +}; + +/** + * @brief test APIs to get a device instance + */ +TEST (ne_libnpuhost_test, get_device_instance) +{ + npudev_h dev; + int num_devices; + + for (int i = 0; i < sizeof(test_types) / sizeof(dev_type); i++) { + dev_type type = test_types[i]; + num_devices = getnumNPUdeviceByType (type); + ASSERT_GT (num_devices, 0); + + for (int j = 0; j < num_devices; j++) { + EXPECT_EQ (getNPUdeviceByType (&dev, type, j), 0); + putNPUdevice (dev); + } + } +} + +/** + * @brief test APIs to get a device instance (negative cases) + */ +TEST (ne_libnpuhost_test, get_device_instance_n) +{ + npudev_h dev; + int num_devices; + + for (int i = 0; i < sizeof(test_types) / sizeof(dev_type); i++) { + dev_type type = test_types[i]; + num_devices = getnumNPUdeviceByType (type); + ASSERT_GT (num_devices, 0); + + for (int j = 0; j < num_devices; j++) { + EXPECT_NE (getNPUdeviceByType (&dev, (dev_type) 0x0, j), 0); + EXPECT_NE (getNPUdeviceByType (&dev, (dev_type) 0x1234, j), 0); + EXPECT_NE (getNPUdeviceByType (&dev, (dev_type) 0xdeadbeef, j), 0); + EXPECT_NE (getNPUdeviceByType (&dev, (dev_type) 0xffffffff, j), 0); + } + } +} + +class TesterTrinity { + public: + TesterTrinity () : done_ (0), repeat_ (0) {} + + void wait_runs () { + std::unique_lock lock (m_); + cv_.wait (lock, [this]() { return done_ == repeat_; }); + } + + void setRepeat (uint32_t repeat) { repeat_ = repeat; } + + static void callback (output_buffers *output, uint64_t sequence, + void *data) { + TesterTrinity *tester = static_cast(data); + tester->finish (output); + } + + private: + void finish (output_buffers * output) { + for (uint32_t idx = 0; idx < output->num_buffers; idx++) { + char * output_data = static_cast (output->bufs[idx].addr); + /* do nothing */ + free (output_data); + + } + + std::unique_lock lock (m_); + done_++; + cv_.notify_one (); + } + + uint32_t done_; + uint32_t repeat_; + + std::mutex m_; + std::condition_variable cv_; +}; + +class TesterTRIA : public TesterTrinity, public UtilTRIA { + public: + TesterTRIA () {} +}; + +class TesterTRIV : public TesterTrinity, public UtilTRIV { + public: + TesterTRIV () {} +}; + +class TesterTRIV2 : public TesterTrinity, public UtilTRIV2 { + public: + TesterTRIV2 () {} +}; + +/** + * @brief test APIs to run model with input data (TRIV) + */ +TEST (ne_libnpuhost_test, run_input_triv_binfmt_v1) +{ + TesterTRIV tester; + + ASSERT_EQ (tester.init (), 0); + + std::string model_dir (NE_DATADIR); + model_dir += "/testdata/npubinfmt_v1/testcase1"; + + tester.setModelDir (model_dir); + ASSERT_EQ (tester.loadModel (), 0); + + const npubin_meta *meta = tester.getMetadata (); + ASSERT_NE (meta, nullptr); + + /* prepare input */ + std::string input_path = model_dir + "/input_fmap.bin"; + + input_buffers input; + input.num_buffers = 1; + + input.bufs[0].size = get_file_size (input_path.c_str ()); + input.bufs[0].filepath = input_path.c_str (); + input.bufs[0].type = BUFFER_FILE; + + /* run input */ + output_buffers output; + EXPECT_EQ (tester.run (&input, &output), 0); + + tester.setRepeat (3); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + tester.wait_runs (); +} + +/** + * @brief test APIs to run model with input data (TRIV) + */ +TEST (ne_libnpuhost_test, run_input_triv_binfmt_v2) +{ + TesterTRIV tester; + + ASSERT_EQ (tester.init (), 0); + + std::string model_dir (NE_DATADIR); + model_dir += "/testdata/npubinfmt_v2/testcase1"; + + tester.setModelDir (model_dir); + ASSERT_EQ (tester.loadModel (), 0); + + const npubin_meta *meta = tester.getMetadata (); + ASSERT_NE (meta, nullptr); + + /* prepare input */ + std::vector input_path; + input_buffers input; + + input_path.resize (meta->input_num); + input.num_buffers = meta->input_num; + for (uint32_t idx = 0; idx < meta->input_num; idx++) { + input_path[idx] = model_dir + "/input_fmap_" + std::to_string (idx) + ".bin"; + input.bufs[idx].size = get_file_size (input_path[idx].c_str ()); + input.bufs[idx].filepath = input_path[idx].c_str (); + input.bufs[idx].type = BUFFER_FILE; + } + + /* run input */ + output_buffers output; + EXPECT_EQ (tester.run (&input, &output), 0); + + tester.setRepeat (3); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + tester.wait_runs (); +} + +#if defined(ENABLE_EMUL) +/** + * @brief test APIs to run model with input data (TRIA) + */ +TEST (ne_libnpuhost_test, run_input_tria) +{ + TesterTRIA tester; + + ASSERT_EQ (tester.init (), 0); + + /* run input */ + input_buffers input; + input.num_buffers = 1; + input.bufs[0].size = 4096; + input.bufs[0].type = BUFFER_MAPPED; + + ASSERT_EQ (allocNPU_inputBuffers (tester.getDeviceHandle (), &input), 0); + + output_buffers output; + EXPECT_EQ (tester.run (&input, &output), 0); + + tester.setRepeat (3); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + tester.wait_runs (); + + cleanNPU_inputBuffers (tester.getDeviceHandle (), &input); +} +#endif + +/** + * @brief test APIs to run model with input data (TRIV2) + */ +TEST (ne_libnpuhost_test, run_input_triv2_binfmt_v3) +{ + TesterTRIV2 tester; + + ASSERT_EQ (tester.init (), 0); + + std::string model_dir (NE_DATADIR); + model_dir += "/testdata/npubinfmt_v3/CONV_I8_008"; + + tester.setModelDir (model_dir); + ASSERT_EQ (tester.loadModel (), 0); + + const npubin_meta *meta = tester.getMetadata (); + ASSERT_NE (meta, nullptr); + + /* prepare input */ + std::vector input_path; + input_buffers input; + + input_path.resize (meta->input_seg_num); + input.num_buffers = meta->input_seg_num; + for (uint32_t idx = 0; idx < meta->input_seg_num; idx++) { + input_path[idx] = model_dir + "/input_fmap_" + std::to_string (idx) + ".bin"; + input.bufs[idx].size = get_file_size (input_path[idx].c_str ()); + input.bufs[idx].filepath = input_path[idx].c_str (); + input.bufs[idx].type = BUFFER_FILE; + } + + /* run input */ + output_buffers output; + EXPECT_EQ (tester.run (&input, &output), 0); + + tester.setRepeat (3); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + EXPECT_EQ (tester.run (&input, TesterTrinity::callback, + dynamic_cast(&tester)), 0); + + tester.wait_runs (); +} + +/** + * @brief test APIs to run model with internal input (TRIV2) + */ +TEST (ne_libnpuhost_test, run_internal_triv2_binfmt_v3) +{ + TesterTRIV2 tester; + + ASSERT_EQ (tester.init (), 0); + + std::string model_dir (NE_DATADIR); + model_dir += "/testdata/npubinfmt_v3/CONV_I8_008"; + + tester.setModelDir (model_dir); + ASSERT_EQ (tester.loadModel (), 0); + + const npubin_meta *meta = tester.getMetadata (); + ASSERT_NE (meta, nullptr); + + /* run internal */ + int id = tester.run ("/dev/dummy"); + EXPECT_GT (id, 0); + + usleep (TEST_SLEEP_MS); + + EXPECT_EQ (tester.stop (id), 0); +} + +/** + * @brief main function for unit test + */ +int +main (int argc, char **argv) +{ + return start_gtest (argc, argv); +} -- 2.7.4