[Profile] Implement the prototype of profiling APIs
[platform/adaptation/npu/trix-engine.git] / src / core / ne-handler.cc
1 /**
2  * Proprietary
3  * Copyright (C) 2020 Samsung Electronics
4  * Copyright (C) 2020 Dongju Chae <dongju.chae@samsung.com>
5  */
6 /**
7  * @file ne-host-handler.cc
8  * @date 03 Apr 2020
9  * @brief Implementation of APIs to access NPU from Host
10  * @see https://code.sec.samsung.net/confluence/display/ODLC/2020+Overall+Software+Stack
11  * @author Dongju Chae <dongju.chae@samsung.com>
12  * @bug No known bugs except for NYI items
13  */
14
15 #include "ne-handler.h"
16 #include "ne-data.h"
17
18 #include <libnpuhost.h>
19 #include <npubinfmt.h>
20 #include <NPUdrvAPI.h>
21 #include <CommPlugin.h>
22
23 #include <string.h>
24 #include <assert.h>
25
26 #include <condition_variable>
27 #include <functional>
28 #include <atomic>
29 #include <map>
30
31 #define TAG _N2
32
33 #define INIT_HOST_HANDLER(handler, dev) \
34   Device *tdev = static_cast <Device *> (dev); \
35   if (tdev == nullptr) return -EINVAL; \
36   HostHandler *handler = tdev->getHostHandler (); \
37   if (handler == nullptr) return -EINVAL;
38
39 /** just for backward-compatability */
40 npudev_h HostHandler::latest_dev_ = nullptr;
41
42 /** implement libnpuhost APIs */
43
44 /**
45  * @brief Returns the number of available NPU devices.
46  * @return @c The number of NPU devices.
47  * @retval 0 if no NPU devices available. if positive (number of NPUs) if NPU devices available. otherwise, a negative error value.
48  * @note the caller should call putNPUdevice() to release the device handle
49  */
50 int getnumNPUdeviceByType (dev_type type)
51 {
52   return HostHandler::getNumDevices (type);
53 }
54
55 /**
56  * @brief Returns the handle of the chosen NPU devices.
57  * @param[out] dev The NPU device handle
58  * @param[in] id The NPU id to get the handle. 0 <= id < getnumNPUdeviceByType().
59  * @return @c 0 if no error. otherwise a negative error value
60  * @note the caller should call putNPUdevice() to release the device handle
61  */
62 int getNPUdeviceByType (npudev_h *dev, dev_type type, uint32_t id)
63 {
64   return HostHandler::getDevice (dev, type, id);
65 }
66
67 /**
68  * @brief release the NPU device instance obtained by getDevice ()
69  * @param[in] dev the NPU device handle
70  */
71 void putNPUdevice (npudev_h dev)
72 {
73   if (dev != nullptr)
74     delete static_cast<Device *> (dev);
75 }
76
77 /**
78  * @brief Get the profile information from NPU
79  * @param[in] dev The NPU device handle
80  * @param[in] task_id The identifier for each inference
81  * @param[out] profile The profile instance
82  * @return 0 if no error, otherwise a negative errno.
83  */
84 int getNPU_profile (npudev_h dev, int task_id, npu_profile *profile)
85 {
86   INIT_HOST_HANDLER (host_handler, dev);
87
88   return host_handler->getProfile (task_id, profile);
89 }
90
91 /**
92  * @brief Free the profile instance obtained by getNPU_profile().
93  * @param[in] profile The profile instance
94  */
95 void putNPU_profile (npu_profile *profile)
96 {
97   if (profile != nullptr)
98     free (profile);
99 }
100
101 /**
102  * @brief Send the NN model to NPU.
103  * @param[in] dev The NPU device handle
104  * @param[in] modelfile The filepath to the compiled NPU NN model in any buffer_type
105  * @param[out] modelid The modelid allocated for this instance of NN model.
106  * @return @c 0 if no error. otherwise a negative error value
107  *
108  * @detail For ASR devices, which do not accept models, but have models
109  *         embedded in devices, you do not need to call register and
110  *         register calls for ASR are ignored.
111  *
112  * @todo Add a variation: in-memory model register.
113  */
114 int registerNPUmodel (npudev_h dev, generic_buffer *modelfile, uint32_t *modelid)
115 {
116   INIT_HOST_HANDLER (host_handler, dev);
117
118   return host_handler->registerModel (modelfile, modelid);
119 }
120
121 /**
122  * @brief Remove the NN model from NPU
123  * @param[in] dev The NPU device handle
124  * @param[in] modelid The model to be removed from the NPU.
125  * @return @c 0 if no error. otherwise a negative error value
126  * @detail This may incur some latency with memory compatcion.
127  */
128 int unregisterNPUmodel(npudev_h dev, uint32_t modelid)
129 {
130   INIT_HOST_HANDLER (host_handler, dev);
131
132   return host_handler->unregisterModel (modelid);
133 }
134
135 /**
136  * @brief Remove all NN models from NPU
137  * @param[in] dev The NPU device handle
138  * @return @c 0 if no error. otherwise a negative error value
139  */
140 int unregisterNPUmodel_all(npudev_h dev)
141 {
142   INIT_HOST_HANDLER (host_handler, dev);
143
144   return host_handler->unregisterModels ();
145 }
146
147 /**
148  * @brief [OPTIONAL] Set the data layout for input/output tensors
149  * @param[in] dev The NPU device handle
150  * @param[in] modelid The ID of model whose layouts are set
151  * @param[in] info_in the layout/type info for input tensors
152  * @param[in] info_out the layout/type info for output tensors
153  * @return @c 0 if no error. otherwise a negative error value
154  * @note if this function is not called, default layout/type will be used.
155  */
156 int setNPU_dataInfo(npudev_h dev, uint32_t modelid,
157     tensors_data_info *info_in, tensors_data_info *info_out)
158 {
159   INIT_HOST_HANDLER (host_handler, dev);
160
161   return host_handler->setDataInfo (modelid, info_in, info_out);
162 }
163
164 /**
165  * @brief [OPTIONAL] Set the inference constraint for next NPU inferences
166  * @param[in] dev The NPU device handle
167  * @param[in] modelid The target model id
168  * @param[in] constraint inference constraint (e.g., timeout, priority)
169  * @return @c 0 if no error. otherwise a negative error value
170  * @note If this function is not called, default values are used.
171  */
172 int setNPU_constraint(npudev_h dev, uint32_t modelid, npuConstraint constraint)
173 {
174   INIT_HOST_HANDLER (host_handler, dev);
175
176   return host_handler->setConstraint (modelid, constraint);
177 }
178
179 /**
180  * @brief Execute inference. Wait (block) until the output is available.
181  * @param[in] dev The NPU device handle
182  * @param[in] modelid The model to be inferred.
183  * @param[in] input The input data to be inferred.
184  * @param[out] output The output result. The caller MUST allocate appropriately before calling this.
185  * @return @c 0 if no error. otherwise a negative error value
186  *
187  * @detail This is a syntactic sugar of runNPU_async().
188  *         CAUTION: There is a memcpy for the output buffer.
189  */
190 int runNPU_sync(npudev_h dev, uint32_t modelid, const input_buffers *input,
191     output_buffers *output)
192 {
193   INIT_HOST_HANDLER (host_handler, dev);
194
195   return host_handler->runSync (modelid, input, output);
196 }
197
198 /**
199  * @brief Invoke NPU inference. Unblocking call.
200  * @param[in] dev The NPU device handle
201  * @param[in] modelid The model to be inferred.
202  * @param[in] input The input data to be inferred.
203  * @param[in] cb The output buffer handler.
204  * @param[out] sequence The sequence number returned with runNPU_async.
205  * @param[in] data The data given as a parameter to the runNPU_async call.
206  * @param[in] mode Configures how this operation works.
207  * @return @c 0 if no error. otherwise a negative error value
208  */
209 int runNPU_async(npudev_h dev, uint32_t modelid, const input_buffers *input,
210     npuOutputNotify cb, uint64_t *sequence, void *data,
211     npu_async_mode mode)
212 {
213   INIT_HOST_HANDLER (host_handler, dev);
214
215   return host_handler->runAsync (modelid, input, cb, data, mode, sequence);
216 }
217
218 /**
219  * @brief Let NPU accept input frames from its internal source continuously
220  * @param[in] dev The NPU device handle
221  * @param[in] modelid The model to be inferred.
222  * @param[in] opmode NPU has different opmode with auto-inputs. Choose one.
223  * @param[in] hw_dev The target device feeding input data
224  * @return @c 0 if no error. otherwise a negative error value
225  */
226 int runNPU_internalInput(npudev_h dev, uint32_t modelid, npu_input_opmode opmode,
227     const char * hw_dev)
228 {
229   INIT_HOST_HANDLER (host_handler, dev);
230
231   return host_handler->runInternal (modelid, opmode, hw_dev);
232 }
233
234 /**
235  * @brief Stop the request with the given id
236  * @param[in] dev The NPU device handle
237  * @param[in] id The request id
238  * @return @c 0 if no error. otherwise a negative error value
239  */
240 int stopNPU_internalInput(npudev_h dev, int id)
241 {
242   INIT_HOST_HANDLER (host_handler, dev);
243
244   return host_handler->stopInternal (id);
245 }
246
247 /**
248  * @brief Allocate a generic buffer with the requested buffer type.
249  * @param[in] dev The NPU device handle
250  * @param[in/out] Buffer the buffer pointer where memory is allocated.
251  * @return 0 if no error, otherwise a negative errno.
252  */
253 int allocNPU_genericBuffer (npudev_h dev, generic_buffer * buffer)
254 {
255   INIT_HOST_HANDLER (host_handler, dev);
256
257   return host_handler->allocGenericBuffer (buffer);
258 }
259
260 /**
261  * @brief Free the generic buffer and remove the address mapping
262  * @param[in] dev The NPU device handle
263  * @param[in] buffer the model buffer
264  * @return 0 if no error, otherwise a negative errno.
265  */
266 int cleanNPU_genericBuffer (npudev_h dev, generic_buffer * buffer)
267 {
268   INIT_HOST_HANDLER (host_handler, dev);
269
270   return host_handler->deallocGenericBuffer (buffer);
271 }
272
273 /**
274  * @brief Allocate generic buffers, which have multiple instances of generic_buffer
275  * @param[in] dev The NPU device handle
276  * @param[in/out] buffers generic buffers.
277  * @return 0 if no error, otherwise a negative errno.
278  * @note it reuses allocGenericBuffer().
279  */
280 int allocNPU_genericBuffers (npudev_h dev, generic_buffers * buffers)
281 {
282   INIT_HOST_HANDLER (host_handler, dev);
283
284   return host_handler->allocGenericBuffer (buffers);
285 }
286
287 /**
288  * @brief Free generic buffers allocated by allocGenericBuffers().
289  * @param[in] dev The NPU device handle
290  * @param[in/out] buffers generic buffers.
291  * @note it reuses cleanGenericbuffer().
292  * @return 0 if no error, otherwise a negative errno.
293  */
294 int cleanNPU_genericBuffers (npudev_h dev, generic_buffers * buffers)
295 {
296   INIT_HOST_HANDLER (host_handler, dev);
297
298   return host_handler->deallocGenericBuffer (buffers);
299 }
300
301 /**
302  * @brief alias of allocNPU_genericBuffer for model buffer
303  */
304 int allocNPU_modelBuffer (npudev_h dev, generic_buffer * model)
305 {
306   return allocNPU_genericBuffer (dev, model);
307 }
308
309 /**
310  * @brief alias of cleanNPU_genericBuffer for model buffer
311  */
312 int cleanNPU_modelBuffer (npudev_h dev, generic_buffer * model)
313 {
314   return cleanNPU_genericBuffer (dev, model);
315 }
316
317 /**
318  * @brief alias of allocNPU_genericBuffer for input buffer
319  */
320 int allocNPU_inputBuffer (npudev_h dev, generic_buffer * input)
321 {
322   return allocNPU_genericBuffer (dev, input);
323 }
324
325 /**
326  * @brief alias of cleanNPU_genericBuffer for input buffer
327  */
328 int cleanNPU_inputBuffer (npudev_h dev, generic_buffer * input)
329 {
330   return cleanNPU_genericBuffer (dev, input);
331 }
332
333 /**
334  * @brief alias of allocNPU_genericBuffers for input buffers
335  */
336 int allocNPU_inputBuffers (npudev_h dev, input_buffers * input)
337 {
338   return allocNPU_genericBuffers (dev, input);
339 }
340
341 /**
342  * @brief alias of cleanNPU_genericBuffers for input buffers
343  */
344 int cleanNPU_inputBuffers (npudev_h dev, input_buffers * input)
345 {
346   return cleanNPU_genericBuffers (dev, input);
347 }
348
349 /**
350  * @brief get the current memory status for the given device
351  * @param[in] dev The NPU device handle
352  * @param[out] alloc_total The size of allocated memory until now
353  * @param[out] free_total The size of freed memory until now
354  * @return @c 0 if no error. otherwise a negatice error value
355  */
356 int getNPU_memoryStatus(npudev_h dev, size_t *alloc_total, size_t *free_total)
357 {
358   INIT_HOST_HANDLER (host_handler, dev);
359
360   return host_handler->getMemoryStatus (alloc_total, free_total);
361 }
362
363 /**
364  * @brief Get the current device status to be used
365  * @param[in] dev The NPU device handle
366  * @param[out] status the device status
367  * @param[out] num_requests the number of running requests (or pending)
368  * @return 0 if no error, otherwise a negative errno.
369  */
370 int getNPU_deviceStatus(npudev_h dev, npu_status *status, uint32_t *num_requests)
371 {
372   INIT_HOST_HANDLER (host_handler, dev);
373
374   return host_handler->getDeviceStatus (status, num_requests);
375 }
376
377 /**
378  * @brief Get metadata for NPU model
379  * @param[in] model The path of model binary file
380  * @param[in] need_extra whether you want to extract the extra data in metadata
381  * @return the metadata structure to be filled if no error, otherwise nullptr
382  *
383  * @note For most npu-engine users, the extra data is not useful because it will be
384  *       used for second-party users (e.g., compiler, simulator).
385  *       Also, the caller needs to free the metadata.
386  *
387  * @note the caller needs to free the metadata
388  */
389 npubin_meta * getNPUmodel_metadata (const char *model, bool need_extra)
390 {
391   npubin_meta *meta;
392   FILE *fp;
393   size_t ret;
394
395   if (!model)
396     return nullptr;
397
398   fp = fopen (model, "rb");
399   if (!fp) {
400     logerr (TAG, "Failed to open the model binary: %d\n", -errno);
401     return nullptr;
402   }
403
404   meta = (npubin_meta *) malloc (NPUBIN_META_SIZE);
405   if (!meta) {
406     logerr (TAG, "Failed to allocate metadata\n");
407     goto exit_err;
408   }
409
410   ret = fread (meta, 1, NPUBIN_META_SIZE, fp);
411   if (ret != NPUBIN_META_SIZE) {
412     logerr (TAG, "Failed to read the metadata\n");
413     goto exit_free;
414   }
415
416   if (!CHECK_NPUBIN (meta->magiccode)) {
417     logerr (TAG, "Invalid metadata provided\n");
418     goto exit_free;
419   }
420
421   if (need_extra && NPUBIN_META_EXTRA (meta->magiccode) > 0) {
422     npubin_meta *new_meta;
423
424     new_meta = (npubin_meta *) malloc (NPUBIN_META_TOTAL_SIZE(meta->magiccode));
425     if (!new_meta) {
426       logerr (TAG, "Failed to allocate extra metadata\n");
427       goto exit_free;
428     }
429
430     memcpy (new_meta, meta, NPUBIN_META_TOTAL_SIZE(meta->magiccode));
431     free (meta);
432     meta = nullptr;
433     ret = fread (new_meta->reserved_extra, 1, NPUBIN_META_EXTRA_SIZE (new_meta->magiccode), fp);
434     if (ret != NPUBIN_META_EXTRA_SIZE (new_meta->magiccode)) {
435       logerr (TAG, "Invalid extra metadata provided\n");
436       free (new_meta);
437       goto exit_err;
438     }
439
440     meta = new_meta;
441   }
442
443   fclose (fp);
444
445   return meta;
446
447 exit_free:
448   free (meta);
449 exit_err:
450   fclose (fp);
451
452   return nullptr;
453 }
454
455 /** implement methods of HostHandler class */
456
457 /** @brief host handler constructor */
458 HostHandler::HostHandler (Device *device)
459   : device_(device),
460     /* ignored as we don't use double buffering anymore, but for backward-compatibility */
461     async_mode_ (NPUASYNC_WAIT)
462 {
463 }
464
465 /** @brief host handler destructor */
466 HostHandler::~HostHandler ()
467 {
468 }
469
470 /**
471  * @brief register model from generic buffer
472  * @param[in] model_buf model buffer
473  * @param[out] modelid model id
474  * @return 0 if no error. otherwise a negative errno
475  */
476 int
477 HostHandler::registerModel (generic_buffer *model_buf, uint32_t *modelid)
478 {
479   if (model_buf == nullptr || modelid == nullptr) {
480     logerr (TAG, "Invalid arguments given\n");
481     return -EINVAL;
482   }
483
484   Model *model = nullptr;
485   int status = device_->setModel (model_buf, &model);
486   if (status != 0) {
487     logerr (TAG, "Failed to set model: %d\n", status);
488     return status;
489   }
490
491   assert (model != nullptr);
492
493   status = models_.insert (model->getID(), model);
494   if (status != 0) {
495     logerr (TAG, "Failed to insert model id\n");
496     delete model;
497     return status;
498   }
499
500   *modelid = model->getID();
501   return 0;
502 }
503
504 /**
505  * @brief remove the registered model
506  * @param[in] modelid model id
507  * @return 0 if no error. otherwise a negative errno
508  */
509 int
510 HostHandler::unregisterModel (uint32_t modelid)
511 {
512   Model *model = models_.find (modelid);
513   if (model == nullptr)
514     return -ENOENT;
515
516   int status = device_->unsetModel (model);
517   if (status != 0) {
518     logerr (TAG, "Failed to unset model: %d\n", status);
519     return status;
520   }
521
522   return models_.remove (modelid);
523 }
524
525 /**
526  * @brief remove all registered models
527  * @return 0
528  */
529 int
530 HostHandler::unregisterModels ()
531 {
532   std::function <bool (Model *)> functor =
533     [&] (Model *m) -> bool {
534       bool can_remove = true;
535       int status = device_->unsetModel (m);
536       if (status != 0) {
537         logwarn (TAG, "Failed to unset model: %d\n", status);
538         can_remove = false;
539       }
540       return can_remove;
541     };
542
543   models_.for_each (functor);
544   return 0;
545 }
546
547 /**
548  * @brief Get the profile information from NPU
549  * @param[in] task_id The identifier for each inference
550  * @param[out] profile The profile instance
551  * @return 0 if no error, otherwise a negative errno.
552  */
553 int
554 HostHandler::getProfile (int task_id, npu_profile *profile)
555 {
556   if (task_id < 0 || profile == nullptr) {
557     logerr (TAG, "Invalid parameter provided\n");
558     return -EINVAL;
559   }
560
561   const DriverAPI * api = device_->getDriverAPI ();
562   assert (api != nullptr);
563
564   void *profile_buffer;
565   size_t profile_size;
566   int status = api->getProfile (task_id, &profile_buffer, &profile_size);
567   if (status != 0) {
568     logerr (TAG, "Failed to get profile information: %d\n", status);
569     return status;
570   }
571
572   profile->num_layers = 0;
573   profile->layers = nullptr;
574   if (profile_buffer != nullptr) {
575     // TODO: Perform parsing
576   }
577
578   return 0;
579 }
580
581 /**
582  * @brief Set the data layout for input/output tensors
583  * @param[in] modelid The ID of model whose layouts are set
584  * @param[in] in the layout/type info for input tensors
585  * @param[in] out the layout/type info for output tensors
586  * @return @c 0 if no error. otherwise a negative error value
587  * @note if this function is not called, default layout/type will be used.
588  */
589 int
590 HostHandler::setDataInfo (uint32_t modelid, tensors_data_info *in,
591     tensors_data_info *out)
592 {
593   Model *model = models_.find (modelid);
594   if (model == nullptr)
595     return -ENOENT;
596
597   return model->setDataInfo (in, out);
598 }
599
600 /**
601  * @brief Set the inference constraint for next NPU inferences
602  * @param[in] modelid The target model id
603  * @param[in] constraint inference constraint (e.g., timeout, priority)
604  * @return @c 0 if no error. otherwise a negative error value
605  * @note If this function is not called, default values are used.
606  */
607 int
608 HostHandler::setConstraint (uint32_t modelid, npuConstraint constraint)
609 {
610   Model *model = models_.find (modelid);
611   if (model == nullptr)
612     return -ENOENT;
613
614   model->setConstraint (constraint);
615
616   return 0;
617 }
618
619 /**
620  * @brief find and return model instance
621  * @param[in] modelid model id
622  * @return model instance if found. otherwise nullptr
623  */
624 Model *
625 HostHandler::getModel (uint32_t modelid)
626 {
627   return models_.find (modelid);
628 }
629
630 /** @brief dummay callback for runSync. */
631 class callbackSync {
632   public:
633     callbackSync (output_buffers *output) : output_(output), done_(false) {}
634
635     static void callback (output_buffers *output, uint64_t sequence, void *data) {
636       callbackSync *sync = static_cast<callbackSync *>(data);
637       sync->callback (output, sequence);
638     }
639
640     void callback (output_buffers *output, uint64_t sequence) {
641       if (output_ != nullptr) {
642         /** just copy internal variables of output buffers */
643         memcpy (output_, output, sizeof (output_buffers));
644       }
645       done_ = true;
646       cv_.notify_one ();
647     }
648
649     void wait () {
650       std::unique_lock<std::mutex> lock (m_);
651       cv_.wait (lock, [this]() { return done_; });
652     }
653
654   private:
655     std::mutex m_;
656     std::condition_variable cv_;
657     output_buffers *output_;
658     bool done_;
659 };
660
661 /**
662  * @brief Execute inference. Wait (block) until the output is available.
663  * @param[in] modelid The model to be inferred.
664  * @param[in] input The input data to be inferred.
665  * @param[out] output The output result.
666  * @return @c 0 if no error. otherwise a negative error value
667  */
668 int
669 HostHandler::runSync (uint32_t modelid, const input_buffers *input,
670     output_buffers *output)
671 {
672   callbackSync sync (output);
673   int status = runAsync (modelid, input, callbackSync::callback,
674       static_cast <void*> (&sync), NPUASYNC_DROP_OLD, nullptr);
675   if (status == 0) {
676     /** sync needs to wait callback */
677     sync.wait ();
678   }
679   return status;
680 }
681
682 /**
683  * @brief Invoke NPU inference. Unblocking call.
684  * @param[in] modelid The model to be inferred.
685  * @param[in] input The input data to be inferred.
686  * @param[in] cb The output buffer handler.
687  * @param[in] cb_data The data given as a parameter to the runNPU_async call.
688  * @param[in] mode Configures how this operation works.
689  * @param[out] sequence The sequence number returned with runNPU_async.
690  * @return @c 0 if no error. otherwise a negative error value
691  */
692 int
693 HostHandler::runAsync (uint32_t modelid, const input_buffers *input,
694     npuOutputNotify cb, void *cb_data, npu_async_mode mode, uint64_t *sequence)
695 {
696   Model *model = nullptr;
697
698   if (device_->needModel()) {
699     model = getModel (modelid);
700     if (model == nullptr)
701       return -ENOENT;
702   }
703
704   /* check the given model before running */
705   if (model != nullptr && !model->finalize ()) {
706     logerr (TAG, "Failed to finalize the model. Please see the log messages\n");
707     return -EINVAL;
708   }
709
710   device_->setAsyncMode (mode);
711   return device_->run (NPUINPUT_HOST, model, input, cb, cb_data, sequence);
712 }
713
714 /**
715  * @brief Let NPU accept input frames from its internal source continuously
716  * @param[in] modelid The model to be inferred.
717  * @param[in] opmode NPU has different opmode with auto-inputs. Choose one.
718  * @param[in] hw_dev The target device feeding input data
719  * @return @c 0 if no error. otherwise a negative error value
720  */
721 int
722 HostHandler::runInternal (uint32_t modelid, npu_input_opmode opmode,
723     std::string hw_dev)
724 {
725   Model *model = nullptr;
726
727   if (device_->needModel()) {
728     model = getModel (modelid);
729     if (model == nullptr)
730       return -ENOENT;
731   }
732
733   /* check the given model before running */
734   if (model != nullptr && !model->finalize ()) {
735     logerr (TAG, "Failed to finalize the model. Please see the log messages\n");
736     return -EINVAL;
737   }
738
739   return device_->runInternal (opmode, model, hw_dev);
740 }
741
742 /**
743  * @brief Stop the request with the given id
744  * @param[in] dev The NPU device handle
745  * @param[in] id The request id
746  * @return @c 0 if no error. otherwise a negative error value
747  */
748 int
749 HostHandler::stopInternal (int id)
750 {
751   if (id <= 0) {
752     logerr (TAG, "Unable to stop this request with id (%d)\n", id);
753     return -EINVAL;
754   }
755
756   const DriverAPI * api = device_->getDriverAPI ();
757   assert (api != nullptr);
758
759   return api->stop_target (id);
760 }
761
762 /**
763  * @brief get number of available devices
764  * @param[in] type device type
765  * @return number of devices
766  */
767 int
768 HostHandler::getNumDevices (dev_type type)
769 {
770   return DriverAPI::getNumDevices (type);
771 }
772
773 /**
774  * @brief get device instance
775  * @param[out] dev device instance
776  * @param[in] type device type
777  * @param[in] id device id
778  * @return 0 if no error. otherwise a negative errno
779  */
780 int
781 HostHandler::getDevice (npudev_h *dev, dev_type type, uint32_t id)
782 {
783   int num_devices = getNumDevices (type);
784
785   /** check the validity of device id */
786   if (!(num_devices > 0 && id < static_cast<uint32_t>(num_devices))) {
787     logerr (TAG, "Invalid arguments provided\n");
788     return -ENODEV;
789   }
790
791   Device *device = Device::createInstance (type, id);
792   if (device == nullptr) {
793     logerr (TAG, "Failed to create a device with the given type\n");
794     return -EINVAL;
795   }
796
797   *dev = device;
798   /** This is just for backward-compatility; we don't guarantee its corresness */
799   latest_dev_ = *dev;
800
801   return 0;
802 }
803
804 /**
805  * @brief allocate generic buffer (just for users)
806  * @param[out] buffer buffer instance
807  * @return 0 if no error. otherwise a negative errno
808  */
809 int
810 HostHandler::allocGenericBuffer (generic_buffer *buffer)
811 {
812   if (buffer == NULL)
813     return -EINVAL;
814
815   if (buffer->size == 0) {
816     logerr (TAG, "Invalid size\n");
817     return -EINVAL;
818   }
819
820   if (buffer->size > UINT32_MAX) {
821     logerr (TAG, "Don't support such a large size");
822     return -ENOMEM;
823   }
824
825   switch (buffer->type) {
826     case BUFFER_FILE:
827       /* nothing to do */
828       if (buffer->filepath == nullptr)
829         return -EINVAL;
830       break;
831     case BUFFER_MAPPED:
832     {
833       /* now, npu-engine always provides dmabuf-based allocation */
834       void *addr = nullptr;
835       int dmabuf = device_->allocMemory (buffer->size, &addr);
836       if (dmabuf < 0)
837         return dmabuf;
838
839       buffer->dmabuf = dmabuf;
840       buffer->offset = 0;
841       buffer->addr = addr;
842     } break;
843     default:
844       return -EINVAL;
845   }
846
847   return 0;
848 }
849
850 /**
851  * @brief deallocate generic buffer (just for users)
852  * @param[in] buffer buffer instance
853  * @return 0 if no error. otherwise a negative errno
854  */
855 int
856 HostHandler::deallocGenericBuffer (generic_buffer *buffer)
857 {
858   if (buffer == NULL)
859     return -EINVAL;
860
861   switch (buffer->type) {
862     case BUFFER_FILE:
863       /** always true cuz nothing to do */
864       break;
865     case BUFFER_MAPPED:
866       return device_->deallocMemory (buffer->dmabuf, buffer->size, buffer->addr);
867     default:
868       return -EINVAL;
869   }
870
871   return 0;
872 }
873
874 /**
875  * @brief allocate multiple generic buffers (just for users)
876  * @param[out] buffers multi-buffer instance
877  * @return 0 if no error. otherwise a negative errno
878  */
879 int
880 HostHandler::allocGenericBuffer (generic_buffers *buffers)
881 {
882   uint32_t idx;
883   int status = 0;
884
885   if (buffers == NULL || buffers->num_buffers < 1)
886     return -EINVAL;
887
888   for (idx = 0; idx < buffers->num_buffers; idx++) {
889     status = allocGenericBuffer (&buffers->bufs[idx]);
890     if (status != 0)
891       goto free_buffer;
892   }
893
894   return 0;
895
896 free_buffer:
897   while (idx != 0) {
898     deallocGenericBuffer (&buffers->bufs[--idx]);
899   }
900
901   return status;
902 }
903
904 /**
905  * @brief deallocate multiple generic buffers (just for users)
906  * @param[in] buffers multi-buffer instance
907  * @return 0 if no error. otherwise a negative errno
908  */
909 int
910 HostHandler::deallocGenericBuffer (generic_buffers *buffers)
911 {
912   if (buffers == NULL || buffers->num_buffers < 1)
913     return -EINVAL;
914
915   for (uint32_t idx = 0; idx < buffers->num_buffers; idx++)
916     deallocGenericBuffer (&buffers->bufs[idx]);
917   buffers->num_buffers = 0;
918
919   return 0;
920 }
921
922 /**
923  * @brief get the current memory status
924  * @param[out] alloc_total The size of allocated memory until now
925  * @param[out] free_total The size of freed memory until now
926  * @return 0 if no error. otherwise a negatice error value
927  */
928 int
929 HostHandler::getMemoryStatus (size_t *alloc_total, size_t *free_total)
930 {
931   /** API is always set in initialize () */
932   const DriverAPI * api = device_->getDriverAPI ();
933   assert (api != nullptr);
934
935   return api->getMemoryStatus (alloc_total, free_total);
936 }
937
938 /**
939  * @brief Get the current device status to be used
940  * @param[out] status the device status
941  * @param[out] num_requests the number of running requests (or pending)
942  * @return 0 if no error, otherwise a negative errno.
943  */
944 int
945 HostHandler::getDeviceStatus (npu_status *status, uint32_t *num_requests)
946 {
947   /** API is always set in initialize () */
948   const DriverAPI * api = device_->getDriverAPI ();
949
950   if (!api)
951     return -EINVAL;
952
953   device_state_t state = api->isReady ();
954   if (state == device_state_t::STATE_READY) {
955     *num_requests = api->numRequests ();
956     if (*num_requests > 0)
957       *status = NPU_READY;
958     else
959       *status = NPU_IDLE;
960   } else {
961     *num_requests = 0;
962     *status = NPU_ERROR;
963   }
964
965   return 0;
966 }
967
968 /** implement methods of Device class */
969
970 /** @brief constructor of device */
971 Device::Device (dev_type type, int id, bool need_model)
972   : comm_ (CommPlugin::getCommPlugin()), type_ (type), id_ (id), need_model_ (true),
973     mode_ (NPUASYNC_WAIT), initialized_ (false), atomic_flag_ (ATOMIC_FLAG_INIT)
974 {
975 }
976
977 /**
978  * @brief create device instance depending on device type and id
979  * @param[in] type device type
980  * @param[in] id device id
981  * @return device instance
982  */
983 Device *
984 Device::createInstance (dev_type type, int id)
985 {
986   Device *device = nullptr;
987
988   switch (type & DEVICETYPE_MASK) {
989     case DEVICETYPE_TRIV:
990       device = new TrinityVision (id);
991       break;
992     case DEVICETYPE_TRIV2:
993       device = new TrinityVision2 (id);
994       break;
995     case DEVICETYPE_TRIA:
996       device = new TrinityAsr (id);
997       device->setNeedModel (false);
998       break;
999     default:
1000       break;
1001   }
1002
1003   if (device != nullptr && device->init () != 0) {
1004     delete device;
1005     device = nullptr;
1006   }
1007
1008   return device;
1009 }
1010
1011 /**
1012  * @brief device initialization
1013  * @return 0 if no error, otherwise a negative errno
1014  * @note Init failures come from createDriverAPI() only.
1015  */
1016 int
1017 Device::init ()
1018 {
1019   /** should be initilizaed only once */
1020   if (!atomic_flag_.test_and_set()) {
1021     /** create the corresponding driver API */
1022     api_ = DriverAPI::createDriverAPI (type_, id_);
1023     if (api_.get() == nullptr) {
1024       atomic_flag_.clear();
1025       logerr (TAG, "Failed to create driver API\n");
1026       return -EINVAL;
1027     }
1028
1029     handler_.reset (new HostHandler (this));
1030     scheduler_.reset (new Scheduler (api_.get()));
1031     mem_ = MemAllocator::createInstance (api_.get());
1032
1033     initialized_ = true;  /** c++11 does not provide test() of atomic flag */
1034   }
1035
1036   return 0;
1037 }
1038
1039 /**
1040  * @brief stop all requests from this device
1041  * @param[in] force_stop indicate the schedduler waits until to handle previous requests
1042  * @return 0 if no error, otherwise a negative errno
1043  */
1044 int
1045 Device::stop (bool force_stop)
1046 {
1047   if (!initialized ()) {
1048     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1049     return -EPERM;
1050   }
1051
1052   Request *req = new Request (NPUINPUT_STOP);
1053   req->setForceStop (force_stop);
1054   return scheduler_->submitRequest (req);
1055 }
1056
1057 /**
1058  * @brief allocate generic memory buffer
1059  * @param[in] size the size to allocate
1060  * @param[out] addr the mapped address
1061  * @return dmabuf fd if no error, otherwise a negative errno
1062  */
1063 int
1064 Device::allocMemory (size_t size, void **addr)
1065 {
1066   if (!initialized ()) {
1067     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1068     return -EPERM;
1069   }
1070
1071   if (size == 0 || addr == nullptr) {
1072     logerr (TAG, "Invalid arguments\n");
1073     return -EINVAL;
1074   }
1075
1076   return mem_->allocMemory (size, addr);
1077 }
1078
1079 /**
1080  * @brief deallocate generic memory buffer
1081  * @param[in] dmabuf_fd dmabuf file descriptor
1082  * @param[in] size buffer size
1083  * @param[in] addr mapped addr
1084  * @return 0 if no error, otherwise a negative errno
1085  */
1086 int
1087 Device::deallocMemory (int dmabuf_fd, size_t size, void * addr)
1088 {
1089   if (!initialized ()) {
1090     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1091     return -EPERM;
1092   }
1093
1094   if (dmabuf_fd < 0 || size == 0 || addr == nullptr) {
1095     logerr (TAG, "Invalid arguments\n");
1096     return -EINVAL;
1097   }
1098
1099   return mem_->deallocMemory (dmabuf_fd, size, addr);
1100 }
1101
1102 /**
1103  * @brief extract the buffer instance from input generic buffers
1104  * @param[in] meta the model metadata
1105  * @param[in] input the input generic buffers
1106  * @return the buffer instance
1107  */
1108 Buffer *
1109 TrinityVision::prepareInputBuffers (const Metadata *meta, const input_buffers *input)
1110 {
1111   if (meta == nullptr || input == nullptr ||
1112       meta->getInputNum() != input->num_buffers) {
1113     logerr (TAG, "Invalid metadata info provided\n");
1114     return nullptr;
1115   }
1116
1117   Buffer * buffer;
1118   const generic_buffer *first = &input->bufs[0];
1119   if (first->type == BUFFER_DMABUF) {
1120     buffer = mem_->allocBuffer (new HWmemExternal);
1121     if (buffer == nullptr)
1122       return nullptr;
1123
1124     buffer->setDmabuf (first->dmabuf);
1125     buffer->setOffset (first->offset);
1126     buffer->setSize (meta->getBufferSize());
1127   } else {
1128     buffer = mem_->allocBuffer (new HWmemDevice);
1129     if (buffer == nullptr)
1130       return nullptr;
1131
1132     int status = buffer->alloc (meta->getBufferSize ());
1133     if (status != 0) {
1134       logerr (TAG, "Failed to allocate buffer: %d\n", status);
1135       delete buffer;
1136       return nullptr;
1137     }
1138   }
1139
1140   int status = buffer->createTensors (meta);
1141   if (status != 0) {
1142     logerr (TAG, "Failed to create tensors: %d\n", status);
1143     delete buffer;
1144     buffer = nullptr;
1145   }
1146
1147   return buffer;
1148 }
1149
1150 /**
1151  * @brief implementation of TRIV's setModel ()
1152  * @param[in] model_buf the model generic buffer
1153  * @param[out] model the model instance
1154  * @return 0 if no error, otherwise a negative errno
1155  */
1156 int
1157 TrinityVision::setModel (const generic_buffer *model_buf, Model ** model_ptr)
1158 {
1159   if (!initialized ()) {
1160     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1161     return -EPERM;
1162   }
1163
1164   if (model_buf == nullptr || model_ptr == nullptr)
1165     return -EINVAL;
1166
1167   Model *model = nullptr;
1168   HWmem * hwmem_prog = nullptr;
1169   HWmem * hwmem_weight = nullptr;
1170   int status;
1171
1172   /** In TRIV1, model data (including program/weight) should be contiguous */
1173
1174   switch (model_buf->type) {
1175   case BUFFER_FILE:
1176   case BUFFER_MAPPED:
1177     model = mem_->allocModel (new HWmemDevice);
1178     if (model == nullptr) {
1179       logerr (TAG, "Failed to allocate model\n");
1180       return -ENOMEM;
1181     }
1182
1183     status = model->alloc (model_buf->size);
1184     if (status != 0) {
1185       logerr (TAG, "Failed to allocate model: %d\n", status);
1186       goto delete_exit;
1187     }
1188
1189     /** extract the whole model data */
1190     status = comm_.extractGenericBuffer (model_buf, model->getData(), nullptr);
1191     if (status != 0) {
1192       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1193       goto delete_exit;
1194     }
1195     break;
1196   default:
1197     return -EINVAL;
1198   }
1199
1200   status = model->setMetadata (model->getData());
1201   if (status != 0)
1202     goto delete_exit;
1203
1204   /** allocate program (optional; NOP) */
1205   if (model->getMetadata()->getProgramSize() > 0) {
1206     hwmem_prog = new HWmem (new HWmemChunk);
1207     model->setProgramData (hwmem_prog);
1208
1209     hwmem_prog->setParent (model);
1210     hwmem_prog->setOffset (model->getMetadata()->getMetaSize());
1211     status = hwmem_prog->alloc (model->getMetadata()->getProgramSize());
1212     if (status != 0) {
1213       logerr (TAG, "Failed to allocate program\n");
1214       goto delete_exit;
1215     }
1216   }
1217
1218   /** allocate weight (optional) */
1219   if (model->getMetadata()->getWeightSize() > 0) {
1220     hwmem_weight = new HWmem (new HWmemChunk);
1221     model->setWeightData (hwmem_weight);
1222
1223     hwmem_weight->setParent (model);
1224     hwmem_weight->setOffset (model->getMetadata()->getMetaSize() +
1225         model->getMetadata()->getProgramSize());
1226     status = hwmem_weight->alloc (model->getMetadata()->getWeightSize());
1227     if (status != 0) {
1228       logerr (TAG, "Failed to allocate program\n");
1229       goto delete_exit;
1230     }
1231   }
1232
1233   if (hwmem_prog != nullptr) {
1234     /** register this model to the driver */
1235     model_config_t config;
1236     config.dbuf_fd = hwmem_prog->getDmabuf ();
1237     config.program_size = hwmem_prog->getSize ();
1238     config.program_offset_addr = hwmem_prog->getOffset ();
1239     if (hwmem_weight != nullptr)
1240       config.weight_offset_addr = hwmem_weight->getOffset ();
1241
1242     status = api_->registerModel (&config);
1243     if (status != 0)
1244       goto delete_exit;
1245
1246     model->setInternalID(config.id);
1247   }
1248
1249   *model_ptr = model;
1250   return status;
1251
1252 delete_exit:
1253   delete model;
1254   return status;
1255 }
1256
1257 /**
1258  * @brief implementation of TRIV's unsetModel ()
1259  * @param[in] model the model instance
1260  * @return 0 if no error, otherwise a negative errno
1261  */
1262 int
1263 TrinityVision::unsetModel (Model * model)
1264 {
1265   if (!initialized ()) {
1266     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1267     return -EPERM;
1268   }
1269
1270   if (model == nullptr) {
1271     logerr (TAG, "Invalid model instance\n");
1272     return -EINVAL;
1273   }
1274
1275   if (model->getMetadata()->getProgramSize() > 0)
1276     return api_->deregisterModel (model->getInternalID ());
1277
1278   return 0;
1279 }
1280
1281 /**
1282  * @brief implementation of TRIV's run()
1283  * @param[in] opmode input opmode
1284  * @param[in] model the model instance
1285  * @param[in] input generic buffers of input data
1286  * @param[in] cb the output callback
1287  * @param[in] cb_data the output callback data
1288  * @param[out] sequence The sequence number returned with runNPU_async.
1289  */
1290 int
1291 TrinityVision::run (npu_input_opmode opmode, const Model *model,
1292     const input_buffers *input, npuOutputNotify cb, void *cb_data,
1293     uint64_t *sequence)
1294 {
1295   if (!initialized ()) {
1296     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1297     return -EPERM;
1298   }
1299
1300   if (opmode != NPUINPUT_HOST) {
1301     logerr (TAG, "TRIV supports only host inputservice\n");
1302     return -EINVAL;
1303   }
1304
1305   if (model == nullptr || input == nullptr) {
1306     logerr (TAG, "TRIV requires both model and input buffers\n");
1307     return -EINVAL;
1308   }
1309
1310   const_cast<Model *>(model)->updateDataInfo ();
1311
1312   Buffer *buffer = prepareInputBuffers (model->getMetadata(), input);
1313   if (buffer == nullptr) {
1314     logerr (TAG, "Failed to extract buffer instance\n");
1315     return -EINVAL;
1316   }
1317
1318   if (!buffer->isExternal ()) {
1319     for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
1320       auto func = std::bind (TrinityVision::manipulateData, model, idx, true,
1321           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1322       int status = comm_.extractGenericBuffer (&input->bufs[idx],
1323           buffer->getInputTensor(idx)->getData(), func);
1324       if (status != 0) {
1325         logerr (TAG, "Failed to feed input buffer: %d\n", status);
1326         return status;
1327       }
1328     }
1329   }
1330
1331   /** this device uses CMA buffer */
1332
1333   Request *req = new Request (opmode);
1334   req->setModel (model);
1335   req->setBuffer (buffer);
1336
1337   if (cb != nullptr)
1338     req->setCallback (std::bind (&TrinityVision::callback, this, req, cb, cb_data));
1339
1340   if (sequence != nullptr)
1341     *sequence = req->getID();
1342
1343   return scheduler_->submitRequest (req);
1344 }
1345
1346 /**
1347  * @brief callback of TRIV2 request
1348  * @param[in] req the request instance
1349  * @param[in] cb callback for completion
1350  * @param[in] cb_data callback data
1351  * @note The callback invoke does not gurantee the request was successful
1352  * @todo Check the request failures
1353  */
1354 void
1355 TrinityVision::callback (Request *req, npuOutputNotify cb, void *cb_data)
1356 {
1357   const Model *model = req->getModel ();
1358   Buffer *buffer = req->getBuffer ();
1359   output_buffers output = {
1360     .num_buffers = buffer->getOutputNum ()
1361   };
1362
1363   for (uint32_t idx = 0; idx < output.num_buffers; idx++) {
1364     uint32_t output_tensor_size = model->getOutputTensorSize (idx);
1365
1366     if (buffer->isExternal ()) {
1367       output.bufs[idx].type = BUFFER_DMABUF;
1368       output.bufs[idx].size = output_tensor_size;
1369       output.bufs[idx].addr = buffer->getOutputTensor(idx)->getData();
1370     } else {
1371       output.bufs[idx].type = BUFFER_MAPPED;
1372       output.bufs[idx].size = output_tensor_size;
1373       /** user needs to free this */
1374       output.bufs[idx].addr = malloc (output_tensor_size);
1375
1376       auto func = std::bind (TrinityVision::manipulateData, model, idx, false,
1377           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1378       int status = comm_.insertGenericBuffer (buffer->getOutputTensor(idx)->getData(),
1379           &output.bufs[idx], func);
1380       if (status != 0) {
1381         logerr (TAG, "Failed to return output buffer: %d\n", status);
1382       }
1383     }
1384   }
1385
1386   cb (&output, req->getID(), cb_data);
1387
1388   delete buffer;
1389 }
1390
1391 /**
1392  * @brief extract the segment table instance from input generic buffers
1393  * @param[in] model the model instance
1394  * @param[in] input the input generic buffers
1395  * @param[in] output the output generic buffers
1396  * @return the segment table instance
1397  */
1398 SegmentTable *
1399 TrinityVision2::prepareSegmentTable (const Model *model, const input_buffers *input,
1400     const output_buffers *output)
1401 {
1402   const Metadata *meta = model->getMetadata ();
1403   if (meta == nullptr || (input != nullptr &&
1404         meta->getInputNum() != input->num_buffers)) {
1405     logerr (TAG, "Invalid metadata info provided\n");
1406     return nullptr;
1407   }
1408
1409   SegmentTable * segt = mem_->allocSegmentTable (new HWmemDevice);
1410   int status = segt->alloc ();
1411   if (status != 0) {
1412     logerr (TAG, "Failed to allocate segment table: %d\n", status);
1413     goto delete_segt;
1414   }
1415
1416   status = segt->createSegments (model, input, output);
1417   if (status != 0) {
1418     logerr (TAG, "Failed to create segments: %d\n", status);
1419     goto delete_segt;
1420   }
1421
1422   return segt;
1423
1424 delete_segt:
1425   delete segt;
1426   return nullptr;
1427 }
1428
1429 /**
1430  * @brief implementation of TRIV2's setModel ()
1431  * @param[in] model_buf the model generic buffer
1432  * @param[out] model the model instance
1433  * @return 0 if no error, otherwise a negative errno
1434  */
1435 int
1436 TrinityVision2::setModel (const generic_buffer *model_buf, Model ** model_ptr)
1437 {
1438   if (!initialized ()) {
1439     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1440     return -EPERM;
1441   }
1442
1443   if (model_buf == nullptr || model_ptr == nullptr)
1444     return -EINVAL;
1445
1446   Model *model;
1447   int status;
1448
1449   switch (model_buf->type) {
1450   case BUFFER_FILE:
1451   case BUFFER_MAPPED:
1452     model = mem_->allocModel (new HWmemDevice);
1453     if (model == nullptr) {
1454       logerr (TAG, "Failed to allocate model\n");
1455       return -ENOMEM;
1456     }
1457
1458     status = model->alloc (NPUBIN_META_SIZE);
1459     if (status != 0) {
1460       logerr (TAG, "Failed to allocate model: %d\n", status);
1461       goto delete_exit;
1462     }
1463
1464     status = comm_.extractGenericBuffer (model_buf, model->getData(), nullptr,
1465         0, NPUBIN_META_SIZE);
1466     if (status != 0) {
1467       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1468       goto delete_exit;
1469     }
1470     break;
1471   default:
1472     return -EINVAL;
1473   }
1474
1475   status = model->setMetadata (model->getData());
1476   if (status != 0)
1477     goto delete_exit;
1478
1479   /** allocate program (optional; NOP) */
1480   if (model->getMetadata()->getProgramSize() > 0) {
1481     HWmem * hwmem_prog = new HWmem (new HWmemDevice);
1482     hwmem_prog->setDriverAPI (api_.get());
1483
1484     model->setProgramData (hwmem_prog);
1485
1486     status = hwmem_prog->alloc (model->getMetadata()->getProgramSize());
1487     if (status != 0) {
1488       logerr (TAG, "Failed to allocate program\n");
1489       goto delete_exit;
1490     }
1491
1492     status = comm_.extractGenericBuffer (model_buf, hwmem_prog->getData(), nullptr,
1493         model->getMetadata()->getMetaSize(),
1494         model->getMetadata()->getProgramSize());
1495     if (status != 0) {
1496       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1497       goto delete_exit;
1498     }
1499
1500     /** register this model to the driver */
1501     model_config_t config;
1502     config.dbuf_fd = hwmem_prog->getDmabuf ();
1503     config.program_size = hwmem_prog->getSize ();
1504     config.program_offset_addr = 0;
1505
1506     status = api_->registerModel (&config);
1507     if (status != 0)
1508       goto delete_exit;
1509
1510     model->setInternalID(config.id);
1511   }
1512
1513   /** allocate weight (optional) */
1514   if (model->getMetadata()->getWeightSize() > 0) {
1515     HWmem * hwmem_weight = new HWmem (new HWmemDevice);
1516     hwmem_weight->setDriverAPI (api_.get());
1517
1518     model->setWeightData (hwmem_weight);
1519
1520     status = hwmem_weight->alloc (model->getMetadata()->getWeightSize());
1521     if (status != 0) {
1522       logerr (TAG, "Failed to allocate program\n");
1523       goto delete_exit;
1524     }
1525
1526     status = comm_.extractGenericBuffer (model_buf, hwmem_weight->getData(), nullptr,
1527         model->getMetadata()->getMetaSize() + model->getMetadata()->getProgramSize(),
1528         model->getMetadata()->getWeightSize());
1529     if (status != 0) {
1530       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
1531       goto delete_exit;
1532     }
1533   }
1534
1535   *model_ptr = model;
1536   return status;
1537
1538 delete_exit:
1539   delete model;
1540   return status;
1541 }
1542
1543 /**
1544  * @brief implementation of TRIV2's unsetModel ()
1545  * @param[in] model the model instance
1546  * @return 0 if no error, otherwise a negative errno
1547  */
1548 int
1549 TrinityVision2::unsetModel (Model * model)
1550 {
1551   if (!initialized ()) {
1552     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1553     return -EPERM;
1554   }
1555
1556   if (model == nullptr) {
1557     logerr (TAG, "Invalid model instance\n");
1558     return -EINVAL;
1559   }
1560
1561   if (model->getMetadata()->getProgramSize() > 0)
1562     return api_->deregisterModel (model->getInternalID ());
1563
1564   return 0;
1565 }
1566
1567 /** @brief implementation of TRIV2's run() */
1568 int
1569 TrinityVision2::run (npu_input_opmode opmode, const Model *model,
1570     const input_buffers *input, npuOutputNotify cb, void *cb_data,
1571     uint64_t *sequence)
1572 {
1573   if (!initialized ()) {
1574     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1575     return -EPERM;
1576   }
1577
1578   if (opmode != NPUINPUT_HOST)
1579     return -EINVAL;
1580
1581   if (input == nullptr || input->num_buffers == 0 || model == nullptr)
1582     return -EINVAL;
1583
1584   const_cast<Model *>(model)->updateDataInfo ();
1585
1586   /** this device uses segment table */
1587   SegmentTable * segt = prepareSegmentTable (model, input);
1588   if (segt == nullptr) {
1589     logerr (TAG, "Failed to create segment table instance\n");
1590     return -EINVAL;
1591   }
1592
1593   /** extract input data */
1594   for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
1595     if (!segt->getInputSegment(idx)->isExternal ()) {
1596       uint32_t seg_offset = segt->getInputSegmentOffset(idx);
1597       auto func = std::bind (TrinityVision2::manipulateData, model, idx, true,
1598           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1599       int status = comm_.extractGenericBuffer (
1600           &input->bufs[idx],
1601           segt->getInputSegment(idx)->getData() + seg_offset,
1602           func);
1603       if (status != 0) {
1604         logerr (TAG, "Failed to feed input segment: %d\n", status);
1605         return status;
1606       }
1607     }
1608   }
1609
1610   Request *req = new Request (opmode);
1611   req->setModel (model);
1612   req->setSegmentTable (segt);
1613   req->setCallback (std::bind (&TrinityVision2::callback, this, req, cb, cb_data));
1614
1615   if (sequence)
1616     *sequence = req->getID();
1617
1618   return scheduler_->submitRequest (req);
1619 }
1620
1621 /** @brief implementation of TRIV2's runInternal() */
1622 int
1623 TrinityVision2::runInternal (npu_input_opmode opmode, const Model *model,
1624     std::string hw_dev)
1625 {
1626   if (!initialized ()) {
1627     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1628     return -EPERM;
1629   }
1630
1631   if (opmode != NPUINPUT_HW_RECURRING)
1632     return -EINVAL;
1633
1634   /** this device uses segment table */
1635   SegmentTable * segt = prepareSegmentTable (model, nullptr, nullptr);
1636   if (segt == nullptr) {
1637     logerr (TAG, "Failed to create segment table instance\n");
1638     return -EINVAL;
1639   }
1640
1641   Request *req = new Request (opmode);
1642   req->setModel (model);
1643   req->setSegmentTable (segt);
1644   req->setHwDevice (hw_dev);
1645
1646   return scheduler_->submitRequest (req);
1647 }
1648
1649 /** @brief callback of TRIV2 request */
1650 void
1651 TrinityVision2::callback (Request *req, npuOutputNotify cb, void *cb_data)
1652 {
1653   const Model *model = req->getModel ();
1654   SegmentTable *segt = req->getSegmentTable ();
1655   output_buffers output = {
1656     .num_buffers = segt->getNumOutputSegments ()
1657   };
1658
1659   for (uint32_t idx = 0; idx < output.num_buffers; idx++) {
1660     uint32_t output_tensor_size = model->getOutputTensorSize (idx);
1661
1662     output.bufs[idx].type = BUFFER_MAPPED;
1663     output.bufs[idx].size = output_tensor_size;
1664     /** user needs to free this */
1665     output.bufs[idx].addr = calloc (1, output_tensor_size);
1666
1667 #if defined(ENABLE_FPGA_WORKAROUND)
1668     api_->fpga_memcpy (
1669         segt->getOutputSegment(idx)->getDmabuf(),
1670         segt->getOutputSegmentOffset(idx),
1671         output.bufs[idx].addr,
1672         output.bufs[idx].size);
1673 #else
1674     auto func = std::bind (TrinityVision2::manipulateData, model, idx, false,
1675         std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1676     int status = comm_.insertGenericBuffer (
1677         segt->getOutputSegment(idx)->getData() + segt->getOutputSegmentOffset(idx),
1678         &output.bufs[idx], func);
1679
1680     if (status != 0) {
1681       logerr (TAG, "Failed to return output buffer: %d\n", status);
1682     }
1683 #endif
1684   }
1685
1686   cb (&output, req->getID(), cb_data);
1687
1688   delete segt;
1689 }
1690
1691 /** @brief implementation of TRIA's run(): WIP */
1692 int
1693 TrinityAsr::run (npu_input_opmode opmode, const Model *model,
1694     const input_buffers *input, npuOutputNotify cb, void *cb_data,
1695     uint64_t *sequence)
1696 {
1697   if (!initialized ()) {
1698     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1699     return -EPERM;
1700   }
1701
1702   if (opmode != NPUINPUT_HOST)
1703     return -EINVAL;
1704
1705   if (input == nullptr || input->num_buffers != 1)
1706     return -EINVAL;
1707
1708   Buffer * buffer;
1709   int status;
1710   /** ASR does not require model and support only a single tensor */
1711   const generic_buffer *first_buf = &input->bufs[0];
1712   if (first_buf->type == BUFFER_DMABUF) {
1713     buffer = mem_->allocBuffer (new HWmemExternal);
1714     if (buffer == nullptr)
1715       return -ENOMEM;
1716
1717     buffer->setDmabuf (first_buf->dmabuf);
1718     buffer->setOffset (first_buf->offset);
1719     buffer->setSize (first_buf->size);
1720   } else {
1721     buffer = mem_->allocBuffer (new HWmemDevice);
1722     if (buffer == nullptr)
1723       return -ENOMEM;
1724
1725     status = buffer->alloc (first_buf->size);
1726     if (status != 0) {
1727       delete buffer;
1728       return status;
1729     }
1730   }
1731
1732   status = buffer->createTensors ();
1733   if (status != 0) {
1734     logerr (TAG, "Failed to create tensors: %d\n", status);
1735     delete buffer;
1736     return status;
1737   }
1738
1739   if (!buffer->isExternal ()) {
1740     status = comm_.extractGenericBuffer (first_buf,
1741         buffer->getInputTensor(0)->getData(), nullptr);
1742     if (status != 0)
1743       return status;
1744   }
1745
1746   Request *req = new Request (opmode);
1747   req->setBuffer (buffer);
1748   req->setCallback (std::bind (&TrinityAsr::callback, this, req, cb, cb_data));
1749
1750   if (sequence)
1751     *sequence = req->getID();
1752
1753   return scheduler_->submitRequest (req);
1754 }
1755
1756 /** @brief callback of TRIA request: WIP */
1757 void
1758 TrinityAsr::callback (Request *req, npuOutputNotify cb, void *cb_data)
1759 {
1760   Buffer *buffer = req->getBuffer ();
1761   output_buffers output = {
1762     .num_buffers = 0
1763   };
1764
1765   /** TODO: finalize this impl. when the ASR's working scenario is determined */
1766   cb (&output, req->getID(), cb_data);
1767
1768   delete buffer;
1769 }
1770
1771 /** Implement data manipulation (each device may have different impl.) */
1772
1773 #ifdef ENABLE_MANIP
1774
1775 /**
1776  * @brief perform data manipulation
1777  * @param[in] model model instance
1778  * @param[in] idx tensor index
1779  * @param[in] is_input indicate it's input manipulation
1780  * @param[out] dst destination buffer
1781  * @param[in] src source buffer (feature map)
1782  * @param[in] size size to be copied
1783  * @return size of memory copy if no error, otherwise zero
1784  *
1785  * @note the input data format should be NHWC
1786  * @detail rules for the memory address of activations in NPU HW.
1787  *         (https://code.sec.samsung.net/confluence/pages/viewpage.action?pageId=146491864)
1788  *
1789  * 1) Special case (depth == 3)
1790  * - addr(x,y,z) = addr(0,0,0) + (z) + 3 * (x + width * y)
1791  *
1792  * 2) Common case
1793  * - addr(x,y,z) = addr(0,0,0) + (z % MPA_L) + MPA_L * (x + width * (y + height * (z / MPA_L)))
1794  *
1795  * Thus, if depth is not a multiple of MPA_L (i.e., 64), zero padding is required
1796  */
1797 size_t
1798 TrinityVision::manipulateData (const Model *model, uint32_t idx, bool is_input,
1799     void *dst, void *src, size_t size)
1800 {
1801   const Metadata *meta = model->getMetadata();
1802   DataConverter converter (is_input);
1803
1804   converter.setData (src, dst, size);
1805
1806   if (is_input) {
1807     const tensor_data_info* info = model->getInputDataInfo (idx);
1808     if (info == nullptr)
1809       return 0;
1810
1811     converter.setDataLayout (info->layout, DATA_LAYOUT_SRNPU);
1812     converter.setDataType (info->type, DATA_TYPE_SRNPU);
1813     converter.setDataDims (meta->getInputDims (idx));
1814     converter.setQuantZero (meta->getInputQuantZero (idx));
1815     converter.setQuantScale (meta->getInputQuantScale (idx));
1816   } else {
1817     const tensor_data_info* info = model->getOutputDataInfo (idx);
1818     if (info == nullptr)
1819       return 0;
1820
1821     converter.setDataLayout (DATA_LAYOUT_SRNPU, info->layout);
1822     converter.setDataType (DATA_TYPE_SRNPU, info->type);
1823     converter.setDataDims (meta->getOutputDims (idx));
1824     converter.setQuantZero (meta->getOutputQuantZero (idx));
1825     converter.setQuantScale (meta->getOutputQuantScale (idx));
1826   }
1827
1828   return converter.perform ();
1829 }
1830
1831 /**
1832  * @brief perform data manipulation
1833  * @param[in] model model instance
1834  * @param[in] idx tensor index
1835  * @param[in] is_input indicate it's input manipulation
1836  * @param[out] dst destination buffer
1837  * @param[in] src source buffer (feature map)
1838  * @param[in] size size to be copied
1839  * @return size of memory copy if no error, otherwise zero
1840  *
1841  * @note the input data format should be NHWC
1842  *
1843  * @detail Feature map data in TRIV2, (x, y, z) = (width, height, depth)
1844  *
1845  *         1) Image input (depth == 1 or depth == 3)
1846  *            Addr(x,y,z) = Addr(0,0,0) + z + depth * x + ymod * y
1847  *
1848  *         2) Common cases
1849  *            Addr(x,y,z) = Addr(0,0,0) + (z % 64) + (64 * x) + ymod * y + zmod * (z / 64)
1850  */
1851 size_t
1852 TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
1853     void *dst, void *src, size_t size)
1854 {
1855   const Metadata *meta = model->getMetadata();
1856   DataConverter converter (is_input);
1857
1858   converter.setData (src, dst, size);
1859
1860   if (is_input) {
1861     const tensor_data_info* info = model->getInputDataInfo (idx);
1862     if (info == nullptr)
1863       return 0;
1864
1865     converter.setDataLayout (info->layout, DATA_LAYOUT_TRIV2);
1866     converter.setDataType (info->type, meta->getInputQuantType (idx));
1867     converter.setDataDims (meta->getInputDims (idx));
1868     converter.setQuantZero (meta->getInputQuantZero (idx));
1869     converter.setQuantScale (meta->getInputQuantScale (idx));
1870   } else {
1871     const tensor_data_info* info = model->getOutputDataInfo (idx);
1872     if (info == nullptr)
1873       return 0;
1874
1875     converter.setDataLayout (DATA_LAYOUT_TRIV2, info->layout);
1876     converter.setDataType (meta->getOutputQuantType (idx), info->type);
1877     converter.setDataDims (meta->getOutputDims (idx));
1878     converter.setQuantZero (meta->getOutputQuantZero (idx));
1879     converter.setQuantScale (meta->getOutputQuantScale (idx));
1880   }
1881
1882   return converter.perform ();
1883 }
1884
1885 #else
1886
1887 size_t
1888 TrinityVision::manipulateData (const Model *model, uint32_t idx, bool is_input,
1889     void *dst, void *src, size_t size)
1890 {
1891   memcpy (dst, src, size);
1892   return size;
1893 }
1894
1895 size_t
1896 TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
1897     void *dst, void *src, size_t size)
1898 {
1899   memcpy (dst, src, size);
1900   return size;
1901 }
1902
1903 #endif
1904
1905 /** other device types don't have data manip impl. yet */
1906
1907 size_t
1908 TrinityAsr::manipulateData (const Model *model, uint32_t idx, bool is_input,
1909     void *dst, void *src, size_t size)
1910 {
1911   memcpy (dst, src, size);
1912   return size;
1913 }