Драйвер delcore30m. Руководство программиста¶
О документе¶
Документ содержит описание драйвера для DSP-кластера DELcore-30M, состоящего из двух DSP-ядер (далее DSP) ELcore-30M.
В документе используются ссылки на системный контроллер DMA (SDMA). Описание SDMA приведено в документе Микросхема интегральная 1892ВМ14Я. Руководство пользователя.
Общее описание¶
Интерфейс драйвера delcore30m предоставляет функциональность:
- Управление памятью XYRAM (выделение, освобождение, mmap).
- Выделение, импорт, экспорт непрерывных буферов DMA в системной памяти (DDR).
- Предоставление доступа к PRAM DSP.
- Управление ресурсами: DSP, SDMA (выделение, освобождение).
- Подготовка программы для SDMA.
- Отправка заданий на DSP. Возможно параллельное выполнение нескольких задач на нескольких DSP, одной задачи на нескольких DSP. Уведомление о готовности заданий.
- Отмена заданий.
- Профилирование кода DSP.
Драйвер delcore30m использует следующие ресурсы 1892ВМ14Я:
- цифровой сигнальный процессор DELcore-30M,
- системный DMA-контроллер SDMA,
- блок поддержки атомарных операций SPINLOCK.
Ограничения драйвера:
- Не поддерживается одновременная работа DSP и VPU.
- Драйвер SDMA pl330 должен быть выгружен.
- Адреса памяти XYRAM, используемые в DMA-пересылках, должны быть выровнены по границе четыре байта, см. главу 4.3.1 в Микросхема интегральная 1892ВМ14Я. Руководство пользователя.
- Запрещено использовать глобальные переменные в коде для DSP, написанном на C.
- Профилирование возможно только для кода, написанного на ассемблере.
- Не поддерживается одновременная передача нескольких SDMA каналов разных типов.
- Не поддерживается работа драйвера более, чем из двух процессов.
Стандартные системные вызовы¶
Интерфейс реализует стандартные для файловых дескрипторов системные вызовы со следующими особенностями:
mmap()
- При указании файлового дескриптора устройства возвращает ошибку.
- При указании файлового дескриптора буфера отображает память буфера в карту памяти процесса.
- При указании файлового дескриптора задания возвращает ошибку.
- При указании файлового дескриптора ресурса DSP отображает память программ
DSP N, где N = offset / 4096, offset — последний аргумент функции
mmap()
.
poll()
- При указании файлового дескриптора задания ожидает завершения задания
(задание считается завершившимся, если его статус —
DELCORE30M_JOB_IDLE
). - При указании файлового дескриптора буфера, дескриптор игнорируется.
- При указании файлового дескриптора устройства, дескриптор игнорируется.
- При указании файлового дескриптора задания ожидает завершения задания
(задание считается завершившимся, если его статус —
Также интерфейс реализует системный вызов ioctl()
c функциями, описанными
ниже.
Описание IOCTL¶
Перед вызовом функций ioctl()
необходимо открыть устройство
/dev/elcore0
посредством вызова функции open()
, которая возвращает
файловый дескриптор устройства.
ELCIOC_BUF_ALLOC¶
Назначение¶
Выделение непрерывного буфера в XYRAM или DDR.
Объявление¶
enum delcore30m_memory_type {
DELCORE30M_MEMORY_XYRAM,
DELCORE30M_MEMORY_SYSTEM
};
struct delcore30m_buffer {
int fd;
enum delcore30m_memory_type type;
size_t size;
};
int ioctl(int fd, ELCIOC_BUF_ALLOC, struct delcore30m_buffer *buf);
Аргументы¶
fd
— дескриптор устройства драйвера.buf
— указатель на структуруdelcore30m_buffer
.
Типы аргументов¶
-
delcore30m_buffer
¶ -
fd
¶ Файловый дескриптор буфера.
-
type
¶ Тип памяти, в которой выделяется буфер:
DELCORE30M_MEMORY_XYRAM
— память данных XYRAM.DELCORE30M_MEMORY_SYSTEM
— общая системная память DDR.
-
size
¶ Размер выделяемого буфера в байтах. Для каждого DSP драйвер выделяет стек размером 4 КБ. Максимально доступный объем памяти XYRAM для каждого DSP равен 124 КБ.
-
Описание¶
Перед вызовом ioctl()
необходимо заполнить поля delcore30m_buffer.type
и delcore30m_buffer.size
.
В случае успеха вызов ioctl()
возвращает нулевое значение, при этом
значение delcore30m_buffer.fd
соответствует значению файлового
дескриптора выделенного буфера.
Для освобождения выделенного буфера необходимо вызвать close()
с
указанием delcore30m_buffer.fd
.
Ошибки, возвращаемые ioctl()
:
ENOMEM
— недостаточно свободной памятиdelcore30m_buffer.type
для выделения данного буфера.EINVAL
— значение поляdelcore30m_buffer.type
некорректно.
ELCIOC_RESOURCE_REQUEST¶
Назначение¶
Выделение ресурсов.
Объявление¶
enum delcore30m_resource_type {
DELCORE30M_CORE,
DELCORE30M_SDMA,
};
struct delcore30m_resource {
int fd;
enum delcore30m_resource_type type;
unsigned int num;
unsigned long mask;
};
int ioctl(int fd, ELCIOC_RESOURCE_REQUEST, struct delcore30m_resource *res);
Аргументы¶
fd
— дескриптор устройства драйвера.res
— указатель на структуруdelcore30m_resource
.
Типы аргументов¶
Описание¶
Перед вызовом ioctl()
необходимо заполнить поля delcore30m_resource.type
и delcore30m_resource.num
.
В случае успеха вызов ioctl()
возвращает нулевое значение, при этом
значение delcore30m_resource.fd
соответствует значению файлового
дескриптора выделенного буфера, а значение delcore30m_resource.mask
будет содержать маску номеров выделенных ресурсов.
Для освобождения выделенного буфера необходимо вызвать close()
с
указанием delcore30m_resource.fd
.
Ошибки, возвращаемые ioctl()
:
ENOMEM
— в системе недостаточно свободной памяти DDR.EINVAL
— значения полейdelcore30m_resource.type
,delcore30m_resource.num
некорректны. Количество ресурсов типаDELCORE30M_CORE
не может превышать 2. Количество ресурсов типаDELCORE30M_SDMA
не может превышать 8.EBUSY
— запрашиваемые ресурсы в данный момент заняты. Необходимо вызватьioctl()
в другое время.
ELCIOC_JOB_CREATE¶
Назначение¶
Cоздание задания для DSP.
Объявление¶
#define MAX_INPUTS 15
#define MAX_OUTPUTS 15
#define DELCORE30M_PROFILE (1 << 0)
enum delcore30m_job_status {
DELCORE30M_JOB_IDLE,
DELCORE30M_JOB_ENQUEUED,
DELCORE30M_JOB_RUNNING,
};
enum delcore30m_job_rc {
DELCORE30M_JOB_ERROR = -2,
DELCORE30M_JOB_CANCELLED = -1,
DELCORE30M_JOB_SUCCESS = 0
};
struct delcore30m_job {
int fd;
unsigned int inum;
unsigned int onum;
int input[MAX_INPUTS];
int output[MAX_OUTPUTS];
int cores_fd;
int sdmas_fd;
enum delcore30m_job_status status;
enum delcore30m_job_rc rc;
__u32 flags;
};
int ioctl(int fd, ELCIOC_JOB_CREATE, struct delcore30m_job *job);
Аргументы¶
fd
— дескриптор устройства драйвера.job
— указатель на структуруdelcore30m_job
.
Типы аргументов¶
-
delcore30m_job
¶ -
fd
¶ Файловый дескриптор задания.
-
inum
¶ Количество входных буферов. Максимально допустимое значение — 15.
-
onum
¶ Количество выходных буферов. Максимально допустимое значение — 15.
-
input
¶ Массив файловых дескрипторов входных буферов размера
delcore30m_job.inum
, выделенных через ELCIOC_BUF_ALLOC. Максимально допустимое значение — 15.
-
output
¶ Массив файловых дескрипторов выходных буферов размера
delcore30m_job.onum
, выделенных через ELCIOC_BUF_ALLOC. Максимально допустимое значение — 15.
-
cores_fd
¶ Файловый дескриптор ресурса DSP, выделенного через ELCIOC_RESOURCE_REQUEST, на которых может быть запущено задание.
-
sdmas_fd
¶ Дескриптор каналов SDMA, выделенный через ELCIOC_RESOURCE_REQUEST, которые могут быть использованы заданием. Если для выполнения задания не нужно использовать SDMA, установить это поле в ноль.
-
status
¶ Статус задания:
DELCORE30M_JOB_IDLE
— задача либо выполнена, либо еще не помещена в очередь на выполнение, см. ELCIOC_JOB_ENQUEUE.DELCORE30M_JOB_ENQUEUED
— задача находится в очереди на выполнение на DSP.DELCORE30M_JOB_RUNNING
— задача выполняется на DSP.
-
rc
¶ Код возврата последнего завершённого задания:
DELCORE30M_JOB_ERROR
— задача завершилась с ошибкой.DELCORE30M_JOB_CANCELED
— задача была отменена.DELCORE30M_JOB_SUCCESS
— задача успешно завершилась.
-
flags
¶ Флаги задания. Допустимые флаги:
DELCORE30M_PROFILE
— флаг профилирования DSP-программы, см. Профилирование программы, исполняемой на DSP.
-
Описание¶
Перед вызовом ioctl()
необходимо заполнить поля
delcore30m_job.inum
, delcore30m_job.onum
,
delcore30m_job.input
, delcore30m_job.output
и
delcore30m_job.flags
.
В случае успеха вызов ioctl()
возвращает нулевое значение, при этом
значение поля delcore30m_job.fd
соответствует значению файлового
дескриптора созданного задания, а поле delcore30m_job.status
равно
DELCORE30M_JOB_IDLE
.
Для уничтожения задания необходимо вызвать close()
с указанием
delcore30m_job.fd
Ошибки, возвращаемые ioctl()
:
ENOMEM
— в системе недостаточно свободной памяти DDR.EBADFD
— значение поляdelcore30m_job.cores_fd
некорректно.EPERM
— в PRAM каждого DSP, для которых создается задание, не загружен исполняемый код, см. описание системного вызоваmmap()
в Стандартные системные вызовы.EINVAL
— поляdelcore30m_job.inum
иdelcore30m_job.onum
выходят за максимальную границу возможных значений.
ELCIOC_JOB_ENQUEUE¶
Назначение¶
Добавление задания в очередь на выполнение.
Объявление¶
int ioctl(int fd, ELCIOC_JOB_ENQUEUE, struct delcore30m_job *job);
Аргументы¶
fd
— дескриптор устройства драйвера.job
— указатель на структуруdelcore30m_job
, который был получен после вызова ELCIOC_JOB_CREATE.
Описание¶
Если DSP находится в состоянии ожидания, то производится старт выполнения задачи. В противном случае, задача будет запущена после завершения задачи, выполняемой на DSP.
В случае успеха вызов ioctl()
возвращает нулевое значение.
Ошибки, возвращаемые ioctl()
:
EBADFD
— значение поляdelcore30m_job.fd
некорректно.EBUSY
— переданная задачаdelcore30m_job
в очереди на исполнение или выполняется на DSP.
ELCIOC_JOB_STATUS¶
Назначение¶
Получение статуса задания.
Объявление¶
int ioctl(int fd, ELCIOC_JOB_STATUS, struct delcore30m_job *job);
Аргументы¶
fd
— дескриптор устройства драйвера.job
— указатель на структуруdelcore30m_job
, который был получен после вызова ELCIOC_JOB_CREATE.
Описание¶
В случае успеха вызов ioctl()
возвращает нулевое значение, при этом
значение поля delcore30m_job.status
соответствует текущему статуса
задания.
Ошибки, возвращаемые ioctl()
:
EBADFD
— значение поляdelcore30m_job.fd
некорректно.
ELCIOC_JOB_CANCEL¶
Назначение¶
Отмена задания.
Объявление¶
int ioctl(int fd, ELCIOC_JOB_CANCEL, struct delcore30m_job *job);
Аргументы¶
fd
— дескриптор устройства драйвера.job
— указатель на структуруdelcore30m_job
, который был получен после вызова ELCIOC_JOB_CREATE.
Описание¶
Задание удаляется из всех очередей. Если задание находится в стадии выполнения, то осуществляется остановка DSP.
В случае успеха вызов ioctl()
возвращает нулевое значение.
Ошибки, возвращаемые ioctl()
:
EBADFD
— значение поляdelcore30m_job.fd
некорректно.
ELCIOC_SYS_INFO¶
Назначение¶
Получение информации об аппаратуре.
Объявление¶
struct delcore30m_hardware {
int ncores;
size_t xyram_size;
size_t core_pram_size;
};
int ioctl(int fd, ELCIOC_SYS_INFO, delcore30m_hardware *hw);
Аргументы¶
fd
— дескриптор устройства драйвера.hw
— указатель на структуруdelcore30m_hardware
.
Типы аргументов¶
-
delcore30m_hardware
¶ -
ncores
¶ Количество DSP в кластере.
-
xyram_size
¶ Суммарный объем памяти данных XYRAM обоих DSP без учета зарезервированных драйвером участков памяти, см.
delcore30m_buffer.size
.
-
core_pram_size
¶ Размер памяти программ PRAM для каждого DSP.
-
Описание¶
В случае успеха вызов ioctl()
возвращает нулевое значение.
ELCIOC_DMACHAIN_SETUP¶
Назначение¶
Настройка канала DMA для заданной DMA цепочки.
Объявление¶
enum sdma_descriptor_type {
SDMA_DESCRIPTOR_E1I1,
SDMA_DESCRIPTOR_E1I0,
SDMA_DESCRIPTOR_E0I0,
SDMA_DESCRIPTOR_E0I1,
};
struct sdma_descriptor {
__u32 a0e;
__u32 a0i;
__u32 astride;
__u32 bcnt;
__u32 ccr;
__u32 asize;
enum sdma_descriptor_type type;
__u32 a_init;
};
enum sdma_channel_type {
SDMA_CHANNEL_INPUT,
SDMA_CHANNEL_OUTPUT,
};
struct sdma_channel {
enum sdma_channel_type type;
unsigned int num;
};
struct delcore30m_dmachain {
int codebuf;
int core;
int external;
int internal[2];
int chain;
int job;
struct sdma_channel channel;
};
int ioctl(int fd, ELCIOC_DMACHAIN_SETUP, struct delcore30m_dmachain *chain);
Аргументы¶
fd
— файловый дескриптор устройства драйвера.chain
— указатель на структуруdelcore30m_dmachain
.
Типы аргументов¶
-
delcore30m_dmachain
¶ -
codebuf
¶ Файловый дескриптор буфера исполняемого кода для канала SDMA. Дескриптор должен быть получен с помощью ELCIOC_BUF_ALLOC. Буфер должен быть выделен в DDR. Размер буфера должен быть не менее количества передаваемых буферов умноженного на 60.
-
core
¶ Номер DSP, которому будут поступать прерывания от канала SDMA. Номер DSP записывается, исходя из значения
delcore30m_resource.mask
для ресурсаDELCORE30M_CORE
, который был выделен через ELCIOC_RESOURCE_REQUEST.
-
external
¶ Файловый дескриптор буфера в DDR, выделенный с помощью ELCIOC_BUF_ALLOC.
-
internal
¶ Массив двух файловых дескрипторов внутренних буферов в XYRAM, выделенных с помощью ELCIOC_BUF_ALLOC. Внутренние буфера участвуют в двухбуферной системе SDMA-пересылок, описание которой приведено ниже.
-
chain
¶ Файловый дескриптор буфера дескрипторов
sdma_descriptor
SDMA. Дескриптор выделяется с помощью ELCIOC_BUF_ALLOC. Буфера дескрипторов SDMA должны иметь заполненные поля:sdma_descriptor.a0e
sdma_descriptor.astride
sdma_descriptor.bcnt
sdma_descriptor.ccr
sdma_descriptor.asize
sdma_descriptor.a_init
Последний дескриптор SDMA должен иметь поле
sdma_descriptor.a_init
установленное в 0.
-
job
¶ Файловый дескриптор задания
delcore30m_job
, выделенный с помощью ELCIOC_JOB_CREATE.
-
channel
¶ Структура см.
sdma_channel
описания канала SDMA.
-
-
sdma_descriptor
¶ -
a0e
¶ Смещение дескриптора буфера данных для обработки в байтах относительно начала буфера в DDR.
-
a0i
¶ Зарезервировано.
-
astride
¶ Расстояние в байтах между соседними строками данных, находящихся в DDR.
-
bcnt
¶ Количество строк, передаваемых в одном буфере.
-
ccr
¶ Конфигурационный регистр канала SDMA. Формат регистра соответствует регистру управления для канала
CCRn
SDMA. Необходимо заполнить следующие поля, описанные в Микросхема интегральная 1892ВМ14Я. Руководство пользователя:src_inc
— автоинкремент адреса источника.src_burst_size
— разрядность одной пересылки из источника внутри пакета может быть 4 или 8 байт, при этом значение поляsdma_descriptor.asize
должно быть кратноsrc_burst_size
.dst_inc
— автоинкремент адреса приемника.dst_burst_size
— разрядность одной пересылки приемнику внутри. пакетаendian_swap_size
— перестановка порядка байт (опционально).
Остальные поля должны быть заполнены нулями.
-
asize
¶ Размер в байтах строки передаваемого буфера.
-
type
¶ Тип дескриптора SDMA:
SDMA_DESCRIPTOR_E1I1
— для начала передачи буфера необходимо отправить событие SDMA. По завершении передачи SDMA генерирует прерывание.SDMA_DESCRIPTOR_E1I0
— для начала передачи буфера необходимо отправить событие SDMA. По завершении передачи SDMA прерывние не генерируется.SDMA_DESCRIPTOR_E0I0
— передача буфера начнется сразу после завершения передачи предыдущего. По завершении передачи прерывание не генерируется.SDMA_DESCRIPTOR_E0I1
— передача буфера начнется сразу после завершения передачи предыдущего. По завершении передачи SDMA генерирует прерывание.
-
a_init
¶ Смещение до следующего дескриптора SDMA относительно текущего дескриптора. Последний дескриптор должен иметь поле
a_init
, равное нулю.
-
-
sdma_channel
¶ -
type
¶ Тип канала SDMA:
SDMA_CHANNEL_INPUT
— входной (передача из DDR в XYRAM).SDMA_CHANNEL_OUTPUT
— выходной (передача из XYRAM в DDR).
-
num
¶ Номер канала SDMA, который записывается исходя из значения
delcore30m_resource.mask
для ресурсаDELCORE30M_SDMA
, который был выделен через ELCIOC_RESOURCE_REQUEST.
-
Описание¶
Ввиду ограниченности памяти XYRAM, передаваемые данные необходимо разбить на фрагменты одинакового размера (размер таких фрагментов не должен превышать 58 КБ), для каждого из которых составляется дескриптор. Драйвер, основываясь на полученных дескрипторах, составляет программу для SDMA и загружает ее в DDR. Программа SDMA состоит из ожидания события для передачи очередного буфера, самой передачи буфера и посылки прерывания DSP, сигнализирующим о завершении передачи очередного буфера. Таким образом, программа DSP должна посылать событие каналу SDMA (см. документацию на SDMA), ожидать прерывание от SDMA и после этого обрабатывать полученный буфер. Драйвер для i-ого канала SDMA настраивает событие с номером 8+i и прерывание с номером i. На рисунке 11 приведена диаграмма взаимодействия DSP и SDMA при двухбуферной обработке. Числами обозначены номера буферов, загружаемых SDMA. Синим цветом обозначена часть кода, выполняемая на DSP и отвечающая за обработку прерываний, ожидание и запуск SDMA-каналов.
Примечание
Для каждой задачи может быть задействовано несколько SDMA-каналов. Для каждого канала необходимо по отдельности вызывать ELCIOC_DMACHAIN_SETUP.
Перед вызовом ioctl()
необходимо заполнить все поля структуры delcore30m_dmachain
,
кроме sdma_descriptor.a0i
. Значение поля sdma_descriptor.a0i
драйвер получает через
delcore30m_dmachain.internal
.
В случае успеха вызов ioctl()
возвращает нулевое значение.
Ошибки, возвращаемые ioctl()
:
EBUSY
— канал с номеромsdma_channel.num
уже запущен.EBADF
— значение поляdelcore30m_dmachain.job
некорректно.ENOMEM
— в системе недостаточно свободной памяти DDR.EFAULT
— неправильно составлена цепочка SDMA-дескрипторов.
ELCIOC_GET_CAPS¶
Назначение¶
Получение информации о драйвере.
Объявление¶
struct elcore_caps {
char drvname[32];
__u32 hw_id;
};
int ioctl(int fd, ELCIOC_GET_CAPS, elcore_caps *caps);
Аргументы¶
fd
— дескриптор устройства драйвера.caps
— указатель на структуруelcore_caps
.
Описание¶
В случае успеха вызов ioctl()
возвращает нулевое значение, при этом
значение поля elcore_caps.drvname
равно значению delcore30m
, а
значение поля elcore_caps.hw_id
— идентификатору DSP.
Передача параметров DSP¶
Аргументы, представляющие адреса буферов, которые передаются DSP, размещаются в регистрах R2 и R4. Если количество аргументов больше двух, то все последующие аргументы помещаются в стеке. Порядок передачи аргументов через стек — прямой (у аргумента с меньшим порядковым номером меньший адрес в стеке). Аргументы в стеке выровнены по границе 8 байт. Адрес третьего аргумента содержится в регистре A7. Драйвер сохраняет адреса буферов в байтах. Программа для DSP, написанная на ассемблере, должна преобразовать адреса в словную адресацию. Для программ, написанных на языке C, все необходимые преобразования выполняются компилятором.
Для одновременного выполнения задачи на двух DSP программе необходимо в буферах входных аргументов предусмотреть хранение данных для обоих DSP. Через регистр R0 DSP передается порядковый номер потока, по которому программа для DSP должна определить смещение до своей части аргументов, хранящихся в буфере, относительно адреса буфера аргумента.
Пример:
Пусть задача запускается на двух DSP, и в качестве входного аргумента задачи является структура
struct foo
.
Пользовательская программа должна:
- Выделить буфер с помощью ELCIOC_BUF_ALLOC размером
2 * sizeof(struct foo)
. - Скопировать в буфер данные структуры
foo
, предназначенные для первого DSP. - Скопировать в буфер данные структуры
foo
, предназначенные для второго DSP, со смещением, равным значениюsizeof(struct foo)
.
Программа DSP для получения доступа к структуре foo
должна:
- Содержимое регистра RO, через который передается номер потока (для первого DSP R0 = 0, для
второго DSP R0 = 1), умножить на значение
sizeof(struct foo)
. - Полученное число прибавить к адресу буфера, адрес которого передается согласно Передача параметров DSP.
Профилирование программы, исполняемой на DSP¶
При установке флага DELCORE30M_PROFILE
в delcore30m_job.flags
драйвер
выделяет в DDR буфер, размер которого равен 32 КБ. Адрес буфера передается в DSP через
регистр A5. Программа на DSP должна записать в буфер два 32-разрядных числа в начале и
конце каждого интересующего участка кода:
- метка, однозначно идентифицирующая данный участок кода
- значение регистра счетчика тактов
TOTAL_CLK_CNTR
После завершения программы на DSP драйвер для каждой метки считает количество вызовов данного участка кода, минимальное, максимальное и среднее времена выполнения (в тактах).