[Test/Util] Revise some apptests to use common test utility
authorDongju Chae <dongju.chae@samsung.com>
Mon, 26 Oct 2020 11:06:50 +0000 (20:06 +0900)
committer송욱/On-Device Lab(SR)/Staff Engineer/삼성전자 <wook16.song@samsung.com>
Tue, 3 Nov 2020 02:45:51 +0000 (11:45 +0900)
This patch revises some apptests to use common test utility.

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
tests/apptests/tvn_triv2.cc
tests/apptests/tvn_triv2_profile.cc
tests/apptests/tvn_triv2_xml.cc
tests/unittests/ne_libnpuhost_test.cc
tests/utils/ne_test_utils.cc
tests/utils/ne_test_utils.h

index e141447..cdefd36 100644 (file)
 /**
  * @file tvn_triv2.cc
  * @date 09 Jul 2020
- * @brief AppTest to test multiple runs with the same model
+ * @brief AppTest to test a single inference for TRIV2 device
  * @author Dongju Chae <dongju.chae@samsung.com>
  * @bug No known bugs except for NYI items
  */
 
-#include <libnpuhost.h>
 #include <ne_test_utils.h>
-#include <string.h>
-#include <unistd.h>
 
-#include <iostream>
-#include <fstream>
-
-#include <mutex>
-#include <condition_variable>
-
-/** ./include/typedef.h */
-#define TRIV2_TYPE NPUCOND_TRIV2_CONN_SOCIP
-#define NPU_MODEL_NAME  "model.tvn"
 #define NPU_TIMEOUT_MS  5000
 
+using namespace std;
+
 /** @brief C++ class to describe how to use npu-engine library */
