[Plugin/NNS] Add NNStreamer filter sub-plguin for TRIV2
authorDongju Chae <dongju.chae@samsung.com>
Tue, 6 Jul 2021 03:26:57 +0000 (12:26 +0900)
committer채동주/On-Device Lab(SR)/Staff Engineer/삼성전자 <dongju.chae@samsung.com>
Tue, 6 Jul 2021 07:56:25 +0000 (16:56 +0900)
This patch adds NNStreamer tensor-filter plugin for TRIV2,
moved from nnstreamer-private-plugins.

Signed-off-by: Dongju Chae <dongju.chae@samsung.com>
packaging/npu-engine.spec
plugins/nnstreamer/meson.build
plugins/nnstreamer/tensor_filter_subplugin.cc [new file with mode: 0644]
plugins/nnstreamer/tensor_filter_subplugin.h [new file with mode: 0644]
plugins/nnstreamer/tensor_filter_subplugin_triv2.cc [new file with mode: 0644]
plugins/npumgr/dummy/meson.build
plugins/npumgr/meson.build

index 31d9815..c7efd64 100644 (file)
@@ -136,9 +136,9 @@ Requires:   nnstreamer
 Reference implementation of NNStreamer filter subplugin for SR-NPU (for now, TRIV2 only).
 %files -n nnstreamer-srnpu
 %manifest npu-engine.manifest
