[TRIV2] Add data manipulation support for TRIV2
authorDongju Chae <dongju.chae@samsung.com>
Thu, 20 Aug 2020 06:25:51 +0000 (15:25 +0900)
committer송욱/On-Device Lab(SR)/Staff Engineer/삼성전자 <wook16.song@samsung.com>
Fri, 28 Aug 2020 04:43:33 +0000 (13:43 +0900)
This patch adds data manipulation support for TRIV2,
which converts data layout between NHWC and HW format.

It includes data quantization as well.

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
src/core/comm/CommPlugin.h
src/core/comm/plugin-comm-ip.cc
src/core/ne-handler.cc
src/core/ne-model.cc
src/core/ne-model.h
tests/apptests/tvn_triv2.cc
tests/unittests/ne_core_comm_test.cc
tests/unittests/ne_core_handler_test.cc
tests/unittests/ne_core_model_test.cc

index d1f406e..a4fd178 100644 (file)
@@ -18,7 +18,7 @@
 #include <functional>
 
 /** @brief the binded data manipulation function */
-typedef std::function<void (void*, void*, size_t)> dataManipFunc;
+typedef std::function<size_t (void*, void*, size_t)> dataManipFunc;
 
 /** @brief communication plugin: singleton */
 class CommPlugin {
index 36df7d1..4b42301 100644 (file)
@@ -50,6 +50,8 @@ public:
    */
   int extractGenericBuffer (const generic_buffer *buf, void *data,
       dataManipFunc func = nullptr, size_t offset = 0, size_t size = 0) {
+    size_t count = 0;
+
     if (buf == nullptr || data == nullptr)
       return -EINVAL;
     if (buf->size == 0)
@@ -57,32 +59,40 @@ public:
     if (size == 0)
       size = buf->size;
 
-    /** Note that npu-engine supports data manipulation only for the BUFFER_MAPPED.
-      * Thus, for other types, user need to ensure that  the data format in the dmabuf
-      * is compatible with the device. */
+    /** Note that npu-engine does not support data manipulation for the BUFFER_DMABUF.
+      * In this case, ensure that the data format is compatible with the device. */
     if (buf->type == BUFFER_FILE) {
       std::ifstream ifs (buf->filepath, std::ios::binary);
       if (ifs.good()) {
         ifs.seekg(offset);
-        ifs.read(static_cast<char *>(data), size);
-        size_t count = ifs.gcount();
-        ifs.close();
+        if (func != nullptr) {
+          char * tmp = new char [size];
 
-        if (count == size)
-          return 0;
+          ifs.read(tmp, size);
+          func (data, tmp, size);
+
+          delete [] tmp;
+        } else {
+          ifs.read(static_cast<char *>(data), size);
+        }
+        count = ifs.gcount();
+        ifs.close();
       }
     } else if (buf->type == BUFFER_MAPPED) {
       if (buf->addr == nullptr)
         return -EINVAL;
 
-      if (func != nullptr)
-        func (data, static_cast<char*>(buf->addr) + offset, size);
-      else
+      if (func != nullptr) {
+        count = func (data, static_cast<char*>(buf->addr) + offset, size);
+      } else {
         memcpy (data, static_cast<char*>(buf->addr) + offset, size);
+        count = size;
+      }
+    }
+    /** BUFFER_DMABUF enables zero-copy: it's impossible to manipulate data */
 
+    if (count == size)
       return 0;
-    }
-    /** BUFFER_DMABUF enables zero-copy, which means it's impossible to manipulate data */
 
     return -EINVAL;
   }
index 6b83a8b..924ea8a 100644 (file)
@@ -13,6 +13,7 @@
  */
 
 #include "ne-handler.h"
+#include "ne-data.h"
 
 #include <libnpuhost.h>
 #include <npubinfmt.h>
@@ -1237,6 +1238,8 @@ TrinityVision::run (npu_input_opmode opmode, const Model *model,
     return -EINVAL;
   }
 