-class TesterTRIV2 {
+class Tester : public UtilTRIV2
+{
   public:
-    TesterTRIV2 () : dev_ (nullptr), meta_ (nullptr), model_id_ (0) {
-      success_ = done_ = 0;
-      async_ = false;
-      noti_mode_ = NPU_INTERRUPT;
-      repeat_ = 1;
-    }
-
-    ~TesterTRIV2 () {
-      /** release resources */
-      if (dev_ != nullptr) {
-        if (model_id_ > 0)
-          unregisterNPUmodel (dev_, model_id_);
-
-        if (meta_ != nullptr)
-          free (meta_);
-
-        putNPUdevice (dev_);
-      }
-    }
-
-    void set_async () {
-      async_ = true;
-    }
-
-    void set_repeat (const std::string repeat_str) {
-      repeat_ = std::stoi (repeat_str);
-    }
-
-    void set_noti_mode (const std::string mode_str) {
-      if (mode_str == "polling")
-        noti_mode_ = NPU_POLLING;
-      else if (mode_str == "intr")
-        noti_mode_ = NPU_INTERRUPT;
-      else
-        std::cerr << "Unknown notimode: " << mode_str << std::endl;
-    }
+    Tester () : model_id_ (0) {}
 
     /** @brief initilize the device handle */
-    int init (const std::string dir) {
-      int num_devices = getnumNPUdeviceByType (TRIV2_TYPE);
-      if (num_devices <= 0)
-        return -ENODEV;
-
-      /** use any available TRIV2 device. (0 <= dev_id < num_devices) */
-      int dev_id = num_devices - 1;
-
-      dir_ = dir;
-      return getNPUdeviceByType (&dev_, TRIV2_TYPE, dev_id);
-    }
-
-    /** @brief run the inference (with dummy data) */
-    int run () {
-      int status = prepare_model ();
-      if (status != 0)
-        return status;
-
-      status = set_data_info ();
-      if (status != 0)
-        return status;
-
-      status = set_constraint ();
-      if (status != 0)
+    int init (std::string model_dir) {
+      int status = UtilTRIV2::init ();
+      if (status != test_ret_success) {
+        cerr << "Failed to initialize\n";
         return status;
-
-      for (uint32_t i = 0; i < repeat_; i++) {
-        input_buffers input;
-
-        status = prepare_input (&input);
-        if (status != 0)
-          return status;
-
-        if (async_) {
-          status = runNPU_async (dev_, model_id_, &input, callback,
-              NULL, this, NPUASYNC_WAIT);
-          if (status != 0)
-            return status;
-        } else {
-          output_buffers output;
-
-          status = runNPU_sync (dev_, model_id_, &input, &output);
-          if (status != 0)
-            return status;
-
-          compare_result (&output);
-        }
-
-        cleanNPU_inputBuffers (dev_, &input);
       }
 
-      return wait_runs ();
+      return UtilTRIV2::loadModel (
+          model_dir, &model_id_, NPU_PRIORITY_MID, NPU_TIMEOUT_MS);
     }
 
-    int wait_runs () {
-      std::unique_lock<std::mutex> lock (m_);
-      cv_.wait (lock, [this]() { return done_ == repeat_; });
-
-      return success_ == repeat_ ? 0 : -EINVAL;
-    }
+    /** @brief run the inference */
+    int run () {
+      if (model_id_ == 0)
+        return test_ret_failure;
 
-    static void callback (output_buffers *output, uint64_t sequence,
-        void *data) {
-      TesterTRIV2 *tester = static_cast<TesterTRIV2 *>(data);
+      int task_id = UtilTRIV2::run (model_id_);
+      if (task_id < 0)
+        return task_id;
 
-      tester->compare_result (output);
+      return wait () == 1 ? test_ret_success : test_ret_failure;
     }
 
   private:
-    int prepare_model () {
-      generic_buffer model;
-      std::string model_path;
-
-      model_path = dir_ + "/" + NPU_MODEL_NAME;
-
-      meta_ = getNPUmodel_metadata (model_path.c_str(), false);
-      if (meta_ == nullptr)
-        return -EINVAL;
-
-      if (NPUBIN_VERSION (meta_->magiccode) != 3)
-        return -EINVAL;
-
-      model.type = BUFFER_FILE;
-      model.size = meta_->size;
-      model.filepath = model_path.c_str();
-
-      int status = allocNPU_modelBuffer (dev_, &model);
-      if (status != 0)
-        return status;
-
-      status = registerNPUmodel (dev_, &model, &model_id_);
-      cleanNPU_modelBuffer (dev_, &model);
-
-      return status;
-    }
-
-    int prepare_input (input_buffers *input) {
-      input->num_buffers = meta_->input_seg_num;
-      for (uint32_t idx = 0; idx < meta_->input_seg_num; idx++) {
-        const char *filepath = input_path_[idx].c_str();
-
-        input->bufs[idx].size = get_file_size (filepath);
-        input->bufs[idx].filepath = filepath;
-        input->bufs[idx].type = BUFFER_FILE;
-      }
-
-      return allocNPU_inputBuffers (dev_, input);
-    }
-
-    int set_data_info () {
-      tensors_data_info info_in;
-      tensors_data_info info_out;
-
-      /* No data manipulation & quantization in this test */
-
-      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;
-
-        input_path_[idx] = dir_ + "/input_fmap_" + std::to_string (idx) + ".bin";
-      }
-
-      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;
-
-        output_path_[idx] = dir_ + "/output_fmap_" + std::to_string (idx) + ".bin";
-      }
-
-      return setNPU_dataInfo (dev_, model_id_, &info_in, &info_out);
-    }
-
-    int set_constraint () {
-      npuConstraint constraint;
-
-      constraint.timeout_ms = NPU_TIMEOUT_MS;
-      constraint.priority = NPU_PRIORITY_MID;
-      constraint.notimode = noti_mode_;
-
-      return setNPU_constraint (dev_, model_id_, constraint);
-    }
-
-    void compare_result (output_buffers * output) {
-      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;
-
-        err = compare_data (output_path_[idx].c_str(), output_data, output_size);
-        assert (output->bufs[idx].type == BUFFER_MAPPED);
-
-        free (output_data);
-
-        if (err != 0)
-          break;
-      }
-
-      {
-        std::unique_lock<std::mutex> lock (m_);
-        done_++;
-        if (err == 0)
-          success_++;
-        cv_.notify_one ();
-      }
-    }
-
-    std::string dir_;
-
-    npudev_h dev_;
-
-    npubin_meta *meta_;
     uint32_t model_id_;
-
-    bool async_;
-    uint32_t success_;
-    uint32_t done_;
-    uint32_t repeat_;
-
-    npu_notimode noti_mode_;
-
-    std::mutex m_;
-    std::condition_variable cv_;
-
-    std::string input_path_[MAX_TENSORS];
-    std::string output_path_[MAX_TENSORS];
 };
 
-static void
-print_usage (const char *prog_name)
-{
-  std::cerr << "Usage: " << prog_name << " [options] model_dir\n";
-  std::cerr << "Options: \n";
-  std::cerr << "  -i <arg> \t Set output notimode [intr|polling]\n";
-  std::cerr << "  -n <arg> \t Set number of repetition\n";
-  std::cerr << "  -a \t\t Set async mode\n";
-  std::cerr << "  -h \t\t Show help messages\n";
-}
-
 /** @brief apptest main  */
 int
 main (int argc, char **argv)
 {
-  TesterTRIV2 tester;
-  int c, status;
-
-  optind = 0;
-  opterr = 0;
-  while ((c = getopt (argc, argv, "ahn:i:")) != -1) {
-    switch (c) {
-      case 'a':
-        tester.set_async ();
-        break;
-      case 'n':
-        tester.set_repeat (optarg);
-        break;
-      case 'i':
-        tester.set_noti_mode (optarg);
-        break;
-      case '?':
-        if (optopt == 'i' || optopt == 'n')
-          std::cerr << "Option -i or -n requires an extra argument";
-        else
-          std::cerr << "Unknown flag: " << c;
-        std::cerr << std::endl;
-        // no-break
-      case 'h':
-        print_usage(argv[0]);
-        return 0;
-    }
-  }
-
-  if (optind >= argc) {
-    std::cerr << "[APPTEST] " << argv[0] << ": SKIPPED\n";
-    return 0;
-  }
+  Tester tester;
+  char * dir;
+  int status;
+
+  status = tester.parseArgs (argc, argv, &dir, "[model dir]");
+  if (status == test_ret_skipped || dir == nullptr)
+    goto skip;
+  else if (status != test_ret_success)
+    goto err;
 
   /** initialize triv2 device */
-  status = tester.init (argv[optind]);
-  if (status != 0)
+  status = tester.init (dir);
+  if (status < test_ret_success)
     goto err;
 
   /** run the inference with the device */
   status = tester.run ();
-  if (status != 0)
+  if (status < test_ret_success)
     goto err;
 
-  std::cerr << "[APPTEST] " << argv[0] << ": PASSED\n";
+  cerr << "[APPTEST] " << argv[0] << ": PASSED\n";
   return 0;
 
 err:
-  std::cerr << "[APPTEST] " << argv[0] << ": FAILED (" << status << ")\n";
+  cerr << "[APPTEST] " << argv[0] << ": FAILED (" << status << ")\n";
   return status;
+
+skip:
+  cerr << "[APPTEST] " << argv[0] << ": SKIPPED\n";
+  return 0;
 }
index eea59cb..e33b694 100644 (file)
  * @bug No known bugs except for NYI items
  */
 
-#include <string.h>
-#include <unistd.h>
-
 #include <ne_test_utils.h>
 
+#define NPU_TIMEOUT_MS  5000
+
 using namespace std;
 
+/** @brief C++ class to describe how to use npu-engine library */
 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 load_model (std::string model) {
-      npu_priority priority = NPU_PRIORITY_MID;
-      uint32_t timeout = 1000;
+    Tester () : model_id_ (0) {}
 
-      total_++;
-
-      return UtilTRIV2::loadModel (model, &model_id_,
-            static_cast<npu_priority>(priority), timeout);
-    }
-
-    int init (std::string model) {
+    /** @brief initilize the device handle */
+    int init (std::string model_dir) {
       int status = UtilTRIV2::init ();
-      if (status != 0) {
+      if (status != test_ret_success) {
         cerr << "Failed to initialize\n";
         return status;
       }
 
-      return load_model (model);
+      return UtilTRIV2::loadModel (
+          model_dir, &model_id_, NPU_PRIORITY_MID, NPU_TIMEOUT_MS);
     }
 
+    /** @brief run the inference */
     int run () {
-      int status = UtilTRIV2::run (model_id_, callback, sync_);
-      if (status < 0) {
-        cerr << "Failed to run the model: " << status << "\n";
-        return status;
-      }
+      if (model_id_ == 0)
+        return test_ret_failure;
 
-      wait_runs ();
+      int task_id = UtilTRIV2::run (model_id_);
+      if (task_id < 0)
+        return task_id;
 
-      print_profile (status);
+      int result = wait () == 1 ? test_ret_success : test_ret_failure;
 
-      return passed_ == total_ ? 0 : -EINVAL;
+      print_profile (task_id);
+
+      return result;
     }
 
+    /** @brief print profile */
     void print_profile (int task_id) {
       npu_profile profile;
       int status = UtilTRIV2::getProfile (task_id, &profile);
@@ -92,107 +71,32 @@ class Tester : public UtilTRIV2
       }
     }
 
-    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 model_id_;
-    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] [model dirpath]\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;
-    }
-  }
+  char * dir;
+  int status;
 