-%{neexampledir}/plugins/nnstreamer/*
+# Uncomment below when test-cases are ready
+# %{neexampledir}/plugins/nnstreamer/*
 %{_prefix}/lib/nnstreamer/filters/libnnstreamer_filter_srnpu.so
-%{_prefix}/lib/nnstreamer/unittest/nnstreamer_filter_srnpu/*
 %endif
 
 %changelog
index c97c64c..52a3b1a 100644 (file)
@@ -1 +1,43 @@
-# Placeholder of NNStreamer TRIV2 subplugin
+gst_api_verision = '1.0'
+
+glib_dep = dependency('glib-2.0')
+gobject_dep = dependency('gobject-2.0')
+gmodule_dep = dependency('gmodule-2.0')
+gst_dep = dependency('gstreamer-' + gst_api_verision)
+nnstreamer_dep = dependency('nnstreamer')
+
+common_inc = include_directories('.')
+common_sources = [
+  'tensor_filter_subplugin.cc'
+]
+triv2_sources = [
+  'tensor_filter_subplugin_triv2.cc'
+]
+
+base_deps = [
+  libm_dep,
+  glib_dep,
+  gobject_dep,
+  gmodule_dep,
+  gst_dep,
+  nnstreamer_dep
+]
+
+if target_platform == 'tizen'
+  base_deps += dependency('dlog')
+endif
+
+subplugin_common_dep = declare_dependency(
+  sources : common_sources,
+  link_with : ne_library_shared,
+  dependencies : [base_deps, iniparser_dep],
+  include_directories : [common_inc, ne_common_inc, ne_host_inc]
+)
+
+triv2_shared = shared_library('nnstreamer_filter_srnpu',
+  triv2_sources,
+  link_with : ne_library_shared,
+  dependencies: subplugin_common_dep,
+  install: true,
+  install_dir: join_paths(get_option('prefix'), 'lib', 'nnstreamer', 'filters')
+)
diff --git a/plugins/nnstreamer/tensor_filter_subplugin.cc b/plugins/nnstreamer/tensor_filter_subplugin.cc
new file mode 100644 (file)
index 0000000..d3378b9
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * Proprietary
+ * Copyright (C) 2020 Samsung Electronics
+ * Copyright (C) 2020 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file tensor_filter_subplugin.cc
+ * @date 20 Jan 2020
+ * @brief NNStreamer tensor-filter subplugin for srnpu devices
+ * @see http://github.com/nnstreamer/nnstreamer
+ * @see https://github.sec.samsung.net/AIP/NPU_SystemService
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include "tensor_filter_subplugin.h"
+
+TensorFilterSRNPU::TensorFilterSRNPU (dev_type type)
+    : dev_ (nullptr), type_ (type), meta_ (nullptr), model_id_ (0) {}
+
+TensorFilterSRNPU::~TensorFilterSRNPU () {
+  if (dev_ != nullptr) {
+    unregisterNPUmodel_all (dev_);
+    putNPUdevice (dev_);
+  }
+
+  if (meta_ != nullptr)
+    free (meta_);
+}
+
+int
+TensorFilterSRNPU::open () {
+  int num_devices = getnumNPUdeviceByType (type_);
+  if (num_devices <= 0)
+    return -ENODEV;
+
+  /* TODO: this always uses the first device */
+  return getNPUdeviceByType (&dev_, type_, 0);
+}
+
+int
+TensorFilterSRNPU::invoke (const GstTensorFilterProperties* prop,
+                           const GstTensorMemory* input,
+                           GstTensorMemory* output) {
+  input_buffers input_buf;
+  output_buffers output_buf;
+
+  input_buf.num_buffers = (prop->input_meta).num_tensors;
+  for (uint32_t idx = 0; idx < input_buf.num_buffers; ++idx) {
+    input_buf.bufs[idx].addr = input[idx].data;
+    input_buf.bufs[idx].size = input[idx].size;
+    input_buf.bufs[idx].type = BUFFER_MAPPED;
+  }
+
+  int status = runNPU_sync (dev_, model_id_, &input_buf, &output_buf);
+  if (status < 0) {
+    ml_loge ("runNPU_sync() failed: %d\n", status);
+    return status;
+  }
+
+  /* extract output buffers; their data will be freed in destroyNotify() */
+  if ((prop->output_meta).num_tensors == output_buf.num_buffers) {
+    for (uint32_t idx = 0; idx < output_buf.num_buffers; ++idx) {
+      output[idx].data = output_buf.bufs[idx].addr;
+    }
+  } else {
+    ml_loge ("Wrong number of output tensors");
+    return -EINVAL;
+  }
+
+  return 0;
+}
diff --git a/plugins/nnstreamer/tensor_filter_subplugin.h b/plugins/nnstreamer/tensor_filter_subplugin.h
new file mode 100644 (file)
index 0000000..adca05a
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * Proprietary
+ * Copyright (C) 2020 Samsung Electronics
+ * Copyright (C) 2020 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file    tensor_filter_subplugin.h
+ * @date    20 Jan 2020
+ * @brief   NNStreamer tensor-filter subplugin srnpu header
+ * @see     https://github.com/nnsuite/nnstreamer
+ * @see     https://github.sec.samsung.net/AIP/NPU_SystemService
+ * @author  Dongju Chae <dongju.chae@samsung.com>
+ * @bug     No known bugs
+ */
+
+#ifndef __TENSOR_FILTER_SUBPLUGIN_H__
+
+/* npu-engine headers */
+#include <npubinfmt.h>
+#include <libnpuhost.h>
+
+/* nnstreamer plugin api headers */
+#include <nnstreamer_plugin_api.h>
+#include <nnstreamer_plugin_api_filter.h>
+
+/* stdlib */
+#include <string>
+
+#if defined(__TIZEN__)
+#include <dlog.h>
+#define TAG_NAME "nnstreamer_srnpu"
+#define ml_logi(...) dlog_print (DLOG_INFO, TAG_NAME, __VA_ARGS__)
+#define ml_logw(...) dlog_print (DLOG_WARN, TAG_NAME, __VA_ARGS__)
+#define ml_loge(...) dlog_print (DLOG_ERROR, TAG_NAME, __VA_ARGS__)
+#define ml_logd(...) dlog_print (DLOG_DEBUG, TAG_NAME, __VA_ARGS__)
+#define ml_logf(...) dlog_print (DLOG_FATAL, TAG_NAME, __VA_ARGS__)
+#else
+#define ml_logi g_info
+#define ml_logw g_warning
+#define ml_loge g_critical
+#define ml_logd g_debug
+#define ml_logf g_error
+#endif
+
+class TensorFilterSRNPU {
+ public:
+  TensorFilterSRNPU (dev_type type);
+  virtual ~TensorFilterSRNPU ();
+
+  int open ();
+  int invoke (const GstTensorFilterProperties *prop,
+              const GstTensorMemory *input, GstTensorMemory *output);
+
+  virtual int loadModel (std::string model_path) { return -EPERM; }
+  virtual int getInputTensorDim (GstTensorsInfo *info) { return -EPERM; }
+  virtual int getOutputTensorDim (GstTensorsInfo *info) { return -EPERM; }
+
+ protected:
+  npudev_h dev_;
+  dev_type type_;
+
+  npubin_meta *meta_;
+  uint32_t model_id_;
+
+  GstTensorsInfo input_tensor_meta_;
+  GstTensorsInfo output_tensor_meta_;
+
+  std::string model_path_;
+};
+
+#endif /* __TENSOR_FILTER_SUBPLUGIN_H__ */
diff --git a/plugins/nnstreamer/tensor_filter_subplugin_triv2.cc b/plugins/nnstreamer/tensor_filter_subplugin_triv2.cc
new file mode 100644 (file)
index 0000000..98b52b2
--- /dev/null
@@ -0,0 +1,326 @@
+/**
+ * Proprietary
+ * Copyright (C) 2020 Samsung Electronics
+ * Copyright (C) 2020 Dongju Chae <dongju.chae@samsung.com>
+ */
+/**
+ * @file tensor_filter_subplugin_triv2.cc
+ * @date 20 Jan 2020
+ * @brief NNStreamer tensor-filter subplugin for TRIV2
+ * @see http://github.com/nnstreamer/nnstreamer
+ * @see https://github.sec.samsung.net/AIP/NPU_SystemService
+ * @author Dongju Chae <dongju.chae@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <tensor_filter_subplugin.h>
+#include <inttypes.h>
+
+#define DEV_TYPE (NPUCOND_TRIV2_CONN_SOCIP)
+
+void init_filter_srnpu_triv2 (void) __attribute__ ((constructor));
+void fini_filter_srnpu_triv2 (void) __attribute__ ((destructor));
+
+static const gchar *triv2_accl_support[] = {ACCL_NPU_SR_STR, NULL};
+
+class TensorFilterTRIV2 : public TensorFilterSRNPU {
+ public:
+  TensorFilterTRIV2 () : TensorFilterSRNPU (DEV_TYPE) {}
+
+  int loadModel (std::string model_path) {
+    meta_ = getNPUmodel_metadata (model_path.c_str (), false);
+    if (meta_ == nullptr) {
+      return -EINVAL;
+    }
+
+    uint64_t version = NPUBIN_VERSION (meta_->magiccode);
+    if (version != 3) {
+      ml_loge ("Unsupported npubinfmt version: %" PRId64, version);
+      return -EINVAL;
+    }
+
+    generic_buffer model;
+    model.type = BUFFER_FILE;
+    model.size = meta_->size;
+    model.filepath = model_path.c_str ();
+
+    int status = registerNPUmodel (dev_, &model, &model_id_);
+    if (status != 0) {
+      ml_loge ("Unable to register NPU model: %d", status);
+      return status;
+    }
+
+    status = set_constraint ();
+    if (status != 0) {
+      ml_loge ("Unable to set constraints: %d", status);
+      return status;
+    }
+
+    return 0;
+  }
+
+  int getInputTensorDim (GstTensorsInfo *info) {
+    if (info == nullptr || meta_ == nullptr)
+      return -EINVAL;
+
+    gst_tensors_info_init (info);
+    gst_tensors_info_copy (info, &input_tensor_meta_);
+
+    return 0;
+  }
+
+  int getOutputTensorDim (GstTensorsInfo *info) {
+    if (info == nullptr || meta_ == nullptr)
+      return -EINVAL;
+
+    gst_tensors_info_init (info);
+    gst_tensors_info_copy (info, &output_tensor_meta_);
+
+    return 0;
+  }
+
+  int setDataInfo (const GstTensorFilterProperties *prop) {
+    tensors_data_info info_in, info_out;
+
+    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_NHWC;
+      info_in.info[idx].type = get_data_type (prop->input_meta.info[idx].type);
+    }
+
+    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_NHWC;
+      info_out.info[idx].type =
+          get_data_type (prop->output_meta.info[idx].type);
+    }
+
+    copy_tensors_info (&info_in, &input_tensor_meta_, true);
+    copy_tensors_info (&info_out, &output_tensor_meta_, false);
+
+    return setNPU_dataInfo (dev_, model_id_, &info_in, &info_out);
+  }
+
+ private:
+  void copy_tensors_info (const tensors_data_info *ne_info,
+                          GstTensorsInfo *nns_info, bool is_input) {
+    gst_tensors_info_init (nns_info);
+
+    nns_info->num_tensors = ne_info->num_info;
+    for (uint32_t i = 0; i < ne_info->num_info; i++) {
+      nns_info->info[i].type = get_tensor_type (ne_info->info[i].type);
+      for (uint32_t j = 0; j < NNS_TENSOR_RANK_LIMIT; ++j) {
+        if (is_input)
+          nns_info->info[i].dimension[j] =
+              meta_->input_seg_dims[i][NNS_TENSOR_RANK_LIMIT - j - 1];
+        else
+          nns_info->info[i].dimension[j] =
+              meta_->output_seg_dims[i][NNS_TENSOR_RANK_LIMIT - j - 1];
+      }
+    }
+  }
+
+  data_layout get_data_layout (tensor_layout layout) {
+    switch (layout) {
+      case _NNS_LAYOUT_NHWC:
+        /* currently, support only NHWC */
+        return DATA_LAYOUT_NHWC;
+      default:
+        return DATA_LAYOUT_MODEL;
+    }
+  }
+
+  data_type get_data_type (tensor_type type) {
+    switch (type) {
+      case _NNS_INT8:
+        return DATA_TYPE_INT8;
+      case _NNS_UINT8:
+        return DATA_TYPE_UINT8;
+      case _NNS_INT16:
+        return DATA_TYPE_INT16;
+      case _NNS_UINT16:
+        return DATA_TYPE_UINT16;
+      case _NNS_INT32:
+        return DATA_TYPE_INT32;
+      case _NNS_UINT32:
+        return DATA_TYPE_UINT32;
+      case _NNS_INT64:
+        return DATA_TYPE_INT32;
+      case _NNS_UINT64:
+        return DATA_TYPE_UINT32;
+      case _NNS_FLOAT32:
+        return DATA_TYPE_FLOAT32;
+      case _NNS_FLOAT64:
+        return DATA_TYPE_FLOAT64;
+      default:
+        /* use the quantization type specified in the model metadata */
+        return DATA_TYPE_MODEL;
+    }
+  }
+
+  tensor_type get_tensor_type (data_type type) {
+    switch (type) {
+      case DATA_TYPE_INT8:
+        return _NNS_INT8;
+      case DATA_TYPE_UINT8:
+        return _NNS_UINT8;
+      case DATA_TYPE_INT16:
+        return _NNS_INT16;
+      case DATA_TYPE_UINT16:
+        return _NNS_UINT16;
+      case DATA_TYPE_INT32:
+        return _NNS_INT32;
+      case DATA_TYPE_UINT32:
+        return _NNS_UINT32;
+      case DATA_TYPE_FLOAT32:
+        return _NNS_FLOAT32;
+      case DATA_TYPE_FLOAT64:
+        return _NNS_FLOAT64;
+      default:
+        /* default is uint8 */
+        return _NNS_UINT8;
+    }
+  }
+
+  int set_constraint () {
+    npuConstraint constraint;
+
+    constraint.timeout_ms = 5000;
+    constraint.priority = NPU_PRIORITY_MID;
+    constraint.notimode = NPU_INTERRUPT;
+
+    return setNPU_constraint (dev_, model_id_, constraint);
+  }
+};
+
+static void
+triv2_close (const GstTensorFilterProperties *prop, void **private_data) {
+  TensorFilterTRIV2 *srnpu = static_cast<TensorFilterTRIV2 *> (*private_data);
+
+  if (srnpu != nullptr)
+    delete srnpu;
+
+  *private_data = nullptr;
+}
+
+static int
+triv2_open (const GstTensorFilterProperties *prop, void **private_data) {
+  if (prop->num_models > 1) {
+    ml_logw ("Multiple models are not supported");
+  }
+
+  if (prop->model_files[0] == nullptr || prop->model_files[0][0] == '\x00') {
+    ml_loge ("Invalid model path provided");
+    return -EINVAL;
+  }
+
+  TensorFilterTRIV2 *srnpu = static_cast<TensorFilterTRIV2 *> (*private_data);
+  if (srnpu != nullptr) {
+    triv2_close (prop, private_data);
+    srnpu = nullptr;
+  }
+
+  srnpu = new TensorFilterTRIV2 ();
+  *private_data = srnpu;
+
+  int status = srnpu->open ();
+  if (status != 0) {
+    ml_loge ("Failed to open TRIV2 device: %d", status);
+    goto err_free;
+  }
+
+  status = srnpu->loadModel (prop->model_files[0]);
+  if (status != 0) {
+    ml_loge ("Failed to load model: %d", status);
+    goto err_free;
+  }
+
+  status = srnpu->setDataInfo (prop);
+  if (status != 0) {
+    ml_loge ("Failed to set data info: %d", status);
+    goto err_free;
+  }
+
+  return 0;
+
+err_free:
+  delete srnpu;
+  *private_data = nullptr;
+
+  return status;
+}
+
+static int
+triv2_invoke (const GstTensorFilterProperties *prop, void **private_data,
+              const GstTensorMemory *input, GstTensorMemory *output) {
+  TensorFilterTRIV2 *srnpu = static_cast<TensorFilterTRIV2 *> (*private_data);
+
+  g_return_val_if_fail (srnpu != nullptr, -EINVAL);
+
+  return srnpu->invoke (prop, input, output);
+}
+
+static int
+triv2_getInputDim (const GstTensorFilterProperties *prop, void **private_data,
+                   GstTensorsInfo *info) {
+  TensorFilterTRIV2 *srnpu = static_cast<TensorFilterTRIV2 *> (*private_data);
+
+  g_return_val_if_fail (srnpu != nullptr, -EINVAL);
+
+  return srnpu->getInputTensorDim (info);
+}
+
+static int
+triv2_getOutputDim (const GstTensorFilterProperties *prop, void **private_data,
+                    GstTensorsInfo *info) {
+  TensorFilterTRIV2 *srnpu = static_cast<TensorFilterTRIV2 *> (*private_data);
+
+  g_return_val_if_fail (srnpu != nullptr, -EINVAL);
+
+  return srnpu->getOutputTensorDim (info);
+}
+
+static int
+triv2_checkAvailability (accl_hw hw) {
+  if (g_strv_contains (triv2_accl_support, get_accl_hw_str (hw)))
+    return 0;
+
+  return -ENOENT;
+}
+
+static gchar filter_subplugin_srnpu_triv2[] = "srnpu";
+
+static GstTensorFilterFramework NNS_support_srnpu_triv2 = {
+    .version = GST_TENSOR_FILTER_FRAMEWORK_V0,
+    .open = triv2_open,
+    .close = triv2_close,
+};
+
+void
+init_filter_srnpu_triv2 (void) {
+  if (getnumNPUdeviceByType (DEV_TYPE) <= 0) {
+    ml_loge ("No available TRIV2 device");
+    return;
+  }
+
+  NNS_support_srnpu_triv2.name = filter_subplugin_srnpu_triv2;
+  NNS_support_srnpu_triv2.allow_in_place = FALSE;
+  NNS_support_srnpu_triv2.allocate_in_invoke = TRUE;
+  NNS_support_srnpu_triv2.run_without_model = FALSE;
+  NNS_support_srnpu_triv2.verify_model_path = FALSE;
+  NNS_support_srnpu_triv2.invoke_NN = triv2_invoke;
+  NNS_support_srnpu_triv2.getInputDimension = triv2_getInputDim;
+  NNS_support_srnpu_triv2.getOutputDimension = triv2_getOutputDim;
+  NNS_support_srnpu_triv2.checkAvailability = triv2_checkAvailability;
+  NNS_support_srnpu_triv2.destroyNotify = nullptr; /* reply on g_free() */
+
+  nnstreamer_filter_probe (&NNS_support_srnpu_triv2);
+}
+
+void
+fini_filter_srnpu_triv2 (void) {
+  if (getnumNPUdeviceByType (DEV_TYPE) <= 0)
+    return;
+
+  nnstreamer_filter_exit (NNS_support_srnpu_triv2.name);
+}
index e68244d..27fc453 100644 (file)
@@ -1,50 +1,48 @@
 # Note that VD NPU Manager is a gdbus-based thread application
