[AppTest] Add apptest to test models with different constraints
authorDongju Chae <dongju.chae@samsung.com>
Mon, 7 Sep 2020 05:56:18 +0000 (14:56 +0900)
committer채동주/On-Device Lab(SR)/Staff Engineer/삼성전자 <dongju.chae@samsung.com>
Wed, 9 Sep 2020 04:59:14 +0000 (13:59 +0900)
This patch adds apptests to test models with different constraints
(e.g., priority), which can be used for multi-tasking.

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
debian/control
packaging/npu-engine.spec
src/core/ne-handler.cc
tests/apptests/meson.build
tests/apptests/tvn_triv2_xml.cc [new file with mode: 0644]
tests/unittests/ne_libnpuhost_test.cc
tests/utils/ne_test_utils.cc
tests/utils/ne_test_utils.h

index 79f4018..71f8424 100644 (file)
@@ -5,7 +5,7 @@ Maintainer: MyungJoo Ham <myungjoo.ham@samsung.com>
 Build-Depends: ninja-build, meson (>=0.50), debhelper (>=9),
  gcc-9 | gcc-8 | gcc-7 | gcc-6 | gcc-5, libgtest-dev, python,
  libiniparser-dev, pkg-config, cmake, libdrm-dev,
- linux-fvp-headers, npu-engine-emul
+ linux-fvp-headers, npu-engine-emul, libtinyxml2-dev
 Standards-Version: 3.8.2
 Homepage: https://research.samsung.com
 
index 7c73c45..fd56a54 100644 (file)
@@ -22,6 +22,7 @@ BuildRequires:        pkgconfig(iniparser)
 # test
 BuildRequires:  gtest-devel
 BuildRequires:  pkgconfig(libdrm)
+BuildRequires:  pkgconfig(tinyxml2)
 
 # test coverage dependency
 %if 0%{?test_coverage}
