Библиотека 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-файл не будет
создан.