Драйвер DSP 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.
Объявление
int ioctl(int fd, ELCIOC_BUF_ALLOC, struct delcore30m_buffer *buf);
Аргументы
fd
— дескриптор устройства драйвера.buf
— указатель на структуруdelcore30m_buffer
.
Типы аргументов
-
enum delcore30m_memory_type
-
struct delcore30m_buffer
-
int fd
Файловый дескриптор буфера.
-
enum delcore30m_memory_type type
Тип памяти, в которой выделяется буфер. Допустимые значения:
DELCORE30M_MEMORY_XYRAM
— память данных XYRAM;DELCORE30M_MEMORY_SYSTEM
— общая системная память DDR.
-
int core_num
Номер DSP-ядра, для которого создается буфер (используется только для
DELCORE30M_MEMORY_XYRAM
).
-
int fd
Описание
Перед вызовом 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
Назначение
Выделение ресурсов.
Объявление
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
Назначение
Создание задания для DSP.
Объявление
int ioctl(int fd, ELCIOC_JOB_CREATE, struct delcore30m_job *job);
Аргументы
fd
— дескриптор устройства драйвера.job
— указатель на структуруdelcore30m_job
.
Типы аргументов
-
DELCORE30M_PROFILE
-
enum delcore30m_job_status
-
enum delcore30m_job_rc
-
struct delcore30m_job
-
int fd
Файловый дескриптор задания.
-
unsigned int inum
Количество входных буферов. Максимально допустимое значение — 15.
-
unsigned int onum
Количество выходных буферов. Максимально допустимое значение — 15.
-
int input[15]
Массив файловых дескрипторов входных буферов размера
delcore30m_job.inum
, выделенных через ELCIOC_BUF_ALLOC. Максимально допустимое значение — 15.
-
int output[15]
Массив файловых дескрипторов выходных буферов размера
delcore30m_job.onum
, выделенных через ELCIOC_BUF_ALLOC. Максимально допустимое значение — 15.
-
int cores_fd
Файловый дескриптор ресурса DSP, выделенного через ELCIOC_RESOURCE_REQUEST, на которых может быть запущено задание.
-
int sdmas_fd
Дескриптор каналов SDMA, выделенный через ELCIOC_RESOURCE_REQUEST, которые могут быть использованы заданием. Если для выполнения задания не нужно использовать SDMA, установить это поле в ноль.
-
enum delcore30m_job_status status
Статус задания:
DELCORE30M_JOB_IDLE
— задача либо выполнена, либо еще не помещена в очередь на выполнение, см. ELCIOC_JOB_ENQUEUE.DELCORE30M_JOB_ENQUEUED
— задача находится в очереди на выполнение на DSP.DELCORE30M_JOB_RUNNING
— задача выполняется на DSP.
-
enum delcore30m_job_rc rc
Код возврата последнего завершённого задания:
DELCORE30M_JOB_ERROR
— задача завершилась с ошибкой.DELCORE30M_JOB_CANCELED
— задача была отменена.DELCORE30M_JOB_SUCCESS
— задача успешно завершилась.
-
__u32 flags
Флаги задания. Допустимые флаги:
DELCORE30M_PROFILE
— флаг профилирования DSP-программы, см. Профилирование программы, исполняемой на DSP.
-
int fd
Описание
Перед вызовом 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
Назначение
Получение информации об аппаратуре.
Объявление
int ioctl(int fd, ELCIOC_SYS_INFO, delcore30m_hardware *hw);
Аргументы
fd
— дескриптор устройства драйвера.hw
— указатель на структуруdelcore30m_hardware
.
Типы аргументов
-
size_t
Описание
В случае успеха вызов ioctl()
возвращает нулевое значение.
ELCIOC_DMACHAIN_SETUP
Назначение
Настройка канала DMA для заданной DMA цепочки.
Объявление
int ioctl(int fd, ELCIOC_DMACHAIN_SETUP, struct delcore30m_dmachain *chain);
Аргументы
fd
— файловый дескриптор устройства драйвера.chain
— указатель на структуруdelcore30m_dmachain
.
Типы аргументов
-
__u32
-
enum sdma_descriptor_type
-
struct delcore30m_dmachain
-
int codebuf
Файловый дескриптор буфера исполняемого кода для канала SDMA. Дескриптор должен быть получен с помощью ELCIOC_BUF_ALLOC. Буфер должен быть выделен в DDR. Размер буфера должен быть не менее количества передаваемых буферов умноженного на 60.
-
int core
Номер DSP, которому будут поступать прерывания от канала SDMA. Номер DSP записывается, исходя из значения
delcore30m_resource.mask
для ресурсаDELCORE30M_CORE
, который был выделен через ELCIOC_RESOURCE_REQUEST.
-
int external
Файловый дескриптор буфера в DDR, выделенный с помощью ELCIOC_BUF_ALLOC.
-
int internal[2]
Массив двух файловых дескрипторов внутренних буферов в XYRAM, выделенных с помощью ELCIOC_BUF_ALLOC. Внутренние буфера участвуют в двухбуферной системе SDMA-пересылок, описание которой приведено ниже.
-
int chain
Файловый дескриптор буфера дескрипторов
sdma_descriptor
SDMA. Дескриптор выделяется с помощью ELCIOC_BUF_ALLOC. Буфера дескрипторов SDMA должны иметь заполненные поля:Последний дескриптор SDMA должен иметь поле
sdma_descriptor.a_init
установленное в 0.
-
int job
Файловый дескриптор задания
delcore30m_job
, выделенный с помощью ELCIOC_JOB_CREATE.
-
struct sdma_channel channel
Структура см.
sdma_channel
описания канала SDMA.
-
int codebuf
-
struct sdma_descriptor
-
__u32 a0e
Смещение дескриптора буфера данных для обработки в байтах относительно начала буфера в DDR.
-
__u32 a0i
Смещение дескриптора буфера данных для обработки в байтах относительно начала буфера в XYRAM.
-
__u32 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
— перестановка порядка байт (опционально).
Остальные поля должны быть заполнены нулями.
-
enum sdma_descriptor_type type
Тип дескриптора SDMA:
SDMA_DESCRIPTOR_E1I1
— для начала передачи буфера необходимо отправить событие SDMA. По завершении передачи SDMA генерирует прерывание.SDMA_DESCRIPTOR_E1I0
— для начала передачи буфера необходимо отправить событие SDMA. По завершении передачи SDMA прерывание не генерируется.SDMA_DESCRIPTOR_E0I0
— передача буфера начнется сразу после завершения передачи предыдущего. По завершении передачи прерывание не генерируется.SDMA_DESCRIPTOR_E0I1
— передача буфера начнется сразу после завершения передачи предыдущего. По завершении передачи SDMA генерирует прерывание.
-
__u32 a0e
-
struct sdma_channel
-
enum sdma_channel_type type
Тип канала SDMA:
SDMA_CHANNEL_INPUT
— входной (передача из DDR в XYRAM).SDMA_CHANNEL_OUTPUT
— выходной (передача из XYRAM в DDR).
-
unsigned int num
Номер канала SDMA, который записывается исходя из значения
delcore30m_resource.mask
для ресурсаDELCORE30M_SDMA
, который был выделен через ELCIOC_RESOURCE_REQUEST.
-
enum sdma_channel_type type
Описание
Ввиду ограниченности памяти XYRAM, передаваемые данные необходимо разбить на фрагменты одинакового размера (размер таких фрагментов не должен превышать 58 КБ), для каждого из которых составляется дескриптор. Драйвер, основываясь на полученных дескрипторах, составляет программу для SDMA и загружает ее в DDR. Программа SDMA состоит из ожидания события для передачи очередного буфера, самой передачи буфера и посылки прерывания DSP, сигнализирующим о завершении передачи очередного буфера. Таким образом, программа DSP должна посылать событие каналу SDMA (см. документацию на SDMA), ожидать прерывание от SDMA и после этого обрабатывать полученный буфер. Драйвер для i-ого канала SDMA настраивает событие с номером 8+i и прерывание с номером i. На рисунке Рис. 2 приведена диаграмма взаимодействия DSP и SDMA при двухбуферной обработке. Числами обозначены номера буферов, загружаемых SDMA. Синим цветом обозначена часть кода, выполняемая на DSP и отвечающая за обработку прерываний, ожидание и запуск SDMA-каналов.
Примечание
Для каждой задачи может быть задействовано несколько SDMA-каналов. Для каждого канала необходимо по отдельности вызывать ELCIOC_DMACHAIN_SETUP.
Перед вызовом ioctl()
необходимо заполнить все поля структуры delcore30m_dmachain
.
В случае успеха вызов ioctl()
возвращает нулевое значение.
Ошибки, возвращаемые ioctl()
:
EBUSY
— канал с номеромsdma_channel.num
уже запущен.EBADF
— значение поляdelcore30m_dmachain.job
некорректно.ENOMEM
— в системе недостаточно свободной памяти DDR.EFAULT
— неправильно составлена цепочка SDMA-дескрипторов.
ELCIOC_GET_CAPS
Назначение
Получение информации о драйвере.
Объявление
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 драйвер для каждой метки считает количество вызовов данного участка кода, минимальное, максимальное и среднее времена выполнения (в тактах).