index 924ea8a..1197bc2 100644 (file)
@@ -1690,7 +1690,7 @@ TrinityAsr::callback (Request *req, npuOutputNotify cb, void *cb_data)
 {
   Buffer *buffer = req->getBuffer ();
   output_buffers output = {
-    .num_buffers = 1
+    .num_buffers = 0
   };
 
   /** TODO: finalize this impl. when the ASR's working scenario is determined */
index 1c0d054..31c7523 100644 (file)
@@ -131,3 +131,14 @@ executable ('apptest_tvn_triv2_recurring',
   install_rpath : ne_libdir,
   install_dir : join_paths(ne_bindir, 'apptests')
 )
+
+tinyxml2_dep = dependency ('tinyxml2')
+executable ('apptest_tvn_triv2_xml',
+  'tvn_triv2_xml.cc',
+  include_directories : ne_apptest_inc,
+  dependencies : [ne_test_utils_dep, thread_dep, tinyxml2_dep],
+  link_with : ne_library_shared,
+  install : true,
+  install_rpath : ne_libdir,
+  install_dir : join_paths(ne_bindir, 'apptests')
+)
diff --git a/tests/apptests/tvn_triv2_xml.cc b/tests/apptests/tvn_triv2_xml.cc
new file mode 100644 (file)
index 0000000..3c424ea
--- /dev/null
@@ -0,0 +1,234 @@
+/**
+ * Proprietary
+ * Copyright (C) 2020 Samsung Electronics
+ * Copyright (C) 2020 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file tvn_triv2_xml.cc
+ * @date 07 Sep 2020
+ * @brief AppTest to test models with different constraints using xml input
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+/**
+ * This program requires model descriptions written in .xml file
+ * The below shows the example xml file.
+ *
+ * <testcases>
+ *  <model name="mobilenet_v1" timeout="3000" priority="1">
+ *   <dirpath>/usr/share/npu-engine/testdata/npubinfmt_v3/MOBILENET_V1</dirpath>
+ *  </model>
+ *  <model name="mobilenet_v2" timeout="1000" priority="2">
+ *   <dirpath>/usr/share/npu-engine/testdata/npubinfmt_v3/MOBILENET_V2</dirpath>
+ *  </model>
+ *  ...
+ * </testcases>
+ */
+
+#include <string.h>
+#include <unistd.h>
+
+#include <tinyxml2.h>
+#include <ne_test_utils.h>
+
+using namespace std;
+using namespace tinyxml2;
+
+class Tester : public UtilTRIV2
+{
+  public:
+    Tester () : total_ (0), debug_ (false), sync_ (false) {}
+
+    void set_debug () {
+      debug_ = true;
+    }
+
+    void set_mute () {
+      /** redirect to /dev/null */
+      if (freopen("/dev/null", "w", stdout) == NULL)
+        cerr << "Warning: Failed to reopen stdout\n";
+      if (freopen("/dev/null", "w", stderr) == NULL)
+        cerr << "Warning: Failed to reopen stderr\n";
+    }
+
+    void set_sync () {
+      sync_ = true;
+    }
+
+    int parse_xml (XMLDocument& xml) {
+      XMLElement *root = xml.RootElement ();
+      if (root == nullptr)
+        return -EINVAL;
+
+      XMLElement *elem = root->FirstChildElement ("model");
+      while (elem != nullptr) {
+        int priority = elem->IntAttribute ("priority");
+        int timeout = elem->IntAttribute ("timeout");
+
+        if (priority < NPU_PRIORITY_LOW ||
+            priority > NPU_PRIORITY_HIGH ||
+            timeout < 0)
+          return -EINVAL;
+
+        XMLElement *item = elem->FirstChildElement ("dirpath");
+        if (item == nullptr)
+          return -EINVAL;
+
+        if (UtilTRIV2::loadModel (item->GetText (), nullptr,
+              static_cast<npu_priority>(priority), timeout) != 0)
+          return -EINVAL;
+
+        total_++;
+        elem = elem->NextSiblingElement ("model");
+      }
+
+      return 0;
+    }
+
+    int init (string xml_file) {
+      int status = UtilTRIV2::init ();
+      if (status != 0) {
+        cerr << "Failed to initialize\n";
+        return status;
+      }
+
+      XMLDocument doc;
+      if (doc.LoadFile (xml_file.c_str ()) != XML_NO_ERROR) {
+        cerr << "Unable to find the given xml file\n";
+        return -ENOENT;
+      }
+
+      status = parse_xml (doc);
+      if (status != 0) {
+        cerr << "Invalid model description detected\n";
+        return status;
+      }
+
+      return 0;
+    }
+
+    int run () {
+      passed_ = 0;
+      done_ = 0;
+
+      int status = UtilTRIV2::runAll (callback, sync_);
+      if (status != 0) {
+        cerr << "Failed to run the model: " << status << "\n";
+        return status;
+      }
+
+      wait_runs ();
+
+      return passed_ == total_ ? 0 : -EINVAL;
+    }
+
+    void wait_runs () {
+      unique_lock<mutex> lock (m_);
+      cv_.wait (lock, [this]() { return done_ == total_; });
+    }
+
+    static void callback (output_buffers *output, uint64_t sequence,
+        void *data) {
+      const char *dirpath = static_cast<const char *> (data);
+      int err = 0;
+
+      for (uint32_t idx = 0; idx < output->num_buffers; idx++) {
+        char * output_data = static_cast<char*> (output->bufs[idx].addr);
+        off_t output_size = output->bufs[idx].size;
+        std::string golden_path (dirpath);
+
+        golden_path += "/output_fmap_" + std::to_string (idx) + ".bin";
+        err = compare_data (golden_path.c_str(), output_data, output_size);
+        free (output_data);
+
+        if (err != 0)
+          break;
+      }
+
+      report (err == 0);
+    }
+
+    static void report (bool passed) {
+      unique_lock<mutex> lock (m_);
+      done_++;
+      if (passed)
+        passed_++;
+      cv_.notify_one ();
+    }
+
+  private:
+    static mutex m_;
+    static condition_variable cv_;
+    static uint32_t done_;
+    static uint32_t passed_;
+
+    uint32_t total_;
+    bool debug_;
+    bool sync_;
+};
+
+mutex Tester::m_;
+condition_variable Tester::cv_;
+uint32_t Tester::done_ = 0;
+uint32_t Tester::passed_ = 0;
+
+static void
+print_usage (const char *prog_name)
+{
+  cerr << "Usage: " << prog_name << " [options] [.xml file]\n";
+  cerr << "Options: \n";
+  cerr << "  -h \t\t Show help messages\n";
+  cerr << "  -m \t\t Mute stdout/stderr messages\n";
+  cerr << "  -d \t\t Enable debugging mode\n";
+  cerr << "  -s \t\t Enable run sync mode\n";
+}
+
+/** @brief apptest main  */
+int
+main (int argc, char **argv)
+{
+  Tester tester;
+  int c, status;
+
+  optind = 0;
+  opterr = 0;
+  while ((c = getopt (argc, argv, "dmsh")) != -1) {
+    switch (c) {
+      case 'd':
+        tester.set_debug ();
+        break;
+      case 'm':
+        tester.set_mute ();
+        break;
+      case 's':
+        tester.set_sync ();
+        break;
+      case 'h':
+        print_usage(argv[0]);
+        return 0;
+    }
+  }
+
+  if (optind >= argc) {
+    cerr << "[APPTEST] " << argv[0] << ": SKIPPED\n";
+    return 0;
+  }
+
+  /** initialize triv2 device */
+  status = tester.init (argv[optind]);
+  if (status != 0)
+    goto err;
+
+  /** run the inference with the device */
+  status = tester.run ();
+  if (status != 0)
+    goto err;
+
+  cerr << "[APPTEST] " << argv[0] << ": PASSED\n";
+  return 0;
+
+err:
+  cerr << "[APPTEST] " << argv[0] << ": FAILED (" << status << ")\n";
+  return status;
+}
index 2828401..0aaff41 100644 (file)
@@ -179,100 +179,62 @@ TEST (ne_libnpuhost_test, register_model_n)
   putNPUdevice (dev);
 }
 
