Драйвер DSP delcore30m

Документ содержит описание драйвера для DSP-кластера DELcore-30M, состоящего из двух DSP-ядер (далее DSP) ELcore-30M.

В документе используются ссылки на системный контроллер DMA (SDMA). Описание SDMA приведено в документе Микросхема интегральная 1892ВМ14Я. Руководство пользователя.

Общее описание

Интерфейс драйвера delcore30m предоставляет функциональность:

  1. Управление памятью XYRAM (выделение, освобождение, mmap).

  2. Выделение, импорт, экспорт непрерывных буферов DMA в системной памяти (DDR).

  3. Предоставление доступа к PRAM DSP.

  4. Управление ресурсами: DSP, SDMA (выделение, освобождение).

  5. Подготовка программы для SDMA.

  6. Отправка заданий на DSP. Возможно параллельное выполнение нескольких задач на нескольких DSP, одной задачи на нескольких DSP. Уведомление о готовности заданий.

  7. Отмена заданий.

  8. Профилирование кода DSP.

Драйвер delcore30m использует следующие ресурсы 1892ВМ14Я:

  1. цифровой сигнальный процессор DELcore-30M,

  2. системный DMA-контроллер SDMA,

  3. блок поддержки атомарных операций SPINLOCK.

Ограничения драйвера:

  1. Не поддерживается одновременная работа DSP и VPU.

  2. Драйвер SDMA pl330 должен быть выгружен.

  3. Адреса памяти XYRAM, используемые в DMA-пересылках, должны быть выровнены по границе четыре байта, см. главу 4.3.1 в Микросхема интегральная 1892ВМ14Я. Руководство пользователя.

  4. Запрещено использовать глобальные переменные в коде для DSP, написанном на C.

  5. Профилирование возможно только для кода, написанного на ассемблере.

  6. Не поддерживается одновременная передача нескольких SDMA каналов разных типов.

  7. Не поддерживается работа драйвера более, чем из двух процессов.

Стандартные системные вызовы

Интерфейс реализует стандартные для файловых дескрипторов системные вызовы со следующими особенностями:

  • 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
enumerator DELCORE30M_MEMORY_XYRAM
enumerator DELCORE30M_MEMORY_SYSTEM
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).

size_t 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

Назначение

Выделение ресурсов.

Объявление

int ioctl(int fd, ELCIOC_RESOURCE_REQUEST, struct delcore30m_resource *res);

Аргументы

  • fd — дескриптор устройства драйвера.

  • res — указатель на структуру delcore30m_resource.

Типы аргументов

enum delcore30m_resource_type
enumerator DELCORE30M_CORE
enumerator DELCORE30M_SDMA
struct delcore30m_resource
int fd

Файловый дескриптор ресурса.

enum delcore30m_resource_type type

Тип выделяемого ресурса:

  • DELCORE30M_CORE — DSP.

  • DELCORE30M_SDMA — каналы SDMA.

unsigned int num

Количество единиц выделяемого ресурса.

unsigned long mask

Маска выделяемого ресурса.

Описание

Перед вызовом 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
enumerator DELCORE30M_JOB_IDLE
enumerator DELCORE30M_JOB_ENQUEUED
enumerator DELCORE30M_JOB_RUNNING
enum delcore30m_job_rc
enumerator DELCORE30M_JOB_ERROR
enumerator DELCORE30M_JOB_CANCELLED
enumerator DELCORE30M_JOB_SUCCESS
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

Флаги задания. Допустимые флаги:

Описание

Перед вызовом 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():

ELCIOC_JOB_CANCEL

Назначение

Отмена задания.

Объявление

int ioctl(int fd, ELCIOC_JOB_CANCEL, struct delcore30m_job *job);

Аргументы

  • fd — дескриптор устройства драйвера.

  • job — указатель на структуру delcore30m_job, который был получен после вызова ELCIOC_JOB_CREATE.

Описание

Задание удаляется из всех очередей. Если задание находится в стадии выполнения, то осуществляется остановка DSP.

В случае успеха вызов ioctl() возвращает нулевое значение.

Ошибки, возвращаемые ioctl():