-  if (optind >= argc) {
-    cerr << "[APPTEST] " << argv[0] << ": SKIPPED\n";
-    return 0;
-  }
+  status = tester.parseArgs (argc, argv, &dir, "[model dir]");
+  if (status == test_ret_skipped || dir == nullptr)
+    goto skip;
+  else if (status != test_ret_success)
+    goto err;
 
   /** initialize triv2 device */
-  status = tester.init (argv[optind]);
-  if (status != 0)
+  status = tester.init (dir);
+  if (status < test_ret_success)
     goto err;
 
   /** run the inference with the device */
   status = tester.run ();
-  if (status != 0)
+  if (status < test_ret_success)
     goto err;
 
   cerr << "[APPTEST] " << argv[0] << ": PASSED\n";
@@ -201,4 +105,8 @@ main (int argc, char **argv)
 err:
   cerr << "[APPTEST] " << argv[0] << ": FAILED (" << status << ")\n";
   return status;
+
+skip:
+  cerr << "[APPTEST] " << argv[0] << ": SKIPPED\n";
+  return 0;
 }
index 1efcd3d..03a1754 100644 (file)
  * </testcases>
  */
 
-#include <string.h>
-#include <unistd.h>
-
 #include <tinyxml2.h>
 #include <ne_test_utils.h>
 
 using namespace std;
 using namespace tinyxml2;
 