-class TesterTrinity {
-  public:
-    TesterTrinity () : done_ (0), repeat_ (0) {}
-
-    void wait_runs () {
-      std::unique_lock<std::mutex> 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<TesterTrinity *>(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<char*> (output->bufs[idx].addr);
-        /* do nothing */
-        free (output_data);
-
-      }
-
-      std::unique_lock<std::mutex> lock (m_);
-      done_++;
-      cv_.notify_one ();
-    }
-
-    uint32_t done_;
-    uint32_t repeat_;
-
-    std::mutex m_;
-    std::condition_variable cv_;
+struct test_sync {
+  std::mutex m;
+  std::condition_variable cv;
+  int done;
 };
 
-class TesterTRIA : public TesterTrinity, public UtilTRIA {
-  public:
-    TesterTRIA () {}
-};
+static struct test_sync tsync;
 
-class TesterTRIV : public TesterTrinity, public UtilTRIV {
-  public:
-    TesterTRIV () {}
-};
+static void wait_runs (int total)
+{
+  std::unique_lock<std::mutex> lock (tsync.m);
+  tsync.cv.wait (lock, [&]() { return tsync.done == total; });
+}
 
-class TesterTRIV2 : public TesterTrinity, public UtilTRIV2 {
-  public:
-    TesterTRIV2 () {}
-};
+/**
+ * @brief dummy callback for testing
+ */
+static void callback (output_buffers *output,
+    uint64_t sequence, void *data)
+{
+  for (uint32_t idx = 0; idx < output->num_buffers; idx++) {
+    /* do not check the validity of output data */
+    free (output->bufs[idx].addr);
+  }
+
+  std::unique_lock<std::mutex> lock (tsync.m);
+  tsync.done++;
+  tsync.cv.notify_one ();
+}
 
 /**
  * @brief test APIs to run model with input data (TRIV)
  */
 TEST (ne_libnpuhost_test, run_input_triv_binfmt_v1)
 {
-  TesterTRIV tester;
+  UtilTRIV tester;
 
   ASSERT_EQ (tester.init (), 0);
 
   std::string model_dir (NE_DATADIR);
   model_dir += "/testdata/npubinfmt_v1/testcase1";
 
-  tester.setModelDir (model_dir);
-  if (tester.loadModel () != 0)
-    return;
-
-  const npubin_meta *meta = tester.getMetadata ();
-  ASSERT_NE (meta, nullptr);
-
-  /* prepare input */
-  std::string input_path = model_dir + "/input_fmap.bin";
+  uint32_t model_id = 0;
 
-  input_buffers input;
-  input.num_buffers = 1;
+  tsync.done = 0;
 
-  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;
+  if (tester.loadModel (model_dir, &model_id) != 0)
+    /* skip */
+    return;
 
-  /* run input */
-  output_buffers output;
-  EXPECT_EQ (tester.run (&input, &output), 0);
+  EXPECT_EQ (tester.run (model_id, callback, true), 0);
+  EXPECT_EQ (tester.run (model_id, callback, false), 0);
+  EXPECT_EQ (tester.runAll (callback, true), 0);
+  EXPECT_EQ (tester.runAll (callback, false), 0);
 
-  tester.setRepeat (3);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  tester.wait_runs ();
+  wait_runs (4);
 }
 
 /**
@@ -280,30 +242,29 @@ TEST (ne_libnpuhost_test, run_input_triv_binfmt_v1)
  */
 TEST (ne_libnpuhost_test, run_input_triv_binfmt_v1_n)
 {
-  TesterTRIV tester;
+  UtilTRIV tester;
+
+  /* without init */
+  EXPECT_NE (tester.run (0, callback, true), 0);
+  EXPECT_NE (tester.run (0, callback, false), 0);
 
   ASSERT_EQ (tester.init (), 0);
 
+  /* run without model */
+  EXPECT_NE (tester.run (0, callback, true), 0);
+  EXPECT_NE (tester.run (0, callback, false), 0);
+
+  /* invalid model id */
   std::string model_dir (NE_DATADIR);
   model_dir += "/testdata/npubinfmt_v1/testcase1";
 
-  tester.setModelDir (model_dir);
-  if (tester.loadModel () != 0)
-    return;
-
-  const npubin_meta *meta = tester.getMetadata ();
-  ASSERT_NE (meta, nullptr);
-
-  /* without input */
-  EXPECT_NE (tester.run (nullptr, nullptr), 0);
+  uint32_t model_id = 0;
 
-  input_buffers input;
-  output_buffers output;
+  if (tester.loadModel (model_dir, &model_id) != 0)
+    /* skip */
+    return;
 
-  input.num_buffers = 0;
-  EXPECT_NE (tester.run (&input, &output), 0);
-  EXPECT_NE (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_NE (tester.run (model_id + 1, callback, true), 0);
 }
 
 /**
@@ -311,45 +272,28 @@ TEST (ne_libnpuhost_test, run_input_triv_binfmt_v1_n)
  */
 TEST (ne_libnpuhost_test, run_input_triv_binfmt_v2)
 {
-  TesterTRIV tester;
+  UtilTRIV tester;
 
   ASSERT_EQ (tester.init (), 0);
 
   std::string model_dir (NE_DATADIR);
   model_dir += "/testdata/npubinfmt_v2/testcase1";
 
-  tester.setModelDir (model_dir);
-  if (tester.loadModel () != 0)
-    return;
+  uint32_t model_id = 0;
 
-  const npubin_meta *meta = tester.getMetadata ();
-  ASSERT_NE (meta, nullptr);
+  tsync.done = 0;
 
-  /* prepare input */
-  std::vector<std::string> input_path;
-  input_buffers input;
+  if (tester.loadModel (model_dir, &model_id) != 0)
+    /* skip */
+    return;
 
-  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;
-  }
+  EXPECT_EQ (tester.run (model_id, callback, true), 0);
+  EXPECT_EQ (tester.run (model_id, callback, false), 0);
+
+  EXPECT_EQ (tester.runAll (callback, true), 0);
+  EXPECT_EQ (tester.runAll (callback, false), 0);
 
-  /* run input */
-  output_buffers output;
-  EXPECT_EQ (tester.run (&input, &output), 0);
-
-  tester.setRepeat (3);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  tester.wait_runs ();
+  wait_runs (4);
 }
 
 /**
@@ -357,31 +301,29 @@ TEST (ne_libnpuhost_test, run_input_triv_binfmt_v2)
  */
 TEST (ne_libnpuhost_test, run_input_triv_binfmt_v2_n)
 {
-  TesterTRIV tester;
+  UtilTRIV tester;
+
+  /* without init */
+  EXPECT_NE (tester.run (0, callback, true), 0);
+  EXPECT_NE (tester.run (0, callback, false), 0);
 
   ASSERT_EQ (tester.init (), 0);
 
+  /* run without model */
+  EXPECT_NE (tester.run (0, callback, true), 0);
+  EXPECT_NE (tester.run (0, callback, false), 0);
+
+  /* invalid model id */
   std::string model_dir (NE_DATADIR);
   model_dir += "/testdata/npubinfmt_v2/testcase1";
 
-  tester.setModelDir (model_dir);
-  if (tester.loadModel () != 0)
-    return;
-
-  const npubin_meta *meta = tester.getMetadata ();
-  ASSERT_NE (meta, nullptr);
-
-  /* without input */
-  EXPECT_NE (tester.run (nullptr, nullptr), 0);
-
-  input_buffers input;
-  output_buffers output;
+  uint32_t model_id = 0;
 
-  input.num_buffers = 0;
+  if (tester.loadModel (model_dir, &model_id) != 0)
+    /* skip */
+    return;
 
-  EXPECT_NE (tester.run (&input, &output), 0);
-  EXPECT_NE (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_NE (tester.run (model_id + 1, callback, true), 0);
 }
 
 #if defined(ENABLE_EMUL)
@@ -390,31 +332,16 @@ TEST (ne_libnpuhost_test, run_input_triv_binfmt_v2_n)
  */
 TEST (ne_libnpuhost_test, run_input_tria)
 {
-  TesterTRIA tester;
+  UtilTRIA 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);
+  tsync.done = 0;
 
-  output_buffers output;
-  EXPECT_EQ (tester.run (&input, &output), 0);
+  EXPECT_EQ (tester.run (0, callback, true), 0);
+  EXPECT_EQ (tester.run (0, callback, false), 0);
 
-  tester.setRepeat (3);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  tester.wait_runs ();
-
-  cleanNPU_inputBuffers (tester.getDeviceHandle (), &input);
+  wait_runs (2);
 }
 
 /**
@@ -422,20 +349,11 @@ TEST (ne_libnpuhost_test, run_input_tria)
  */
 TEST (ne_libnpuhost_test, run_input_tria_n)
 {
-  TesterTRIA tester;
+  UtilTRIA tester;
 
-  ASSERT_EQ (tester.init (), 0);
-
-  /* without input */
-  EXPECT_NE (tester.run (nullptr, nullptr), 0);
-
-  input_buffers input;
-  output_buffers output;
-
-  input.num_buffers = 0;
-  EXPECT_NE (tester.run (&input, &output), 0);
-  EXPECT_NE (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  /* without init */
+  EXPECT_NE (tester.run (0, callback, true), 0);
+  EXPECT_NE (tester.run (0, callback, false), 0);
 }
 
 #endif
@@ -445,46 +363,28 @@ TEST (ne_libnpuhost_test, run_input_tria_n)
  */
 TEST (ne_libnpuhost_test, run_input_triv2_binfmt_v3)
 {
-  TesterTRIV2 tester;
+  UtilTRIV2 tester;
 
   ASSERT_EQ (tester.init (), 0);
 
   std::string model_dir (NE_DATADIR);
   model_dir += "/testdata/npubinfmt_v3/CONV_I8_008";
 
-  tester.setModelDir (model_dir);
-  if (tester.loadModel () != 0)
-    return;
-
-  const npubin_meta *meta = tester.getMetadata ();
-  ASSERT_NE (meta, nullptr);
+  uint32_t model_id = 0;
 
-  /* prepare input */
-  std::vector<std::string> input_path;
-  input_buffers input;
+  tsync.done = 0;
 
-  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;
-  }
+  if (tester.loadModel (model_dir, &model_id) != 0)
+    /* skip */
+    return;
 
-  /* run input */
-  output_buffers output;
-  EXPECT_EQ (tester.run (&input, &output), 0);
+  EXPECT_EQ (tester.run (model_id, callback, true), 0);
+  EXPECT_EQ (tester.run (model_id, callback, false), 0);
 
-  tester.setRepeat (3);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
-  EXPECT_EQ (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_EQ (tester.runAll (callback, true), 0);
+  EXPECT_EQ (tester.runAll (callback, false), 0);
 
-  tester.wait_runs ();
+  wait_runs (4);
 }
 
 /**
@@ -492,30 +392,29 @@ TEST (ne_libnpuhost_test, run_input_triv2_binfmt_v3)
  */
 TEST (ne_libnpuhost_test, run_input_triv2_binfmt_v3_n)
 {
-  TesterTRIV2 tester;
+  UtilTRIV2 tester;
+
+  /* without init */
+  EXPECT_NE (tester.run (0, callback, true), 0);
+  EXPECT_NE (tester.run (0, callback, false), 0);
 
   ASSERT_EQ (tester.init (), 0);
 
+  /* run without model */
+  EXPECT_NE (tester.run (0, callback, true), 0);
+  EXPECT_NE (tester.run (0, callback, false), 0);
+
+  /* invalid model id */
   std::string model_dir (NE_DATADIR);
   model_dir += "/testdata/npubinfmt_v3/CONV_I8_008";
 
-  tester.setModelDir (model_dir);
-  if (tester.loadModel () != 0)
-    return;
-
-  const npubin_meta *meta = tester.getMetadata ();
-  ASSERT_NE (meta, nullptr);
-
-  /* without input */
-  EXPECT_NE (tester.run (nullptr, nullptr), 0);
+  uint32_t model_id = 0;
 
-  input_buffers input;
-  output_buffers output;
+  if (tester.loadModel (model_dir, &model_id) != 0)
+    /* skip */
+    return;
 
-  input.num_buffers = 0;
-  EXPECT_NE (tester.run (&input, &output), 0);
-  EXPECT_NE (tester.run (&input, TesterTrinity::callback,
-        dynamic_cast<TesterTrinity *>(&tester)), 0);
+  EXPECT_NE (tester.run (model_id + 1, callback, true), 0);
 }
 
 /**
@@ -523,27 +422,26 @@ TEST (ne_libnpuhost_test, run_input_triv2_binfmt_v3_n)
  */
 TEST (ne_libnpuhost_test, run_internal_triv2_binfmt_v3)
 {
-  TesterTRIV2 tester;
+  UtilTRIV2 tester;
 
   ASSERT_EQ (tester.init (), 0);
 
   std::string model_dir (NE_DATADIR);
   model_dir += "/testdata/npubinfmt_v3/CONV_I8_008";
 
-  tester.setModelDir (model_dir);
-  if (tester.loadModel () != 0)
-    return;
+  uint32_t model_id = 0;
 
-  const npubin_meta *meta = tester.getMetadata ();
-  ASSERT_NE (meta, nullptr);
+  if (tester.loadModel (model_dir, &model_id) != 0)
+    /* skip */
+    return;
 
   /* run internal */
-  int id = tester.run ("/dev/triv2-0");
+  int id = tester.runInternal (model_id, "/dev/triv2-0");
   EXPECT_GT (id, 0);
 
   usleep (TEST_SLEEP_MS);
 
-  EXPECT_EQ (tester.stop (id), 0);
+  EXPECT_EQ (tester.stopInternal (id), 0);
 }
 
 /**
@@ -551,33 +449,30 @@ TEST (ne_libnpuhost_test, run_internal_triv2_binfmt_v3)
  */
 TEST (ne_libnpuhost_test, run_internal_triv2_binfmt_v3_n)
 {
-  TesterTRIV2 tester;
+  UtilTRIV2 tester;
 
   ASSERT_EQ (tester.init (), 0);
 
   /* without model */
-  EXPECT_NE (tester.run ("/dev/triv2-0"), 0);
+  EXPECT_NE (tester.runInternal (0, "/dev/triv2-0"), 0);
 
   /* TODO: taskid of stop() is currently not checked */
 #if 0
   std::string model_dir (NE_DATADIR);
   model_dir += "/testdata/npubinfmt_v3/CONV_I8_008";
 
-  tester.setModelDir (model_dir);
-  if (tester.loadModel () != 0)
-    return;
+  uint32_t model_id = 0;
 
-  const npubin_meta *meta = tester.getMetadata ();
-  ASSERT_NE (meta, nullptr);
+  EXPECT_EQ (tester.loadModel (model_dir, &model_id), 0);
 
   /* invalid id */
-  int id = tester.run ("/dev/triv2-0");
+  int id = tester.runInternal (model_id, "/dev/triv2-0");
   ASSERT_GT (id, 0);
 
   usleep (TEST_SLEEP_MS);
 
-  EXPECT_NE (tester.stop (id + 1), 0);
-  EXPECT_EQ (tester.stop (id), 0);
+  EXPECT_NE (tester.stopInternal (id + 1), 0);
+  EXPECT_EQ (tester.stopInternal (id), 0);
 #endif
 }
 
index 8d85ec2..b8a7d26 100644 (file)
@@ -274,14 +274,12 @@ out:
 }
 
 /** @brief constructor of UtilTrinity */
-UtilTrinity::UtilTrinity (dev_type type)
+UtilTrinity::UtilTrinity (dev_type type, bool need_model)
 {
   dev_ = nullptr;
   type_ = type;
-  meta_ = nullptr;
-  version_ = 0;
-  model_id_ = 0;
   noti_mode_ = NPU_INTERRUPT;
+  need_model_ = need_model;
 }
 
 /** @brief destructor of UtilTrinity */
@@ -291,8 +289,7 @@ UtilTrinity::~UtilTrinity () {
     putNPUdevice (dev_);
   }
 
-  if (meta_ != nullptr)
-    free (meta_);
+  models_.clear ();
 }
 
 /** @brief initialize trinity device and get the device handle */
