Драйвер 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.

Объявление

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.

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

delcore30m_resource
fd

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

type

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

  • DELCORE30M_CORE — DSP.
  • DELCORE30M_SDMA — каналы SDMA.
num

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

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

Назначение

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

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

Описание

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

Назначение

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

Объявление

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 должен иметь поле 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-каналов.

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

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

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

elcore_caps
drvname

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

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