+/** @brief C++ class to describe how to use npu-engine library */
 class Tester : public UtilTRIV2
 {
   public:
-    Tester () : total_ (0), debug_ (false), sync_ (false) {}
+    Tester () : total_ (0) {}
 
-    void set_debug () {
-      debug_ = true;
-    }
+    /** @brief initilize the device handle */
+    int init (std::string xml_file) {
+      int status = UtilTRIV2::init ();
+      if (status != 0) {
+        cerr << "Failed to initialize\n";
+        return status;
+      }
 
-    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";
-    }
+      XMLDocument doc;
+      if (doc.LoadFile (xml_file.c_str ()) != XML_SUCCESS) {
+        cerr << "Unable to find the given xml file\n";
+        return -ENOENT;
+      }
 
-    void set_sync () {
-      sync_ = true;
+      status = parse_xml (doc);
+      if (status != 0) {
+        cerr << "Invalid model description detected\n";
+        return status;
+      }
+
+      return 0;
     }
 
+    /** @brief parse the model xml file */
     int parse_xml (XMLDocument& xml) {
       XMLElement *root = xml.RootElement ();
       if (root == nullptr)
@@ -86,140 +92,43 @@ class Tester : public UtilTRIV2
       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_SUCCESS) {
-        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;
-    }
-
+    /** @brief run the inference */
     int run () {
-      int status = UtilTRIV2::runAll (callback, sync_);
+      int status = UtilTRIV2::runAll ();
       if (status != 0) {
-        cerr << "Failed to run the model: " << status << "\n";
+        cerr << "Failed to run models in .xml file: " << 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 ();
+      return wait () == total_ ? test_ret_success : test_ret_failure;
     }
 
   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;
-    }
-  }
+  char * xml_file;
+  int status;
 
-  if (optind >= argc) {
-    cerr << "[APPTEST] " << argv[0] << ": SKIPPED\n";
-    return 0;
-  }
+  status = tester.parseArgs (argc, argv, &xml_file, "[.xml file]");
+  if (status == test_ret_skipped || xml_file == nullptr)
+    goto skip;
+  else if (status != test_ret_success)
+    goto err;
 
   /** initialize triv2 device */
-  status = tester.init (argv[optind]);
-  if (status != 0)
+  status = tester.init (xml_file);
+  if (status < test_ret_success)
     goto err;
 
   /** run the inference with the device */
   status = tester.run ();
-  if (status != 0)
+  if (status < test_ret_success)
     goto err;
 
   cerr << "[APPTEST] " << argv[0] << ": PASSED\n";
@@ -228,4 +137,8 @@ main (int argc, char **argv)
 err:
   cerr << "[APPTEST] " << argv[0] << ": FAILED (" << status << ")\n";
   return status;
+
+skip:
+  cerr << "[APPTEST] " << argv[0] << ": SKIPPED\n";
+  return 0;
 }
index 0aaff41..91dc104 100644 (file)
@@ -179,36 +179,6 @@ TEST (ne_libnpuhost_test, register_model_n)
   putNPUdevice (dev);
 }
 
-struct test_sync {
-  std::mutex m;
-  std::condition_variable cv;
-  int done;
-};
-
-static struct test_sync tsync;
-
-static void wait_runs (int total)
-{
-  std::unique_lock<std::mutex> lock (tsync.m);
-  tsync.cv.wait (lock, [&]() { return tsync.done == total; });
-}
-
-/**
- * @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)
  */
@@ -223,18 +193,17 @@ TEST (ne_libnpuhost_test, run_input_triv_binfmt_v1)
 
   uint32_t model_id = 0;
 
-  tsync.done = 0;
-
   if (tester.loadModel (model_dir, &model_id) != 0)
     /* skip */
     return;
 
-  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);
+  EXPECT_EQ (tester.run (model_id, true), 0);
+  EXPECT_EQ (tester.run (model_id, false), 0);
 
-  wait_runs (4);
+  EXPECT_EQ (tester.runAll (true), 0);
+  EXPECT_EQ (tester.runAll (false), 0);
+
+  EXPECT_EQ (tester.wait (), 4);
 }
 
 /**
@@ -245,14 +214,14 @@ TEST (ne_libnpuhost_test, run_input_triv_binfmt_v1_n)
   UtilTRIV tester;
 
   /* without init */
-  EXPECT_NE (tester.run (0, callback, true), 0);
-  EXPECT_NE (tester.run (0, callback, false), 0);
+  EXPECT_NE (tester.run (0, true), 0);
+  EXPECT_NE (tester.run (0, 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);
+  EXPECT_NE (tester.run (0, true), 0);
+  EXPECT_NE (tester.run (0, false), 0);
 
   /* invalid model id */
   std::string model_dir (NE_DATADIR);
@@ -264,7 +233,7 @@ TEST (ne_libnpuhost_test, run_input_triv_binfmt_v1_n)
     /* skip */
     return;
 
-  EXPECT_NE (tester.run (model_id + 1, callback, true), 0);
+  EXPECT_NE (tester.run (model_id + 1, true), 0);
 }
 
 /**
@@ -281,19 +250,17 @@ TEST (ne_libnpuhost_test, run_input_triv_binfmt_v2)
 
   uint32_t model_id = 0;
 
-  tsync.done = 0;
-
   if (tester.loadModel (model_dir, &model_id) != 0)
     /* skip */
     return;
 