@@ -308,87 +305,197 @@ int UtilTrinity::init ()
 }
 
 /** @brief load NPU model and set relevant info */
-int UtilTrinity::loadModel ()
+int UtilTrinity::loadModel (std::string dirpath, uint32_t *model_id_ptr,
+    npu_priority priority, uint32_t timeout)
 {
-  if (model_dir_ == "")
+  if (dirpath == "")
     return -EINVAL;
 
-  std::string model_path;
-
-  model_path = model_dir_ + "/model.tvn";
-  meta_ = getNPUmodel_metadata (model_path.c_str(), false);
-  if (meta_ == nullptr)
+  std::string model_path = dirpath + "/model.tvn";
+  npubin_meta * meta = getNPUmodel_metadata (model_path.c_str(), false);
+  if (meta == nullptr)
     return -EINVAL;
 
-  version_ = NPUBIN_VERSION (meta_->magiccode);
-  if (!check_version ())
-    return -EINVAL;
+  generic_buffer model_buf;
+  UtilModel *model;
+  uint32_t model_id;
+  int status = -EINVAL;
 
-  generic_buffer model;
-  model.type = BUFFER_FILE;
-  model.size = meta_->size;
-  model.filepath = model_path.c_str();
+  if (!check_version (meta))
+    goto free_meta;
 
-  int status = allocNPU_modelBuffer (dev_, &model);
-  if (status != 0)
-    return status;
+  model_buf.type = BUFFER_FILE;
+  model_buf.size = meta->size;
+  model_buf.filepath = model_path.c_str();
 
-  status = registerNPUmodel (dev_, &model, &model_id_);
-  cleanNPU_modelBuffer (dev_, &model);
+  status = registerNPUmodel (dev_, &model_buf, &model_id);
   if (status != 0)
-    return status;
+    goto free_meta;
 
-  status = set_data_info ();
+  status = set_data_info (meta, model_id);
   if (status != 0)
-    return status;
+    goto free_meta;
 
-  status = set_constraint ();
+  status = set_constraint (model_id, timeout, priority);
   if (status != 0)
+    goto free_meta;
+
+  model = new UtilModel;
+  model->setMetadata (meta);
+  model->setDirpath (dirpath);
+  model->setModelID (model_id);
+
+  status = prepare_input (model);
+  if (status != 0) {
+    delete model;
     return status;
+  }
+
+  models_.push_back (std::unique_ptr<UtilModel>(model));
+  if (model_id_ptr)
+    *model_id_ptr = model_id;
+
+  return 0;
+
+free_meta:
+  free (meta);
+  return status;
+}
+
+/** @brief find the utility model instance */
+UtilModel *UtilTrinity::findModel (uint32_t model_id)
+{
+  for (auto& model : models_) {
+    if (model->getModelID () == model_id)
+      return model.get ();
+  }
+
+  return nullptr;
+}
+
+/** @brief run inference with the given model info */
+int UtilTrinity::run_each (UtilModel *model, npuOutputNotify cb, bool sync)
+{
+  int status = -EINVAL;
+  void *cb_data = nullptr;
+
+  if (cb == nullptr)
+    return -EINVAL;
+
+  if (need_model_ && model == nullptr)
+    return -ENOENT;
+
+  if (model)
+    cb_data = const_cast <char *> (model->getDirpath ().c_str ());
+
+  if (sync) {
+    output_buffers output;
+
+    if (model) {
+      status = runNPU_sync (dev_, model->getModelID (), model->getInput (),
+          &output);
+    } else {
+      /* TODO: revise this when TRIA is implemented */
+      input_buffers input;
+      input.num_buffers = 1;
+      input.bufs[0].size = 4096;
+      input.bufs[0].type = BUFFER_MAPPED;
+
+      status = allocNPU_inputBuffers (dev_, &input);
+      if (status != 0)
+        return status;
+
+      status = runNPU_sync (dev_, 0, &input, &output);
+      cleanNPU_inputBuffers (dev_, &input);
+    }
+
+    if (status != 0)
+      return status;
+
+    cb (&output, 0, cb_data);
+  } else {
+    if (model) {
+      status = runNPU_async (dev_, model->getModelID (), model->getInput (),
+          cb, NULL, cb_data, NPUASYNC_WAIT);
+    } else {
+      /* TODO: revise this when TRIA is implemented */
+      input_buffers input;
+      input.num_buffers = 1;
+      input.bufs[0].size = 4096;
+      input.bufs[0].type = BUFFER_MAPPED;
+
+      status = allocNPU_inputBuffers (dev_, &input);
+      if (status != 0)
+        return status;
+
+      status = runNPU_async (dev_, 0, &input, cb, NULL, cb_data, NPUASYNC_WAIT);
+      cleanNPU_inputBuffers (dev_, &input);
+    }
+
+    if (status != 0)
+      return status;
+  }
 
   return 0;
 }
 
