1 /*
2  * Copyright (c) 2019-2022 Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Licensed under the Apache License, Version 2.0 (the License); you may
7  * not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
14  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 /******************************************************************************
20  * Includes
21  ******************************************************************************/
22 
23 #include "ethosu_driver.h"
24 #include "ethosu_device.h"
25 #include "ethosu_log.h"
26 
27 #ifdef ETHOSU55
28 #include "ethosu_config_u55.h"
29 #else
30 #include "ethosu_config_u65.h"
31 #endif
32 
33 #include <assert.h>
34 #include <cmsis_compiler.h>
35 #include <inttypes.h>
36 #include <stdbool.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 
41 /******************************************************************************
42  * Defines
43  ******************************************************************************/
44 
45 #define UNUSED(x) ((void)x)
46 
47 #define BYTES_IN_32_BITS 4
48 #define MASK_16_BYTE_ALIGN (0xF)
49 #define OPTIMIZER_CONFIG_LENGTH_32_BIT_WORD 2
50 #define DRIVER_ACTION_LENGTH_32_BIT_WORD 1
51 #define ETHOSU_FOURCC ('1' << 24 | 'P' << 16 | 'O' << 8 | 'C') // "Custom Operator Payload 1"
52 
53 #define FAST_MEMORY_BASE_ADDR_INDEX 2
54 
55 /******************************************************************************
56  * Types
57  ******************************************************************************/
58 
59 // Driver actions
60 enum DRIVER_ACTION_e
61 {
62     RESERVED         = 0,
63     OPTIMIZER_CONFIG = 1,
64     COMMAND_STREAM   = 2,
65     NOP              = 5,
66 };
67 
68 // Custom operator payload data struct
69 struct cop_data_s
70 {
71     union
72     {
73         // Driver action data
74         struct
75         {
76             uint8_t driver_action_command; // (valid values in DRIVER_ACTION_e)
77             uint8_t reserved;
78 
79             // Driver action data
80             union
81             {
82                 // DA_CMD_OPT_CFG
83                 struct
84                 {
85                     uint16_t rel_nbr : 4;
86                     uint16_t patch_nbr : 4;
87                     uint16_t opt_cfg_reserved : 8;
88                 };
89 
90                 // DA_CMD_CMSTRM
91                 struct
92                 {
93                     uint16_t length;
94                 };
95 
96                 uint16_t driver_action_data;
97             };
98         };
99 
100         uint32_t word;
101     };
102 };
103 
104 // optimizer config struct
105 struct opt_cfg_s
106 {
107     struct cop_data_s da_data;
108     uint32_t cfg;
109     uint32_t id;
110 };
111 
112 /******************************************************************************
113  * Variables
114  ******************************************************************************/
115 
116 // Registered drivers linked list HEAD
117 static struct ethosu_driver *registered_drivers = NULL;
118 
119 /******************************************************************************
120  * Weak functions - Cache
121  *
122  * Default NOP operations. Override if available on the targeted device.
123  ******************************************************************************/
124 
125 /*
126  * Flush/clean the data cache by address and size. Passing NULL as p argument
127  * expects the whole cache to be flushed.
128  */
ethosu_flush_dcache(uint32_t * p,size_t bytes)129 void __attribute__((weak)) ethosu_flush_dcache(uint32_t *p, size_t bytes)
130 {
131     UNUSED(p);
132     UNUSED(bytes);
133 }
134 
135 /*
136  * Invalidate the data cache by address and size. Passing NULL as p argument
137  * expects the whole cache to be invalidated.
138  */
ethosu_invalidate_dcache(uint32_t * p,size_t bytes)139 void __attribute__((weak)) ethosu_invalidate_dcache(uint32_t *p, size_t bytes)
140 {
141     UNUSED(p);
142     UNUSED(bytes);
143 }
144 
145 /******************************************************************************
146  * Weak functions - Semaphore/Mutex for multi NPU
147  *
148  * Following section handles the minimal sempahore and mutex implementation in
149  * case of baremetal applications. Weak symbols will be overridden by RTOS
150  * definitions and implement true thread-safety (in application layer).
151  ******************************************************************************/
152 
153 struct ethosu_semaphore_t
154 {
155     uint8_t count;
156 };
157 
158 static void *ethosu_mutex;
159 static void *ethosu_semaphore;
160 
ethosu_mutex_create(void)161 void *__attribute__((weak)) ethosu_mutex_create(void)
162 {
163     return NULL;
164 }
165 
ethosu_mutex_destroy(void * mutex)166 void __attribute__((weak)) ethosu_mutex_destroy(void *mutex)
167 {
168     UNUSED(mutex);
169 }
170 
ethosu_mutex_lock(void * mutex)171 int __attribute__((weak)) ethosu_mutex_lock(void *mutex)
172 {
173     UNUSED(mutex);
174     return 0;
175 }
176 
ethosu_mutex_unlock(void * mutex)177 int __attribute__((weak)) ethosu_mutex_unlock(void *mutex)
178 {
179     UNUSED(mutex);
180     return 0;
181 }
182 
183 // Baremetal implementation of creating a semaphore
ethosu_semaphore_create(void)184 void *__attribute__((weak)) ethosu_semaphore_create(void)
185 {
186     struct ethosu_semaphore_t *sem = malloc(sizeof(*sem));
187     sem->count                     = 0;
188     return sem;
189 }
190 
ethosu_semaphore_destroy(void * sem)191 void __attribute__((weak)) ethosu_semaphore_destroy(void *sem)
192 {
193     free((struct ethosu_semaphore_t *)sem);
194 }
195 
196 // Baremetal simulation of waiting/sleeping for and then taking a semaphore using intrisics
ethosu_semaphore_take(void * sem)197 int __attribute__((weak)) ethosu_semaphore_take(void *sem)
198 {
199     struct ethosu_semaphore_t *s = sem;
200     while (s->count == 0)
201     {
202         __WFE();
203     }
204     s->count = 0;
205     return 0;
206 }
207 
208 // Baremetal simulation of giving a semaphore and waking up processes using intrinsics
ethosu_semaphore_give(void * sem)209 int __attribute__((weak)) ethosu_semaphore_give(void *sem)
210 {
211     struct ethosu_semaphore_t *s = sem;
212     s->count                     = 1;
213     __SEV();
214     return 0;
215 }
216 
217 /******************************************************************************
218  * Weak functions - Inference begin/end callbacks
219  ******************************************************************************/
220 
ethosu_inference_begin(struct ethosu_driver * drv,void * user_arg)221 void __attribute__((weak)) ethosu_inference_begin(struct ethosu_driver *drv, void *user_arg)
222 {
223     UNUSED(user_arg);
224     UNUSED(drv);
225 }
226 
ethosu_inference_end(struct ethosu_driver * drv,void * user_arg)227 void __attribute__((weak)) ethosu_inference_end(struct ethosu_driver *drv, void *user_arg)
228 {
229     UNUSED(user_arg);
230     UNUSED(drv);
231 }
232 
233 /******************************************************************************
234  * Static functions
235  ******************************************************************************/
ethosu_register_driver(struct ethosu_driver * drv)236 static void ethosu_register_driver(struct ethosu_driver *drv)
237 {
238     // Register driver as new HEAD of list
239     drv->next          = registered_drivers;
240     registered_drivers = drv;
241 
242     LOG_INFO("New NPU driver registered (handle: 0x%p, NPU: 0x%p)", drv, drv->dev->reg);
243 }
244 
ethosu_deregister_driver(struct ethosu_driver * drv)245 static int ethosu_deregister_driver(struct ethosu_driver *drv)
246 {
247     struct ethosu_driver *cur   = registered_drivers;
248     struct ethosu_driver **prev = &registered_drivers;
249 
250     while (cur != NULL)
251     {
252         if (cur == drv)
253         {
254             *prev = cur->next;
255             LOG_INFO("NPU driver handle %p deregistered.", drv);
256             return 0;
257         }
258 
259         prev = &cur->next;
260         cur  = cur->next;
261     }
262 
263     LOG_ERR("No NPU driver handle registered at address %p.", drv);
264 
265     return -1;
266 }
267 
ethosu_find_and_reserve_driver(void)268 static struct ethosu_driver *ethosu_find_and_reserve_driver(void)
269 {
270     struct ethosu_driver *drv = registered_drivers;
271 
272     while (drv != NULL)
273     {
274         if (!drv->reserved)
275         {
276             drv->reserved = true;
277             LOG_DEBUG("NPU driver handle %p reserved.", drv);
278             return drv;
279         }
280         drv = drv->next;
281     }
282 
283     LOG_WARN("No NPU driver handle available.");
284 
285     return NULL;
286 }
287 
ethosu_reset_job(struct ethosu_driver * drv)288 static void ethosu_reset_job(struct ethosu_driver *drv)
289 {
290     memset(&drv->job, 0, sizeof(struct ethosu_job));
291 }
292 
handle_optimizer_config(struct ethosu_driver * drv,struct opt_cfg_s * opt_cfg_p)293 static int handle_optimizer_config(struct ethosu_driver *drv, struct opt_cfg_s *opt_cfg_p)
294 {
295     LOG_INFO("Optimizer release nbr: %d patch: %d", opt_cfg_p->da_data.rel_nbr, opt_cfg_p->da_data.patch_nbr);
296 
297     if (ethosu_dev_verify_optimizer_config(drv->dev, opt_cfg_p->cfg, opt_cfg_p->id) != true)
298     {
299         return -1;
300     }
301 
302     return 0;
303 }
304 
handle_command_stream(struct ethosu_driver * drv,const uint8_t * cmd_stream,const int cms_length)305 static int handle_command_stream(struct ethosu_driver *drv, const uint8_t *cmd_stream, const int cms_length)
306 {
307     uint32_t cms_bytes       = cms_length * BYTES_IN_32_BITS;
308     ptrdiff_t cmd_stream_ptr = (ptrdiff_t)cmd_stream;
309 
310     LOG_INFO("handle_command_stream: cmd_stream=%p, cms_length %d", cmd_stream, cms_length);
311 
312     if (0 != ((ptrdiff_t)cmd_stream & MASK_16_BYTE_ALIGN))
313     {
314         LOG_ERR("Command stream addr %p not aligned to 16 bytes", cmd_stream);
315         return -1;
316     }
317 
318     // Verify 16 byte alignment for base address'
319     for (int i = 0; i < drv->job.num_base_addr; i++)
320     {
321         if (0 != (drv->job.base_addr[i] & MASK_16_BYTE_ALIGN))
322         {
323             LOG_ERR("Base addr %d: 0x%llx not aligned to 16 bytes", i, drv->job.base_addr[i]);
324             return -1;
325         }
326     }
327 
328     // Flush the cache if available on CPU.
329     // The upcasting to uin32_t* is ok since the pointer never is dereferenced.
330     // The base_addr_size is null if invoking from prior to invoke_V2, in that case
331     // the whole cache is being flushed.
332 
333     if (drv->job.base_addr_size != NULL)
334     {
335         ethosu_flush_dcache((uint32_t *)cmd_stream_ptr, cms_bytes);
336         for (int i = 0; i < drv->job.num_base_addr; i++)
337         {
338             ethosu_flush_dcache((uint32_t *)(uintptr_t)drv->job.base_addr[i], drv->job.base_addr_size[i]);
339         }
340     }
341     else
342     {
343         ethosu_flush_dcache(NULL, 0);
344     }
345 
346     // Request power gating disabled during inference run
347     if (!ethosu_request_power(drv))
348     {
349         LOG_ERR("Failed to request power");
350         return -1;
351     }
352 
353     drv->job.state = ETHOSU_JOB_RUNNING;
354 
355     // Inference begin callback
356     ethosu_inference_begin(drv, drv->job.user_arg);
357 
358     // Execute the command stream
359     ethosu_dev_run_command_stream(drv->dev, cmd_stream, cms_bytes, drv->job.base_addr, drv->job.num_base_addr);
360 
361     return 0;
362 }
363 
364 /******************************************************************************
365  * Weak functions - Interrupt handler
366  ******************************************************************************/
ethosu_irq_handler(struct ethosu_driver * drv)367 void __attribute__((weak)) ethosu_irq_handler(struct ethosu_driver *drv)
368 {
369     LOG_DEBUG("Got interrupt from Ethos-U");
370 
371     drv->job.state = ETHOSU_JOB_DONE;
372     if (!ethosu_dev_handle_interrupt(drv->dev))
373     {
374         drv->status_error = true;
375     }
376     /* TODO: feedback needed aout how to handle error (-1) return value */
377     ethosu_semaphore_give(drv->semaphore);
378 }
379 
380 /******************************************************************************
381  * Functions API
382  ******************************************************************************/
383 
ethosu_init(struct ethosu_driver * drv,const void * base_address,const void * fast_memory,const size_t fast_memory_size,uint32_t secure_enable,uint32_t privilege_enable)384 int ethosu_init(struct ethosu_driver *drv,
385                 const void *base_address,
386                 const void *fast_memory,
387                 const size_t fast_memory_size,
388                 uint32_t secure_enable,
389                 uint32_t privilege_enable)
390 {
391     LOG_INFO("Initializing NPU: base_address=%p, fast_memory=%p, fast_memory_size=%zu, secure=%" PRIu32
392              ", privileged=%" PRIu32,
393              base_address,
394              fast_memory,
395              fast_memory_size,
396              secure_enable,
397              privilege_enable);
398 
399     if (!ethosu_mutex)
400     {
401         ethosu_mutex = ethosu_mutex_create();
402     }
403 
404     if (!ethosu_semaphore)
405     {
406         ethosu_semaphore = ethosu_semaphore_create();
407     }
408 
409     drv->fast_memory           = (uint32_t)fast_memory;
410     drv->fast_memory_size      = fast_memory_size;
411     drv->power_request_counter = 0;
412 
413     // Initialize the device and set requested security state and privilege mode
414     drv->dev = ethosu_dev_init(base_address, secure_enable, privilege_enable);
415 
416     if (drv->dev == NULL)
417     {
418         LOG_ERR("Failed to initialize Ethos-U device");
419         return -1;
420     }
421 
422     drv->semaphore    = ethosu_semaphore_create();
423     drv->status_error = false;
424 
425     ethosu_reset_job(drv);
426 
427     ethosu_register_driver(drv);
428 
429     return 0;
430 }
431 
ethosu_deinit(struct ethosu_driver * drv)432 void ethosu_deinit(struct ethosu_driver *drv)
433 {
434     ethosu_deregister_driver(drv);
435     ethosu_semaphore_destroy(drv->semaphore);
436     ethosu_dev_deinit(drv->dev);
437     drv->dev = NULL;
438 }
439 
ethosu_soft_reset(struct ethosu_driver * drv)440 bool ethosu_soft_reset(struct ethosu_driver *drv)
441 {
442     // Soft reset the NPU
443     if (ethosu_dev_soft_reset(drv->dev) != ETHOSU_SUCCESS)
444     {
445         LOG_ERR("Failed to soft-reset NPU");
446         return false;
447     }
448 
449     // Update power and clock gating after the soft reset
450     ethosu_dev_set_clock_and_power(drv->dev,
451                                    drv->power_request_counter > 0 ? ETHOSU_CLOCK_Q_DISABLE : ETHOSU_CLOCK_Q_ENABLE,
452                                    drv->power_request_counter > 0 ? ETHOSU_POWER_Q_DISABLE : ETHOSU_POWER_Q_ENABLE);
453 
454     return true;
455 }
456 
ethosu_request_power(struct ethosu_driver * drv)457 bool ethosu_request_power(struct ethosu_driver *drv)
458 {
459     // Check if this is the first power request, increase counter
460     if (drv->power_request_counter++ == 0)
461     {
462         // Always reset to a known state. Changes to requested
463         // security state/privilege mode if necessary.
464         if (ethosu_soft_reset(drv) == false)
465         {
466             LOG_ERR("Failed to request power for Ethos-U");
467             drv->power_request_counter--;
468             return false;
469         }
470     }
471     return true;
472 }
473 
ethosu_release_power(struct ethosu_driver * drv)474 void ethosu_release_power(struct ethosu_driver *drv)
475 {
476     if (drv->power_request_counter == 0)
477     {
478         LOG_WARN("No power request left to release, reference counter is 0");
479     }
480     else
481     {
482         // Decrement ref counter and enable power gating if no requests remain
483         if (--drv->power_request_counter == 0)
484         {
485             ethosu_dev_set_clock_and_power(drv->dev, ETHOSU_CLOCK_Q_ENABLE, ETHOSU_POWER_Q_ENABLE);
486         }
487     }
488 }
489 
ethosu_get_driver_version(struct ethosu_driver_version * ver)490 void ethosu_get_driver_version(struct ethosu_driver_version *ver)
491 {
492     assert(ver != NULL);
493     ver->major = ETHOSU_DRIVER_VERSION_MAJOR;
494     ver->minor = ETHOSU_DRIVER_VERSION_MINOR;
495     ver->patch = ETHOSU_DRIVER_VERSION_PATCH;
496 }
497 
ethosu_get_hw_info(struct ethosu_driver * drv,struct ethosu_hw_info * hw)498 void ethosu_get_hw_info(struct ethosu_driver *drv, struct ethosu_hw_info *hw)
499 {
500     assert(hw != NULL);
501     ethosu_dev_get_hw_info(drv->dev, hw);
502 }
503 
ethosu_wait(struct ethosu_driver * drv,bool block)504 int ethosu_wait(struct ethosu_driver *drv, bool block)
505 {
506     int ret = 0;
507 
508     switch (drv->job.state)
509     {
510     case ETHOSU_JOB_IDLE:
511         LOG_ERR("Inference job not running...");
512         ret = -2;
513         break;
514     case ETHOSU_JOB_RUNNING:
515         if (!block)
516         {
517             // Inference still running, do not block
518             ret = 1;
519             break;
520         }
521         // fall through
522     case ETHOSU_JOB_DONE:
523         // Wait for interrupt in blocking mode. In non-blocking mode
524         // the interrupt has already triggered
525         /* TODO: feedback needed aout how to handle error (-1) return value */
526         ethosu_semaphore_take(drv->semaphore);
527 
528         // Inference done callback
529         ethosu_inference_end(drv, drv->job.user_arg);
530 
531         // Relase power gating disabled requirement
532         ethosu_release_power(drv);
533 
534         // Check NPU and interrupt status
535         if (drv->status_error)
536         {
537             LOG_ERR("NPU error(s) occured during inference.");
538             ethosu_dev_print_err_status(drv->dev);
539 
540             // Reset the NPU
541             (void)ethosu_soft_reset(drv);
542             // NPU is no longer in error state
543             drv->status_error = false;
544 
545             ret = -1;
546         }
547 
548         if (ret == 0)
549         {
550             // Invalidate cache
551             if (drv->job.base_addr_size != NULL)
552             {
553                 for (int i = 0; i < drv->job.num_base_addr; i++)
554                 {
555                     ethosu_invalidate_dcache((uint32_t *)(uintptr_t)drv->job.base_addr[i], drv->job.base_addr_size[i]);
556                 }
557             }
558             else
559             {
560                 ethosu_invalidate_dcache(NULL, 0);
561             }
562 
563             LOG_DEBUG("Inference finished successfully...");
564         }
565 
566         // Reset internal job (state resets to IDLE)
567         ethosu_reset_job(drv);
568         break;
569 
570     default:
571         LOG_ERR("Unexpected job state");
572         ethosu_reset_job(drv);
573         ret = -1;
574         break;
575     }
576 
577     // Return inference job status
578     return ret;
579 }
580 
ethosu_invoke_async(struct ethosu_driver * drv,const void * custom_data_ptr,const int custom_data_size,const uint64_t * base_addr,const size_t * base_addr_size,const int num_base_addr,void * user_arg)581 int ethosu_invoke_async(struct ethosu_driver *drv,
582                         const void *custom_data_ptr,
583                         const int custom_data_size,
584                         const uint64_t *base_addr,
585                         const size_t *base_addr_size,
586                         const int num_base_addr,
587                         void *user_arg)
588 {
589 
590     const struct cop_data_s *data_ptr = custom_data_ptr;
591     const struct cop_data_s *data_end = (struct cop_data_s *)((ptrdiff_t)custom_data_ptr + custom_data_size);
592 
593     // Make sure an inference is not already running
594     if (drv->job.state != ETHOSU_JOB_IDLE)
595     {
596         LOG_ERR("Inference already running, or waiting to be cleared...");
597         return -1;
598     }
599 
600     drv->job.state            = ETHOSU_JOB_IDLE;
601     drv->job.custom_data_ptr  = custom_data_ptr;
602     drv->job.custom_data_size = custom_data_size;
603     drv->job.base_addr        = base_addr;
604     drv->job.base_addr_size   = base_addr_size;
605     drv->job.num_base_addr    = num_base_addr;
606     drv->job.user_arg         = user_arg;
607 
608     // First word in custom_data_ptr should contain "Custom Operator Payload 1"
609     if (data_ptr->word != ETHOSU_FOURCC)
610     {
611         LOG_ERR("Custom Operator Payload: %" PRIu32 " is not correct, expected %x", data_ptr->word, ETHOSU_FOURCC);
612         goto err;
613     }
614 
615     // Custom data length must be a multiple of 32 bits
616     if ((custom_data_size % BYTES_IN_32_BITS) != 0)
617     {
618         LOG_ERR("custom_data_size=0x%x not a multiple of 4", custom_data_size);
619         goto err;
620     }
621 
622     data_ptr++;
623 
624     // Adjust base address to fast memory area
625     if (drv->fast_memory != 0 && num_base_addr >= FAST_MEMORY_BASE_ADDR_INDEX)
626     {
627         uint64_t *fast_memory = (uint64_t *)&base_addr[FAST_MEMORY_BASE_ADDR_INDEX];
628 
629         if (base_addr_size != NULL && base_addr_size[FAST_MEMORY_BASE_ADDR_INDEX] > drv->fast_memory_size)
630         {
631             LOG_ERR("Fast memory area too small. fast_memory_size=%u, base_addr_size=%u",
632                     drv->fast_memory_size,
633                     base_addr_size[FAST_MEMORY_BASE_ADDR_INDEX]);
634             goto err;
635         }
636 
637         *fast_memory = drv->fast_memory;
638     }
639 
640     drv->status_error = false;
641 
642     // Parse Custom Operator Payload data
643     while (data_ptr < data_end)
644     {
645         switch (data_ptr->driver_action_command)
646         {
647         case OPTIMIZER_CONFIG:
648             LOG_DEBUG("OPTIMIZER_CONFIG");
649             struct opt_cfg_s *opt_cfg_p = (struct opt_cfg_s *)data_ptr;
650 
651             if (handle_optimizer_config(drv, opt_cfg_p) < 0)
652             {
653                 goto err;
654             }
655             data_ptr += DRIVER_ACTION_LENGTH_32_BIT_WORD + OPTIMIZER_CONFIG_LENGTH_32_BIT_WORD;
656             break;
657         case COMMAND_STREAM:
658             // Vela only supports putting one COMMAND_STREAM per op
659             LOG_DEBUG("COMMAND_STREAM");
660             void *command_stream = (uint8_t *)(data_ptr) + sizeof(struct cop_data_s);
661             int cms_length       = (data_ptr->reserved << 16) | data_ptr->length;
662 
663             if (handle_command_stream(drv, command_stream, cms_length) < 0)
664             {
665                 goto err;
666             }
667             data_ptr += DRIVER_ACTION_LENGTH_32_BIT_WORD + cms_length;
668             break;
669         case NOP:
670             LOG_DEBUG("NOP");
671             data_ptr += DRIVER_ACTION_LENGTH_32_BIT_WORD;
672             break;
673         default:
674             LOG_ERR("UNSUPPORTED driver_action_command: %d", data_ptr->driver_action_command);
675             goto err;
676             break;
677         }
678     }
679 
680     return 0;
681 err:
682     LOG_ERR("Failed to invoke inference.");
683     ethosu_reset_job(drv);
684     return -1;
685 }
686 
ethosu_invoke_v3(struct ethosu_driver * drv,const void * custom_data_ptr,const int custom_data_size,const uint64_t * base_addr,const size_t * base_addr_size,const int num_base_addr,void * user_arg)687 int ethosu_invoke_v3(struct ethosu_driver *drv,
688                      const void *custom_data_ptr,
689                      const int custom_data_size,
690                      const uint64_t *base_addr,
691                      const size_t *base_addr_size,
692                      const int num_base_addr,
693                      void *user_arg)
694 {
695     if (ethosu_invoke_async(
696             drv, custom_data_ptr, custom_data_size, base_addr, base_addr_size, num_base_addr, user_arg) < 0)
697     {
698         return -1;
699     }
700 
701     return ethosu_wait(drv, true);
702 }
703 
ethosu_reserve_driver(void)704 struct ethosu_driver *ethosu_reserve_driver(void)
705 {
706     struct ethosu_driver *drv = NULL;
707 
708     do
709     {
710         /* TODO: feedback needed aout how to handle error (-1) return value */
711         ethosu_mutex_lock(ethosu_mutex);
712         drv = ethosu_find_and_reserve_driver();
713         /* TODO: feedback needed aout how to handle error (-1) return value */
714         ethosu_mutex_unlock(ethosu_mutex);
715 
716         if (drv != NULL)
717         {
718             break;
719         }
720 
721         LOG_INFO("Waiting for NPU driver handle to become available...");
722         /* TODO: feedback needed aout how to handle error (-1) return value */
723         ethosu_semaphore_take(ethosu_semaphore);
724 
725     } while (1);
726 
727     return drv;
728 }
729 
ethosu_release_driver(struct ethosu_driver * drv)730 void ethosu_release_driver(struct ethosu_driver *drv)
731 {
732     /* TODO: feedback needed aout how to handle error (-1) return value */
733     ethosu_mutex_lock(ethosu_mutex);
734     if (drv != NULL && drv->reserved)
735     {
736         if (drv->job.state == ETHOSU_JOB_RUNNING || drv->job.state == ETHOSU_JOB_DONE)
737         {
738             // Give the inference one shot to complete or force kill the job
739             if (ethosu_wait(drv, false) == 1)
740             {
741                 // Still running, soft reset the NPU and reset driver
742                 drv->power_request_counter = 0;
743                 ethosu_soft_reset(drv);
744                 ethosu_reset_job(drv);
745                 drv->status_error = false;
746                 /* TODO: feedback needed aout how to handle error (-1) return value */
747                 ethosu_semaphore_give(drv->semaphore);
748             }
749         }
750 
751         drv->reserved = false;
752         LOG_DEBUG("NPU driver handle %p released", drv);
753         /* TODO: feedback needed aout how to handle error (-1) return value */
754         ethosu_semaphore_give(ethosu_semaphore);
755     }
756     /* TODO: feedback needed aout how to handle error (-1) return value */
757     ethosu_mutex_unlock(ethosu_mutex);
758 }
759