-  EXPECT_EQ (tester.run (model_id, callback, true), 0);
-  EXPECT_EQ (tester.run (model_id, callback, false), 0);
+  EXPECT_EQ (tester.run (model_id, true), 0);
+  EXPECT_EQ (tester.run (model_id, false), 0);
 
-  EXPECT_EQ (tester.runAll (callback, true), 0);
-  EXPECT_EQ (tester.runAll (callback, false), 0);
+  EXPECT_EQ (tester.runAll (true), 0);
+  EXPECT_EQ (tester.runAll (false), 0);
 
-  wait_runs (4);
+  EXPECT_EQ (tester.wait (), 4);
 }
 
 /**
@@ -304,14 +271,14 @@ TEST (ne_libnpuhost_test, run_input_triv_binfmt_v2_n)
   UtilTRIV tester;
 
   /* without init */
-  EXPECT_NE (tester.run (0, callback, true), 0);
-  EXPECT_NE (tester.run (0, callback, false), 0);
+  EXPECT_NE (tester.run (0, true), 0);
+  EXPECT_NE (tester.run (0, 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);
+  EXPECT_NE (tester.run (0, true), 0);
+  EXPECT_NE (tester.run (0, false), 0);
 
   /* invalid model id */
   std::string model_dir (NE_DATADIR);
@@ -323,7 +290,7 @@ TEST (ne_libnpuhost_test, run_input_triv_binfmt_v2_n)
     /* skip */
     return;
 
-  EXPECT_NE (tester.run (model_id + 1, callback, true), 0);
+  EXPECT_NE (tester.run (model_id + 1, true), 0);
 }
 
 #if defined(ENABLE_EMUL)
@@ -336,12 +303,10 @@ TEST (ne_libnpuhost_test, run_input_tria)
 
   ASSERT_EQ (tester.init (), 0);
 
-  tsync.done = 0;
+  EXPECT_EQ (tester.run (0, true), 0);
+  EXPECT_EQ (tester.run (0, false), 0);
 
-  EXPECT_EQ (tester.run (0, callback, true), 0);
-  EXPECT_EQ (tester.run (0, callback, false), 0);
-
-  wait_runs (2);
+  EXPECT_EQ (tester.wait (), 2);
 }
 
 /**
@@ -352,8 +317,8 @@ TEST (ne_libnpuhost_test, run_input_tria_n)
   UtilTRIA tester;
 
   /* without init */
-  EXPECT_NE (tester.run (0, callback, true), 0);
-  EXPECT_NE (tester.run (0, callback, false), 0);
+  EXPECT_NE (tester.run (0, true), 0);
+  EXPECT_NE (tester.run (0, false), 0);
 }
 
 #endif
@@ -372,19 +337,17 @@ TEST (ne_libnpuhost_test, run_input_triv2_binfmt_v3)
 
   uint32_t model_id = 0;
 
-  tsync.done = 0;
-
   if (tester.loadModel (model_dir, &model_id) != 0)
     /* skip */
     return;
 
-  EXPECT_EQ (tester.run (model_id, callback, true), 0);
-  EXPECT_EQ (tester.run (model_id, callback, false), 0);
+  EXPECT_EQ (tester.run (model_id, true), 0);
+  EXPECT_EQ (tester.run (model_id, false), 0);
 
-  EXPECT_EQ (tester.runAll (callback, true), 0);
-  EXPECT_EQ (tester.runAll (callback, false), 0);
+  EXPECT_EQ (tester.runAll (true), 0);
+  EXPECT_EQ (tester.runAll (false), 0);
 
-  wait_runs (4);
+  EXPECT_EQ (tester.wait (), 4);
 }
 
 /**
@@ -395,14 +358,14 @@ TEST (ne_libnpuhost_test, run_input_triv2_binfmt_v3_n)
   UtilTRIV2 tester;
 
   /* without init */