-/** @brief run inference in sync mode */
-int UtilTrinity::run (input_buffers *input, output_buffers *output)
+/** @brief run inference with the given model id */
+int UtilTrinity::run (uint32_t model_id, npuOutputNotify cb, bool sync)
 {
-  return runNPU_sync (dev_, model_id_, input, output);
+  UtilModel *model = findModel (model_id);
+
+  return run_each (model, cb, sync);
 }
 
-/** @brief run inference in async mode */
-int UtilTrinity::run (input_buffers *input, npuOutputNotify cb,
-    void *cb_data)
+/** @brief run inference across all registered models */
+int UtilTrinity::runAll (npuOutputNotify cb, bool sync)
 {
-  return runNPU_async (dev_, model_id_, input, cb,
-      NULL, cb_data, NPUASYNC_WAIT);
+  for (auto& model : models_) {
+    int status = run_each (model.get (), cb, sync);
+    if (status != 0)
+      return status;
+  }
+
+  return 0;
 }
 
 /** @brief run inference with internal input */
-int UtilTrinity::run (std::string dev_path)
+int UtilTrinity::runInternal (uint32_t model_id, std::string dev_path)
 {
-  return runNPU_internalInput (dev_, model_id_, NPUINPUT_HW_RECURRING,
+  return runNPU_internalInput (dev_, model_id, NPUINPUT_HW_RECURRING,
       dev_path.c_str ());
 }
 
 /** @brief stop inference from run_internal */
-int UtilTrinity::stop (int id)
+int UtilTrinity::stopInternal (int task_id)
 {
-  return stopNPU_internalInput (dev_, id);
+  return stopNPU_internalInput (dev_, task_id);
+}
+
+/** @brief configure constraint for the model */
+int UtilTrinity::set_constraint (uint32_t model_id, uint32_t timeout,
+    npu_priority priority) {
+  npuConstraint constraint;
+
+  constraint.timeout_ms = timeout;
+  constraint.priority = priority;
+  constraint.notimode = noti_mode_;
+
+  return setNPU_constraint (dev_, model_id, constraint);
 }
 
 /** @brief implementation of check_version for TRIV */
-bool UtilTRIV::check_version () {
-  if (!meta_)
-    return false;
-  return (version_ == 1 || version_ == 2);
+bool UtilTRIV::check_version (npubin_meta *meta) {
+  uint64_t version = NPUBIN_VERSION (meta->magiccode);
+  return (version <= 2);
 }
 
 /** @brief implementation of set_data_info for TRIV */
-int UtilTRIV::set_data_info () {
+int UtilTRIV::set_data_info (npubin_meta *meta, uint32_t model_id) {
   tensors_data_info info_in;
   tensors_data_info info_out;
 
-  if (version_ == 1) {
+  if (NPUBIN_VERSION (meta->magiccode) <= 1) {
     info_in.num_info = 1;
     info_in.info[0].layout = DATA_LAYOUT_SRNPU;
     info_in.info[0].type = DATA_TYPE_SRNPU;
@@ -397,91 +504,89 @@ int UtilTRIV::set_data_info () {
     info_out.info[0].layout = DATA_LAYOUT_SRNPU;
     info_out.info[0].type = DATA_TYPE_SRNPU;
   } else {
-    info_in.num_info = meta_->input_num;
+    info_in.num_info = meta->input_num;
     for (uint32_t idx = 0; idx < info_in.num_info; idx++) {
       info_in.info[idx].layout = DATA_LAYOUT_SRNPU;
       info_in.info[idx].type = DATA_TYPE_SRNPU;
     }
 
-    info_out.num_info = meta_->output_num;
+    info_out.num_info = meta->output_num;
     for (uint32_t idx = 0; idx < info_out.num_info; idx++) {
       info_out.info[idx].layout = DATA_LAYOUT_SRNPU;
       info_out.info[idx].type = DATA_TYPE_SRNPU;
     }
   }
 
-  return setNPU_dataInfo (dev_, model_id_, &info_in, &info_out);
+  return setNPU_dataInfo (dev_, model_id, &info_in, &info_out);
 }
 
-/** @brief implementation of set_constraint for TRIV */
-int UtilTRIV::set_constraint () {
-  npuConstraint constraint;
-
-  constraint.timeout_ms = 5000;
-  constraint.priority = NPU_PRIORITY_MID;
-  constraint.notimode = noti_mode_;
+int UtilTRIV::prepare_input (UtilModel* model) {
+  input_buffers *input = model->getInput ();
+  npubin_meta *meta = model->getMetadata ();
+  std::string dirpath = model->getDirpath ();
+  uint32_t input_num;
 
-  return setNPU_constraint (dev_, model_id_, constraint);
-}
-
-/** @brief implementation of check_version for TRIA */
-bool UtilTRIA::check_version () {
-  if (!meta_)
-    return false;
-  return (version_ == 1 || version_ == 2);
-}
+  if (NPUBIN_VERSION (meta->magiccode) <= 1) {
+    input_num = 1;
+  } else {
+    input_num = meta->input_num;
+  }
 
-/** @brief implementation of set_data_info for TRIA */
-int UtilTRIA::set_data_info () {
-  /* no need to specify data info */
-  return 0;
-}
+  model->prepareInputPath (input_num);
+  input->num_buffers = input_num;
 
-/** @brief implementation of set_constraint for TRIA */
-int UtilTRIA::set_constraint () {
-  npuConstraint constraint;
+  for (uint32_t idx = 0; idx < input_num; idx++) {
+    const char *input_path = model->getInputPath (idx);
 
-  constraint.timeout_ms = 5000;
-  constraint.priority = NPU_PRIORITY_MID;
-  constraint.notimode = noti_mode_;
+    input->bufs[idx].size = get_file_size (input_path);
+    input->bufs[idx].filepath = input_path;
+    input->bufs[idx].type = BUFFER_FILE;
+  }
 
-  return setNPU_constraint (dev_, model_id_, constraint);
+  return 0;
 }
 
 /** @brief implementation of check_version for TRIV2 */
-bool UtilTRIV2::check_version () {
-  if (!meta_)
-    return false;
-  return (version_ == 3);
+bool UtilTRIV2::check_version (npubin_meta *meta) {
+  uint64_t version = NPUBIN_VERSION (meta->magiccode);
+  return (version == 3);
 }
 
 /** @brief implementation of set_data_info for TRIV2 */
-int UtilTRIV2::set_data_info () {
+int UtilTRIV2::set_data_info (npubin_meta *meta, uint32_t model_id) {
   tensors_data_info info_in;
   tensors_data_info info_out;
 
-  info_in.num_info = meta_->input_seg_num;
+  info_in.num_info = meta->input_seg_num;
   for (uint32_t idx = 0; idx < info_in.num_info; idx++) {
     info_in.info[idx].layout = DATA_LAYOUT_TRIV2;
     info_in.info[idx].type = DATA_TYPE_QASYMM8;
   }
 
-  info_out.num_info = meta_->output_seg_num;
+  info_out.num_info = meta->output_seg_num;
   for (uint32_t idx = 0; idx < info_out.num_info; idx++) {
     info_out.info[idx].layout = DATA_LAYOUT_TRIV2;
     info_out.info[idx].type = DATA_TYPE_QASYMM8;
   }
 
-  return setNPU_dataInfo (dev_, model_id_, &info_in, &info_out);
+  return setNPU_dataInfo (dev_, model_id, &info_in, &info_out);
 }
 
-/** @brief implementation of set_constraint for TRIV2 */
-int UtilTRIV2::set_constraint () {
-  npuConstraint constraint;
+int UtilTRIV2::prepare_input (UtilModel* model) {
+  input_buffers *input = model->getInput ();
+  npubin_meta *meta = model->getMetadata ();
+  std::string dirpath = model->getDirpath ();
 
-  constraint.timeout_ms = 5000;
-  constraint.priority = NPU_PRIORITY_MID;
-  constraint.notimode = noti_mode_;
+  model->prepareInputPath (meta->input_seg_num);
+  input->num_buffers = meta->input_seg_num;
 
-  return setNPU_constraint (dev_, model_id_, constraint);
+  for (uint32_t idx = 0; idx < meta->input_seg_num; idx++) {
+    const char *input_path = model->getInputPath (idx);
+
+    input->bufs[idx].size = get_file_size (input_path);
+    input->bufs[idx].filepath = input_path;
+    input->bufs[idx].type = BUFFER_FILE;
+  }
+
+  return 0;
 }
index bcfef30..9e2e1cf 100644 (file)
 #include <string>
 #include <mutex>
 #include <condition_variable>
+#include <vector>
 
+/** @brief utility to access test model */
+class UtilModel {
+  public:
+    UtilModel () : meta_ (nullptr) {}
+    ~UtilModel () { if (meta_) free (meta_); }
+
+    void setMetadata (npubin_meta *meta) { meta_ = meta; }
+    void setDirpath (std::string dirpath) { dirpath_ = dirpath; }
+    void setModelID (uint32_t model_id) { model_id_ = model_id; }
+    void prepareInputPath (uint32_t num) {
+      if (NPUBIN_VERSION (meta_->magiccode) <= 1) {
+        inpath_.resize (1);
+        inpath_[0] = dirpath_ + "/input_fmap.bin";
+      } else {
+        inpath_.resize (num);
+        for (uint32_t idx = 0; idx < num; idx++)
+          inpath_[idx] = dirpath_ + "/input_fmap_" + std::to_string (idx) + ".bin";
+      }
+    }
+
+    const char *getInputPath (uint32_t idx) { return inpath_[idx].c_str (); }
+    npubin_meta *getMetadata () { return meta_; }
+    std::string& getDirpath () { return dirpath_; }
+    uint32_t getModelID () { return model_id_; }
+    input_buffers* getInput () { return &input_; }
+
+  private:
+    npubin_meta *meta_;
+    uint32_t model_id_;
+
+    std::string dirpath_;
+
+    input_buffers input_;
+    std::vector<std::string> inpath_;
+};
+
+/** @brief utility to access trinity device */
 class UtilTrinity {
   public:
-    UtilTrinity (dev_type type);
+    UtilTrinity (dev_type type, bool need_model);
     ~UtilTrinity ();
 
     npudev_h getDeviceHandle () { return dev_; }
 
-    void setModelDir (std::string model_dir) { model_dir_ = model_dir; }
-    std::string getModelDir () { return model_dir_; }
-
-    const npubin_meta * getMetadata () { return meta_; }
-
     void setNotiMode (npu_notimode noti_mode) { noti_mode_ = noti_mode; }
 
     int init ();
-    int loadModel ();
+    int loadModel (std::string dirpath, uint32_t *model_id,
+        npu_priority priority = NPU_PRIORITY_MID,
+        uint32_t timeout = 5000);
+    int run (uint32_t model_id, npuOutputNotify cb, bool sync = false);
+    int runAll (npuOutputNotify cb, bool sync = false);
+
+    int runInternal (uint32_t model_id, std::string dev_path);
+    int stopInternal (int task_id);
 
-    int run (input_buffers *input, output_buffers *output);
-    int run (input_buffers *input, npuOutputNotify cb, void *cb_data);
-    int run (std::string dev_path);
-    int stop (int id);
+    UtilModel *findModel (uint32_t model_id);
 
   protected:
     npudev_h dev_;
     npu_notimode noti_mode_;
-
-    /* model parameters */
-    uint32_t model_id_;
-    uint64_t version_;
-    std::string model_dir_;
-    npubin_meta *meta_;
+    std::vector<std::unique_ptr<UtilModel>> models_;
+    bool need_model_;
 
   private:
-    virtual bool check_version () { return false; }
-    virtual int set_data_info () { return -EPERM; }
-    virtual int set_constraint () { return -EPERM; }
+    int set_constraint (uint32_t model_id, uint32_t timeout, npu_priority priority);
+    int run_each (UtilModel *model, npuOutputNotify cb, bool sync);
+
+    virtual bool check_version (npubin_meta *meta) { return false; }
+    virtual int prepare_input (UtilModel* model) { return -EPERM; }
+    virtual int set_data_info (npubin_meta *meta, uint32_t model_id) { return -EPERM; }
 
     dev_type type_;
 };
 
 class UtilTRIV : public UtilTrinity {
   public:
-    UtilTRIV () : UtilTrinity (NPUCOND_TRIV_CONN_SOCIP) {}
+    UtilTRIV () : UtilTrinity (NPUCOND_TRIV_CONN_SOCIP, true) {}
 
   private:
-    bool check_version ();
-    int set_data_info ();
-    int set_constraint ();
+    bool check_version (npubin_meta *meta);
+    int set_data_info (npubin_meta *meta, uint32_t model_id);
+    int prepare_input (UtilModel* model);
 };
 
 class UtilTRIA : public UtilTrinity {
   public:
-    UtilTRIA () : UtilTrinity (NPUCOND_TRIA_CONN_SOCIP) {}
-
-  private:
-    bool check_version ();
-    int set_data_info ();
-    int set_constraint ();
+    UtilTRIA () : UtilTrinity (NPUCOND_TRIA_CONN_SOCIP, false) {}
 };
 
 class UtilTRIV2 : public UtilTrinity {
   public:
-    UtilTRIV2 () : UtilTrinity (NPUCOND_TRIV2_CONN_SOCIP) {}
+    UtilTRIV2 () : UtilTrinity (NPUCOND_TRIV2_CONN_SOCIP, true) {}
 
   private:
-    bool check_version ();
-    int set_data_info ();
-    int set_constraint ();
+    bool check_version (npubin_meta *meta);
+    int set_data_info (npubin_meta *meta, uint32_t model_id);
+    int prepare_input (UtilModel* model);
 };
 
 extern "C" {