-gmodule_dep = dependency('gmodule-2.0', required: false)
-giounix_dep = dependency('gio-unix-2.0', required: false)
-if gmodule_dep.found() and giounix_dep.found()
-  npumgr_deps = [
-    glib_dep,
-    gmodule_dep,
-    giounix_dep
-  ]
+gmodule_dep = dependency('gmodule-2.0')
+giounix_dep = dependency('gio-unix-2.0')
+npumgr_deps = [
+  glib_dep,
+  gmodule_dep,
+  giounix_dep
+]
 
-  if target_platform == 'tizen'
-    npumgr_deps += dependency('dlog')
-  endif
+if target_platform == 'tizen'
+  npumgr_deps += dependency('dlog')
+endif
 
-  npumgr_lib = shared_library ('npumgr',
-    'npumgr_api.cc',
-    include_directories : [npumgr_common_inc],
-    dependencies: npumgr_deps,
-    build_rpath : ne_libdir,
-    install : true,
-    install_rpath : ne_libdir,
-    install_dir : ne_libdir
-  )
-  npumgr_dep = declare_dependency (link_with : npumgr_lib,
-    include_directories : include_directories('.')
-  )
+npumgr_lib = shared_library ('npumgr',
+  'npumgr_api.cc',
+  include_directories : [npumgr_common_inc],
+  dependencies: npumgr_deps,
+  build_rpath : ne_libdir,
+  install : true,
+  install_rpath : ne_libdir,
+  install_dir : ne_libdir
+)
+npumgr_dep = declare_dependency (link_with : npumgr_lib,
+  include_directories : include_directories('.')
+)
 
