blob: cfc1b0409df645706a4b0253b85d4d1c8ba1fb20 [file] [log] [blame]
Jim Cownie33f7b242014-04-09 15:40:23 +00001//===----------------------------------------------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is dual licensed under the MIT and the University of Illinois Open
6// Source Licenses. See LICENSE.txt for details.
7//
8//===----------------------------------------------------------------------===//
9
10
11#include "offload_target.h"
12#include <stdlib.h>
13#include <unistd.h>
14#ifdef SEP_SUPPORT
15#include <fcntl.h>
16#include <sys/ioctl.h>
17#endif // SEP_SUPPORT
18#include <omp.h>
19#include <map>
20
21// typedef offload_func_with_parms.
22// Pointer to function that represents an offloaded entry point.
23// The parameters are a temporary fix for parameters on the stack.
24typedef void (*offload_func_with_parms)(void *);
25
26// Target console and file logging
27const char *prefix;
28int console_enabled = 0;
29int offload_report_level = 0;
30
31// Trace information
32static const char* vardesc_direction_as_string[] = {
33 "NOCOPY",
34 "IN",
35 "OUT",
36 "INOUT"
37};
38static const char* vardesc_type_as_string[] = {
39 "unknown",
40 "data",
41 "data_ptr",
42 "func_ptr",
43 "void_ptr",
44 "string_ptr",
45 "dv",
46 "dv_data",
47 "dv_data_slice",
48 "dv_ptr",
49 "dv_ptr_data",
50 "dv_ptr_data_slice",
51 "cean_var",
52 "cean_var_ptr",
53 "c_data_ptr_array"
54};
55
56int mic_index = -1;
57int mic_engines_total = -1;
58uint64_t mic_frequency = 0;
59int offload_number = 0;
60static std::map<void*, RefInfo*> ref_data;
61static mutex_t add_ref_lock;
62
63#ifdef SEP_SUPPORT
64static const char* sep_monitor_env = "SEP_MONITOR";
65static bool sep_monitor = false;
66static const char* sep_device_env = "SEP_DEVICE";
67static const char* sep_device = "/dev/sep3.8/c";
68static int sep_counter = 0;
69
70#define SEP_API_IOC_MAGIC 99
71#define SEP_IOCTL_PAUSE _IO (SEP_API_IOC_MAGIC, 31)
72#define SEP_IOCTL_RESUME _IO (SEP_API_IOC_MAGIC, 32)
73
74static void add_ref_count(void * buf, bool created)
75{
76 mutex_locker_t locker(add_ref_lock);
77 RefInfo * info = ref_data[buf];
78
79 if (info) {
80 info->count++;
81 }
82 else {
83 info = new RefInfo((int)created,(long)1);
84 }
85 info->is_added |= created;
86 ref_data[buf] = info;
87}
88
89static void BufReleaseRef(void * buf)
90{
91 mutex_locker_t locker(add_ref_lock);
92 RefInfo * info = ref_data[buf];
93
94 if (info) {
95 --info->count;
96 if (info->count == 0 && info->is_added) {
97 BufferReleaseRef(buf);
98 info->is_added = 0;
99 }
100 }
101}
102
103static int VTPauseSampling(void)
104{
105 int ret = -1;
106 int handle = open(sep_device, O_RDWR);
107 if (handle > 0) {
108 ret = ioctl(handle, SEP_IOCTL_PAUSE);
109 close(handle);
110 }
111 return ret;
112}
113
114static int VTResumeSampling(void)
115{
116 int ret = -1;
117 int handle = open(sep_device, O_RDWR);
118 if (handle > 0) {
119 ret = ioctl(handle, SEP_IOCTL_RESUME);
120 close(handle);
121 }
122 return ret;
123}
124#endif // SEP_SUPPORT
125
126void OffloadDescriptor::offload(
127 uint32_t buffer_count,
128 void** buffers,
129 void* misc_data,
130 uint16_t misc_data_len,
131 void* return_data,
132 uint16_t return_data_len
133)
134{
135 FunctionDescriptor *func = (FunctionDescriptor*) misc_data;
136 const char *name = func->data;
137 OffloadDescriptor ofld;
138 char *in_data = 0;
139 char *out_data = 0;
140 char *timer_data = 0;
141
142 console_enabled = func->console_enabled;
143 timer_enabled = func->timer_enabled;
144 offload_report_level = func->offload_report_level;
145 offload_number = func->offload_number;
146 ofld.set_offload_number(func->offload_number);
147
148#ifdef SEP_SUPPORT
149 if (sep_monitor) {
150 if (__sync_fetch_and_add(&sep_counter, 1) == 0) {
151 OFFLOAD_DEBUG_TRACE(2, "VTResumeSampling\n");
152 VTResumeSampling();
153 }
154 }
155#endif // SEP_SUPPORT
156
157 OFFLOAD_DEBUG_TRACE_1(2, ofld.get_offload_number(),
158 c_offload_start_target_func,
159 "Offload \"%s\" started\n", name);
160
161 // initialize timer data
162 OFFLOAD_TIMER_INIT();
163
164 OFFLOAD_TIMER_START(c_offload_target_total_time);
165
166 OFFLOAD_TIMER_START(c_offload_target_descriptor_setup);
167
168 // get input/output buffer addresses
169 if (func->in_datalen > 0 || func->out_datalen > 0) {
170 if (func->data_offset != 0) {
171 in_data = (char*) misc_data + func->data_offset;
172 out_data = (char*) return_data;
173 }
174 else {
175 char *inout_buf = (char*) buffers[--buffer_count];
176 in_data = inout_buf;
177 out_data = inout_buf;
178 }
179 }
180
181 // assign variable descriptors
182 ofld.m_vars_total = func->vars_num;
183 if (ofld.m_vars_total > 0) {
184 uint64_t var_data_len = ofld.m_vars_total * sizeof(VarDesc);
185
186 ofld.m_vars = (VarDesc*) malloc(var_data_len);
187 memcpy(ofld.m_vars, in_data, var_data_len);
188
189 in_data += var_data_len;
190 func->in_datalen -= var_data_len;
191 }
192
193 // timer data
194 if (func->timer_enabled) {
195 uint64_t timer_data_len = OFFLOAD_TIMER_DATALEN();
196
197 timer_data = out_data;
198 out_data += timer_data_len;
199 func->out_datalen -= timer_data_len;
200 }
201
202 // init Marshallers
203 ofld.m_in.init_buffer(in_data, func->in_datalen);
204 ofld.m_out.init_buffer(out_data, func->out_datalen);
205
206 // copy buffers to offload descriptor
207 std::copy(buffers, buffers + buffer_count,
208 std::back_inserter(ofld.m_buffers));
209
210 OFFLOAD_TIMER_STOP(c_offload_target_descriptor_setup);
211
212 // find offload entry address
213 OFFLOAD_TIMER_START(c_offload_target_func_lookup);
214
215 offload_func_with_parms entry = (offload_func_with_parms)
216 __offload_entries.find_addr(name);
217
218 if (entry == NULL) {
219#if OFFLOAD_DEBUG > 0
220 if (console_enabled > 2) {
221 __offload_entries.dump();
222 }
223#endif
224 LIBOFFLOAD_ERROR(c_offload_descriptor_offload, name);
225 exit(1);
226 }
227
228 OFFLOAD_TIMER_STOP(c_offload_target_func_lookup);
229
230 OFFLOAD_TIMER_START(c_offload_target_func_time);
231
232 // execute offload entry
233 entry(&ofld);
234
235 OFFLOAD_TIMER_STOP(c_offload_target_func_time);
236
237 OFFLOAD_TIMER_STOP(c_offload_target_total_time);
238
239 // copy timer data to the buffer
240 OFFLOAD_TIMER_TARGET_DATA(timer_data);
241
242 OFFLOAD_DEBUG_TRACE(2, "Offload \"%s\" finished\n", name);
243
244#ifdef SEP_SUPPORT
245 if (sep_monitor) {
246 if (__sync_sub_and_fetch(&sep_counter, 1) == 0) {
247 OFFLOAD_DEBUG_TRACE(2, "VTPauseSampling\n");
248 VTPauseSampling();
249 }
250 }
251#endif // SEP_SUPPORT
252}
253
254void OffloadDescriptor::merge_var_descs(
255 VarDesc *vars,
256 VarDesc2 *vars2,
257 int vars_total
258)
259{
260 // number of variable descriptors received from host and generated
261 // locally should match
262 if (m_vars_total < vars_total) {
263 LIBOFFLOAD_ERROR(c_merge_var_descs1);
264 exit(1);
265 }
266
267 for (int i = 0; i < m_vars_total; i++) {
268 if (i < vars_total) {
269 // variable type must match
270 if (m_vars[i].type.bits != vars[i].type.bits) {
271 LIBOFFLOAD_ERROR(c_merge_var_descs2);
272 exit(1);
273 }
274
275 m_vars[i].ptr = vars[i].ptr;
276 m_vars[i].into = vars[i].into;
277
278 const char *var_sname = "";
279 if (vars2 != NULL) {
280 if (vars2[i].sname != NULL) {
281 var_sname = vars2[i].sname;
282 }
283 }
284 OFFLOAD_DEBUG_TRACE_1(2, get_offload_number(), c_offload_var,
285 " VarDesc %d, var=%s, %s, %s\n",
286 i, var_sname,
287 vardesc_direction_as_string[m_vars[i].direction.bits],
288 vardesc_type_as_string[m_vars[i].type.src]);
289 if (vars2 != NULL && vars2[i].dname != NULL) {
290 OFFLOAD_TRACE(2, " into=%s, %s\n", vars2[i].dname,
291 vardesc_type_as_string[m_vars[i].type.dst]);
292 }
293 }
294 OFFLOAD_TRACE(2,
295 " type_src=%d, type_dstn=%d, direction=%d, "
296 "alloc_if=%d, free_if=%d, align=%d, mic_offset=%d, flags=0x%x, "
297 "offset=%lld, size=%lld, count/disp=%lld, ptr=%p into=%p\n",
298 m_vars[i].type.src,
299 m_vars[i].type.dst,
300 m_vars[i].direction.bits,
301 m_vars[i].alloc_if,
302 m_vars[i].free_if,
303 m_vars[i].align,
304 m_vars[i].mic_offset,
305 m_vars[i].flags.bits,
306 m_vars[i].offset,
307 m_vars[i].size,
308 m_vars[i].count,
309 m_vars[i].ptr,
310 m_vars[i].into);
311 }
312}
313
314void OffloadDescriptor::scatter_copyin_data()
315{
316 OFFLOAD_TIMER_START(c_offload_target_scatter_inputs);
317
318 OFFLOAD_DEBUG_TRACE(2, "IN buffer @ %p size %lld\n",
319 m_in.get_buffer_start(),
320 m_in.get_buffer_size());
321 OFFLOAD_DEBUG_DUMP_BYTES(2, m_in.get_buffer_start(),
322 m_in.get_buffer_size());
323
324 // receive data
325 for (int i = 0; i < m_vars_total; i++) {
326 bool src_is_for_mic = (m_vars[i].direction.out ||
327 m_vars[i].into == NULL);
328 void** ptr_addr = src_is_for_mic ?
329 static_cast<void**>(m_vars[i].ptr) :
330 static_cast<void**>(m_vars[i].into);
331 int type = src_is_for_mic ? m_vars[i].type.src :
332 m_vars[i].type.dst;
333 bool is_static = src_is_for_mic ?
334 m_vars[i].flags.is_static :
335 m_vars[i].flags.is_static_dstn;
336 void *ptr = NULL;
337
338 if (m_vars[i].flags.alloc_disp) {
339 int64_t offset = 0;
340 m_in.receive_data(&offset, sizeof(offset));
341 m_vars[i].offset = -offset;
342 }
343 if (VAR_TYPE_IS_DV_DATA_SLICE(type) ||
344 VAR_TYPE_IS_DV_DATA(type)) {
345 ArrDesc *dvp = (type == c_dv_data_slice || type == c_dv_data)?
346 reinterpret_cast<ArrDesc*>(ptr_addr) :
347 *reinterpret_cast<ArrDesc**>(ptr_addr);
348 ptr_addr = reinterpret_cast<void**>(&dvp->Base);
349 }
350
351 // Set pointer values
352 switch (type) {
353 case c_data_ptr_array:
354 {
355 int j = m_vars[i].ptr_arr_offset;
356 int max_el = j + m_vars[i].count;
357 char *dst_arr_ptr = (src_is_for_mic)?
358 *(reinterpret_cast<char**>(m_vars[i].ptr)) :
359 reinterpret_cast<char*>(m_vars[i].into);
360
361 for (; j < max_el; j++) {
362 if (src_is_for_mic) {
363 m_vars[j].ptr =
364 dst_arr_ptr + m_vars[j].ptr_arr_offset;
365 }
366 else {
367 m_vars[j].into =
368 dst_arr_ptr + m_vars[j].ptr_arr_offset;
369 }
370 }
371 }
372 break;
373 case c_data:
374 case c_void_ptr:
375 case c_cean_var:
376 case c_dv:
377 break;
378
379 case c_string_ptr:
380 case c_data_ptr:
381 case c_cean_var_ptr:
382 case c_dv_ptr:
383 if (m_vars[i].alloc_if) {
384 void *buf;
385 if (m_vars[i].flags.sink_addr) {
386 m_in.receive_data(&buf, sizeof(buf));
387 }
388 else {
389 buf = m_buffers.front();
390 m_buffers.pop_front();
391 }
392 if (buf) {
393 if (!is_static) {
394 if (!m_vars[i].flags.sink_addr) {
395 // increment buffer reference
396 OFFLOAD_TIMER_START(c_offload_target_add_buffer_refs);
397 BufferAddRef(buf);
398 OFFLOAD_TIMER_STOP(c_offload_target_add_buffer_refs);
399 }
400 add_ref_count(buf, 0 == m_vars[i].flags.sink_addr);
401 }
402 ptr = static_cast<char*>(buf) +
403 m_vars[i].mic_offset +
404 (m_vars[i].flags.is_stack_buf ?
405 0 : m_vars[i].offset);
406 }
407 *ptr_addr = ptr;
408 }
409 else if (m_vars[i].flags.sink_addr) {
410 void *buf;
411 m_in.receive_data(&buf, sizeof(buf));
412 void *ptr = static_cast<char*>(buf) +
413 m_vars[i].mic_offset +
414 (m_vars[i].flags.is_stack_buf ?
415 0 : m_vars[i].offset);
416 *ptr_addr = ptr;
417 }
418 break;
419
420 case c_func_ptr:
421 break;
422
423 case c_dv_data:
424 case c_dv_ptr_data:
425 case c_dv_data_slice:
426 case c_dv_ptr_data_slice:
427 if (m_vars[i].alloc_if) {
428 void *buf;
429 if (m_vars[i].flags.sink_addr) {
430 m_in.receive_data(&buf, sizeof(buf));
431 }
432 else {
433 buf = m_buffers.front();
434 m_buffers.pop_front();
435 }
436 if (buf) {
437 if (!is_static) {
438 if (!m_vars[i].flags.sink_addr) {
439 // increment buffer reference
440 OFFLOAD_TIMER_START(c_offload_target_add_buffer_refs);
441 BufferAddRef(buf);
442 OFFLOAD_TIMER_STOP(c_offload_target_add_buffer_refs);
443 }
444 add_ref_count(buf, 0 == m_vars[i].flags.sink_addr);
445 }
446 ptr = static_cast<char*>(buf) +
447 m_vars[i].mic_offset + m_vars[i].offset;
448 }
449 *ptr_addr = ptr;
450 }
451 else if (m_vars[i].flags.sink_addr) {
452 void *buf;
453 m_in.receive_data(&buf, sizeof(buf));
454 ptr = static_cast<char*>(buf) +
455 m_vars[i].mic_offset + m_vars[i].offset;
456 *ptr_addr = ptr;
457 }
458 break;
459
460 default:
461 LIBOFFLOAD_ERROR(c_unknown_var_type, type);
462 abort();
463 }
464 // Release obsolete buffers for stack of persistent objects
465 if (type = c_data_ptr &&
466 m_vars[i].flags.is_stack_buf &&
467 !m_vars[i].direction.bits &&
468 m_vars[i].alloc_if &&
469 m_vars[i].size != 0) {
470 for (int j=0; j < m_vars[i].size; j++) {
471 void *buf;
472 m_in.receive_data(&buf, sizeof(buf));
473 BufferReleaseRef(buf);
474 ref_data.erase(buf);
475 }
476 }
477 // Do copyin
478 switch (m_vars[i].type.dst) {
479 case c_data_ptr_array:
480 break;
481 case c_data:
482 case c_void_ptr:
483 case c_cean_var:
484 if (m_vars[i].direction.in &&
485 !m_vars[i].flags.is_static_dstn) {
486 int64_t size;
487 int64_t disp;
488 char* ptr = m_vars[i].into ?
489 static_cast<char*>(m_vars[i].into) :
490 static_cast<char*>(m_vars[i].ptr);
491 if (m_vars[i].type.dst == c_cean_var) {
492 m_in.receive_data((&size), sizeof(int64_t));
493 m_in.receive_data((&disp), sizeof(int64_t));
494 }
495 else {
496 size = m_vars[i].size;
497 disp = 0;
498 }
499 m_in.receive_data(ptr + disp, size);
500 }
501 break;
502
503 case c_dv:
504 if (m_vars[i].direction.bits ||
505 m_vars[i].alloc_if ||
506 m_vars[i].free_if) {
507 char* ptr = m_vars[i].into ?
508 static_cast<char*>(m_vars[i].into) :
509 static_cast<char*>(m_vars[i].ptr);
510 m_in.receive_data(ptr + sizeof(uint64_t),
511 m_vars[i].size - sizeof(uint64_t));
512 }
513 break;
514
515 case c_string_ptr:
516 case c_data_ptr:
517 case c_cean_var_ptr:
518 case c_dv_ptr:
519 case c_dv_data:
520 case c_dv_ptr_data:
521 case c_dv_data_slice:
522 case c_dv_ptr_data_slice:
523 break;
524
525 case c_func_ptr:
526 if (m_vars[i].direction.in) {
527 m_in.receive_func_ptr((const void**) m_vars[i].ptr);
528 }
529 break;
530
531 default:
532 LIBOFFLOAD_ERROR(c_unknown_var_type, m_vars[i].type.dst);
533 abort();
534 }
535 }
536
537 OFFLOAD_TRACE(1, "Total copyin data received from host: [%lld] bytes\n",
538 m_in.get_tfr_size());
539
540 OFFLOAD_TIMER_STOP(c_offload_target_scatter_inputs);
541
542 OFFLOAD_TIMER_START(c_offload_target_compute);
543}
544
545void OffloadDescriptor::gather_copyout_data()
546{
547 OFFLOAD_TIMER_STOP(c_offload_target_compute);
548
549 OFFLOAD_TIMER_START(c_offload_target_gather_outputs);
550
551 for (int i = 0; i < m_vars_total; i++) {
552 bool src_is_for_mic = (m_vars[i].direction.out ||
553 m_vars[i].into == NULL);
554
555 switch (m_vars[i].type.src) {
556 case c_data_ptr_array:
557 break;
558 case c_data:
559 case c_void_ptr:
560 case c_cean_var:
561 if (m_vars[i].direction.out &&
562 !m_vars[i].flags.is_static) {
563 m_out.send_data(
564 static_cast<char*>(m_vars[i].ptr) + m_vars[i].disp,
565 m_vars[i].size);
566 }
567 break;
568
569 case c_dv:
570 break;
571
572 case c_string_ptr:
573 case c_data_ptr:
574 case c_cean_var_ptr:
575 case c_dv_ptr:
576 if (m_vars[i].free_if &&
577 src_is_for_mic &&
578 !m_vars[i].flags.is_static) {
579 void *buf = *static_cast<char**>(m_vars[i].ptr) -
580 m_vars[i].mic_offset -
581 (m_vars[i].flags.is_stack_buf?
582 0 : m_vars[i].offset);
583 if (buf == NULL) {
584 break;
585 }
586 // decrement buffer reference count
587 OFFLOAD_TIMER_START(c_offload_target_release_buffer_refs);
588 BufReleaseRef(buf);
589 OFFLOAD_TIMER_STOP(c_offload_target_release_buffer_refs);
590 }
591 break;
592
593 case c_func_ptr:
594 if (m_vars[i].direction.out) {
595 m_out.send_func_ptr(*((void**) m_vars[i].ptr));
596 }
597 break;
598
599 case c_dv_data:
600 case c_dv_ptr_data:
601 case c_dv_data_slice:
602 case c_dv_ptr_data_slice:
603 if (src_is_for_mic &&
604 m_vars[i].free_if &&
605 !m_vars[i].flags.is_static) {
606 ArrDesc *dvp = (m_vars[i].type.src == c_dv_data ||
607 m_vars[i].type.src == c_dv_data_slice) ?
608 static_cast<ArrDesc*>(m_vars[i].ptr) :
609 *static_cast<ArrDesc**>(m_vars[i].ptr);
610
611 void *buf = reinterpret_cast<char*>(dvp->Base) -
612 m_vars[i].mic_offset -
613 m_vars[i].offset;
614
615 if (buf == NULL) {
616 break;
617 }
618
619 // decrement buffer reference count
620 OFFLOAD_TIMER_START(c_offload_target_release_buffer_refs);
621 BufReleaseRef(buf);
622 OFFLOAD_TIMER_STOP(c_offload_target_release_buffer_refs);
623 }
624 break;
625
626 default:
627 LIBOFFLOAD_ERROR(c_unknown_var_type, m_vars[i].type.dst);
628 abort();
629 }
630
631 if (m_vars[i].into) {
632 switch (m_vars[i].type.dst) {
633 case c_data_ptr_array:
634 break;
635 case c_data:
636 case c_void_ptr:
637 case c_cean_var:
638 case c_dv:
639 break;
640
641 case c_string_ptr:
642 case c_data_ptr:
643 case c_cean_var_ptr:
644 case c_dv_ptr:
645 if (m_vars[i].direction.in &&
646 m_vars[i].free_if &&
647 !m_vars[i].flags.is_static_dstn) {
648 void *buf = *static_cast<char**>(m_vars[i].into) -
649 m_vars[i].mic_offset -
650 (m_vars[i].flags.is_stack_buf?
651 0 : m_vars[i].offset);
652
653 if (buf == NULL) {
654 break;
655 }
656 // decrement buffer reference count
657 OFFLOAD_TIMER_START(
658 c_offload_target_release_buffer_refs);
659 BufReleaseRef(buf);
660 OFFLOAD_TIMER_STOP(
661 c_offload_target_release_buffer_refs);
662 }
663 break;
664
665 case c_func_ptr:
666 break;
667
668 case c_dv_data:
669 case c_dv_ptr_data:
670 case c_dv_data_slice:
671 case c_dv_ptr_data_slice:
672 if (m_vars[i].free_if &&
673 m_vars[i].direction.in &&
674 !m_vars[i].flags.is_static_dstn) {
675 ArrDesc *dvp =
676 (m_vars[i].type.dst == c_dv_data_slice ||
677 m_vars[i].type.dst == c_dv_data) ?
678 static_cast<ArrDesc*>(m_vars[i].into) :
679 *static_cast<ArrDesc**>(m_vars[i].into);
680 void *buf = reinterpret_cast<char*>(dvp->Base) -
681 m_vars[i].mic_offset -
682 m_vars[i].offset;
683
684 if (buf == NULL) {
685 break;
686 }
687 // decrement buffer reference count
688 OFFLOAD_TIMER_START(
689 c_offload_target_release_buffer_refs);
690 BufReleaseRef(buf);
691 OFFLOAD_TIMER_STOP(
692 c_offload_target_release_buffer_refs);
693 }
694 break;
695
696 default:
697 LIBOFFLOAD_ERROR(c_unknown_var_type, m_vars[i].type.dst);
698 abort();
699 }
700 }
701 }
702
703 OFFLOAD_DEBUG_TRACE(2, "OUT buffer @ p %p size %lld\n",
704 m_out.get_buffer_start(),
705 m_out.get_buffer_size());
706
707 OFFLOAD_DEBUG_DUMP_BYTES(2,
708 m_out.get_buffer_start(),
709 m_out.get_buffer_size());
710
711 OFFLOAD_DEBUG_TRACE_1(1, get_offload_number(), c_offload_copyout_data,
712 "Total copyout data sent to host: [%lld] bytes\n",
713 m_out.get_tfr_size());
714
715 OFFLOAD_TIMER_STOP(c_offload_target_gather_outputs);
716}
717
718void __offload_target_init(void)
719{
720#ifdef SEP_SUPPORT
721 const char* env_var = getenv(sep_monitor_env);
722 if (env_var != 0 && *env_var != '\0') {
723 sep_monitor = atoi(env_var);
724 }
725 env_var = getenv(sep_device_env);
726 if (env_var != 0 && *env_var != '\0') {
727 sep_device = env_var;
728 }
729#endif // SEP_SUPPORT
730
731 prefix = report_get_message_str(c_report_mic);
732
733 // init frequency
734 mic_frequency = COIPerfGetCycleFrequency();
735}
736
737// User-visible offload API
738
739int _Offload_number_of_devices(void)
740{
741 return mic_engines_total;
742}
743
744int _Offload_get_device_number(void)
745{
746 return mic_index;
747}
748
749int _Offload_get_physical_device_number(void)
750{
751 uint32_t index;
752 EngineGetIndex(&index);
753 return index;
754}