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

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