-  EXPECT_NE (tester.run (0, callback, true), 0);
-  EXPECT_NE (tester.run (0, callback, false), 0);
+  EXPECT_NE (tester.run (0, true), 0);
+  EXPECT_NE (tester.run (0, 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);
+  EXPECT_NE (tester.run (0, true), 0);
+  EXPECT_NE (tester.run (0, false), 0);
 
   /* invalid model id */
   std::string model_dir (NE_DATADIR);
@@ -414,7 +377,7 @@ TEST (ne_libnpuhost_test, run_input_triv2_binfmt_v3_n)
     /* skip */
     return;
 
-  EXPECT_NE (tester.run (model_id + 1, callback, true), 0);
+  EXPECT_NE (tester.run (model_id + 1, true), 0);
 }
 
 /**
index f619ae4..9565698 100644 (file)
@@ -278,6 +278,12 @@ out:
   return err;
 }
 
+std::mutex UtilTrinity::m_;
+std::condition_variable UtilTrinity::cv_;
+uint32_t UtilTrinity::success_ = 0;
+uint32_t UtilTrinity::total_ = 0;
+uint32_t UtilTrinity::done_ = 0;
+
 /** @brief constructor of UtilTrinity */
 UtilTrinity::UtilTrinity (dev_type type, bool need_model)
 {
@@ -285,6 +291,10 @@ UtilTrinity::UtilTrinity (dev_type type, bool need_model)
   type_ = type;
   noti_mode_ = NPU_INTERRUPT;
   need_model_ = need_model;
+
+  mute_ = false;
+  sync_ = false;
+  debug_ = false;
 }
 
 /** @brief destructor of UtilTrinity */
@@ -309,6 +319,89 @@ int UtilTrinity::init ()
   return getNPUdeviceByType (&dev_, type_, dev_id);
 }
 
+/** @brief print program usage */
+void UtilTrinity::printUsage (const char *prog_name, const char *param_str)
+{
+  if (param_str)
+    std::cerr << "Usage: " << prog_name << " [options] " << param_str << "\n";
+  else
+    std::cerr << "Usage: " << prog_name << " [options]\n";
+  std::cerr << "Options: \n";
+  std::cerr << "  -n <arg> \t Set output notimode [intr|polling]\n";
+  std::cerr << "  -m \t\t Mute stdout/stderr messages\n";
+  std::cerr << "  -d \t\t Enable debugging mode (and dump results)\n";
+  std::cerr << "  -s \t\t Enable run sync mode\n";
+  std::cerr << "  -h \t\t Show help messages\n";
+}
+
+/** @brief disable stdout/stderr */
+void UtilTrinity::setMute ()
+{
+  /** redirect to /dev/null */
+  if (freopen("/dev/null", "w", stdout) == NULL)
+    std::cerr << "Warning: Failed to reopen stdout\n";
+  if (freopen("/dev/null", "w", stderr) == NULL)
+    std::cerr << "Warning: Failed to reopen stderr\n";
+
+  mute_ = true;
+}
+
+/** @brief set output notification mode */
+void UtilTrinity::setNotiMode (const std::string mode)
+{
+  if (mode == "polling") {
+    noti_mode_ = NPU_POLLING;
+  } else if (mode == "intr") {
+    noti_mode_ = NPU_INTERRUPT;
+  } else {
+    std::cerr << "Unknown notimode: " << mode << std::endl;
+  }
+}
+
+/** @brief parse/set arguments and get working directory */
+int UtilTrinity::parseArgs (int argc, char **argv, char **param,
+    const char *param_str)
+{
+  int c;
+
+  optind = 0;
+  opterr = 0;
+  while ((c = getopt (argc, argv, "dmshn:")) != -1) {
+    switch (c) {
+      case 'd':
+        setDebug ();
+        break;
+      case 'm':
+        setMute ();
+        break;
+      case 's':
+        setSync ();
+        break;
+      case 'n':
+        setNotiMode (optarg);
+        break;
+      case '?':
+        if (optopt == 'n')
+          std::cerr << "Option -i requires an extra argument";
+        else
+          std::cerr << "Unknown flag: " << c;
+        std::cerr << std::endl;
+        printUsage (argv[0], param_str);
+        return test_ret_failure;
+      case 'h':
+        printUsage (argv[0], param_str);
+        return test_ret_skipped;
+    }
+  }
+
+  if (optind < argc)
+    *param = argv[optind];
+  else
+    *param = nullptr;
+
+  return test_ret_success;
+}
+
 /** @brief load NPU model and set relevant info */
 int UtilTrinity::loadModel (std::string dirpath, uint32_t *model_id_ptr,
     npu_priority priority, uint32_t timeout)
