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 = ®istered_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