ELCIOC_SYS_INFO

Назначение

Получение информации об аппаратуре.

Объявление

int ioctl(int fd, ELCIOC_SYS_INFO, delcore30m_hardware *hw);

Аргументы

  • fd — дескриптор устройства драйвера.

  • hw — указатель на структуру delcore30m_hardware.

Типы аргументов

size_t
struct delcore30m_hardware
int ncores

Количество DSP в кластере.

size_t xyram_size

Суммарный объем памяти данных XYRAM обоих DSP без учета зарезервированных драйвером участков памяти, см. delcore30m_buffer.size.

size_t core_pram_size

Размер памяти программ PRAM для каждого DSP.

Описание

В случае успеха вызов 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
enumerator SDMA_DESCRIPTOR_E1I1
enumerator SDMA_DESCRIPTOR_E1I0
enumerator SDMA_DESCRIPTOR_E0I0
enumerator SDMA_DESCRIPTOR_E0I1
enum sdma_channel_type
enumerator SDMA_CHANNEL_INPUT
enumerator SDMA_CHANNEL_OUTPUT
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.

struct sdma_descriptor
__u32 a0e

Смещение дескриптора буфера данных для обработки в байтах относительно начала буфера в DDR.

__u32 a0i

Смещение дескриптора буфера данных для обработки в байтах относительно начала буфера в XYRAM.

__u32 astride

Расстояние в байтах между соседними строками данных, находящихся в DDR.

__u32 bcnt

Количество строк, передаваемых в одном буфере.

__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 — перестановка порядка байт (опционально).

Остальные поля должны быть заполнены нулями.

__u32 asize

Размер в байтах строки передаваемого буфера.

enum sdma_descriptor_type type

Тип дескриптора SDMA:

  • SDMA_DESCRIPTOR_E1I1 — для начала передачи буфера необходимо отправить событие SDMA. По завершении передачи SDMA генерирует прерывание.

  • SDMA_DESCRIPTOR_E1I0 — для начала передачи буфера необходимо отправить событие SDMA. По завершении передачи SDMA прерывание не генерируется.

  • SDMA_DESCRIPTOR_E0I0 — передача буфера начнется сразу после завершения передачи предыдущего. По завершении передачи прерывание не генерируется.

  • SDMA_DESCRIPTOR_E0I1 — передача буфера начнется сразу после завершения передачи предыдущего. По завершении передачи SDMA генерирует прерывание.

__u32 a_init

Смещение до следующего дескриптора SDMA относительно текущего дескриптора. Последний дескриптор должен иметь поле a_init, равное нулю.

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.

Описание

Ввиду ограниченности памяти XYRAM, передаваемые данные необходимо разбить на фрагменты одинакового размера (размер таких фрагментов не должен превышать 58 КБ), для каждого из которых составляется дескриптор. Драйвер, основываясь на полученных дескрипторах, составляет программу для SDMA и загружает ее в DDR. Программа SDMA состоит из ожидания события для передачи очередного буфера, самой передачи буфера и посылки прерывания DSP, сигнализирующим о завершении передачи очередного буфера. Таким образом, программа DSP должна посылать событие каналу SDMA (см. документацию на SDMA), ожидать прерывание от SDMA и после этого обрабатывать полученный буфер. Драйвер для i-ого канала SDMA настраивает событие с номером 8+i и прерывание с номером i. На рисунке Рис. 2 приведена диаграмма взаимодействия DSP и SDMA при двухбуферной обработке. Числами обозначены номера буферов, загружаемых SDMA. Синим цветом обозначена часть кода, выполняемая на DSP и отвечающая за обработку прерываний, ожидание и запуск SDMA-каналов.

../../_images/dsp-sdma-interaction.png

Рис. 2 Диаграмма последовательности взаимодействия 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.

Типы аргументов

struct elcore_caps
char drvname[32]

Название драйвера.

__u32 hw_id

Идентификатор DSP из регистра IDR.

Описание

В случае успеха вызов 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 драйвер для каждой метки считает количество вызовов данного участка кода, минимальное, максимальное и среднее времена выполнения (в тактах).