@@ -350,7 +443,7 @@ int UtilTrinity::loadModel (std::string dirpath, uint32_t *model_id_ptr,
   model->setDirpath (dirpath);
   model->setModelID (model_id);
 
-  status = prepare_input (model);
+  status = prepare_model (model);
   if (status != 0) {
     delete model;
     return status;
@@ -378,20 +471,57 @@ UtilModel *UtilTrinity::findModel (uint32_t model_id)
   return nullptr;
 }
 
+uint32_t UtilTrinity::wait ()
+{
+  uint32_t result;
+  std::unique_lock<std::mutex> lock (m_);
+
+  cv_.wait (lock, [this]() { return done_ == total_; });
+  result = success_;
+
+  total_ = 0;
+  done_ = 0;
+  success_ = 0;
+
+  return result;
+}
+
+void UtilTrinity::callback (output_buffers *output, uint64_t sequence,
+    void *data)
+{
+  bool success = true;
+  UtilModel *model = static_cast <UtilModel *> (data);
+
+  for (uint32_t idx = 0; idx < output->num_buffers; idx++) {
+    if (model != nullptr) {
+      char *buf = static_cast<char*>(output->bufs[idx].addr);
+      size_t size = output->bufs[idx].size;
+
+      if (compare_data (model->getOutputPath (idx), buf, size) != 0)
+        success = false;
+    }
+
+    free (output->bufs[idx].addr);
+  }
+
+  std::unique_lock<std::mutex> lock (m_);
+  done_++;
+  if (success)
+    success_++;
+  cv_.notify_one ();
+}
+
 /** @brief run inference with the given model info */
-int UtilTrinity::run_each (UtilModel *model, npuOutputNotify cb, bool sync)
+int UtilTrinity::run_each (UtilModel *model, bool sync)
 {
   int status = -EINVAL;
-  void *cb_data = nullptr;
-
-  if (cb == nullptr)
-    return -EINVAL;
+  void *callback_data = nullptr;
 
   if (need_model_ && model == nullptr)
     return -ENOENT;
 
   if (model)
-    cb_data = const_cast <char *> (model->getDirpath ().c_str ());
+    callback_data = static_cast<void*> (model);
 
   if (sync) {
     output_buffers output;
@@ -414,12 +544,18 @@ int UtilTrinity::run_each (UtilModel *model, npuOutputNotify cb, bool sync)
       cleanNPU_inputBuffers (dev_, &input);
     }
 
-    if (status >= 0)
-      cb (&output, 0, cb_data);
+    if (status >= 0) {
+      {
+        std::unique_lock<std::mutex> lock (m_);
+        total_++;
+      }
+
+      callback (&output, 0, callback_data);
+    }
   } else {
     if (model) {
       status = runNPU_async (dev_, model->getModelID (), model->getInput (),
-          cb, NULL, cb_data, NPUASYNC_WAIT);
+          callback, NULL, callback_data, NPUASYNC_WAIT);
     } else {
       /* TODO: revise this when TRIA is implemented */
       input_buffers input;
@@ -431,27 +567,45 @@ int UtilTrinity::run_each (UtilModel *model, npuOutputNotify cb, bool sync)
       if (status != 0)
         return status;
 
-      status = runNPU_async (dev_, 0, &input, cb, NULL, cb_data, NPUASYNC_WAIT);
+      status = runNPU_async (dev_, 0, &input, callback, NULL, callback_data,
+          NPUASYNC_WAIT);
       cleanNPU_inputBuffers (dev_, &input);
     }
+
+    if (status >= 0) {
+      std::unique_lock<std::mutex> lock (m_);
+      total_++;
+    }
   }
 
   return status;
 }
 
+/** @brief run inference with the given model id (default sync) */
+int UtilTrinity::run (uint32_t model_id)
+{
+  return run (model_id, sync_);
+}
+
 /** @brief run inference with the given model id */
-int UtilTrinity::run (uint32_t model_id, npuOutputNotify cb, bool sync)
+int UtilTrinity::run (uint32_t model_id, bool sync)
 {
   UtilModel *model = findModel (model_id);
 
-  return run_each (model, cb, sync);
+  return run_each (model, sync);
+}
+
+/** @brief run inference across all registered models (default sync) */
+int UtilTrinity::runAll ()
+{
+  return runAll (sync_);
 }
 
 /** @brief run inference across all registered models */