+  const_cast<Model *>(model)->updateDataInfo ();
+
   Buffer *buffer = prepareInputBuffers (model->getMetadata(), input);
   if (buffer == nullptr) {
     logerr (TAG, "Failed to extract buffer instance\n");
@@ -1327,11 +1330,6 @@ SegmentTable *
 TrinityVision2::prepareSegmentTable (const Model *model, const input_buffers *input,
     const output_buffers *output)
 {
-  if (model == nullptr) {
-    logerr (TAG, "Invalid arguments provided\n");
-    return nullptr;
-  }
-
   const Metadata *meta = model->getMetadata ();
   if (meta == nullptr || (input != nullptr &&
         meta->getInputNum() != input->num_buffers)) {
@@ -1511,9 +1509,11 @@ TrinityVision2::run (npu_input_opmode opmode, const Model *model,
   if (opmode != NPUINPUT_HOST)
     return -EINVAL;
 
-  if (input == nullptr || input->num_buffers == 0)
+  if (input == nullptr || input->num_buffers == 0 || model == nullptr)
     return -EINVAL;
 
+  const_cast<Model *>(model)->updateDataInfo ();
+
   /** this device uses segment table */
   SegmentTable * segt = prepareSegmentTable (model, input);
   if (segt == nullptr) {
@@ -1523,16 +1523,8 @@ TrinityVision2::run (npu_input_opmode opmode, const Model *model,
 
   /** extract input data */
   for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
-    size_t max_seg_size = segt->getInputSegment(idx)->getSize();
-    uint32_t seg_offset = segt->getInputSegmentOffset(idx);
-
-    if (input->bufs[idx].size + seg_offset > max_seg_size) {
-      logerr (TAG, "Too large input data provided: max segment size (%zu)\n",
-          max_seg_size);
-      return -ERANGE;
-    }
-
     if (!segt->getInputSegment(idx)->isExternal ()) {
+      uint32_t seg_offset = segt->getInputSegmentOffset(idx);
       auto func = std::bind (TrinityVision2::manipulateData, model, idx, true,
           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
       int status = comm_.extractGenericBuffer (
@@ -1711,76 +1703,6 @@ TrinityAsr::callback (Request *req, npuOutputNotify cb, void *cb_data)
 
 #ifdef ENABLE_MANIP
 
-#define do_quantized_memcpy(type) do {\
-    idx = 0;\
-    if (quant) {\
-      while (idx < num_elems) {\
-          val = ((type *) src)[idx];\
-          val = val / _scale;\
-          val += _zero_point;\
-          val = (val > 255.0) ? 255.0 : 0.0;\
-          ((uint8_t *) dst)[idx++] = (uint8_t) val;\
-      }\
-    } else {\
-      while (idx < num_elems) {\
-          val = *(uint8_t *) src;\
-          val -= _zero_point;\
-          val *= _scale;\
-          ((type *) dst)[idx++] = (type) val;\
-          dst = (void*)(((uint8_t *) dst) + data_size);\
-          src = (void*)(((uint8_t *) src) + 1);\
-      }\
-    }\
-  } while (0)
-
-/**
- * @brief memcpy during quantization
- */
-static void memcpy_with_quant (bool quant, data_type type, float scale, uint32_t zero_point,
-    void *dst, const void *src, uint32_t num_elems)
-{
-  double _scale = (double) scale;
-  double _zero_point = (double) zero_point;
-  double val;
-  uint32_t data_size = get_data_size (type);
-  uint32_t idx;
-
-  switch (type) {
-    case DATA_TYPE_INT8:
-      do_quantized_memcpy (int8_t);
-      break;
-    case DATA_TYPE_UINT8:
-      do_quantized_memcpy (uint8_t);
-      break;
-    case DATA_TYPE_INT16:
-      do_quantized_memcpy (int16_t);
-      break;
-    case DATA_TYPE_UINT16:
-      do_quantized_memcpy (uint16_t);
-      break;
-    case DATA_TYPE_INT32:
-      do_quantized_memcpy (int32_t);
-      break;
-    case DATA_TYPE_UINT32:
-      do_quantized_memcpy (uint32_t);
-      break;
-    case DATA_TYPE_INT64:
-      do_quantized_memcpy (int64_t);
-      break;
-    case DATA_TYPE_UINT64:
-      do_quantized_memcpy (uint64_t);
-      break;
-    case DATA_TYPE_FLOAT32:
-      do_quantized_memcpy (float);
-      break;
-    case DATA_TYPE_FLOAT64:
-      do_quantized_memcpy (double);
-      break;
-    default:
-      logerr (TAG, "Unsupported datatype %d\n", type);
-  }
-}
-
 /**
  * @brief perform data manipulation
  * @param[in] model model instance
@@ -1808,120 +1730,87 @@ TrinityVision::manipulateData (const Model *model, uint32_t idx, bool is_input,
     void *dst, void *src, size_t size)
 {
   const Metadata *meta = model->getMetadata();
-  const tensor_data_info* info;
-  const uint32_t *dims;
-  uint32_t zero_point;
-  float scale;
+  DataConverter converter (is_input);
+
+  converter.setData (src, dst, size);
 
-  /** extract required information from the metadata */
   if (is_input) {
-    if (idx >= meta->getInputNum()) {
-      logerr (TAG, "Wrong information for input tensors in metadata\n");
+    const tensor_data_info* info = model->getInputDataInfo (idx);
+    if (info == nullptr)
       return 0;
-    }
 
-    info = model->getInputDataInfo (idx);
-    dims = meta->getInputDims (idx);
-    zero_point = meta->getInputQuantZero (idx);
-    scale = meta->getInputQuantScale (idx);
+    converter.setDataLayout (info->layout, DATA_LAYOUT_SRNPU);
+    converter.setDataType (info->type, DATA_TYPE_SRNPU);
+    converter.setDataDims (meta->getInputDims (idx));
+    converter.setQuantZero (meta->getInputQuantZero (idx));
+    converter.setQuantScale (meta->getInputQuantScale (idx));
   } else {
-    if (idx >= meta->getOutputNum()) {
-      logerr (TAG, "Wrong information for output tensors in metadata\n");
+    const tensor_data_info* info = model->getOutputDataInfo (idx);
+    if (info == nullptr)
       return 0;
-    }
-
-    info = model->getOutputDataInfo (idx);
-    dims = meta->getOutputDims (idx);
-    zero_point = meta->getOutputQuantZero (idx);
-    scale = meta->getOutputQuantScale (idx);
-  }
-
-  if (info == nullptr) {
-    logerr (TAG, "Unmatched tensors info\n");
-    return 0;
-  }
-
-  uint32_t batch = dims[0];
-  uint32_t height = dims[1];
-  uint32_t width = dims[2];
-  uint32_t depth = dims[3];
 
-  uint32_t data_size = get_data_size (info->type);
-  if (data_size == 0) {
-    logerr (TAG, "Invalid data size\n");
-    return 0;
+    converter.setDataLayout (DATA_LAYOUT_SRNPU, info->layout);
+    converter.setDataType (DATA_TYPE_SRNPU, info->type);
+    converter.setDataDims (meta->getOutputDims (idx));
+    converter.setQuantZero (meta->getOutputQuantZero (idx));
+    converter.setQuantScale (meta->getOutputQuantScale (idx));
   }
 
-  bool need_quantization = false;
-  /**
-   * note that we assume DATA_TYPE_SRNPU is the smallest data type that we consider.
-   * Also, DATA_TYPE_SRNPU and uint8_t may be regarded as the same in the view of apps.
-   */
-  if (info->type != DATA_TYPE_SRNPU) {
-    assert (data_size >= get_data_size (DATA_TYPE_SRNPU));
+  return converter.perform ();
+}
 
-    if (data_size > get_data_size (DATA_TYPE_SRNPU) ||
-        !(zero_point == default_quant_zero && scale == default_quant_scale))
-      need_quantization = true;
-  }
+/**
+ * @brief perform data manipulation
+ * @param[in] model model instance
+ * @param[in] idx tensor index
+ * @param[in] is_input indicate it's input manipulation
+ * @param[out] dst destination buffer
+ * @param[in] src source buffer (feature map)
+ * @param[in] size size to be copied
+ * @return size of memory copy if no error, otherwise zero
+ *
+ * @note the input data format should be NHWC
+ *
+ * @detail Feature map data in TRIV2, (x, y, z) = (width, height, depth)
+ *
+ *         1) Image input (depth == 1 or depth == 3)
+ *            Addr(x,y,z) = Addr(0,0,0) + z + depth * x + ymod * y
+ *
+ *         2) Common cases
+ *            Addr(x,y,z) = Addr(0,0,0) + (z % 64) + (64 * x) + ymod * y + zmod * (z / 64)
+ */
+size_t
+TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
+    void *dst, void *src, size_t size)
+{
+  const Metadata *meta = model->getMetadata();
+  DataConverter converter (is_input);
 
-  /** check data manipulation is required */
-  if (depth != 3 && depth != 64 && info->layout != DATA_LAYOUT_SRNPU) {
-    uint32_t MPA_L = DATA_GRANULARITY;
-    uint32_t n, h, w, d;
-    uint32_t std_offset;  /* standard offset in NHWC data format */
-    uint32_t npu_offset;  /* npu offset in NPU HW data format*/
-    uint32_t src_offset;
-    uint32_t dst_offset;
-    uint32_t slice_size;
+  converter.setData (src, dst, size);
 
-    /* @todo we currently support only NHWC */
-    if (info->layout != DATA_LAYOUT_NHWC) {
-      logerr (TAG, "data manipulation is supported for NHWC only\n");
+  if (is_input) {
+    const tensor_data_info* info = model->getInputDataInfo (idx);
+    if (info == nullptr)
       return 0;
-    }
 
-    for (n = 0; n < batch; n++) {
-      for (h = 0; h < height; h++) {
-        for (w = 0; w < width; w++) {
-          for (d = 0; d < depth; d += MPA_L) {
-            std_offset = d + depth * (w + width * (h + n * height));
-            npu_offset = MPA_L * (w + width * (h + (n + d / MPA_L) * height));
-            slice_size = (depth - d >= MPA_L) ? MPA_L : depth - d;
-
-            if (is_input) {
-              src_offset = std_offset * data_size;
-              dst_offset = npu_offset;
-            } else {
-              src_offset = npu_offset;
-              dst_offset = std_offset * data_size;
-            }
-
-            /* if depth is not a multiple of MPA_L, add zero paddings (not exact values) */
-            if (need_quantization) {
-              memcpy_with_quant (is_input, info->type, scale, zero_point,
-                  static_cast<char*>(dst) + dst_offset,
-                  static_cast<char*>(src) + src_offset,
-                  slice_size);
-            } else {
-              memcpy (
-                  static_cast<char*>(dst) + dst_offset,
-                  static_cast<char*>(src) + src_offset,
-                  slice_size);
-            }
-          }
-        }
-      }
-    }
-  } else if (need_quantization) {
-    /** depth == 3 || depth == 64; special cases which can directly copy input tensor data */
-    memcpy_with_quant (is_input, info->type, scale, zero_point,
-        dst, src, is_input ? size / data_size : size);
+    converter.setDataLayout (info->layout, DATA_LAYOUT_TRIV2);
+    converter.setDataType (info->type, meta->getInputQuantType (idx));
+    converter.setDataDims (meta->getInputDims (idx));
+    converter.setQuantZero (meta->getInputQuantZero (idx));
+    converter.setQuantScale (meta->getInputQuantScale (idx));
   } else {
-    memcpy (dst, src, size);
+    const tensor_data_info* info = model->getOutputDataInfo (idx);
+    if (info == nullptr)
+      return 0;
+
+    converter.setDataLayout (DATA_LAYOUT_TRIV2, info->layout);
+    converter.setDataType (meta->getOutputQuantType (idx), info->type);
+    converter.setDataDims (meta->getOutputDims (idx));
+    converter.setQuantZero (meta->getOutputQuantZero (idx));
+    converter.setQuantScale (meta->getOutputQuantScale (idx));
   }
 
-  return size;
+  return converter.perform ();
 }
 
 #else
@@ -1934,10 +1823,6 @@ TrinityVision::manipulateData (const Model *model, uint32_t idx, bool is_input,
   return size;
 }
 
-#endif
-
-/** other device types don't have data manip impl. yet */
-
 size_t
 TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
     void *dst, void *src, size_t size)
@@ -1946,6 +1831,10 @@ TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
   return size;
 }
 
+#endif
+
+/** other device types don't have data manip impl. yet */
+
 size_t
 TrinityAsr::manipulateData (const Model *model, uint32_t idx, bool is_input,
     void *dst, void *src, size_t size)
index 9420603..e2bb0e0 100644 (file)
@@ -339,6 +339,32 @@ Model::setDataInfo (const tensors_data_info *in, const tensors_data_info *out)
   return 0;
 }
 
+void
+Model::updateDataInfo ()
+{
+  for (uint32_t idx = 0; idx < in_.num_info; idx++) {
+    if (in_.info[idx].layout == DATA_LAYOUT_MODEL) {
+      if (meta_->getVersion () < 3)
+        in_.info[idx].layout = DATA_LAYOUT_TRIV;
+      else
+        in_.info[idx].layout = DATA_LAYOUT_TRIV2;
+    }
+    if (in_.info[idx].type == DATA_TYPE_MODEL)
+      in_.info[idx].type = meta_->getInputQuantType (idx);
+  }
+
+  for (uint32_t idx = 0; idx < out_.num_info; idx++) {
+    if (out_.info[idx].layout == DATA_LAYOUT_MODEL) {
+      if (meta_->getVersion () < 3)
+        out_.info[idx].layout = DATA_LAYOUT_TRIV;
+      else
+        out_.info[idx].layout = DATA_LAYOUT_TRIV2;
+    }
+    if (out_.info[idx].type == DATA_TYPE_MODEL)
+      out_.info[idx].type = meta_->getOutputQuantType (idx);
+  }
+}
+
 /**
  * @brief extract and set the metadata
  * @param[in] data raw-data of model binary
@@ -354,6 +380,18 @@ Model::setMetadata (void *data)
   if (this->meta_.get() == nullptr)
     return -EINVAL;
 
+  in_.num_info = getInputTensorNum ();
+  for (uint32_t idx = 0; idx < getInputTensorNum (); idx++) {
+    in_.info[idx].layout = DATA_LAYOUT_MODEL;
+    in_.info[idx].type = DATA_TYPE_MODEL;
+  }
+
+  out_.num_info = getOutputTensorNum ();
+  for (uint32_t idx = 0; idx < getOutputTensorNum (); idx++) {
+    out_.info[idx].layout = DATA_LAYOUT_MODEL;
+    out_.info[idx].type = DATA_TYPE_MODEL;
+  }
+
   return 0;
 }
 
index e0ee6e3..9404f7e 100644 (file)
@@ -28,8 +28,8 @@
 #include <assert.h>
 
 /** default data layout and type */
-static const data_type default_data_type = DATA_TYPE_SRNPU;
-static const data_layout default_data_layout = DATA_LAYOUT_SRNPU;
+static const data_type default_data_type = DATA_TYPE_MODEL;
+static const data_layout default_data_layout = DATA_LAYOUT_MODEL;
 /** this assumes uint8 zero quantization */
 static const uint32_t default_quant_zero = 127;
 static const float default_quant_scale = 1.0;
@@ -81,6 +81,12 @@ class Metadata {
     virtual uint32_t getInputSegmentOffset (uint32_t idx) const { return 0; }
     virtual uint32_t getOutputSegmentOffset (uint32_t idx) const { return 0; }
 
+    virtual uint32_t getInputEmodY (uint32_t idx) const { return 0; }
+    virtual uint32_t getInputEmodZ (uint32_t idx) const { return 0; }
+
+    virtual uint32_t getOutputEmodY (uint32_t idx) const { return 0; }
+    virtual uint32_t getOutputEmodZ (uint32_t idx) const { return 0; }
+
     uint64_t getSize () const { return meta_->size; }
     uint64_t getProgramSize () const { return meta_->program_size; }
     uint64_t getWeightSize () const { return meta_->weight_size; }
@@ -130,6 +136,13 @@ class Metadata_v1 : public Metadata {
       return output_dims[idx];
     }
 
+    data_type getInputQuantType (uint32_t idx) const override {
+      return DATA_TYPE_SRNPU;
+    }
+
+    data_type getOutputQuantType (uint32_t idx) const override {
+      return DATA_TYPE_SRNPU;
+    }
   private:
     Metadata_v1 (npubin_meta *meta);
 
@@ -192,6 +205,16 @@ class Metadata_v2 : public Metadata {
       return meta_->output_quant_s[idx];
     }
 
+    data_type getInputQuantType (uint32_t idx) const override {
+      assert (idx < getOutputNum ());
+      return DATA_TYPE_SRNPU;
+    }
+
+    data_type getOutputQuantType (uint32_t idx) const override {
+      assert (idx < getOutputNum ());
+      return DATA_TYPE_SRNPU;
+    }
+
   private:
     Metadata_v2 (npubin_meta *meta);
 };
@@ -227,6 +250,24 @@ class Metadata_v3 : public Metadata {
       return meta_->output_seg_dims[idx];
     }
 
+    uint32_t getInputEmodY (uint32_t idx) const override {
+      assert (idx < getInputNum ());
+      return meta_->input_seg_emod_y[idx];
+    }
+    uint32_t getInputEmodZ (uint32_t idx) const override {
+      assert (idx < getInputNum ());
+      return meta_->input_seg_emod_z[idx];
+    }
+
+    uint32_t getOutputEmodY (uint32_t idx) const override {
+      assert (idx < getOutputNum ());
+      return meta_->output_seg_emod_y[idx];
+    }
+    uint32_t getOutputEmodZ (uint32_t idx) const override {
+      assert (idx < getOutputNum ());
+      return meta_->output_seg_emod_z[idx];
+    }
+
     uint32_t getInputQuantZero (uint32_t idx) const override {
       assert (idx < getInputNum ());
       return meta_->input_seg_quant_z[idx];
@@ -295,6 +336,8 @@ class Model : public HWmem {
     void setProgramData (HWmem *data) { program_data_ = data; }
     void setInternalID (uint64_t id) { internal_id_ = id; }
 
+    void updateDataInfo ();
+
     const Metadata *getMetadata () const { return meta_.get(); }
 
     uint32_t getID () const { return model_id_; }
index cb1f17b..e141447 100644 (file)
@@ -50,8 +50,8 @@ class TesterTRIV2 {
       }
     }
 
-    void set_async (int async) {
-      async_ = (async == 1);
+    void set_async () {
+      async_ = true;
     }
 
     void set_repeat (const std::string repeat_str) {
@@ -267,6 +267,7 @@ print_usage (const char *prog_name)
   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  */
@@ -278,10 +279,10 @@ main (int argc, char **argv)
 
   optind = 0;
   opterr = 0;
-  while ((c = getopt (argc, argv, "an:i:")) != -1) {
+  while ((c = getopt (argc, argv, "ahn:i:")) != -1) {
     switch (c) {
       case 'a':
-        tester.set_async (1);
+        tester.set_async ();
         break;
       case 'n':
         tester.set_repeat (optarg);
@@ -295,8 +296,10 @@ main (int argc, char **argv)
         else
           std::cerr << "Unknown flag: " << c;
         std::cerr << std::endl;
-
+        // no-break
+      case 'h':
         print_usage(argv[0]);
+        return 0;
     }
   }
 
index 6dc10c7..7e107c2 100644 (file)
@@ -29,10 +29,12 @@ TEST (ne_core_comm_test, get_comm_plugin)
   EXPECT_EQ (&plugin, &plugin2);
 }
 
-static void test_manip (char *data, size_t size)
+static size_t test_manip (char *data, size_t size)
 {
   for (size_t i = 0; i < size; i++)
     data[i] = 'b';
+
+  return size;
 }
 
 /**
index fcb1361..f2eb1f4 100644 (file)
@@ -779,10 +779,10 @@ TEST (ne_core_handler_test, triv_quantization)
 
         if (val > 255.0)
           val = 255.0;
-        else
+        else if (val < 0.0)
           val = 0.0;
 
-        EXPECT_DOUBLE_EQ (val, (double)((uint8_t *) each_data_ptr)[j]);
+        EXPECT_EQ ((uint8_t) val, ((uint8_t *) each_data_ptr)[j]);
       }
     }
   }
@@ -824,10 +824,204 @@ TEST (ne_core_handler_test, triv_quantization)
 
         if (val > 255.0)
           val = 255.0;
-        else
+        else if (val < 0.0)
           val = 0.0;
 
-        EXPECT_DOUBLE_EQ (val, (double)((uint8_t *) each_data_ptr)[j % 32 + 64 * (j / 32)]);
+        EXPECT_EQ ((uint8_t) val, ((uint8_t *) each_data_ptr)[j % 32 + 64 * (j / 32)]);
+      }
+    }
+  }
+
+  destroy_input_buffers (input_buf);
+  destroy_model_buffer (model_buf);
+}
+
+/**
+ * @brief test TRIV's dequantization during data manipulation
+ */
+TEST (ne_core_handler_test, triv_dequantization)
+{
+  std::unique_ptr<Device> device (Device::createInstance (NPUCOND_TRIV_CONN_SOCIP, 0));
+  EXPECT_NE (device.get (), nullptr);
+
+  srand (time (NULL));
+
+  /* metadata version 2 */
+  generic_buffer model_buf;
+  create_model_buffer_v2 (model_buf);
+
+  /* setup for input tensors */
+  input_buffers input_buf;
+  create_input_buffers (input_buf);
+
+  uint32_t num_tensors = 4;
+  uint32_t size_tensor = 4096;
+
+  tensors_data_info info;
+  info.num_info = num_tensors;
+  for (uint32_t i = 0; i < num_tensors; i++) {
+    info.info[i].layout = DATA_LAYOUT_NHWC; /* need manipulation */
+    info.info[i].type = DATA_TYPE_FLOAT32;    /* need quantization */
+  }
+
+  for (uint32_t i = 0; i < num_tensors; i++) {
+    /* fill any data */
+    for (uint32_t j = 0; j < size_tensor / sizeof (float); j++) {
+      ((float *) input_buf.bufs[i].addr)[j] = i * j;
+    }
+  }
+
+  npubin_meta *meta = static_cast<npubin_meta *> (model_buf.addr);
+
+  /* case 1: no data manipulation (depth == 64) */
+  meta->input_num = num_tensors;
+  meta->buffer_size = size_tensor / sizeof (float); /** 32bit -> 8bit */
+  for (uint32_t i = 0; i < num_tensors; i++) {
+    meta->input_offsets[i] = 0;
+    meta->input_elem_size[i] = sizeof (float); /* float */
+    meta->input_dims[i][0] = 1;
+    meta->input_dims[i][1] = 4;
+    meta->input_dims[i][2] = 4;
+    meta->input_dims[i][3] = 64;
+
+    meta->input_quant_s[i] = 10.0;
+    meta->input_quant_z[i] = 127;
+
+    meta->output_offsets[i] = 0;
+    meta->output_elem_size[i] = sizeof (float); /* float */
+    meta->output_dims[i][0] = 1;
+    meta->output_dims[i][1] = 4;
+    meta->output_dims[i][2] = 4;
+    meta->output_dims[i][3] = 64;
+
+    meta->output_quant_s[i] = 10.0;
+    meta->output_quant_z[i] = 127;
+  }
+
+  Model *model = nullptr;
+  EXPECT_EQ (device->setModel (&model_buf, &model), 0);
+  EXPECT_EQ (model->setDataInfo (&info, &info), 0);
+
+  {
+    std::unique_ptr<uint8_t []> output_data (new uint8_t [meta->buffer_size]);
+    std::unique_ptr<float []> output_dequant_data (new float [meta->buffer_size]);
+
+    double zero_point, scale;
+    void *each_data_ptr;
+
+    for (uint32_t i = 0; i < num_tensors; i++) {
+      EXPECT_EQ (TrinityVision::manipulateData (model, i, true,
+            output_data.get (), input_buf.bufs[i].addr, size_tensor), size_tensor);
+
+      zero_point = (double) meta->input_quant_z[i];
+      scale = (double) meta->input_quant_s[i];
+      each_data_ptr = (char *) output_data.get ();
+
+      for (uint32_t j = 0; j < size_tensor / sizeof(float); j++) {
+        double val = (double)((float *) input_buf.bufs[i].addr)[j];
+
+        val = val / scale;
+        val = val + zero_point;
+
+        if (val > 255.0)
+          val = 255.0;
+        else if (val < 0.0)
+          val = 0.0;
+
+        EXPECT_EQ ((uint8_t) val, ((uint8_t *) each_data_ptr)[j]);
+      }
+
+      EXPECT_EQ (TrinityVision::manipulateData (model, i, false,
+            output_dequant_data.get (), output_data.get (), size_tensor / sizeof (float)),
+          size_tensor / sizeof (float));
+
+      zero_point = (double) meta->output_quant_z[i];
+      scale = (double) meta->output_quant_s[i];
+      each_data_ptr = (char *) output_dequant_data.get ();
+
+      for (uint32_t j = 0; j < size_tensor / sizeof(float); j++) {
+        double val = (double)((uint8_t *) output_data.get ())[j];
+
+        val = val - zero_point;
+        val = val * scale;
+
+        ASSERT_DOUBLE_EQ (val, (double)((float *) each_data_ptr)[j]);
+      }
+    }
+  }
+
+  /* case 2: data manipulation (depth == 32) */
+  meta->input_num = num_tensors;
+  meta->buffer_size = size_tensor / sizeof (float) * 2; /** 32bit -> 8bit but need zero padding */
+  for (uint32_t i = 0; i < num_tensors; i++) {
+    meta->input_offsets[i] = 0;
+    meta->input_elem_size[i] = sizeof (float); /* float */
+    meta->input_dims[i][0] = 1;
+    meta->input_dims[i][1] = 8;
+    meta->input_dims[i][2] = 4;
+    meta->input_dims[i][3] = 32;
+
+    meta->input_quant_s[i] = 10.0;
+    meta->input_quant_z[i] = 127;
+
+    meta->output_offsets[i] = 0;
+    meta->output_elem_size[i] = sizeof (float); /* float */
+    meta->output_dims[i][0] = 1;
+    meta->output_dims[i][1] = 8;
+    meta->output_dims[i][2] = 4;
+    meta->output_dims[i][3] = 32;
+
+    meta->output_quant_s[i] = 10.0;
+    meta->output_quant_z[i] = 127;
+  }
+
+  EXPECT_EQ (device->setModel (&model_buf, &model), 0);
+  EXPECT_EQ (model->setDataInfo (&info, &info), 0);
+
+  {
+    std::unique_ptr<uint8_t []> output_data (new uint8_t [meta->buffer_size]);
+    std::unique_ptr<float []> output_dequant_data (new float [meta->buffer_size]);
+
+    double zero_point, scale;
+    void *each_data_ptr;
+
+    for (uint32_t i = 0; i < num_tensors; i++) {
+      EXPECT_EQ (TrinityVision::manipulateData (model, i, true,
+            output_data.get (), input_buf.bufs[i].addr, size_tensor), size_tensor);
+
+      zero_point = (double) meta->input_quant_z[i];
+      scale = (double) meta->input_quant_s[i];
+      each_data_ptr = (char *) output_data.get ();
+
+      for (uint32_t j = 0; j < size_tensor / sizeof(float); j++) {
+        double val = (double)((float *) input_buf.bufs[i].addr)[j];
+
+        val = val / scale;
+        val = val + zero_point;
+
+        if (val > 255.0)
+          val = 255.0;
+        else if (val < 0.0)
+          val = 0.0;
+
+        EXPECT_EQ ((uint8_t) val, ((uint8_t *) each_data_ptr)[j % 32 + 64 * (j / 32)]);
+      }
+
+      EXPECT_EQ (TrinityVision::manipulateData (model, i, false,
+            output_dequant_data.get (), output_data.get (), size_tensor / sizeof (float) * 2),
+          size_tensor / sizeof (float) * 2);
+
+      zero_point = (double) meta->output_quant_z[i];
+      scale = (double) meta->output_quant_s[i];
+      each_data_ptr = (char *) output_dequant_data.get ();
+
+      for (uint32_t j = 0; j < size_tensor / sizeof(float); j++) {
+        double val = (double)((uint8_t *) output_data.get ())[j % 32 + 64 * (j / 32)];
+
+        val = val - zero_point;
+        val = val * scale;
+
+        EXPECT_DOUBLE_EQ (val, (double)((float *) each_data_ptr)[j]);
       }
     }
   }
index 7856070..b430849 100644 (file)
@@ -291,8 +291,8 @@ TEST (ne_core_model_test, metadata_primitives_v1)
   EXPECT_EQ (meta->getOutputQuantZero (0), default_quant_zero);
   EXPECT_FLOAT_EQ (meta->getInputQuantScale (0), default_quant_scale);
   EXPECT_FLOAT_EQ (meta->getOutputQuantScale (0), default_quant_scale);
-  EXPECT_EQ (meta->getInputQuantType (0), default_data_type);
-  EXPECT_EQ (meta->getOutputQuantType (0), default_data_type);
+  EXPECT_EQ (meta->getInputQuantType (0), DATA_TYPE_SRNPU);
+  EXPECT_EQ (meta->getOutputQuantType (0), DATA_TYPE_SRNPU);
 
   EXPECT_EQ (meta->getSegmentsNum (), (uint32_t) 0);
   EXPECT_EQ (meta->getSegmentSize (0), (uint32_t) 0);