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