Библиотека DSP elcore-runtime¶
Библиотека elcore-runtime содержит функции для разработки DSP-библиотек.
__elcore_before_main()¶
-
int __elcore_before_main()¶
Пользовательская библиотека является статически скомпонованным исполняемым ELF-файлом.
Код инициализации глобальных объектов выполняется до вызова функции __elcore_before_main()
и эти объекты могут использоваться во время выполнения заданий. Реализация функции
__elcore_before_main()
из библиотеки elcore-runtime имеет следующий вид:
int __elcore_before_main() {
asm("STOP");
asm("NOP");
return 0;
}
ElcoreCL во время вызова eclCreateProgramWithBinary()
запускает задание на
DSP, которое выполняет код начиная с точки входа ELF-файла, а приведённая реализация
__elcore_before_main()
останавливает выполнение до того, как будет вызвана
функция main() и выполнены деструкторы глобальных объектов.
Деструкторы глобальных объектов не вызываются никогда.
elcore_main_wrapper()¶
-
void elcore_main_wrapper(char *args, uint32_t *retval)¶
Описание аргументов функции elcore_main_wrapper()
:
args — буфер, содержащий список строк-аргументов, разделенных пустым символов 0. Список должен заканчиваться строкой нулевой длины;
retval — буфер для сохранения кода возврата;
Функция elcore_main_wrapper()
является оберткой над функцией
main(int argc, char *argv[])
ELF-файла, в которой выполняется формирование аргументов
argc и argv, вызов функции main()
и сохранение кода возврата в выходной аргумент retval.
При необходимости запустить задание, которое начинается с функции main()
,
пользователю следует вызывать функцию c:function:: void elcore_main_wrapper(), используя
ELcoreCL-функции eclCreateKernel()
и eclEnqueueNDRangeKernel()
.
elcorecl_run_wrapper()¶
-
void elcorecl_run_wrapper(char *args, uint32_t *retval, char *shared_mem, uint32_t shared_mem_size)¶
Описание аргументов функции elcorecl_run_wrapper()
:
args — буфер, содержащий список строк-аргументов, разделенных пустым символов 0;
retval — буфер для сохранения кода возврата;
shared_mem — указатель на память, общую для всех DSP;
shared_mem_size — размер памяти, общей для всех DSP.
Функция elcorecl_run_wrapper()
является оберткой над функцией
main_with_share_mem(int32_t argc, char *argv[], char *shmem_ptr, int32_t shmem_size),
в которой выполняется формирование аргументов argc и argv, вызов функции
main_with_share_mem()
и сохранение кода возврата в специальный буфер.
Функция main_with_share_mem()
является аналогом функции main()
за
исключением того, что принимает дополнительно память, общую для всех DSP.
Описание аргументов функции main_with_share_mem()
:
argc, argv — количество и массив аргументов;
shmem_ptr — указатель на память, общую для всех DSP;
shmem_size — размер памяти, общей для всех DSP.
При необходимости запустить задание, которое начинается с функции main_with_share_mem()
,
пользователю следует вызывать функцию c:function:: void elcorecl_run_wrapper(), используя
ELcoreCL-функции eclCreateKernel()
и eclEnqueueNDRangeKernel()
.
Пользователь библиотеки не должен самостоятельно вызывать elcorecl_run_wrapper()
внутри DSP-функции.
elcore_job_launcher()¶
-
void elcore_job_launcher()¶
Функция elcore_job_launcher()
необходима для корректной остановки
задания и имеет следующую реализацию:
elсorecl_job_launcher:
JS R8.L, R15.L
STOP
NOP
Библиотека ElcoreCL во время вызова eclCreateProgramWithBinary()
находит
адрес elcore_job_launcher()
и передаёт его в драйвер при каждом запуске
задания драйверу. Драйвер устанавливает R8.L на адрес функции, вызываемой
пользователем, а program counter на адрес elcore_job_launcher()
.
Таким образом, если функция закончила выполнение штатно, задание должно остановиться
внутри elcore_job_launcher()
.
Пользователь библиотеки ElcoreCL:
не должен сам вызывать или как-то иначе использовать
elcore_job_launcher()
;не должен вставлять оператор
STOP
в запускаемые на DSP функции.
elcore_kernel_wrapper¶
-
void elcore_kernel_wrapper(void *spawn_mem, kernel_spawn_internal_data *data, ...)¶
Описание аргументов функции elcore_kernel_wrapper()
:
spawn_mem — указатель на некэшируемую область памяти;
- data — структура
kernel_spawn_internal_data
со следующими полями: nc_mem_size
— размер передаваемой некэширумой памятиspawn_mem
;context_capacity
— количество DSP в ELcoreCL-контексте;index_in_context[16]`
— порядковый номер ядер в ELcoreCL-контексте;kernel[16]
— вызываемый DSP-kernel;argc[16]
— количество аргументов дляkernel
.
- data — структура
shared_mem — указатель на память, общую для всех DSP;
shared_mem_size — размер памяти, общей для всех DSP.
Функция elcore_kernel_wrapper()
является дополнительной оберткой над DSP-kernel’ом при
запуске ELcoreCL-функцией eclEnqueueKernelWithSpawn()
.
Функция elcore_kernel_wrapper()
:
Сохраняет в глобальных переменных
spawn_mem
,nc_mem_size
,context_capacity
иindex_in_context
;выделяет память под
context_capacity
стеков из своей кучи;запускает
kernel
;после завершения
kernel
выставляет флаги завершения вспомогательных ядер.
Пользователь библиотеки не должен самостоятельно вызывать elcore_kernel_wrapper()
внутри DSP-функции.
spawner_loop()¶
-
void spawner_loop()¶
Функция spawner_loop()
содержит основной цикл работы вспомогательных DSP,
необходимых для корректной работы функций elcore_spawn()
и elcore_sync()
в
рамках модели spawn/sync (см. Модель параллельного программирования spawn/sync).
Функция spawner_loop()
выполняет поиск активных заданий в очереди заданий,
размещаемой в общей некэшируемой памяти. При появлении активного задания функция
spawner_loop()
выполняет попытку атомарного захвата. В случае успешного захвата
функция spawner_loop()
выполняет переход в виртуальное адресное пространство основного
DSP, породившего данное задание для его выполнения.
После выполнения задания функция spawner_loop()
переходит в исходное адресное
пространство для поиска очередного активного задания.
Пользователь библиотеки не должен самостоятельно вызывать spawner_loop()
внутри DSP-функции.
__init_elcore_environ()¶
-
int __init_elcore_environ()¶
Функция __init_elcore_environ()
настраивает окружение для DSP путем
копирования переменных окружения CPU-процесса.
Функция __init_elcore_environ()
вызывается во время выполнения функции
__start
— точки входа в программу.
get_core_id()¶
-
int get_core_id()¶
Функция get_core_id()
необходима для получения DSP-функцией порядкового номера
ядра, на котором она запущена.
elcore_spawn()¶
-
int elcore_spawn(void *function, size_t argc, ...)¶
Функция elcore_spawn()
предназначена для порождения параллельной ветви программы для
выполнения function
в рамках модели spawn/sync (см. Модель параллельного программирования spawn/sync).
Функция elcore_spawn()
формирует в некэшируемой очереди новое задание. Свободные
вспомогательные DSP просматривают очередь и при появлении активного задания выполняют попытку
атомарного захвата. Вспомогательный DSP, который успешно захватил задание, переключается в адресное
пространство основного DSP и выполняет его.
Описание аргументов функции elcore_spawn()
:
function — указатель на запускаемую функцию;
argc — количество аргументов, передаваемых в функцию
function
. Максимально допустимое значение — 32.список из argc аргументов, передаваемых в функцию
function
.
В случае успеха функция elcore_spawn()
возвращает идентификатор задания.
Ожидание завершения задания осуществляется с помощью функции elcore_sync()
.
Функция elcore_spawn()
определена в заголовочном файле elcore-runtime.h.
elcore_sync()¶
-
int elcore_sync(int task_id, void *ret)¶
Функция elcore_sync()
ожидает завершения задания task_id, созданного
elcore_spawn()
и выполняет запись возвращаемого значения задания, если указатель
ret
не равен нулю.
Если задание task_id в момент вызова функции elcore_sync()
не было захвачено ни одним
из вспомогательных ядер, функция elcore_sync()
выполняет задание самостоятельно.
Если задание task_id в момент вызова функция elcore_sync()
было захвачено одним из
вспомогательных ядер, но еще не завершено, функция elcore_sync()
ожидает его завершения,
параллельно выполняя по одному заданию из конца очереди до тех пор, пока задание task_id не будет
завершено.
Описание аргументов функции elcore_sync()
:
task_id — идентификатор задания, созданный с помощью
elcore_spawn()
;ret — указатель на переменную для записи возвращаемого значения задания;
В случае успеха функция elcore_sync()
возвращает нулевое значение.
Функция elcore_sync()
определена в заголовочном файле elcore-runtime.h.
Системные вызовы¶
В elcore-runtime реализуются системные вызовы, аналогичные библиотеке newlib. Системные вызовы реализованы в отдельных файлах. Реализация конкретного системного вызова выбирается приоритетом линковщика.
Управление процессами:
execve()
— реализован «заглушкой» (при использовании процессор будет остановлен по исключениюTRAP 10
)._exit()
— реализован «заглушкой».fork()
— реализован «заглушкой».getpid()
— реализован «заглушкой».kill()
— реализован «заглушкой».wait()
— реализован «заглушкой».
Работа с файлами, ввод/вывод:
fstat()
isatty()
chdir()
chown()
— реализован «заглушкой».close()
link()
lseek()
open()
read()
readlink()
— реализован «заглушкой».stat()
symlink()
— реализован «заглушкой».unlink()
write()
Работа со временем:
gettimeofday()
times()
Работа с памятью:
sbrk()
Профилирование:
profil()
elcore-runtime дополнительно реализует вспомогательные системные вызовы, необходимые для настройки окружения DSP:
_syscall_get_env()
— получение списка переменных окружений CPU-процесса, разделенных символами\0
и его размера._syscall_get_kernel_name()
— получение имени вызываемой функции для данного задания.
Поддержка инструментального профилирования gprof
¶
Библиотека elcore-runtime реализует необходимый для профилирования функционал согласно Implementation of Profiling:
mcount()
moncontrol()
_mcleanup()
Сэмплирование осуществляется в драйвере с использованием таймеров. Включение/завершение
сэмплирования осуществляется с помощью системного вызова profil()
.
При завершении сэмплирования системный вызов profil()
возвращает количество потерянных сэмплов.
Если количество потерянных сэмплов достаточно велико по сравнению с общим временем выполнения
программы, то результаты профилирования считаются невалидными.
Особенность реализации:
Профилирование выполняется отдельно для каждого задания в рамках одного ELF-файла.
По окончанию профилирования задания в рабочей директории создается файл
gmon-elcore<elnum>-<function_name>-<job_number>.out
, где:elnum
— номер DSP, на котором выполнялось данное задание.function_name
— имя вызываемой функции для данного задания.job_number
— порядковый номер данного задания в рамках одного ELF-файла.
Предупреждение
Профилируемое приложение должно завершаться штатно. В противном случае, gmon
-файл не будет
создан.