-  npumgr_triv2_lib = shared_library ('npumgr_triv2',
-    ['npumgr_triv2.cc', 'npumgr_device.cc'],
-    include_directories : [ne_common_inc, ne_host_inc],
-    dependencies: npumgr_deps,
-    link_with : ne_library_shared,
-    build_rpath : ne_libdir,
-    install : true,
-    install_rpath : ne_libdir,
-    install_dir : ne_libdir
-  )
+npumgr_triv2_lib = shared_library ('npumgr_triv2',
+  ['npumgr_triv2.cc', 'npumgr_device.cc'],
+  include_directories : [ne_common_inc, ne_host_inc],
+  dependencies: npumgr_deps,
+  link_with : ne_library_shared,
+  build_rpath : ne_libdir,
+  install : true,
+  install_rpath : ne_libdir,
+  install_dir : ne_libdir
+)
 
-  executable ('dummy_npumgr',
-    ['npumgr.cc', 'npumgr_device.cc'],
-    dependencies: npumgr_deps,
-    install : true,
-    install_rpath : ne_libdir,
-    install_dir : join_paths(ne_bindir, 'apptests', 'npumgr')
-  )
+executable ('dummy_npumgr',
+  ['npumgr.cc', 'npumgr_device.cc'],
+  dependencies: npumgr_deps,
+  install : true,
+  install_rpath : ne_libdir,
+  install_dir : join_paths(ne_bindir, 'apptests', 'npumgr')
+)
 
-  install_data ('sr.odl.NPUManager.conf',
-    install_dir: join_paths(get_option('datadir'), 'dbus-1', 'system.d')
-  )
-endif
+install_data ('sr.odl.NPUManager.conf',
+  install_dir: join_paths(get_option('datadir'), 'dbus-1', 'system.d')
+)
index 8900e94..c67c380 100644 (file)
@@ -1,11 +1,6 @@
-glib_dep = dependency('glib-2.0', required: false)
-if glib_dep.found()
-  npumgr_common_inc = include_directories('common')
-  npumgr_dep = disabler()
+glib_dep = dependency('glib-2.0')
+npumgr_common_inc = include_directories('common')
 
-  # dummy npu manager
-  subdir('dummy')
-  if npumgr_dep.found()
-    subdir('tests')
-  endif
-endif
+# dummy npu manager
+subdir('dummy')
+subdir('tests')