[Svace] Fix svace defects reported by ahub service
[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 {
622 }
623
624 /** @brief host handler destructor */
625 HostHandler::~HostHandler ()
626 {
627 }
628
629 /**
630  * @brief register model from generic buffer
631  * @param[in] model_buf model buffer
632  * @param[out] modelid model id
633  * @return 0 if no error. otherwise a negative errno
634  */
635 int
636 HostHandler::registerModel (generic_buffer *model_buf, uint32_t *modelid)
637 {
638   Model *model = device_->registerModel (model_buf);
639   if (model == nullptr) {
640     logerr (TAG, "Failed to register model\n");
641     return -EINVAL;
642   }
643
644   int status = models_.insert (model->getID(), model);
645   if (status != 0) {
646     logerr (TAG, "Failed to insert model id\n");
647     delete model;
648     return status;
649   }
650
651   *modelid = model->getID();
652   return 0;
653 }
654
655 /**
656  * @brief remove the registered model
657  * @param[in] modelid model id
658  * @return 0 if no error. otherwise a negative errno
659  */
660 int
661 HostHandler::unregisterModel (uint32_t modelid)
662 {
663   return models_.remove (modelid);
664 }
665
666 /**
667  * @brief remove all registered models
668  * @return 0
669  */
670 int
671 HostHandler::unregisterModels ()
672 {
673   models_.clear ();
674   return 0;
675 }
676
677 /**
678  * @brief Set the data layout for input/output tensors
679  * @param[in] modelid The ID of model whose layouts are set
680  * @param[in] in the layout/type info for input tensors
681  * @param[in] out the layout/type info for output tensors
682  * @return @c 0 if no error. otherwise a negative error value
683  * @note if this function is not called, default layout/type will be used.
684  */
685 int
686 HostHandler::setDataInfo (uint32_t modelid, tensors_data_info *in,
687     tensors_data_info *out)
688 {
689   Model *model = models_.find (modelid);
690   if (model == nullptr)
691     return -ENOENT;
692
693   model->setDataInfo (in, out);
694
695   return 0;
696 }
697
698 /**
699  * @brief Set the inference constraint for next NPU inferences
700  * @param[in] modelid The target model id
701  * @param[in] constraint inference constraint (e.g., timeout, priority)
702  * @return @c 0 if no error. otherwise a negative error value
703  * @note If this function is not called, default values are used.
704  */
705 int
706 HostHandler::setConstraint (uint32_t modelid, npuConstraint constraint)
707 {
708   Model *model = models_.find (modelid);
709   if (model == nullptr)
710     return -ENOENT;
711
712   model->setConstraint (constraint);
713
714   return 0;
715 }
716
717 /**
718  * @brief find and return model instance
719  * @param[in] modelid model id
720  * @return model instance if found. otherwise nullptr
721  */
722 Model *
723 HostHandler::getModel (uint32_t modelid)
724 {
725   return models_.find (modelid);
726 }
727
728 /** @brief dummay callback for runSync. */
729 class callbackSync {
730   public:
731     callbackSync (output_buffers *output) : output_(output), done_(false) {}
732
733     static void callback (output_buffers *output, uint64_t sequence, void *data) {
734       callbackSync *sync = static_cast<callbackSync *>(data);
735       sync->callback (output, sequence);
736     }
737
738     void callback (output_buffers *output, uint64_t sequence) {
739       /** just copy internal variables of output buffers */
740       memcpy (output_, output, sizeof (output_buffers));
741       done_ = true;
742       cv_.notify_one ();
743     }
744
745     void wait () {
746       std::unique_lock<std::mutex> lock (m_);
747       cv_.wait (lock, [this]() { return done_; });
748     }
749
750   private:
751     std::mutex m_;
752     std::condition_variable cv_;
753     output_buffers *output_;
754     bool done_;
755 };
756
757 /**
758  * @brief Execute inference. Wait (block) until the output is available.
759  * @param[in] modelid The model to be inferred.
760  * @param[in] input The input data to be inferred.
761  * @param[out] output The output result.
762  * @return @c 0 if no error. otherwise a negative error value
763  */
764 int
765 HostHandler::runSync (uint32_t modelid, const input_buffers *input,
766     output_buffers *output)
767 {
768   callbackSync sync (output);
769   int status = runAsync (modelid, input, callbackSync::callback,
770       static_cast <void*> (&sync), NPUASYNC_DROP_OLD, nullptr);
771   if (status == 0) {
772     /** sync needs to wait callback */
773     sync.wait ();
774   }
775   return status;
776 }
777
778 /**
779  * @brief Invoke NPU inference. Unblocking call.
780  * @param[in] modelid The model to be inferred.
781  * @param[in] input The input data to be inferred.
782  * @param[in] cb The output buffer handler.
783  * @param[in] cb_data The data given as a parameter to the runNPU_async call.
784  * @param[in] mode Configures how this operation works.
785  * @param[out] sequence The sequence number returned with runNPU_async.
786  * @return @c 0 if no error. otherwise a negative error value
787  */
788 int
789 HostHandler::runAsync (uint32_t modelid, const input_buffers *input,
790     npuOutputNotify cb, void *cb_data, npu_async_mode mode, uint64_t *sequence)
791 {
792   Model *model = nullptr;
793
794   if (device_->needModel()) {
795     model = getModel (modelid);
796     if (model == nullptr)
797       return -ENOENT;
798   }
799
800   device_->setAsyncMode (mode);
801   return device_->run (NPUINPUT_HOST, model, input, cb, cb_data, sequence);
802 }
803
804 /**
805  * @brief get number of available devices
806  * @param[in] type device type
807  * @return number of devices
808  */
809 int
810 HostHandler::getNumDevices (dev_type type)
811 {
812   return DriverAPI::getNumDevices (type);
813 }
814
815 /**
816  * @brief get device instance
817  * @param[out] dev device instance
818  * @param[in] type device type
819  * @param[in] id device id
820  * @return 0 if no error. otherwise a negative errno
821  */
822 int
823 HostHandler::getDevice (npudev_h *dev, dev_type type, uint32_t id)
824 {
825   int num_devices = getNumDevices (type);
826
827   /** check the validity of device id */
828   if (!(num_devices > 0 && id < static_cast<uint32_t>(num_devices))) {
829     logerr (TAG, "Invalid arguments provided\n");
830     return -ENODEV;
831   }
832
833   Device *device = Device::createInstance (type, id);
834   if (device == nullptr) {
835     logerr (TAG, "Failed to create a device with the given type\n");
836     return -EINVAL;
837   }
838
839   *dev = device;
840   /** This is just for backward-compatility; we don't guarantee its corresness */
841   latest_dev_ = *dev;
842
843   return 0;
844 }
845
846 /**
847  * @brief allocate generic buffer (just for users)
848  * @param[out] buffer buffer instance
849  * @return 0 if no error. otherwise a negative errno
850  */
851 int
852 HostHandler::allocGenericBuffer (generic_buffer *buffer)
853 {
854   if (buffer == NULL)
855     return -EINVAL;
856
857   if (buffer->size > UINT32_MAX) {
858     logerr (TAG, "Don't support such a large size");
859     return -ENOMEM;
860   }
861
862   if (buffer->type == BUFFER_FILE) {
863     /* nothing to do */
864     if (buffer->filepath == nullptr)
865       return -EINVAL;
866   } else {
867     /* now, npu-engine always provides dmabuf-based allocation */
868     HWmem *hwmem = device_->allocMemory ();
869     if (hwmem == nullptr || hwmem->alloc (buffer->size) < 0)
870       return -ENOMEM;
871
872     buffer->dmabuf = hwmem->getDmabuf();
873     buffer->offset = hwmem->getOffset();
874     buffer->addr = hwmem->getData();
875   }
876   return 0;
877 }
878
879 /**
880  * @brief deallocate generic buffer (just for users)
881  * @param[in] buffer buffer instance
882  * @return 0 if no error. otherwise a negative errno
883  */
884 int
885 HostHandler::deallocGenericBuffer (generic_buffer *buffer)
886 {
887   if (buffer == NULL)
888     return -EINVAL;
889
890   if (buffer->type != BUFFER_FILE)
891     device_->deallocMemory (buffer->dmabuf);
892
893   return 0;
894 }
895
896 /**
897  * @brief allocate multiple generic buffers (just for users)
898  * @param[out] buffers multi-buffer instance
899  * @return 0 if no error. otherwise a negative errno
900  */
901 int
902 HostHandler::allocGenericBuffer (generic_buffers *buffers)
903 {
904   if (buffers == NULL || buffers->num_buffers < 1)
905     return -EINVAL;
906
907   buffer_types type = buffers->bufs[0].type;
908   if (type == BUFFER_FILE)
909     return 0;
910
911   uint64_t total_size = 0;
912   for (uint32_t idx = 0; idx < buffers->num_buffers; idx++)
913     total_size += buffers->bufs[idx].size;
914
915   uint64_t first_size = buffers->bufs[0].size;
916   buffers->bufs[0].size = total_size;
917   int status = allocGenericBuffer (&buffers->bufs[0]);
918   if (status != 0)
919     return status;
920
921   uint64_t offset = first_size;
922   for (uint32_t idx = 1; idx < buffers->num_buffers; idx++) {
923     buffers->bufs[idx].dmabuf = buffers->bufs[0].dmabuf;
924     buffers->bufs[idx].offset = buffers->bufs[0].offset + offset;
925     buffers->bufs[idx].type = type;
926
927     offset += buffers->bufs[idx].size;
928   }
929
930   return 0;
931 }
932
933 /**
934  * @brief deallocate multiple generic buffers (just for users)
935  * @param[in] buffers multi-buffer instance
936  * @return 0 if no error. otherwise a negative errno
937  */
938 int
939 HostHandler::deallocGenericBuffer (generic_buffers *buffers)
940 {
941   if (buffers == NULL || buffers->num_buffers < 1)
942     return -EINVAL;
943
944   return deallocGenericBuffer (&buffers->bufs[0]);
945 }
946
947 /** just for backward-compatability */
948 npudev_h HostHandler::latest_dev_ = nullptr;
949
950 /** implementation of libnpuhost APIs */
951
952 /**
953  * @brief Returns the number of available NPU devices.
954  * @return @c The number of NPU devices.
955  * @retval 0 if no NPU devices available. if positive (number of NPUs) if NPU devices available. otherwise, a negative error value.
956  * @note the caller should call putNPUdevice() to release the device handle
957  */
958 int getnumNPUdeviceByType (dev_type type)
959 {
960   return HostHandler::getNumDevices (type);
961 }
962
963 /**
964  * @brief Returns the handle of the chosen NPU devices.
965  * @param[out] dev The NPU device handle
966  * @param[in] id The NPU id to get the handle. 0 <= id < getnumNPUdeviceByType().
967  * @return @c 0 if no error. otherwise a negative error value
968  * @note the caller should call putNPUdevice() to release the device handle
969  */
970 int getNPUdeviceByType (npudev_h *dev, dev_type type, uint32_t id)
971 {
972   return HostHandler::getDevice (dev, type, id);
973 }
974
975 /**
976  * @brief Returns the handle of an NPU device meeting the condition
977  * @param[out] dev The NPU device handle
978  * @param[in] cond The condition for device search.
979  * @return @c 0 if no error. otherwise a negative error value
980  * @note the caller should call putNPUdevice() to release the device handle
981  * @note it's not supported yet
982  */
983 int getNPUdeviceByCondition(npudev_h *dev, const npucondition *cond)
984 {
985   /** not implmeneted yet */
986   return getNPUdeviceByType (dev, NPUCOND_TRIV_CONN_SOCIP, 0);
987 }
988
989 /**
990  * @brief release the NPU device instance obtained by getDevice ()
991  * @param[in] dev the NPU device handle
992  */
993 void putNPUdevice (npudev_h dev)
994 {
995   if (dev != nullptr)
996     delete static_cast<Device *> (dev);
997 }
998
999 /**
1000  * @brief Send the NN model to NPU.
1001  * @param[in] dev The NPU device handle
1002  * @param[in] modelfile The filepath to the compiled NPU NN model in any buffer_type
1003  * @param[out] modelid The modelid allocated for this instance of NN model.
1004  * @return @c 0 if no error. otherwise a negative error value
1005  *
1006  * @detail For ASR devices, which do not accept models, but have models
1007  *         embedded in devices, you do not need to call register and
1008  *         register calls for ASR are ignored.
1009  *
1010  * @todo Add a variation: in-memory model register.
1011  */
1012 int registerNPUmodel (npudev_h dev, generic_buffer *modelfile, uint32_t *modelid)
1013 {
1014   INIT_HOST_HANDLER (host_handler, dev);
1015
1016   return host_handler->registerModel (modelfile, modelid);
1017 }
1018
1019 /**
1020  * @brief Remove the NN model from NPU
1021  * @param[in] dev The NPU device handle
1022  * @param[in] modelid The model to be removed from the NPU.
1023  * @return @c 0 if no error. otherwise a negative error value
1024  * @detail This may incur some latency with memory compatcion.
1025  */
1026 int unregisterNPUmodel(npudev_h dev, uint32_t modelid)
1027 {
1028   INIT_HOST_HANDLER (host_handler, dev);
1029
1030   return host_handler->unregisterModel (modelid);
1031 }
1032
1033 /**
1034  * @brief Remove all NN models from NPU
1035  * @param[in] dev The NPU device handle
1036  * @return @c 0 if no error. otherwise a negative error value
1037  */
1038 int unregisterNPUmodel_all(npudev_h dev)
1039 {
1040   INIT_HOST_HANDLER (host_handler, dev);
1041
1042   return host_handler->unregisterModels ();
1043 }
1044
1045 /**
1046  * @brief [OPTIONAL] Set the data layout for input/output tensors
1047  * @param[in] dev The NPU device handle
1048  * @param[in] modelid The ID of model whose layouts are set
1049  * @param[in] info_in the layout/type info for input tensors
1050  * @param[in] info_out the layout/type info for output tensors
1051  * @return @c 0 if no error. otherwise a negative error value
1052  * @note if this function is not called, default layout/type will be used.
1053  */
1054 int setNPU_dataInfo(npudev_h dev, uint32_t modelid,
1055     tensors_data_info *info_in, tensors_data_info *info_out)
1056 {
1057   INIT_HOST_HANDLER (host_handler, dev);
1058
1059   return host_handler->setDataInfo (modelid, info_in, info_out);
1060 }
1061
1062 /**
1063  * @brief [OPTIONAL] Set the inference constraint for next NPU inferences
1064  * @param[in] dev The NPU device handle
1065  * @param[in] modelid The target model id
1066  * @param[in] constraint inference constraint (e.g., timeout, priority)
1067  * @return @c 0 if no error. otherwise a negative error value
1068  * @note If this function is not called, default values are used.
1069  */
1070 int setNPU_constraint(npudev_h dev, uint32_t modelid, npuConstraint constraint)
1071 {
1072   INIT_HOST_HANDLER (host_handler, dev);
1073
1074   return host_handler->setConstraint (modelid, constraint);
1075 }
1076
1077 /**
1078  * @brief Execute inference. Wait (block) until the output is available.
1079  * @param[in] dev The NPU device handle
1080  * @param[in] modelid The model to be inferred.
1081  * @param[in] input The input data to be inferred.
1082  * @param[out] output The output result. The caller MUST allocate appropriately before calling this.
1083  * @return @c 0 if no error. otherwise a negative error value
1084  *
1085  * @detail This is a syntactic sugar of runNPU_async().
1086  *         CAUTION: There is a memcpy for the output buffer.
1087  */
1088 int runNPU_sync(npudev_h dev, uint32_t modelid, const input_buffers *input,
1089     output_buffers *output)
1090 {
1091   INIT_HOST_HANDLER (host_handler, dev);
1092
1093   return host_handler->runSync (modelid, input, output);
1094 }
1095
1096 /**
1097  * @brief Invoke NPU inference. Unblocking call.
1098  * @param[in] dev The NPU device handle
1099  * @param[in] modelid The model to be inferred.
1100  * @param[in] input The input data to be inferred.
1101  * @param[in] cb The output buffer handler.
1102  * @param[out] sequence The sequence number returned with runNPU_async.
1103  * @param[in] data The data given as a parameter to the runNPU_async call.
1104  * @param[in] mode Configures how this operation works.
1105  * @return @c 0 if no error. otherwise a negative error value
1106  */
1107 int runNPU_async(npudev_h dev, uint32_t modelid, const input_buffers *input,
1108     npuOutputNotify cb, uint64_t *sequence, void *data,
1109     npu_async_mode mode)
1110 {
1111   INIT_HOST_HANDLER (host_handler, dev);
1112
1113   return host_handler->runAsync (modelid, input, cb, data, mode, sequence);
1114 }
1115
1116 /**
1117  * @brief Allocate a buffer for NPU model with the requested buffer type.
1118  * @param[in] dev The NPU device handle
1119  * @param[in/out] Buffer the buffer pointer where memory is allocated.
1120  * @return 0 if no error, otherwise a negative errno.
1121  */
1122 int allocNPU_modelBuffer (npudev_h dev, generic_buffer *buffer)
1123 {
1124   INIT_HOST_HANDLER (host_handler, dev);
1125
1126   return host_handler->allocGenericBuffer (buffer);
1127 }
1128
1129 /**
1130  * @brief Free the buffer and remove the address mapping.
1131  * @param[in] dev The NPU device handle
1132  * @param[in] buffer the model buffer
1133  * @return 0 if no error, otherwise a negative errno.
1134  */
1135 int cleanNPU_modelBuffer (npudev_h dev, generic_buffer *buffer)
1136 {
1137   INIT_HOST_HANDLER (host_handler, dev);
1138
1139   return host_handler->deallocGenericBuffer (buffer);
1140 }
1141
1142 /**
1143  * @brief Allocate a buffer for NPU input with the requested buffer type.
1144  * @param[in] dev The NPU device handle
1145  * @param[in/out] Buffer the buffer pointer where memory is allocated.
1146  * @return 0 if no error, otherwise a negative errno.
1147  * @note please utilize allocInputBuffers() for multiple input tensors because subsequent
1148  *       calls of allocInputBuffer() don't gurantee contiguous allocations between them.
1149  */
1150 int allocNPU_inputBuffer (npudev_h dev, generic_buffer *buffer)
1151 {
1152   INIT_HOST_HANDLER (host_handler, dev);
1153
1154   return host_handler->allocGenericBuffer (buffer);
1155 }
1156
1157 /**
1158  * @brief Free the buffer and remove the address mapping.
1159  * @param[in] dev The NPU device handle
1160  * @param[in] buffer the input buffer
1161  * @return 0 if no error, otherwise a negative errno.
1162  */
1163 int cleanNPU_inputBuffer (npudev_h dev, generic_buffer *buffer)
1164 {
1165   INIT_HOST_HANDLER (host_handler, dev);
1166
1167   return host_handler->deallocGenericBuffer (buffer);
1168 }
1169
1170 /**
1171  * @brief Allocate input buffers, which have multiple instances of generic_buffer
1172  * @param[in] dev The NPU device handle
1173  * @param[in/out] input input buffers.
1174  * @return 0 if no error, otherwise a negative errno.
1175  * @note it reuses allocInputBuffer().
1176  * @details in case of BUFFER_DMABUF, this function can be used to gurantee physically-contiguous
1177  *          memory mapping for multiple tensors (in a single inference, not batch size).
1178  */
1179 int allocNPU_inputBuffers (npudev_h dev, input_buffers * input)
1180 {
1181   INIT_HOST_HANDLER (host_handler, dev);
1182
1183   return host_handler->allocGenericBuffer (input);
1184 }
1185
1186 /**
1187  * @brief Free input buffers allocated by allocInputBuffers().
1188  * @param[in] dev The NPU device handle
1189  * @param[in/out] input input buffers.
1190  * @note it reuses cleanInputbuffer().
1191  * @return 0 if no error, otherwise a negative errno.
1192  */
1193 int cleanNPU_inputBuffers (npudev_h dev, input_buffers * input)
1194 {
1195   INIT_HOST_HANDLER (host_handler, dev);
1196
1197   return host_handler->deallocGenericBuffer (input);
1198 }
1199
1200 /**
1201  * @brief Get metadata for NPU model
1202  * @param[in] model The path of model binary file
1203  * @param[in] need_extra whether you want to extract the extra data in metadata
1204  * @return the metadata structure to be filled if no error, otherwise nullptr
1205  *
1206  * @note For most npu-engine users, the extra data is not useful because it will be
1207  *       used for second-party users (e.g., compiler, simulator).
1208  *       Also, the caller needs to free the metadata.
1209  *
1210  * @note the caller needs to free the metadata
1211  */
1212 npubin_meta * getNPUmodel_metadata (const char *model, bool need_extra)
1213 {
1214   npubin_meta *meta;
1215   FILE *fp;
1216   size_t ret;
1217
1218   if (!model)
1219     return nullptr;
1220
1221   fp = fopen (model, "rb");
1222   if (!fp) {
1223     logerr (TAG, "Failed to open the model binary: %d\n", -errno);
1224     return nullptr;
1225   }
1226
1227   meta = (npubin_meta *) malloc (NPUBIN_META_SIZE);
1228   if (!meta) {
1229     logerr (TAG, "Failed to allocate metadata\n");
1230     goto exit_err;
1231   }
1232
1233   ret = fread (meta, 1, NPUBIN_META_SIZE, fp);
1234   if (ret != NPUBIN_META_SIZE) {
1235     logerr (TAG, "Failed to read the metadata\n");
1236     goto exit_free;
1237   }
1238
1239   if (!CHECK_NPUBIN (meta->magiccode)) {
1240     logerr (TAG, "Invalid metadata provided\n");
1241     goto exit_free;
1242   }
1243
1244   if (need_extra && NPUBIN_META_EXTRA (meta->magiccode) > 0) {
1245     npubin_meta *new_meta;
1246
1247     new_meta = (npubin_meta *) realloc (meta, NPUBIN_META_TOTAL_SIZE(meta->magiccode));
1248     if (!new_meta) {
1249       logerr (TAG, "Failed to allocate extra metadata\n");
1250       goto exit_free;
1251     }
1252
1253     ret = fread (new_meta->reserved_extra, 1, NPUBIN_META_EXTRA_SIZE (meta->magiccode), fp);
1254     if (ret != NPUBIN_META_EXTRA_SIZE (meta->magiccode)) {
1255       logerr (TAG, "Invalid extra metadata provided\n");
1256       free (new_meta);
1257       goto exit_err;
1258     }
1259
1260     meta = new_meta;
1261   }
1262
1263   fclose (fp);
1264
1265   return meta;
1266
1267 exit_free:
1268   free (meta);
1269 exit_err:
1270   fclose (fp);
1271
1272   return nullptr;
1273 }
1274
1275 /** deprecated buffer APIs; please use the above APIs */
1276
1277 /**
1278  * @brief Returns the number of NPU devices (TRIV).
1279  */
1280 int getnumNPUdevice (void)
1281 {
1282   logwarn (TAG, "deprecated. Please use getnumNPUdeviceByType ()\n");
1283   return getnumNPUdeviceByType (NPUCOND_TRIV_CONN_SOCIP);
1284 }
1285
1286 /**
1287  * @brief Returns the list of ASR devices (TRIA)
1288  */
1289 int getnumASRdevice (void)
1290 {
1291   logwarn (TAG, "deprecated. Please use getnumNPUdeviceByType ()\n");
1292   return getnumNPUdeviceByType (NPUCOND_TRIA_CONN_SOCIP);
1293 }
1294
1295 /**
1296  * @brief Returns the handle of the chosen TRIV device.
1297  */
1298 int getNPUdevice (npudev_h *dev, uint32_t id)
1299 {
1300   logwarn (TAG, "deprecated. Please use getNPUdeviceByType ()\n");
1301   return getNPUdeviceByType (dev, NPUCOND_TRIV_CONN_SOCIP, id);
1302 }
1303
1304 /**
1305  * @brief Returns the handle of the chosen TRIA device.
1306  */
1307 int getASRdevice (npudev_h *dev, uint32_t id)
1308 {
1309   logwarn (TAG, "deprecated. Please use getNPUdeviceByType ()\n");
1310   return getNPUdeviceByType (dev, NPUCOND_TRIA_CONN_SOCIP, id);
1311 }
1312
1313 /** @brief deprecated */
1314 int allocModelBuffer (generic_buffer *buffer)
1315 {
1316   logwarn (TAG, "deprecated. Please use allocNPU_modelBuffer\n");
1317   return allocNPU_modelBuffer (HostHandler::getLatestDevice(), buffer);
1318 }
1319
1320 /** @brief deprecated */
1321 int cleanModelBuffer (generic_buffer *buffer)
1322 {
1323   logwarn (TAG, "deprecated. Please use cleanNPU_modelBuffer\n");
1324   return allocNPU_modelBuffer (HostHandler::getLatestDevice(), buffer);
1325 }
1326
1327 /** @brief deprecated */
1328 int allocInputBuffer (generic_buffer *buffer)
1329 {
1330   logwarn (TAG, "deprecated. Please use allocNPU_inputBuffer\n");
1331   return allocNPU_inputBuffer (HostHandler::getLatestDevice(), buffer);
1332 }
1333
1334 /** @brief deprecated */
1335 int cleanInputBuffer (generic_buffer *buffer)
1336 {
1337   logwarn (TAG, "deprecated. Please use cleanNPU_inputBuffer\n");
1338   return cleanNPU_inputBuffer (HostHandler::getLatestDevice(), buffer);
1339 }
1340
1341 /** @brief deprecated */
1342 int allocInputBuffers (input_buffers * input)
1343 {
1344   logwarn (TAG, "deprecated. Please use allocNPU_inputBuffers\n");
1345   return allocNPU_inputBuffers (HostHandler::getLatestDevice(), input);
1346 }
1347
1348 /** @brief deprecated */
1349 int cleanInputBuffers (input_buffers * input)
1350 {
1351   logwarn (TAG, "deprecated. Please use cleanNPU_inputBuffers\n");
1352   return cleanNPU_inputBuffers (HostHandler::getLatestDevice(), input);
1353 }
1354
1355 /** @brief deprecated */
1356 int allocNPUBuffer (uint64_t size, buffer_types type,
1357     const char * filepath, generic_buffer *buffer)
1358 {
1359   if (buffer) {
1360     buffer->size = size;
1361     buffer->type = type;
1362     buffer->filepath = filepath;
1363   }
1364
1365   logwarn (TAG, "deprecated. Please use allocNPU_* APIs\n");
1366   return allocModelBuffer (buffer);
1367 }
1368
1369 /** @brief deprecated */
1370 int cleanNPUBuffer (generic_buffer * buffer)
1371 {
1372   logwarn (TAG, "deprecated. Please use cleanNPU_* APIs\n");
1373   return cleanModelBuffer (buffer);
1374 }