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