Библиотека 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()
необходима для корректной остановки
задания.
Библиотека 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 реализуются системные вызовы. Системные вызовы реализованы в отдельных файлах. Реализация конкретного системного вызова выбирается приоритетом линковщика.
Стандартные системные вызовы
Управление процессами:
execve()
— реализован «заглушкой» (при использовании процессор будет остановлен по исключениюTRAP 10
)._exit()
— реализован «заглушкой».fork()
— реализован «заглушкой».getpid()
— реализован «заглушкой».kill()
— реализован «заглушкой».wait()
— реализован «заглушкой».
Работа с файлами, ввод/вывод:
fstat()
getrandom()
isatty()
chdir()
chown()
— реализован «заглушкой».close()
link()
lseek()
open()
read()
readlink()
— реализован «заглушкой».stat()
symlink()
— реализован «заглушкой».unlink()
write()
Работа со временем:
gettimeofday()
times()
Работа с памятью:
sbrk()
Профилирование:
profil()
Нестандартные системные вызовы
pmmap()
-
void *pmmap(void *pref_vaddr, uint32_t length, uint64_t paddr)
Системный вызов pmmap()
позволяет отобразить физическую область памяти, начиная с адреса
paddr
и размером length
байт в виртуальное адресное пространство DSP, начиная с адреса
pref_vaddr
.
Предупреждение
Значения paddr
, pref_vaddr
и length
должны быть кратны 4 КБ (минимальный размер
MMU-страницы).
При необходимости использования страниц большего размера значения параметров должны быть
кратны размеру страниц (2 МБ, 1 ГБ).
Предупреждение
paddr
не должен указывать на диапазоны адресов, которые соответствуют областям PRAM, XYRAM и
регистров DSP-ядер, а также ведут к обращению в EPort. Доступ к такой памяти возможен только
по физическим адресам без необходимости дополнительного отображения.
Если pref_vaddr
равен NULL
, отображение выполняется в первый свободный участок виртуального
адресного пространства DSP.
Если pref_vaddr
не равен NULL
, отображение в адресное пространство DSP выполняется, начиная
с адреса pref_vaddr
только тогда, когда диапазон адресов
pref_vaddr
- pref_vaddr + length
свободен. В противном случае, отображение выполняется в
первый свободный участок виртуального адресного пространства DSP.
В случае успеха системный вызов pmmap()
возвращает указатель на начало отображенной
области памяти в адресном пространстве DSP.
В случае ошибки системный вызов pmmap()
возвращает NULL.
Системный вызов pmmap()
определен в заголовочном файле elcore-runtime.h.
pmunmap()
-
int pmunmap(void *vaddr, uint32_t length)
Системный вызов pmunmap()
удаляет отображение, начиная с адреса vaddr
и размером
length
байт, созданное ранее с помощью системного вызова pmmap()
.
В случае успеха системный вызов pmunmap()
возвращает нулевое значение.
Системный вызов pmunmap()
определен в заголовочном файле elcore-runtime.h.
_syscall_get_env()
-
int _syscall_get_env(char *env, void *size)
Системный вызов _syscall_get_env()
позволяет получить список переменных окружений
CPU-процесса, разделенных символами \0
, а также его размер.
В случае успеха системный вызов _syscall_get_env()
возвращает нулевое значение.
Пользователь библиотеки не должен самостоятельно вызывать _syscall_get_env()
внутри DSP-функции.
_syscall_get_kernel_name()
-
int _syscall_get_kernel_name(char*)
Системный вызов _syscall_get_kernel_name()
позволяет получить имя вызываемой функции для
данного DSP-задания.
В случае успеха системный вызов _syscall_get_kernel_name()
возвращает нулевое значение.
Пользователь библиотеки не должен самостоятельно вызывать _syscall_get_kernel_name()
внутри DSP-функции.
Поддержка инструментального профилирования 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
-файл не будет
создан.