[Refactoring] return 'task_id' when calling runNPU_sync/async
[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-handler.cc
8  * @date 03 Apr 2020
9  * @brief Impelemetation of NPU Engine entrypoint that handles APIs 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 <npubinfmt.h>
19 #include <NPUdrvAPI.h>
20 #include <CommPlugin.h>
21
22 #include <string.h>
23 #include <assert.h>
24
25 #include <condition_variable>
26 #include <functional>
27 #include <atomic>
28 #include <map>
29
30 #define TAG _N2
31
32 /** @brief host handler constructor */
33 HostHandler::HostHandler (Device *device)
34   : device_(device),
35     /* ignored as we don't use double buffering anymore, but for backward-compatibility */
36     async_mode_ (NPUASYNC_WAIT)
37 {
38 }
39
40 /** @brief host handler destructor */
41 HostHandler::~HostHandler ()
42 {
43 }
44
45 /**
46  * @brief register model from generic buffer
47  * @param[in] model_buf model buffer
48  * @param[out] modelid model id
49  * @return 0 if no error. otherwise a negative errno
50  */
51 int
52 HostHandler::registerModel (generic_buffer *model_buf, uint32_t *modelid)
53 {
54   if (model_buf == nullptr || modelid == nullptr) {
55     logerr (TAG, "Invalid arguments given\n");
56     return -EINVAL;
57   }
58
59   Model *model = nullptr;
60   int status = device_->setModel (model_buf, &model);
61   if (status != 0) {
62     logerr (TAG, "Failed to set model: %d\n", status);
63     return status;
64   }
65
66   assert (model != nullptr);
67
68   status = models_.insert (model->getID(), model);
69   if (status != 0) {
70     logerr (TAG, "Failed to insert model id\n");
71     delete model;
72     return status;
73   }
74
75   *modelid = model->getID();
76   return 0;
77 }
78
79 /**
80  * @brief remove the registered model
81  * @param[in] modelid model id
82  * @return 0 if no error. otherwise a negative errno
83  */
84 int
85 HostHandler::unregisterModel (uint32_t modelid)
86 {
87   Model *model = models_.find (modelid);
88   if (model == nullptr)
89     return -ENOENT;
90
91   int status = device_->unsetModel (model);
92   if (status != 0) {
93     logerr (TAG, "Failed to unset model: %d\n", status);
94     return status;
95   }
96
97   return models_.remove (modelid);
98 }
99
100 /**
101  * @brief remove all registered models
102  * @return 0
103  */
104 int
105 HostHandler::unregisterModels ()
106 {
107   std::function <bool (Model *)> functor =
108     [&] (Model *m) -> bool {
109       bool can_remove = true;
110       int status = device_->unsetModel (m);
111       if (status != 0) {
112         logwarn (TAG, "Failed to unset model: %d\n", status);
113         can_remove = false;
114       }
115       return can_remove;
116     };
117
118   models_.for_each (functor);
119   return 0;
120 }
121
122 /**
123  * @brief Get the profile information from NPU
124  * @param[in] task_id The identifier for each inference
125  * @param[out] profile The profile instance
126  * @return 0 if no error, otherwise a negative errno.
127  */
128 int
129 HostHandler::getProfile (int task_id, npu_profile *profile)
130 {
131   if (task_id < 0 || profile == nullptr) {
132     logerr (TAG, "Invalid parameter provided\n");
133     return -EINVAL;
134   }
135
136   const DriverAPI * api = device_->getDriverAPI ();
137   assert (api != nullptr);
138
139   profile->num_layers = 0;
140   profile->layers = nullptr;
141
142   int status = api->getProfile (task_id, profile);
143   if (status != 0) {
144     logerr (TAG, "Failed to get profile information: %d\n", status);
145     return status;
146   }
147
148   return 0;
149 }
150
151 /**
152  * @brief get the stats for the latest apps of the target device
153  * @param[out] stat The list of app stat
154  * @note The caller has the responsibility to free the resources.
155  *       This API is not working on the emulated envionment.
156  */
157 int
158 HostHandler::getStatApps (npu_stat_apps *stat)
159 {
160   const DriverAPI * api = device_->getDriverAPI ();
161   assert (api != nullptr);
162
163   return api->getStatApps (stat);
164 }
165
166 /**
167  * @brief get the stats for the latest tasks of the target app
168  * @param[in] appid The identifier of target app
169  * @param[out] stat The list of task stat
170  * @note The caller has the responsibility to free the resources.
171  *       This API is not working on the emulated envionment.
172  */
173 int
174 HostHandler::getStatTasks (int appid, npu_stat_tasks *stat)
175 {
176   const DriverAPI * api = device_->getDriverAPI ();
177   assert (api != nullptr);
178
179   return api->getStatTasks (appid, stat);
180 }
181
182 /**
183  * @brief Get the driver API level of opened NPU device
184  * @param[out] level driver API level
185  * @return 0 if no error, otherwise a negative errno
186  */
187 int
188 HostHandler::getAPILevel (uint32_t *level)
189 {
190   const DriverAPI * api = device_->getDriverAPI ();
191   assert (api != nullptr);
192
193   return api->getAPILevel (level);
194 }
195
196 /**
197  * @brief Get the TOPS of the opened NPU device
198  * @param[in] dev the NPU device handle
199  * @param[out] tops npu tops
200  * @return 0 if no error, otherwise a negative errno
201  * @note this does not support for emulated devices
202  */
203 int
204 HostHandler::getTops (uint32_t *tops)
205 {
206   const DriverAPI * api = device_->getDriverAPI ();
207   assert (api != nullptr);
208
209   return api->getTops (tops);
210 }
211
212 /**
213  * @brief Get the DSP DSPM size of the opened NPU device
214  * @param[in] dev the NPU device handle
215  * @param[out] dspm dspm size
216  * @return 0 if no error, otherwise a negative errno
217  * @note this does not support for emulated devices
218  */
219 int
220 HostHandler::getDspmSize (uint32_t *dspm)
221 {
222   const DriverAPI * api = device_->getDriverAPI ();
223   assert (api != nullptr);
224
225   return api->getDspmSize (dspm);
226 }
227 /**
228  * @brief Set the data layout for input/output tensors
229  * @param[in] modelid The ID of model whose layouts are set
230  * @param[in] in the layout/type info for input tensors
231  * @param[in] out the layout/type info for output tensors
232  * @return @c 0 if no error. otherwise a negative error value
233  * @note if this function is not called, default layout/type will be used.
234  */
235 int
236 HostHandler::setDataInfo (uint32_t modelid, tensors_data_info *in,
237     tensors_data_info *out)
238 {
239   Model *model = models_.find (modelid);
240   if (model == nullptr)
241     return -ENOENT;
242
243   return model->setDataInfo (in, out);
244 }
245
246 /**
247  * @brief Set the inference constraint for next NPU inferences
248  * @param[in] modelid The target model id
249  * @param[in] constraint inference constraint (e.g., timeout, priority)
250  * @return @c 0 if no error. otherwise a negative error value
251  * @note If this function is not called, default values are used.
252  */
253 int
254 HostHandler::setConstraint (uint32_t modelid, npuConstraint constraint)
255 {
256   Model *model = models_.find (modelid);
257   if (model == nullptr)
258     return -ENOENT;
259
260   model->setConstraint (constraint);
261
262   return 0;
263 }
264
265 /**
266  * @brief find and return model instance
267  * @param[in] modelid model id
268  * @return model instance if found. otherwise nullptr
269  */
270 Model *
271 HostHandler::getModel (uint32_t modelid)
272 {
273   return models_.find (modelid);
274 }
275
276 /** @brief dummay callback for runSync. */
277 class callbackSync {
278   public:
279     callbackSync (output_buffers *output) : output_(output), done_(false) {}
280
281     static void callback (output_buffers *output, uint64_t sequence, void *data) {
282       callbackSync *sync = static_cast<callbackSync *>(data);
283       sync->callback (output, sequence);
284     }
285
286     void callback (output_buffers *output, uint64_t sequence) {
287       if (output_ != nullptr && output != nullptr) {
288         /** just copy internal variables of output buffers */
289         memcpy (output_, output, sizeof (output_buffers));
290       }
291       done_ = true;
292       cv_.notify_one ();
293     }
294
295     void wait () {
296       std::unique_lock<std::mutex> lock (m_);
297       cv_.wait (lock, [this]() { return done_; });
298     }
299
300   private:
301     std::mutex m_;
302     std::condition_variable cv_;
303     output_buffers *output_;
304     bool done_;
305 };
306
307 /**
308  * @brief Execute inference. Wait (block) until the output is available.
309  * @param[in] modelid The model to be inferred.
310  * @param[in] input The input data to be inferred.
311  * @param[out] output The output result.
312  * @return @c positive id if no error. otherwise a negative error value
313  */
314 int
315 HostHandler::runSync (uint32_t modelid, const input_buffers *input,
316     output_buffers *output)
317 {
318   callbackSync sync (output);
319   int status = runAsync (modelid, input, callbackSync::callback,
320       static_cast <void*> (&sync), NPUASYNC_DROP_OLD, nullptr);
321   if (status > 0) {
322     /** sync needs to wait callback */
323     sync.wait ();
324   }
325   return status;
326 }
327
328 /**
329  * @brief Invoke NPU inference. Unblocking call.
330  * @param[in] modelid The model to be inferred.
331  * @param[in] input The input data to be inferred.
332  * @param[in] cb The output buffer handler.
333  * @param[in] cb_data The data given as a parameter to the runNPU_async call.
334  * @param[in] mode Configures how this operation works.
335  * @param[out] sequence The sequence number returned with runNPU_async.
336  * @return @c positive id if no error. otherwise a negative error value
337  */
338 int
339 HostHandler::runAsync (uint32_t modelid, const input_buffers *input,
340     npuOutputNotify cb, void *cb_data, npu_async_mode mode, uint64_t *sequence)
341 {
342   Model *model = nullptr;
343
344   if (device_->needModel()) {
345     model = getModel (modelid);
346     if (model == nullptr)
347       return -ENOENT;
348   }
349
350   /* check the given model before running */
351   if (model != nullptr && !model->finalize ()) {
352     logerr (TAG, "Failed to finalize the model. Please see the log messages\n");
353     return -EINVAL;
354   }
355
356   device_->setAsyncMode (mode);
357   return device_->run (NPUINPUT_HOST, model, input, cb, cb_data, sequence);
358 }
359
360 /**
361  * @brief Let NPU accept input frames from its internal source continuously
362  * @param[in] modelid The model to be inferred.
363  * @param[in] opmode NPU has different opmode with auto-inputs. Choose one.
364  * @param[in] hw_dev The target device feeding input data
365  * @return @c 0 if no error. otherwise a negative error value
366  */
367 int
368 HostHandler::runInternal (uint32_t modelid, npu_input_opmode opmode,
369     std::string hw_dev)
370 {
371   Model *model = nullptr;
372
373   if (device_->needModel()) {
374     model = getModel (modelid);
375     if (model == nullptr)
376       return -ENOENT;
377   }
378
379   /* check the given model before running */
380   if (model != nullptr && !model->finalize ()) {
381     logerr (TAG, "Failed to finalize the model. Please see the log messages\n");
382     return -EINVAL;
383   }
384
385   return device_->runInternal (opmode, model, hw_dev);
386 }
387
388 /**
389  * @brief Stop the request with the given id
390  * @param[in] dev The NPU device handle
391  * @param[in] id The request id
392  * @return @c 0 if no error. otherwise a negative error value
393  */
394 int
395 HostHandler::stopInternal (int id)
396 {
397   if (id <= 0) {
398     logerr (TAG, "Unable to stop this request with id (%d)\n", id);
399     return -EINVAL;
400   }
401
402   const DriverAPI * api = device_->getDriverAPI ();
403   assert (api != nullptr);
404
405   return api->stop_target (id);
406 }
407
408 /**
409  * @brief get number of available devices
410  * @param[in] type device type
411  * @return number of devices
412  */
413 int
414 HostHandler::getNumDevices (dev_type type)
415 {
416   return DriverAPI::getNumDevices (type);
417 }
418
419 /**
420  * @brief get device instance
421  * @param[out] dev device instance
422  * @param[in] type device type
423  * @param[in] id device id
424  * @return 0 if no error. otherwise a negative errno
425  */
426 int
427 HostHandler::getDevice (npudev_h *dev, dev_type type, uint32_t id)
428 {
429   int num_devices = getNumDevices (type);
430
431   /** check the validity of device id */
432   if (!(num_devices > 0 && id < static_cast<uint32_t>(num_devices))) {
433     logerr (TAG, "Invalid arguments provided\n");
434     return -ENODEV;
435   }
436
437   Device *device = Device::createInstance (type, id);
438   if (device == nullptr) {
439     logerr (TAG, "Failed to create a device with the given type\n");
440     return -EINVAL;
441   }
442
443   *dev = device;
444
445   return 0;
446 }
447
448 /**
449  * @brief allocate generic buffer (just for users)
450  * @param[out] buffer buffer instance
451  * @return 0 if no error. otherwise a negative errno
452  */
453 int
454 HostHandler::allocGenericBuffer (generic_buffer *buffer)
455 {
456   if (buffer == NULL)
457     return -EINVAL;
458
459   if (buffer->size == 0) {
460     logerr (TAG, "Invalid size\n");
461     return -EINVAL;
462   }
463
464   if (buffer->size > UINT32_MAX) {
465     logerr (TAG, "Don't support such a large size");
466     return -ENOMEM;
467   }
468
469   switch (buffer->type) {
470     case BUFFER_FILE:
471       /* nothing to do */
472       if (buffer->filepath == nullptr)
473         return -EINVAL;
474       break;
475     case BUFFER_MAPPED:
476     {
477       /* now, npu-engine always provides dmabuf-based allocation */
478       void *addr = nullptr;
479       int dmabuf = device_->allocMemory (buffer->size, &addr);
480       if (dmabuf < 0)
481         return dmabuf;
482
483       buffer->dmabuf = dmabuf;
484       buffer->offset = 0;
485       buffer->addr = addr;
486     } break;
487     default:
488       return -EINVAL;
489   }
490
491   return 0;
492 }
493
494 /**
495  * @brief deallocate generic buffer (just for users)
496  * @param[in] buffer buffer instance
497  * @return 0 if no error. otherwise a negative errno
498  */
499 int
500 HostHandler::deallocGenericBuffer (generic_buffer *buffer)
501 {
502   if (buffer == NULL)
503     return -EINVAL;
504
505   switch (buffer->type) {
506     case BUFFER_FILE:
507       /** always true cuz nothing to do */
508       break;
509     case BUFFER_MAPPED:
510       return device_->deallocMemory (buffer->dmabuf, buffer->size, buffer->addr);
511     default:
512       return -EINVAL;
513   }
514
515   return 0;
516 }
517
518 /**
519  * @brief allocate multiple generic buffers (just for users)
520  * @param[out] buffers multi-buffer instance
521  * @return 0 if no error. otherwise a negative errno
522  */
523 int
524 HostHandler::allocGenericBuffer (generic_buffers *buffers)
525 {
526   uint32_t idx;
527   int status = 0;
528
529   if (buffers == NULL || buffers->num_buffers < 1)
530     return -EINVAL;
531
532   for (idx = 0; idx < buffers->num_buffers; idx++) {
533     status = allocGenericBuffer (&buffers->bufs[idx]);
534     if (status != 0)
535       goto free_buffer;
536   }
537
538   return 0;
539
540 free_buffer:
541   while (idx != 0) {
542     deallocGenericBuffer (&buffers->bufs[--idx]);
543   }
544
545   return status;
546 }
547
548 /**
549  * @brief deallocate multiple generic buffers (just for users)
550  * @param[in] buffers multi-buffer instance
551  * @return 0 if no error. otherwise a negative errno
552  */
553 int
554 HostHandler::deallocGenericBuffer (generic_buffers *buffers)
555 {
556   if (buffers == NULL || buffers->num_buffers < 1)
557     return -EINVAL;
558
559   for (uint32_t idx = 0; idx < buffers->num_buffers; idx++)
560     deallocGenericBuffer (&buffers->bufs[idx]);
561   buffers->num_buffers = 0;
562
563   return 0;
564 }
565
566 /**
567  * @brief get the current memory status
568  * @param[out] alloc_total The size of allocated memory until now
569  * @param[out] free_total The size of freed memory until now
570  * @return 0 if no error. otherwise a negatice error value
571  */
572 int
573 HostHandler::getMemoryStatus (size_t *alloc_total, size_t *free_total)
574 {
575   /** API is always set in initialize () */
576   const DriverAPI * api = device_->getDriverAPI ();
577   assert (api != nullptr);
578
579   return api->getMemoryStatus (alloc_total, free_total);
580 }
581
582 /**
583  * @brief Get the current device status to be used
584  * @param[out] status the device status
585  * @param[out] num_requests the number of running requests (or pending)
586  * @return 0 if no error, otherwise a negative errno.
587  */
588 int
589 HostHandler::getDeviceStatus (npu_status *status, uint32_t *num_requests)
590 {
591   /** API is always set in initialize () */
592   const DriverAPI * api = device_->getDriverAPI ();
593
594   if (!api)
595     return -EINVAL;
596
597   device_state_t state = api->isReady ();
598   if (state == device_state_t::STATE_READY) {
599     *num_requests = api->numRequests ();
600     if (*num_requests > 0)
601       *status = NPU_READY;
602     else
603       *status = NPU_IDLE;
604   } else {
605     *num_requests = 0;
606     *status = NPU_ERROR;
607   }
608
609   return 0;
610 }
611
612 /** implement methods of Device class */
613
614 /** @brief constructor of device */
615 Device::Device (dev_type type, int id, bool need_model)
616   : comm_ (CommPlugin::getCommPlugin()), type_ (type), id_ (id), need_model_ (true),
617     mode_ (NPUASYNC_WAIT), initialized_ (false), atomic_flag_ (ATOMIC_FLAG_INIT)
618 {
619 }
620
621 /**
622  * @brief create device instance depending on device type and id
623  * @param[in] type device type
624  * @param[in] id device id
625  * @return device instance
626  */
627 Device *
628 Device::createInstance (dev_type type, int id)
629 {
630   Device *device = nullptr;
631
632   switch (type & DEVICETYPE_MASK) {
633     case DEVICETYPE_TRIV2:
634       device = new TrinityVision2 (id);
635       break;
636     case DEVICETYPE_DEPR:
637       logwarn (TAG, "You're trying to open deprecated devices..\n");
638       break;
639     default:
640       break;
641   }
642
643   if (device != nullptr && device->init () != 0) {
644     delete device;
645     device = nullptr;
646   }
647
648   return device;
649 }
650
651 /**
652  * @brief device initialization
653  * @return 0 if no error, otherwise a negative errno
654  * @note Init failures come from createDriverAPI() only.
655  */
656 int
657 Device::init ()
658 {
659   /** should be initilizaed only once */
660   if (!atomic_flag_.test_and_set()) {
661     /** create the corresponding driver API */
662     api_ = DriverAPI::createDriverAPI (type_, id_);
663     if (api_.get() == nullptr) {
664       atomic_flag_.clear();
665       logerr (TAG, "Failed to create driver API\n");
666       return -EINVAL;
667     }
668
669     handler_.reset (new HostHandler (this));
670     scheduler_.reset (new Scheduler (api_.get()));
671     mem_ = MemAllocator::createInstance (api_.get());
672
673     initialized_ = true;  /** c++11 does not provide test() of atomic flag */
674   }
675
676   return 0;
677 }
678
679 /**
680  * @brief stop all requests from this device
681  * @param[in] force_stop indicate the schedduler waits until to handle previous requests
682  * @return 0 if no error, otherwise a negative errno
683  */
684 int
685 Device::stop (bool force_stop)
686 {
687   if (!initialized ()) {
688     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
689     return -EPERM;
690   }
691
692   Request *req = new Request (NPUINPUT_STOP);
693   req->setForceStop (force_stop);
694   return scheduler_->submitRequest (req);
695 }
696
697 /**
698  * @brief allocate generic memory buffer
699  * @param[in] size the size to allocate
700  * @param[out] addr the mapped address
701  * @return dmabuf fd if no error, otherwise a negative errno
702  */
703 int
704 Device::allocMemory (size_t size, void **addr)
705 {
706   if (!initialized ()) {
707     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
708     return -EPERM;
709   }
710
711   if (size == 0 || addr == nullptr) {
712     logerr (TAG, "Invalid arguments\n");
713     return -EINVAL;
714   }
715
716   return mem_->allocMemory (size, addr);
717 }
718
719 /**
720  * @brief deallocate generic memory buffer
721  * @param[in] dmabuf_fd dmabuf file descriptor
722  * @param[in] size buffer size
723  * @param[in] addr mapped addr
724  * @return 0 if no error, otherwise a negative errno
725  */
726 int
727 Device::deallocMemory (int dmabuf_fd, size_t size, void * addr)
728 {
729   if (!initialized ()) {
730     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
731     return -EPERM;
732   }
733
734   if (dmabuf_fd < 0 || size == 0 || addr == nullptr) {
735     logerr (TAG, "Invalid arguments\n");
736     return -EINVAL;
737   }
738
739   return mem_->deallocMemory (dmabuf_fd, size, addr);
740 }
741
742 /**
743  * @brief extract the segment table instance from input generic buffers
744  * @param[in] model the model instance
745  * @param[in] input the input generic buffers
746  * @param[in] output the output generic buffers
747  * @return the segment table instance
748  */
749 SegmentTable *
750 TrinityVision2::prepareSegmentTable (const Model *model, const input_buffers *input,
751     const output_buffers *output)
752 {
753   const Metadata *meta = model->getMetadata ();
754   if (meta == nullptr || (input != nullptr &&
755         meta->getInputNum() != input->num_buffers)) {
756     logerr (TAG, "Invalid metadata info provided\n");
757     return nullptr;
758   }
759
760   SegmentTable * segt = mem_->allocSegmentTable (new HWmemDevice);
761   int status = segt->alloc ();
762   if (status != 0) {
763     logerr (TAG, "Failed to allocate segment table: %d\n", status);
764     goto delete_segt;
765   }
766
767   status = segt->createSegments (model, input, output);
768   if (status != 0) {
769     logerr (TAG, "Failed to create segments: %d\n", status);
770     goto delete_segt;
771   }
772
773   return segt;
774
775 delete_segt:
776   delete segt;
777   return nullptr;
778 }
779
780 /**
781  * @brief implementation of TRIV2's setModel ()
782  * @param[in] model_buf the model generic buffer
783  * @param[out] model the model instance
784  * @return 0 if no error, otherwise a negative errno
785  */
786 int
787 TrinityVision2::setModel (const generic_buffer *model_buf, Model ** model_ptr)
788 {
789   if (!initialized ()) {
790     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
791     return -EPERM;
792   }
793
794   if (model_buf == nullptr || model_ptr == nullptr)
795     return -EINVAL;
796
797   Model *model;
798   int status;
799
800   switch (model_buf->type) {
801   case BUFFER_FILE:
802   case BUFFER_MAPPED:
803     model = mem_->allocModel (new HWmemDevice);
804     if (model == nullptr) {
805       logerr (TAG, "Failed to allocate model\n");
806       return -ENOMEM;
807     }
808
809     status = model->alloc (NPUBIN_META_SIZE);
810     if (status != 0) {
811       logerr (TAG, "Failed to allocate model: %d\n", status);
812       goto delete_exit;
813     }
814
815     status = comm_.extractGenericBuffer (model_buf, model->getData(), nullptr,
816         0, NPUBIN_META_SIZE);
817     if (status != 0) {
818       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
819       goto delete_exit;
820     }
821     break;
822   default:
823     return -EINVAL;
824   }
825
826   status = model->setMetadata (model->getData());
827   if (status != 0)
828     goto delete_exit;
829
830   /** allocate program (optional; NOP) */
831   if (model->getMetadata()->getProgramSize() > 0) {
832     HWmem * hwmem_prog = new HWmem (new HWmemDevice);
833     hwmem_prog->setDriverAPI (api_.get());
834     hwmem_prog->setContiguous (true);
835
836     model->setProgramData (hwmem_prog);
837
838     status = hwmem_prog->alloc (model->getMetadata()->getProgramSize());
839     if (status != 0) {
840       logerr (TAG, "Failed to allocate program\n");
841       goto delete_exit;
842     }
843
844     status = comm_.extractGenericBuffer (model_buf, hwmem_prog->getData(), nullptr,
845         model->getMetadata()->getMetaSize(),
846         model->getMetadata()->getProgramSize());
847     if (status != 0) {
848       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
849       goto delete_exit;
850     }
851
852     /** register this model to the driver */
853     model_config_t config;
854     config.version = model->getMetadata()->getVersion ();
855     config.dbuf_fd = hwmem_prog->getDmabuf ();
856     config.program_size = hwmem_prog->getSize ();
857     config.program_offset_addr = 0;
858     config.metadata_dbuf_fd = model->getDmabuf ();
859
860     /** for metadata extended section */
861     size_t extended_size = model->getMetadata()->getMetaExtendedSize();
862     if (extended_size > 0) {
863       HWmem * hwmem_extended = new HWmem (new HWmemDevice);
864       hwmem_extended->setDriverAPI (api_.get ());
865
866       model->setExtendedMetadata (hwmem_extended);
867
868       status = hwmem_extended->alloc (extended_size);
869       if (status != 0) {
870         logerr (TAG, "Failed to allocate extended metadata: %d\n", status);
871         goto delete_exit;
872       }
873
874       config.metadata_ext_dbuf_fd = hwmem_extended->getDmabuf ();
875       config.metadata_ext_size = extended_size;
876
877       status = comm_.extractGenericBuffer (model_buf, hwmem_extended->getData (),
878           nullptr, NPUBIN_META_SIZE, extended_size);
879       if (status != 0) {
880         logerr (TAG, "Failed to extract generic buffer: %d\n", status);
881         goto delete_exit;
882       }
883     } else {
884       config.metadata_ext_dbuf_fd = -1;
885       config.metadata_ext_size = 0;
886     }
887
888     status = api_->registerModel (&config, model->getMetadata()->getNPUVersion());
889     if (status != 0)
890       goto delete_exit;
891
892     model->setInternalID(config.id);
893   }
894
895   /** allocate weight (optional) */
896   if (model->getMetadata()->getWeightSize() > 0) {
897     HWmem * hwmem_weight = new HWmem (new HWmemDevice);
898     hwmem_weight->setDriverAPI (api_.get());
899
900     model->setWeightData (hwmem_weight);
901
902     status = hwmem_weight->alloc (model->getMetadata()->getWeightSize());
903     if (status != 0) {
904       logerr (TAG, "Failed to allocate program\n");
905       goto delete_exit;
906     }
907
908     status = comm_.extractGenericBuffer (model_buf, hwmem_weight->getData(), nullptr,
909         model->getMetadata()->getMetaSize() + model->getMetadata()->getProgramSize(),
910         model->getMetadata()->getWeightSize());
911     if (status != 0) {
912       logerr (TAG, "Failed to extract generic buffer: %d\n", status);
913       goto delete_exit;
914     }
915   }
916
917   *model_ptr = model;
918   return status;
919
920 delete_exit:
921   delete model;
922   return status;
923 }
924
925 /**
926  * @brief implementation of TRIV2's unsetModel ()
927  * @param[in] model the model instance
928  * @return 0 if no error, otherwise a negative errno
929  */
930 int
931 TrinityVision2::unsetModel (Model * model)
932 {
933   if (!initialized ()) {
934     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
935     return -EPERM;
936   }
937
938   if (model == nullptr) {
939     logerr (TAG, "Invalid model instance\n");
940     return -EINVAL;
941   }
942
943   if (model->getMetadata()->getProgramSize() > 0)
944     return api_->deregisterModel (model->getInternalID ());
945
946   return 0;
947 }
948
949 /** @brief implementation of TRIV2's run() */
950 int
951 TrinityVision2::run (npu_input_opmode opmode, const Model *model,
952     const input_buffers *input, npuOutputNotify cb, void *cb_data,
953     uint64_t *sequence)
954 {
955   if (!initialized ()) {
956     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
957     return -EPERM;
958   }
959
960   if (opmode != NPUINPUT_HOST)
961     return -EINVAL;
962
963   if (input == nullptr || input->num_buffers == 0 || model == nullptr)
964     return -EINVAL;
965
966   const_cast<Model *>(model)->updateDataInfo ();
967
968   /** this device uses segment table */
969   SegmentTable * segt = prepareSegmentTable (model, input);
970   if (segt == nullptr) {
971     logerr (TAG, "Failed to create segment table instance\n");
972     return -EINVAL;
973   }
974
975   /** extract input data */
976   for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
977     if (!segt->getInputSegment(idx)->isExternal ()) {
978       uint32_t seg_offset = segt->getInputSegmentOffset(idx);
979       auto func = std::bind (TrinityVision2::manipulateData, model, idx, true,
980           std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
981       int status = comm_.extractGenericBuffer (
982           &input->bufs[idx],
983           segt->getInputSegment(idx)->getData() + seg_offset,
984           func);
985       if (status != 0) {
986         logerr (TAG, "Failed to feed input segment: %d\n", status);
987         return status;
988       }
989     }
990   }
991
992   Request *req = new Request (opmode);
993   req->setModel (model);
994   req->setInferData (segt);
995   req->setCallback (std::bind (&TrinityVision2::callback, this, req, cb, cb_data));
996
997   if (sequence && req->getID () > 0) {
998     *sequence = (uint32_t) req->getID ();
999   }
1000
1001   return scheduler_->submitRequest (req);
1002 }
1003
1004 /** @brief implementation of TRIV2's runInternal() */
1005 int
1006 TrinityVision2::runInternal (npu_input_opmode opmode, const Model *model,
1007     std::string hw_dev)
1008 {
1009   if (!initialized ()) {
1010     logerr (TAG, "Uninitialized device; should use libnpuhost APIs\n");
1011     return -EPERM;
1012   }
1013
1014   if (opmode != NPUINPUT_HW_RECURRING)
1015     return -EINVAL;
1016
1017   /** this device uses segment table */
1018   SegmentTable * segt = prepareSegmentTable (model, nullptr, nullptr);
1019   if (segt == nullptr) {
1020     logerr (TAG, "Failed to create segment table instance\n");
1021     return -EINVAL;
1022   }
1023
1024   Request *req = new Request (opmode);
1025   req->setModel (model);
1026   req->setInferData (segt);
1027   req->setHwDevice (hw_dev);
1028
1029   return scheduler_->submitRequest (req);
1030 }
1031
1032 /** @brief callback of TRIV2 request */
1033 void
1034 TrinityVision2::callback (Request *req, npuOutputNotify cb, void *cb_data)
1035 {
1036   if (cb == nullptr)
1037     return;
1038
1039   const Model *model = req->getModel ();
1040   SegmentTable *segt = dynamic_cast<SegmentTable *> (req->getInferData ());
1041   /** internal logic error */
1042   assert (segt != nullptr);
1043
1044   output_buffers output = {
1045     .num_buffers = segt->getNumOutputSegments ()
1046   };
1047
1048   for (uint32_t idx = 0; idx < output.num_buffers; idx++) {
1049     uint32_t output_tensor_size = model->getOutputTensorSize (idx);
1050
1051     output.bufs[idx].type = BUFFER_MAPPED;
1052     output.bufs[idx].size = output_tensor_size;
1053     /** user needs to free this */
1054     output.bufs[idx].addr = calloc (1, output_tensor_size);
1055
1056 #if defined(ENABLE_FPGA_WORKAROUND)
1057     api_->fpga_memcpy (
1058         segt->getOutputSegment(idx)->getDmabuf(),
1059         segt->getOutputSegmentOffset(idx),
1060         output.bufs[idx].addr,
1061         output.bufs[idx].size);
1062 #else
1063     auto func = std::bind (TrinityVision2::manipulateData, model, idx, false,
1064         std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
1065     int status = comm_.insertGenericBuffer (
1066         segt->getOutputSegment(idx)->getData() + segt->getOutputSegmentOffset(idx),
1067         &output.bufs[idx], func);
1068
1069     if (status != 0) {
1070       logerr (TAG, "Failed to return output buffer: %d\n", status);
1071     }
1072 #endif
1073   }
1074
1075   cb (&output, req->getID(), cb_data);
1076
1077   delete segt;
1078 }
1079
1080 /** Implement data manipulation (each device may have different impl.) */
1081
1082 #ifdef ENABLE_MANIP
1083 /**
1084  * @brief perform data manipulation
1085  * @param[in] model model instance
1086  * @param[in] idx tensor index
1087  * @param[in] is_input indicate it's input manipulation
1088  * @param[out] dst destination buffer
1089  * @param[in] src source buffer (feature map)
1090  * @param[in] size size to be copied
1091  * @return size of memory copy if no error, otherwise zero
1092  *
1093  * @note the input data format should be NHWC
1094  *
1095  * @detail Feature map data in TRIV2, (x, y, z) = (width, height, depth)
1096  *
1097  *         1) Image input (depth == 1 or depth == 3)
1098  *            Addr(x,y,z) = Addr(0,0,0) + z + depth * x + ymod * y
1099  *
1100  *         2) Common cases
1101  *            Addr(x,y,z) = Addr(0,0,0) + (z % 64) + (64 * x) + ymod * y + zmod * (z / 64)
1102  */
1103 size_t
1104 TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
1105     void *dst, void *src, size_t size)
1106 {
1107   const Metadata *meta = model->getMetadata ();
1108   DataConverter converter (is_input);
1109
1110   converter.setData (src, dst, size);
1111   converter.setTops (meta->getTops ());
1112   if (is_input) {
1113     const tensor_data_info* info = model->getInputDataInfo (idx);
1114     if (info == nullptr)
1115       return 0;
1116
1117     converter.setDataLayout (info->layout, DATA_LAYOUT_TRIV2);
1118     converter.setDataType (info->type, meta->getInputQuantType (idx));
1119     converter.setDataDims (meta->getInputDims (idx));
1120     converter.setQuantZero (meta->getInputQuantZero (idx));
1121     converter.setQuantScale (meta->getInputQuantScale (idx));
1122   } else {
1123     const tensor_data_info* info = model->getOutputDataInfo (idx);
1124     if (info == nullptr)
1125       return 0;
1126
1127     converter.setDataLayout (DATA_LAYOUT_TRIV2, info->layout);
1128     converter.setDataType (meta->getOutputQuantType (idx), info->type);
1129     converter.setDataDims (meta->getOutputDims (idx));
1130     converter.setQuantZero (meta->getOutputQuantZero (idx));
1131     converter.setQuantScale (meta->getOutputQuantScale (idx));
1132   }
1133
1134   return converter.perform ();
1135 }
1136
1137 #else
1138
1139 size_t
1140 TrinityVision2::manipulateData (const Model *model, uint32_t idx, bool is_input,
1141     void *dst, void *src, size_t size)
1142 {
1143   memcpy (dst, src, size);
1144   return size;
1145 }
1146
1147 #endif