[Svace] Fix unresolved svace issues
[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 <npubinfmt.h>
16 #include <NPUdrvAPI.h>
17 #include <CommPlugin.h>
18
19 #include "ne-utils.h"
20 #include "ne-mem.h"
21 #include "ne-scheduler.h"
22 #include "ne-handler.h"
23
24 #include <string.h>
25 #include <assert.h>
26
27 #include <condition_variable>
28 #include <functional>
29 #include <atomic>
30 #include <map>
31
32 #define TAG _N2
33
34 #define INIT_HOST_HANDLER(handler, dev) \
35   Device *tdev = static_cast <Device *> (dev); \
36   if (tdev == nullptr) return -EINVAL; \
37   HostHandler *handler = tdev->getHostHandler (); \
38   if (handler == nullptr) return -EINVAL;
39
40 /** @brief device class. it contains all related instances */
41 class Device {
42   public:
43     /** @brief Factory method to create a trinity device dependong on dev type */
44     static Device *createInstance (dev_type device_type, int device_id);
45
46     /** @brief constructor of device */
47     Device (dev_type type, int id, bool need_model = true)
48       : comm_(CommPlugin::getCommPlugin()), type_ (type), id_ (id),
49         need_model_ (true), mode_ (NPUASYNC_WAIT), initialized_ (ATOMIC_FLAG_INIT) {}
50
51     /** @brief destructor of device */
52     virtual ~Device () {}
53
54     /** @brief initialization */
55     int init () {
56       if (!initialized_.test_and_set()) {
57         /** create the corresponding driver API */
58         api_ = DriverAPI::createDriverAPI (type_, id_);
59         if (api_.get() == nullptr) {
60           initialized_.clear();
61           logerr (TAG, "Failed to create driver API\n");
62           return -EINVAL;
63         }
64
65         handler_.reset (new HostHandler (this));
66         scheduler_.reset (new Scheduler (api_.get()));
67         mem_ = MemAllocator::createInstance (api_.get());
68       }
69
70       return 0;
71     }
72
73     HostHandler *getHostHandler () { return handler_.get(); }
74     dev_type getType () { return type_; }
75     int getID () { return id_; }
76     bool needModel () { return need_model_; }
77     void setAsyncMode (npu_async_mode mode) { mode_ = mode; }
78
79     HWmem * allocMemory () { return mem_->allocMemory (); }
80     void deallocMemory (int dmabuf_fd) { mem_->deallocMemory (dmabuf_fd); }
81
82     /** it stops all requests in this device (choose wait or force) */
83     int stop (bool force_stop) {
84       Request *req = new Request (NPUINPUT_STOP);
85       req->setForceStop (force_stop);
86       return scheduler_->submitRequest (req);
87     }
88
89     virtual Model * registerModel (const generic_buffer *model) = 0;
90     virtual int run (npu_input_opmode opmode, const Model *model,
91         const input_buffers *input, npuOutputNotify cb, void *cb_data,
92         uint64_t *sequence) = 0;
93
94   protected:
95     /** the device instance has ownership of all related components */
96     std::unique_ptr<DriverAPI>    api_;       /**< device api */
97     std::unique_ptr<MemAllocator> mem_;       /**< memory allocator */
98     std::unique_ptr<HostHandler>  handler_;   /**< host handler */
99     std::unique_ptr<Scheduler>    scheduler_; /**< scheduler */
100
101     CommPlugin& comm_;                        /**< plugin communicator */
102
103     dev_type type_;                           /**< device type */
104     int id_;                                  /**< device id */
105     bool need_model_;                         /**< indicates whether the device needs model */
106     npu_async_mode mode_;                     /**< async run mode */
107
108   private:
109     std::atomic_flag initialized_;
110 };
111
112 /** @brief Trinity Vision (TRIV) classs */
113 class TrinityVision : public Device {
114   public:
115     TrinityVision (int id) : Device (NPUCOND_TRIV_CONN_SOCIP, id) {}
116     ~TrinityVision () {}
117
118     static size_t manipulateData (const Model *model, uint32_t idx, bool is_input,
119         void *dst, void *src, size_t size);
120
121     Model * registerModel (const generic_buffer *model_buf) {
122       Model *model = mem_->allocModel ();
123       if (model == nullptr) {
124         logerr (TAG, "Failed to allocate model\n");
125         return nullptr;
126       }
127
128       int status;
129       if (model_buf->type == BUFFER_DMABUF) {
130         model->setDmabuf (model_buf->dmabuf);
131         model->setOffset (model_buf->offset);
132         model->setSize (model_buf->size);
133       } else {
134         status = model->alloc (model_buf->size);
135         if (status != 0) {
136           logerr (TAG, "Failed to allocate model: %d\n", status);
137           goto delete_exit;
138         }
139
140         status = comm_.extractGenericBuffer (model_buf, model->getData(), nullptr);
141         if (status != 0) {
142           logerr (TAG, "Failed to extract generic buffer: %d\n", status);
143           goto delete_exit;
144         }
145       }
146
147       status = model->setMetadata (model->getData());
148       if (status != 0)
149         goto delete_exit;
150
151       model_config_t config;
152       config.dmabuf_id = model->getDmabuf();
153       config.program_size = model->getMetadata()->getProgramSize();
154       config.program_offset_addr = model->getOffset() + model->getMetadata()->getMetaSize();
155       config.weight_offset_addr = config.program_offset_addr + config.program_size;
156
157       status = api_->setModel (&config);
158       if (status != 0)
159         goto delete_exit;
160
161       return model;
162
163 delete_exit:
164       delete model;
165       return nullptr;
166     }
167
168     Buffer * prepareInputBuffers (const Model *model, const input_buffers *input) {
169       const Metadata *meta = model->getMetadata();
170       const generic_buffer *first = &input->bufs[0];
171
172       if (meta->getInputNum() != input->num_buffers)
173         return nullptr;
174
175       Buffer * buffer = mem_->allocBuffer ();
176       if (buffer != nullptr) {
177         if (first->type == BUFFER_DMABUF) {
178           buffer->setDmabuf (first->dmabuf);
179           buffer->setOffset (first->offset);
180           buffer->setSize (meta->getBufferSize());
181         } else {
182           int status = buffer->alloc (meta->getBufferSize ());
183           if (status != 0) {
184             logerr (TAG, "Failed to allocate buffer: %d\n", status);
185             delete buffer;
186             return nullptr;
187           }
188         }
189       }
190
191       buffer->createTensors (meta);
192       return buffer;
193     }
194
195     int run (npu_input_opmode opmode, const Model *model,
196         const input_buffers *input, npuOutputNotify cb, void *cb_data,
197         uint64_t *sequence) {
198       if (opmode != NPUINPUT_HOST)
199         return -EINVAL;
200
201       Buffer *buffer = prepareInputBuffers (model, input);
202       if (buffer == nullptr)
203         return -EINVAL;
204
205       for (uint32_t idx = 0; idx < input->num_buffers; idx++) {
206         auto func = std::bind (TrinityVision::manipulateData, model, idx, true,
207             std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
208         int status = comm_.extractGenericBuffer (&input->bufs[idx],
209             buffer->getInputTensor(idx)->getData(), func);
210         if (status != 0) {
211           logerr (TAG, "Failed to feed input buffer: %d\n", status);
212           return status;
213         }
214       }
215
216       /** this device uses CMA buffer */
217
218       Request *req = new Request (opmode);
219       req->setModel (model);
220       req->setBuffer (buffer);
221       req->setCallback (std::bind (&TrinityVision::callback, this, req, cb, cb_data));
222
223       if (sequence)
224         *sequence = req->getID();
225
226       return scheduler_->submitRequest (req);
227     }
228
229     void callback (Request *req, npuOutputNotify cb, void *cb_data) {
230       const Model *model = req->getModel ();
231       Buffer *buffer = req->getBuffer ();
232       output_buffers output = {
233         .num_buffers = buffer->getOutputNum ()
234       };
235
236       for (uint32_t idx = 0; idx < output.num_buffers; idx++) {
237         uint32_t output_tensor_size = model->getOutputTensorSize (idx);
238
239         output.bufs[idx].type = BUFFER_MAPPED;
240         output.bufs[idx].size = output_tensor_size;
241         /** user needs to free this */
242         output.bufs[idx].addr = malloc (output_tensor_size);
243
244         auto func = std::bind (TrinityVision::manipulateData, model, idx, false,
245             std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
246         int status = comm_.insertGenericBuffer (buffer->getOutputTensor(idx)->getData(),
247             &output.bufs[idx], func);
248         if (status != 0) {
249           logerr (TAG, "Failed to return output buffer: %d\n", status);
250         }
251       }
252
253       cb (&output, req->getID(), cb_data);
254     }
255 };
256
257 /** @brief Trinity Vision2 (TRIV2) classs */
258 class TrinityVision2 : public Device {
259   public:
260     TrinityVision2 (int id) : Device (NPUCOND_TRIV2_CONN_SOCIP, id) {}
261     ~TrinityVision2 () {}
262
263     static size_t manipulateData (const Model *model, uint32_t idx, bool is_input,
264         void *dst, void *src, size_t size) {
265       memcpy (dst, src, size);
266       return size;
267     }
268
269     Model * registerModel (const generic_buffer *model_buf) {
270       /** TODO: model's weight values are stored in segments */
271       return nullptr;
272     }
273
274     int run (npu_input_opmode opmode, const Model *model,
275         const input_buffers *input, npuOutputNotify cb, void *cb_data,
276         uint64_t *sequence) {
277       if (opmode != NPUINPUT_HOST && opmode != NPUINPUT_HW_RECURRING)
278         return -EINVAL;
279
280       /** this device uses segment table */
281
282       Request *req = new Request (opmode);
283       req->setModel (model);
284 #if 0
285       req->setSegmentTable (segt);
286 #endif
287       req->setCallback (std::bind (&TrinityVision2::callback, this, req, cb, cb_data));
288
289       if (sequence)
290         *sequence = req->getID();
291
292       return scheduler_->submitRequest (req);
293     }
294
295     void callback (Request *req, npuOutputNotify cb, void *cb_data) {
296     }
297 };
298
299 /** @brief Trinity Asr (TRIA) classs */
300 class TrinityAsr : public Device {
301   public:
302     TrinityAsr (int id) : Device (NPUCOND_TRIA_CONN_SOCIP, id, false) {}
303     ~TrinityAsr () {}
304
305     static size_t manipulateData (const Model *model, uint32_t idx, bool is_input,
306         void *dst, void *src, size_t size) {
307       memcpy (dst, src, size);
308       return size;
309     }
310
311     Model * registerModel (const generic_buffer *model_buf) { return nullptr; }
312
313     int run (npu_input_opmode opmode, const Model *model,
314         const input_buffers *input, npuOutputNotify cb, void *cb_data,
315         uint64_t *sequence) {
316       if (opmode != NPUINPUT_HOST)
317         return -EINVAL;
318
319       /** ASR does not require model and support only a single tensor */
320       const generic_buffer *first_buf = &input->bufs[0];
321       Buffer * buffer = mem_->allocBuffer ();
322       int status;
323       if (first_buf->type == BUFFER_DMABUF) {
324         buffer->setDmabuf (first_buf->dmabuf);
325         buffer->setOffset (first_buf->offset);
326         buffer->setSize (first_buf->size);
327       } else {
328         status = buffer->alloc (first_buf->size);
329         if (status != 0) {
330           delete buffer;
331           return status;
332         }
333       }
334       buffer->createTensors ();
335
336       status = comm_.extractGenericBuffer (first_buf,
337           buffer->getInputTensor(0)->getData(), nullptr);
338       if (status != 0)
339         return status;
340
341       Request *req = new Request (opmode);
342       req->setBuffer (buffer);
343       req->setCallback (std::bind (&TrinityAsr::callback, this, req, cb, cb_data));
344
345       if (sequence)
346         *sequence = req->getID();
347
348       return scheduler_->submitRequest (req);
349     }
350
351     void callback (Request *req, npuOutputNotify cb, void *cb_data) {
352     }
353 };
354
355 #ifdef ENABLE_MANIP
356
357 #define do_quantized_memcpy(type) do {\
358     idx = 0;\
359     if (quant) {\
360       while (idx < num_elems) {\
361           val = ((type *) src)[idx];\
362           val = val / _scale;\
363           val += _zero_point;\
364           val = (val > 255.0) ? 255.0 : 0.0;\
365           ((uint8_t *) dst)[idx++] = (uint8_t) val;\
366       }\
367     } else {\
368       while (idx < num_elems) {\
369           val = *(uint8_t *) src;\
370           val -= _zero_point;\
371           val *= _scale;\
372           ((type *) dst)[idx++] = (type) val;\
373           dst = (void*)(((uint8_t *) dst) + data_size);\
374           src = (void*)(((uint8_t *) src) + 1);\
375       }\
376     }\
377   } while (0)
378
379 /**
380  * @brief memcpy during quantization
381  */
382 static void memcpy_with_quant (bool quant, data_type type, float scale, uint32_t zero_point,
383     void *dst, const void *src, uint32_t num_elems)
384 {
385   double _scale = (double) scale;
386   double _zero_point = (double) zero_point;
387   double val;
388   uint32_t data_size = get_data_size (type);
389   uint32_t idx;
390
391   switch (type) {
392     case DATA_TYPE_INT8:
393       do_quantized_memcpy (int8_t);
394       break;
395     case DATA_TYPE_UINT8:
396       do_quantized_memcpy (uint8_t);
397       break;
398     case DATA_TYPE_INT16:
399       do_quantized_memcpy (int16_t);
400       break;
401     case DATA_TYPE_UINT16:
402       do_quantized_memcpy (uint16_t);
403       break;
404     case DATA_TYPE_INT32:
405       do_quantized_memcpy (int32_t);
406       break;
407     case DATA_TYPE_UINT32:
408       do_quantized_memcpy (uint32_t);
409       break;
410     case DATA_TYPE_INT64:
411       do_quantized_memcpy (int64_t);
412       break;
413     case DATA_TYPE_UINT64:
414       do_quantized_memcpy (uint64_t);
415       break;
416     case DATA_TYPE_FLOAT32:
417       do_quantized_memcpy (float);
418       break;
419     case DATA_TYPE_FLOAT64:
420       do_quantized_memcpy (double);
421       break;
422     default:
423       logerr (TAG, "Unsupported datatype %d\n", type);
424   }
425 }
426
427 /**
428  * @brief perform data manipulation
429  * @param[in] model model instance
430  * @param[in] idx tensor index
431  * @param[in] is_input indicate it's input manipulation
432  * @param[out] dst destination buffer
433  * @param[in] src source buffer (feature map)
434  * @param[in] size size to be copied
435  * @return size of memory copy if no error, otherwise zero
436  *
437  * @note the input data format should be NHWC
438  * @detail rules for the memory address of activations in NPU HW.
439  *         (https://code.sec.samsung.net/confluence/pages/viewpage.action?pageId=146491864)
440  *
441  * 1) Special case (depth == 3)
442  * - addr(x,y,z) = addr(0,0,0) + (z) + 3 * (x + width * y)
443  *
444  * 2) Common case
445  * - addr(x,y,z) = addr(0,0,0) + (z % MPA_L) + MPA_L * (x + width * (y + height * (z / MPA_L)))
446  *
447  * Thus, if depth is not a multiple of MPA_L (i.e., 64), zero padding is required
448  */
449 size_t
450 TrinityVision::manipulateData (const Model *model, uint32_t idx, bool is_input,
451     void *dst, void *src, size_t size)
452 {
453   const Metadata *meta = model->getMetadata();
454   const tensor_data_info* info;
455   const uint32_t *dims;
456   uint32_t zero_point;
457   float scale;
458
459   /** extract required information from the metadata */
460   if (is_input) {
461     if (idx >= meta->getInputNum()) {
462       logerr (TAG, "Wrong information for input tensors in metadata\n");
463       return 0;
464     }
465
466     info = model->getInputDataInfo (idx);
467     dims = meta->getInputDims (idx);
468     zero_point = meta->getInputQuantZero (idx);
469     scale = meta->getInputQuantScale (idx);
470   } else {
471     if (idx >= meta->getOutputNum()) {
472       logerr (TAG, "Wrong information for output tensors in metadata\n");
473       return 0;
474     }
475
476     info = model->getOutputDataInfo (idx);
477     dims = meta->getOutputDims (idx);
478     zero_point = meta->getOutputQuantZero (idx);
479     scale = meta->getOutputQuantScale (idx);
480   }
481
482   if (info == nullptr) {
483     logerr (TAG, "Unmatched tensors info\n");
484     return 0;
485   }
486
487   uint32_t batch = dims[0];
488   uint32_t height = dims[1];
489   uint32_t width = dims[2];
490   uint32_t depth = dims[3];
491
492   uint32_t data_size = get_data_size (info->type);
493   if (data_size == 0) {
494     logerr (TAG, "Invalid data size\n");
495     return 0;
496   }
497
498   bool need_quantization = false;
499   /**
500    * note that we assume DATA_TYPE_SRNPU is the smallest data type that we consider.
501    * Also, DATA_TYPE_SRNPU and uint8_t may be regarded as the same in the view of apps.
502    */
503   if (info->type != DATA_TYPE_SRNPU) {
504     assert (data_size >= get_data_size (DATA_TYPE_SRNPU));
505
506     if (data_size > get_data_size (DATA_TYPE_SRNPU) ||
507         !(zero_point == DEFAULT_ZERO_POINT && scale == DEFAULT_SCALE))
508       need_quantization = true;
509   }
510
511   /** check data manipulation is required */
512   if (depth != 3 && depth != 64 && info->layout != DATA_LAYOUT_SRNPU) {
513     uint32_t MPA_L = DATA_GRANULARITY;
514     uint32_t n, h, w, d;
515     uint32_t std_offset;  /* standard offset in NHWC data format */
516     uint32_t npu_offset;  /* npu offset in NPU HW data format*/
517     uint32_t src_offset;
518     uint32_t dst_offset;
519     uint32_t slice_size;
520
521     /* @todo we currently support only NHWC */
522     if (info->layout != DATA_LAYOUT_NHWC) {
523       logerr (TAG, "data manipulation is supported for NHWC only\n");
524       return -EINVAL;
525     }
526
527     for (n = 0; n < batch; n++) {
528       for (h = 0; h < height; h++) {
529         for (w = 0; w < width; w++) {
530           for (d = 0; d < depth; d += MPA_L) {
531             std_offset = d + depth * (w + width * (h + n * height));
532             npu_offset = MPA_L * (w + width * (h + (n + d / MPA_L) * height));
533             slice_size = (depth - d >= MPA_L) ? MPA_L : depth - d;
534
535             if (is_input) {
536               src_offset = std_offset * data_size;
537               dst_offset = npu_offset;
538             } else {
539               src_offset = npu_offset;
540               dst_offset = std_offset * data_size;
541             }
542
543             /* if depth is not a multiple of MPA_L, add zero paddings (not exact values) */
544             if (need_quantization) {
545               memcpy_with_quant (is_input, info->type, scale, zero_point,
546                   static_cast<char*>(dst) + dst_offset,
547                   static_cast<char*>(src) + src_offset,
548                   slice_size);
549             } else {
550               memcpy (
551                   static_cast<char*>(dst) + dst_offset,
552                   static_cast<char*>(src) + src_offset,
553                   slice_size);
554             }
555           }
556         }
557       }
558     }
559   } else if (need_quantization) {
560     /** depth == 3 || depth == 64; special cases which can directly copy input tensor data */
561     if (is_input)
562       size = size / data_size;
563
564     memcpy_with_quant (is_input, info->type, scale, zero_point,
565         dst, src, size);
566   } else {
567     memcpy (dst, src, size);
568   }
569
570   return 0;
571 }
572
573 #else
574
575 size_t
576 TrinityVision::manipulateData (const Model *model, uint32_t idx, bool is_input,
577     void *dst, void *src, size_t size)
578 {
579   memcpy (dst, src, size);
580   return size;
581 }
582
583 #endif
584
585 /**
586  * @brief create device instance depending on device type and id
587  * @param[in] type device type
588  * @param[in] id device id
589  * @return device instance
590  */
591 Device *
592 Device::createInstance (dev_type type, int id)
593 {
594   Device *device = nullptr;
595
596   switch (type & DEVICETYPE_MASK) {
597     case DEVICETYPE_TRIV:
598       device = new TrinityVision (id);
599       break;
600     case DEVICETYPE_TRIV2:
601       device = new TrinityVision2 (id);
602       break;
603     case DEVICETYPE_TRIA:
604       device = new TrinityAsr (id);
605       break;
606     default:
607       break;
608   }
609
610   if (device != nullptr && device->init () != 0) {
611     delete device;
612     device = nullptr;
613   }
614
615   return device;
616 }
617
618 /** @brief host handler constructor */
619 HostHandler::HostHandler (Device *device)
620   : device_(device),
621     /* ignored as we don't use double buffering anymore, but for backward-compatibility */
622     async_mode_ (NPUASYNC_WAIT)
623 {
624 }
625
626 /** @brief host handler destructor */
627 HostHandler::~HostHandler ()
628 {
629 }
630
631 /**
632  * @brief register model from generic buffer
633  * @param[in] model_buf model buffer
634  * @param[out] modelid model id
635  * @return 0 if no error. otherwise a negative errno
636  */
637 int
638 HostHandler::registerModel (generic_buffer *model_buf, uint32_t *modelid)
639 {
640   Model *model = device_->registerModel (model_buf);
641   if (model == nullptr) {
642     logerr (TAG, "Failed to register model\n");
643     return -EINVAL;
644   }
645
646   int status = models_.insert (model->getID(), model);
647   if (status != 0) {
648     logerr (TAG, "Failed to insert model id\n");
649     delete model;
650     return status;
651   }
652
653   *modelid = model->getID();
654   return 0;
655 }
656
657 /**
658  * @brief remove the registered model
659  * @param[in] modelid model id
660  * @return 0 if no error. otherwise a negative errno
661  */
662 int
663 HostHandler::unregisterModel (uint32_t modelid)
664 {
665   return models_.remove (modelid);
666 }
667
668 /**
669  * @brief remove all registered models
670  * @return 0
671  */
672 int
673 HostHandler::unregisterModels ()
674 {
675   models_.clear ();
676   return 0;
677 }
678
679 /**
680  * @brief Set the data layout for input/output tensors
681  * @param[in] modelid The ID of model whose layouts are set
682  * @param[in] in the layout/type info for input tensors
683  * @param[in] out the layout/type info for output tensors
684  * @return @c 0 if no error. otherwise a negative error value
685  * @note if this function is not called, default layout/type will be used.
686  */
687 int
688 HostHandler::setDataInfo (uint32_t modelid, tensors_data_info *in,
689     tensors_data_info *out)
690 {
691   Model *model = models_.find (modelid);
692   if (model == nullptr)
693     return -ENOENT;
694
695   model->setDataInfo (in, out);
696
697   return 0;
698 }
699
700 /**
701  * @brief Set the inference constraint for next NPU inferences
702  * @param[in] modelid The target model id
703  * @param[in] constraint inference constraint (e.g., timeout, priority)
704  * @return @c 0 if no error. otherwise a negative error value
705  * @note If this function is not called, default values are used.
706  */
707 int
708 HostHandler::setConstraint (uint32_t modelid, npuConstraint constraint)
709 {
710   Model *model = models_.find (modelid);
711   if (model == nullptr)
712     return -ENOENT;
713
714   model->setConstraint (constraint);
715
716   return 0;
717 }
718
719 /**
720  * @brief find and return model instance
721  * @param[in] modelid model id
722  * @return model instance if found. otherwise nullptr
723  */
724 Model *
725 HostHandler::getModel (uint32_t modelid)
726 {
727   return models_.find (modelid);
728 }
729
730 /** @brief dummay callback for runSync. */
731 class callbackSync {
732   public:
733     callbackSync (output_buffers *output) : output_(output), done_(false) {}
734
735     static void callback (output_buffers *output, uint64_t sequence, void *data) {
736       callbackSync *sync = static_cast<callbackSync *>(data);
737       sync->callback (output, sequence);
738     }
739
740     void callback (output_buffers *output, uint64_t sequence) {
741       /** just copy internal variables of output buffers */
742       memcpy (output_, output, sizeof (output_buffers));
743       done_ = true;
744       cv_.notify_one ();
745     }
746
747     void wait () {
748       std::unique_lock<std::mutex> lock (m_);
749       cv_.wait (lock, [this]() { return done_; });
750     }
751
752   private:
753     std::mutex m_;
754     std::condition_variable cv_;
755     output_buffers *output_;
756     bool done_;
757 };
758
759 /**
760  * @brief Execute inference. Wait (block) until the output is available.
761  * @param[in] modelid The model to be inferred.
762  * @param[in] input The input data to be inferred.
763  * @param[out] output The output result.
764  * @return @c 0 if no error. otherwise a negative error value
765  */
766 int
767 HostHandler::runSync (uint32_t modelid, const input_buffers *input,
768     output_buffers *output)
769 {
770   callbackSync sync (output);
771   int status = runAsync (modelid, input, callbackSync::callback,
772       static_cast <void*> (&sync), NPUASYNC_DROP_OLD, nullptr);
773   if (status == 0) {
774     /** sync needs to wait callback */
775     sync.wait ();
776   }
777   return status;
778 }
779
780 /**
781  * @brief Invoke NPU inference. Unblocking call.
782  * @param[in] modelid The model to be inferred.
783  * @param[in] input The input data to be inferred.
784  * @param[in] cb The output buffer handler.
785  * @param[in] cb_data The data given as a parameter to the runNPU_async call.
786  * @param[in] mode Configures how this operation works.
787  * @param[out] sequence The sequence number returned with runNPU_async.
788  * @return @c 0 if no error. otherwise a negative error value
789  */
790 int
791 HostHandler::runAsync (uint32_t modelid, const input_buffers *input,
792     npuOutputNotify cb, void *cb_data, npu_async_mode mode, uint64_t *sequence)
793 {
794   Model *model = nullptr;
795
796   if (device_->needModel()) {
797     model = getModel (modelid);
798     if (model == nullptr)
799       return -ENOENT;
800   }
801
802   device_->setAsyncMode (mode);
803   return device_->run (NPUINPUT_HOST, model, input, cb, cb_data, sequence);
804 }
805
806 /**
807  * @brief get number of available devices
808  * @param[in] type device type
809  * @return number of devices
810  */
811 int
812 HostHandler::getNumDevices (dev_type type)
813 {
814   return DriverAPI::getNumDevices (type);
815 }
816
817 /**
818  * @brief get device instance
819  * @param[out] dev device instance
820  * @param[in] type device type
821  * @param[in] id device id
822  * @return 0 if no error. otherwise a negative errno
823  */
824 int
825 HostHandler::getDevice (npudev_h *dev, dev_type type, uint32_t id)
826 {
827   int num_devices = getNumDevices (type);
828
829   /** check the validity of device id */
830   if (!(num_devices > 0 && id < static_cast<uint32_t>(num_devices))) {
831     logerr (TAG, "Invalid arguments provided\n");
832     return -ENODEV;
833   }
834
835   Device *device = Device::createInstance (type, id);
836   if (device == nullptr) {
837     logerr (TAG, "Failed to create a device with the given type\n");
838     return -EINVAL;
839   }
840
841   *dev = device;
842   /** This is just for backward-compatility; we don't guarantee its corresness */
843   latest_dev_ = *dev;
844
845   return 0;
846 }
847
848 /**
849  * @brief allocate generic buffer (just for users)
850  * @param[out] buffer buffer instance
851  * @return 0 if no error. otherwise a negative errno
852  */
853 int
854 HostHandler::allocGenericBuffer (generic_buffer *buffer)
855 {
856   if (buffer == NULL)
857     return -EINVAL;
858
859   if (buffer->size > UINT32_MAX) {
860     logerr (TAG, "Don't support such a large size");
861     return -ENOMEM;
862   }
863
864   if (buffer->type == BUFFER_FILE) {
865     /* nothing to do */
866     if (buffer->filepath == nullptr)
867       return -EINVAL;
868   } else {
869     /* now, npu-engine always provides dmabuf-based allocation */
870     HWmem *hwmem = device_->allocMemory ();
871     if (hwmem == nullptr || hwmem->alloc (buffer->size) < 0)
872       return -ENOMEM;
873
874     buffer->dmabuf = hwmem->getDmabuf();
875     buffer->offset = hwmem->getOffset();
876     buffer->addr = hwmem->getData();
877   }
878   return 0;
879 }
880
881 /**
882  * @brief deallocate generic buffer (just for users)
883  * @param[in] buffer buffer instance
884  * @return 0 if no error. otherwise a negative errno
885  */
886 int
887 HostHandler::deallocGenericBuffer (generic_buffer *buffer)
888 {
889   if (buffer == NULL)
890     return -EINVAL;
891
892   if (buffer->type != BUFFER_FILE)
893     device_->deallocMemory (buffer->dmabuf);
894
895   return 0;
896 }
897
898 /**
899  * @brief allocate multiple generic buffers (just for users)
900  * @param[out] buffers multi-buffer instance
901  * @return 0 if no error. otherwise a negative errno
902  */
903 int
904 HostHandler::allocGenericBuffer (generic_buffers *buffers)
905 {
906   if (buffers == NULL || buffers->num_buffers < 1)
907     return -EINVAL;
908
909   buffer_types type = buffers->bufs[0].type;
910   if (type == BUFFER_FILE)
911     return 0;
912
913   uint64_t total_size = 0;
914   for (uint32_t idx = 0; idx < buffers->num_buffers; idx++)
915     total_size += buffers->bufs[idx].size;
916
917   uint64_t first_size = buffers->bufs[0].size;
918   buffers->bufs[0].size = total_size;
919   int status = allocGenericBuffer (&buffers->bufs[0]);
920   if (status != 0)
921     return status;
922
923   uint64_t offset = first_size;
924   for (uint32_t idx = 1; idx < buffers->num_buffers; idx++) {
925     buffers->bufs[idx].dmabuf = buffers->bufs[0].dmabuf;
926     buffers->bufs[idx].offset = buffers->bufs[0].offset + offset;
927     buffers->bufs[idx].type = type;
928
929     offset += buffers->bufs[idx].size;
930   }
931
932   return 0;
933 }
934
935 /**
936  * @brief deallocate multiple generic buffers (just for users)
937  * @param[in] buffers multi-buffer instance
938  * @return 0 if no error. otherwise a negative errno
939  */
940 int
941 HostHandler::deallocGenericBuffer (generic_buffers *buffers)
942 {
943   if (buffers == NULL || buffers->num_buffers < 1)
944     return -EINVAL;
945
946   return deallocGenericBuffer (&buffers->bufs[0]);
947 }
948
949 /** just for backward-compatability */
950 npudev_h HostHandler::latest_dev_ = nullptr;
951
952 /** implementation of libnpuhost APIs */
953
954 /**
955  * @brief Returns the number of available NPU devices.
956  * @return @c The number of NPU devices.
957  * @retval 0 if no NPU devices available. if positive (number of NPUs) if NPU devices available. otherwise, a negative error value.
958  * @note the caller should call putNPUdevice() to release the device handle
959  */
960 int getnumNPUdeviceByType (dev_type type)
961 {
962   return HostHandler::getNumDevices (type);
963 }
964
965 /**
966  * @brief Returns the handle of the chosen NPU devices.
967  * @param[out] dev The NPU device handle
968  * @param[in] id The NPU id to get the handle. 0 <= id < getnumNPUdeviceByType().
969  * @return @c 0 if no error. otherwise a negative error value
970  * @note the caller should call putNPUdevice() to release the device handle
971  */
972 int getNPUdeviceByType (npudev_h *dev, dev_type type, uint32_t id)
973 {
974   return HostHandler::getDevice (dev, type, id);
975 }
976
977 /**
978  * @brief Returns the handle of an NPU device meeting the condition
979  * @param[out] dev The NPU device handle
980  * @param[in] cond The condition for device search.
981  * @return @c 0 if no error. otherwise a negative error value
982  * @note the caller should call putNPUdevice() to release the device handle
983  * @note it's not supported yet
984  */
985 int getNPUdeviceByCondition(npudev_h *dev, const npucondition *cond)
986 {
987   /** not implmeneted yet */
988   return getNPUdeviceByType (dev, NPUCOND_TRIV_CONN_SOCIP, 0);
989 }
990
991 /**
992  * @brief release the NPU device instance obtained by getDevice ()
993  * @param[in] dev the NPU device handle
994  */
995 void putNPUdevice (npudev_h dev)
996 {
997   if (dev != nullptr)
998     delete static_cast<Device *> (dev);
999 }
1000
1001 /**
1002  * @brief Send the NN model to NPU.
1003  * @param[in] dev The NPU device handle
1004  * @param[in] modelfile The filepath to the compiled NPU NN model in any buffer_type
1005  * @param[out] modelid The modelid allocated for this instance of NN model.
1006  * @return @c 0 if no error. otherwise a negative error value
1007  *
1008  * @detail For ASR devices, which do not accept models, but have models
1009  *         embedded in devices, you do not need to call register and
1010  *         register calls for ASR are ignored.
1011  *
1012  * @todo Add a variation: in-memory model register.
1013  */
1014 int registerNPUmodel (npudev_h dev, generic_buffer *modelfile, uint32_t *modelid)
1015 {
1016   INIT_HOST_HANDLER (host_handler, dev);
1017
1018   return host_handler->registerModel (modelfile, modelid);
1019 }
1020
1021 /**
1022  * @brief Remove the NN model from NPU
1023  * @param[in] dev The NPU device handle
1024  * @param[in] modelid The model to be removed from the NPU.
1025  * @return @c 0 if no error. otherwise a negative error value
1026  * @detail This may incur some latency with memory compatcion.
1027  */
1028 int unregisterNPUmodel(npudev_h dev, uint32_t modelid)
1029 {
1030   INIT_HOST_HANDLER (host_handler, dev);
1031
1032   return host_handler->unregisterModel (modelid);
1033 }
1034
1035 /**
1036  * @brief Remove all NN models from NPU
1037  * @param[in] dev The NPU device handle
1038  * @return @c 0 if no error. otherwise a negative error value
1039  */
1040 int unregisterNPUmodel_all(npudev_h dev)
1041 {
1042   INIT_HOST_HANDLER (host_handler, dev);
1043
1044   return host_handler->unregisterModels ();
1045 }
1046
1047 /**
1048  * @brief [OPTIONAL] Set the data layout for input/output tensors
1049  * @param[in] dev The NPU device handle
1050  * @param[in] modelid The ID of model whose layouts are set
1051  * @param[in] info_in the layout/type info for input tensors
1052  * @param[in] info_out the layout/type info for output tensors
1053  * @return @c 0 if no error. otherwise a negative error value
1054  * @note if this function is not called, default layout/type will be used.
1055  */
1056 int setNPU_dataInfo(npudev_h dev, uint32_t modelid,
1057     tensors_data_info *info_in, tensors_data_info *info_out)
1058 {
1059   INIT_HOST_HANDLER (host_handler, dev);
1060
1061   return host_handler->setDataInfo (modelid, info_in, info_out);
1062 }
1063
1064 /**
1065  * @brief [OPTIONAL] Set the inference constraint for next NPU inferences
1066  * @param[in] dev The NPU device handle
1067  * @param[in] modelid The target model id
1068  * @param[in] constraint inference constraint (e.g., timeout, priority)
1069  * @return @c 0 if no error. otherwise a negative error value
1070  * @note If this function is not called, default values are used.
1071  */
1072 int setNPU_constraint(npudev_h dev, uint32_t modelid, npuConstraint constraint)
1073 {
1074   INIT_HOST_HANDLER (host_handler, dev);
1075
1076   return host_handler->setConstraint (modelid, constraint);
1077 }
1078
1079 /**
1080  * @brief Execute inference. Wait (block) until the output is available.
1081  * @param[in] dev The NPU device handle
1082  * @param[in] modelid The model to be inferred.
1083  * @param[in] input The input data to be inferred.
1084  * @param[out] output The output result. The caller MUST allocate appropriately before calling this.
1085  * @return @c 0 if no error. otherwise a negative error value
1086  *
1087  * @detail This is a syntactic sugar of runNPU_async().
1088  *         CAUTION: There is a memcpy for the output buffer.
1089  */
1090 int runNPU_sync(npudev_h dev, uint32_t modelid, const input_buffers *input,
1091     output_buffers *output)
1092 {
1093   INIT_HOST_HANDLER (host_handler, dev);
1094
1095   return host_handler->runSync (modelid, input, output);
1096 }
1097
1098 /**
1099  * @brief Invoke NPU inference. Unblocking call.
1100  * @param[in] dev The NPU device handle
1101  * @param[in] modelid The model to be inferred.
1102  * @param[in] input The input data to be inferred.
1103  * @param[in] cb The output buffer handler.
1104  * @param[out] sequence The sequence number returned with runNPU_async.
1105  * @param[in] data The data given as a parameter to the runNPU_async call.
1106  * @param[in] mode Configures how this operation works.
1107  * @return @c 0 if no error. otherwise a negative error value
1108  */
1109 int runNPU_async(npudev_h dev, uint32_t modelid, const input_buffers *input,
1110     npuOutputNotify cb, uint64_t *sequence, void *data,
1111     npu_async_mode mode)
1112 {
1113   INIT_HOST_HANDLER (host_handler, dev);
1114
1115   return host_handler->runAsync (modelid, input, cb, data, mode, sequence);
1116 }
1117
1118 /**
1119  * @brief Allocate a buffer for NPU model with the requested buffer type.
1120  * @param[in] dev The NPU device handle
1121  * @param[in/out] Buffer the buffer pointer where memory is allocated.
1122  * @return 0 if no error, otherwise a negative errno.
1123  */
1124 int allocNPU_modelBuffer (npudev_h dev, generic_buffer *buffer)
1125 {
1126   INIT_HOST_HANDLER (host_handler, dev);
1127
1128   return host_handler->allocGenericBuffer (buffer);
1129 }
1130
1131 /**
1132  * @brief Free the buffer and remove the address mapping.
1133  * @param[in] dev The NPU device handle
1134  * @param[in] buffer the model buffer
1135  * @return 0 if no error, otherwise a negative errno.
1136  */
1137 int cleanNPU_modelBuffer (npudev_h dev, generic_buffer *buffer)
1138 {
1139   INIT_HOST_HANDLER (host_handler, dev);
1140
1141   return host_handler->deallocGenericBuffer (buffer);
1142 }
1143
1144 /**
1145  * @brief Allocate a buffer for NPU input with the requested buffer type.
1146  * @param[in] dev The NPU device handle
1147  * @param[in/out] Buffer the buffer pointer where memory is allocated.
1148  * @return 0 if no error, otherwise a negative errno.
1149  * @note please utilize allocInputBuffers() for multiple input tensors because subsequent
1150  *       calls of allocInputBuffer() don't gurantee contiguous allocations between them.
1151  */
1152 int allocNPU_inputBuffer (npudev_h dev, generic_buffer *buffer)
1153 {
1154   INIT_HOST_HANDLER (host_handler, dev);
1155
1156   return host_handler->allocGenericBuffer (buffer);
1157 }
1158
1159 /**
1160  * @brief Free the buffer and remove the address mapping.
1161  * @param[in] dev The NPU device handle
1162  * @param[in] buffer the input buffer
1163  * @return 0 if no error, otherwise a negative errno.
1164  */
1165 int cleanNPU_inputBuffer (npudev_h dev, generic_buffer *buffer)
1166 {
1167   INIT_HOST_HANDLER (host_handler, dev);
1168
1169   return host_handler->deallocGenericBuffer (buffer);
1170 }
1171
1172 /**
1173  * @brief Allocate input buffers, which have multiple instances of generic_buffer
1174  * @param[in] dev The NPU device handle
1175  * @param[in/out] input input buffers.
1176  * @return 0 if no error, otherwise a negative errno.
1177  * @note it reuses allocInputBuffer().
1178  * @details in case of BUFFER_DMABUF, this function can be used to gurantee physically-contiguous
1179  *          memory mapping for multiple tensors (in a single inference, not batch size).
1180  */
1181 int allocNPU_inputBuffers (npudev_h dev, input_buffers * input)
1182 {
1183   INIT_HOST_HANDLER (host_handler, dev);
1184
1185   return host_handler->allocGenericBuffer (input);
1186 }
1187
1188 /**
1189  * @brief Free input buffers allocated by allocInputBuffers().
1190  * @param[in] dev The NPU device handle
1191  * @param[in/out] input input buffers.
1192  * @note it reuses cleanInputbuffer().
1193  * @return 0 if no error, otherwise a negative errno.
1194  */
1195 int cleanNPU_inputBuffers (npudev_h dev, input_buffers * input)
1196 {
1197   INIT_HOST_HANDLER (host_handler, dev);
1198
1199   return host_handler->deallocGenericBuffer (input);
1200 }
1201
1202 /**
1203  * @brief Get metadata for NPU model
1204  * @param[in] model The path of model binary file
1205  * @param[in] need_extra whether you want to extract the extra data in metadata
1206  * @return the metadata structure to be filled if no error, otherwise nullptr
1207  *
1208  * @note For most npu-engine users, the extra data is not useful because it will be
1209  *       used for second-party users (e.g., compiler, simulator).
1210  *       Also, the caller needs to free the metadata.
1211  *
1212  * @note the caller needs to free the metadata
1213  */
1214 npubin_meta * getNPUmodel_metadata (const char *model, bool need_extra)
1215 {
1216   npubin_meta *meta;
1217   FILE *fp;
1218   size_t ret;
1219
1220   if (!model)
1221     return nullptr;
1222
1223   fp = fopen (model, "rb");
1224   if (!fp) {
1225     logerr (TAG, "Failed to open the model binary: %d\n", -errno);
1226     return nullptr;
1227   }
1228
1229   meta = (npubin_meta *) malloc (NPUBIN_META_SIZE);
1230   if (!meta) {
1231     logerr (TAG, "Failed to allocate metadata\n");
1232     goto exit_err;
1233   }
1234
1235   ret = fread (meta, 1, NPUBIN_META_SIZE, fp);
1236   if (ret != NPUBIN_META_SIZE) {
1237     logerr (TAG, "Failed to read the metadata\n");
1238     goto exit_free;
1239   }
1240
1241   if (!CHECK_NPUBIN (meta->magiccode)) {
1242     logerr (TAG, "Invalid metadata provided\n");
1243     goto exit_free;
1244   }
1245
1246   if (need_extra && NPUBIN_META_EXTRA (meta->magiccode) > 0) {
1247     npubin_meta *new_meta;
1248
1249     new_meta = (npubin_meta *) realloc (meta, NPUBIN_META_TOTAL_SIZE(meta->magiccode));
1250     if (!new_meta) {
1251       logerr (TAG, "Failed to allocate extra metadata\n");
1252       goto exit_free;
1253     }
1254
1255     ret = fread (new_meta->reserved_extra, 1, NPUBIN_META_EXTRA_SIZE (meta->magiccode), fp);
1256     if (ret != NPUBIN_META_EXTRA_SIZE (meta->magiccode)) {
1257       logerr (TAG, "Invalid extra metadata provided\n");
1258       free (new_meta);
1259       goto exit_err;
1260     }
1261
1262     meta = new_meta;
1263   }
1264
1265   fclose (fp);
1266
1267   return meta;
1268
1269 exit_free:
1270   free (meta);
1271 exit_err:
1272   fclose (fp);
1273
1274   return nullptr;
1275 }
1276
1277 /** deprecated buffer APIs; please use the above APIs */
1278
1279 /**
1280  * @brief Returns the number of NPU devices (TRIV).
1281  */
1282 int getnumNPUdevice (void)
1283 {
1284   logwarn (TAG, "deprecated. Please use getnumNPUdeviceByType ()\n");
1285   return getnumNPUdeviceByType (NPUCOND_TRIV_CONN_SOCIP);
1286 }
1287
1288 /**
1289  * @brief Returns the list of ASR devices (TRIA)
1290  */
1291 int getnumASRdevice (void)
1292 {
1293   logwarn (TAG, "deprecated. Please use getnumNPUdeviceByType ()\n");
1294   return getnumNPUdeviceByType (NPUCOND_TRIA_CONN_SOCIP);
1295 }
1296
1297 /**
1298  * @brief Returns the handle of the chosen TRIV device.
1299  */
1300 int getNPUdevice (npudev_h *dev, uint32_t id)
1301 {
1302   logwarn (TAG, "deprecated. Please use getNPUdeviceByType ()\n");
1303   return getNPUdeviceByType (dev, NPUCOND_TRIV_CONN_SOCIP, id);
1304 }
1305
1306 /**
1307  * @brief Returns the handle of the chosen TRIA device.
1308  */
1309 int getASRdevice (npudev_h *dev, uint32_t id)
1310 {
1311   logwarn (TAG, "deprecated. Please use getNPUdeviceByType ()\n");
1312   return getNPUdeviceByType (dev, NPUCOND_TRIA_CONN_SOCIP, id);
1313 }
1314
1315 /** @brief deprecated */
1316 int allocModelBuffer (generic_buffer *buffer)
1317 {
1318   logwarn (TAG, "deprecated. Please use allocNPU_modelBuffer\n");
1319   return allocNPU_modelBuffer (HostHandler::getLatestDevice(), buffer);
1320 }
1321
1322 /** @brief deprecated */
1323 int cleanModelBuffer (generic_buffer *buffer)
1324 {
1325   logwarn (TAG, "deprecated. Please use cleanNPU_modelBuffer\n");
1326   return allocNPU_modelBuffer (HostHandler::getLatestDevice(), buffer);
1327 }
1328
1329 /** @brief deprecated */
1330 int allocInputBuffer (generic_buffer *buffer)
1331 {
1332   logwarn (TAG, "deprecated. Please use allocNPU_inputBuffer\n");
1333   return allocNPU_inputBuffer (HostHandler::getLatestDevice(), buffer);
1334 }
1335
1336 /** @brief deprecated */
1337 int cleanInputBuffer (generic_buffer *buffer)
1338 {
1339   logwarn (TAG, "deprecated. Please use cleanNPU_inputBuffer\n");
1340   return cleanNPU_inputBuffer (HostHandler::getLatestDevice(), buffer);
1341 }
1342
1343 /** @brief deprecated */
1344 int allocInputBuffers (input_buffers * input)
1345 {
1346   logwarn (TAG, "deprecated. Please use allocNPU_inputBuffers\n");
1347   return allocNPU_inputBuffers (HostHandler::getLatestDevice(), input);
1348 }
1349
1350 /** @brief deprecated */
1351 int cleanInputBuffers (input_buffers * input)
1352 {
1353   logwarn (TAG, "deprecated. Please use cleanNPU_inputBuffers\n");
1354   return cleanNPU_inputBuffers (HostHandler::getLatestDevice(), input);
1355 }
1356
1357 /** @brief deprecated */
1358 int allocNPUBuffer (uint64_t size, buffer_types type,
1359     const char * filepath, generic_buffer *buffer)
1360 {
1361   if (buffer) {
1362     buffer->size = size;
1363     buffer->type = type;
1364     buffer->filepath = filepath;
1365   }
1366
1367   logwarn (TAG, "deprecated. Please use allocNPU_* APIs\n");
1368   return allocModelBuffer (buffer);
1369 }
1370
1371 /** @brief deprecated */
1372 int cleanNPUBuffer (generic_buffer * buffer)
1373 {
1374   logwarn (TAG, "deprecated. Please use cleanNPU_* APIs\n");
1375   return cleanModelBuffer (buffer);
1376 }