-int UtilTrinity::runAll (npuOutputNotify cb, bool sync)
+int UtilTrinity::runAll (bool sync)
 {
   for (auto& model : models_) {
-    int status = run_each (model.get (), cb, sync);
+    int status = run_each (model.get (), sync);
     if (status < 0)
       return status;
   }
@@ -526,19 +680,24 @@ int UtilTRIV::set_data_info (npubin_meta *meta, uint32_t model_id) {
   return setNPU_dataInfo (dev_, model_id, &info_in, &info_out);
 }
 
-int UtilTRIV::prepare_input (UtilModel* model) {
+int UtilTRIV::prepare_model (UtilModel *model) {
   input_buffers *input = model->getInput ();
   npubin_meta *meta = model->getMetadata ();
   std::string dirpath = model->getDirpath ();
   uint32_t input_num;
+  uint32_t output_num;
 
   if (NPUBIN_VERSION (meta->magiccode) <= 1) {
     input_num = 1;
+    output_num = 1;
   } else {
     input_num = meta->input_num;
+    output_num = meta->output_num;
   }
 
   model->prepareInputPath (input_num);
+  model->prepareOutputPath (output_num);
+
   input->num_buffers = input_num;
 
   for (uint32_t idx = 0; idx < input_num; idx++) {
@@ -578,12 +737,14 @@ int UtilTRIV2::set_data_info (npubin_meta *meta, uint32_t model_id) {
   return setNPU_dataInfo (dev_, model_id, &info_in, &info_out);
 }
 
-int UtilTRIV2::prepare_input (UtilModel* model) {
+int UtilTRIV2::prepare_model (UtilModel *model) {
   input_buffers *input = model->getInput ();
   npubin_meta *meta = model->getMetadata ();
   std::string dirpath = model->getDirpath ();
 
   model->prepareInputPath (meta->input_seg_num);
+  model->prepareOutputPath (meta->output_seg_num);
+
   input->num_buffers = meta->input_seg_num;
 
   for (uint32_t idx = 0; idx < meta->input_seg_num; idx++) {
index fb68210..7c5a6ed 100644 (file)
@@ -50,8 +50,19 @@ class UtilModel {
           inpath_[idx] = dirpath_ + "/input_fmap_" + std::to_string (idx) + ".bin";
       }
     }
-
+    void prepareOutputPath (uint32_t num) {
+      if (NPUBIN_VERSION (meta_->magiccode) <= 1) {
+        outpath_.resize (1);
+        outpath_[0] = dirpath_ + "/output_fmap.bin";
+      } else {
+        outpath_.resize (num);
+        for (uint32_t idx = 0; idx < num; idx++)
+          outpath_[idx] = dirpath_ + "/output_fmap_" + std::to_string (idx) + ".bin";
+      }
+    }
     const char *getInputPath (uint32_t idx) { return inpath_[idx].c_str (); }
+    const char *getOutputPath (uint32_t idx) { return outpath_[idx].c_str (); }
+
     npubin_meta *getMetadata () { return meta_; }
     std::string& getDirpath () { return dirpath_; }
     uint32_t getModelID () { return model_id_; }
@@ -65,6 +76,7 @@ class UtilModel {
 
     input_buffers input_;
     std::vector<std::string> inpath_;
+    std::vector<std::string> outpath_;
 };
 
 /** @brief utility to access trinity device */
@@ -76,13 +88,22 @@ class UtilTrinity {
     npudev_h getDeviceHandle () { return dev_; }
 
     void setNotiMode (npu_notimode noti_mode) { noti_mode_ = noti_mode; }
+    void setSync () { sync_ = true; }
+    void setDebug () { debug_ = true; }
+    void setMute ();
+    void setNotiMode (const std::string mode);
 
     int init ();
+    void printUsage (const char *prog_name, const char *param_str = nullptr);
+    int parseArgs (int argc, char **argv, char **param, const char *param_str = nullptr);
     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 run (uint32_t model_id);
+    int run (uint32_t model_id, bool sync);
+    int runAll ();
+    int runAll (bool sync);
 
     int runInternal (uint32_t model_id, std::string dev_path);
     int stopInternal (int task_id);
@@ -91,21 +112,37 @@ class UtilTrinity {
 
     UtilModel *findModel (uint32_t model_id);
 
+    static void callback (output_buffers *output, uint64_t sequence,
+        void *data);
+
+    uint32_t wait ();
+
   protected:
     npudev_h dev_;
-    npu_notimode noti_mode_;
     std::vector<std::unique_ptr<UtilModel>> models_;
+    npu_notimode noti_mode_;
     bool need_model_;
 
+    bool mute_;
+    bool sync_;
+    bool debug_;
+
   private:
     int set_constraint (uint32_t model_id, uint32_t timeout, npu_priority priority);
-    int run_each (UtilModel *model, npuOutputNotify cb, bool sync);
+    int run_each (UtilModel *model, bool sync);
 
     virtual bool check_version (npubin_meta *meta) { return false; }
-    virtual int prepare_input (UtilModel* model) { return -EPERM; }
+    virtual int prepare_model (UtilModel *model) { return -EPERM; }
     virtual int set_data_info (npubin_meta *meta, uint32_t model_id) { return -EPERM; }
 
     dev_type type_;
+
+    static std::mutex m_;
+    static std::condition_variable cv_;
+
+    static uint32_t success_;
+    static uint32_t done_;
+    static uint32_t total_;
 };
 
 class UtilTRIV : public UtilTrinity {
@@ -115,7 +152,7 @@ class UtilTRIV : public UtilTrinity {
   private:
     bool check_version (npubin_meta *meta);
     int set_data_info (npubin_meta *meta, uint32_t model_id);
-    int prepare_input (UtilModel* model);
+    int prepare_model (UtilModel *model);
 };
 
 class UtilTRIA : public UtilTrinity {
@@ -130,13 +167,14 @@ class UtilTRIV2 : public UtilTrinity {
   private:
     bool check_version (npubin_meta *meta);
     int set_data_info (npubin_meta *meta, uint32_t model_id);
-    int prepare_input (UtilModel* model);
+    int prepare_model (UtilModel *model);
 };
 
 extern "C" {
 #endif
 
 enum test_ret {
+  test_ret_failure = -1,
   test_ret_success = 0,
   test_ret_skipped,
   test_ret_skipped_wrong_num_args,