Инструменты пользователя

Инструменты сайта


prog:spec:dma

Работа с DMA в процессорах серии 1967ВНxx

Контроллер прямого доступа в память, он же DMA (Direct Memory Access) – механизм передачи данных без исполнения команд в ядре процессора. Это определение ни раз уже использовалось во всех источниках, в том числе и в соседней статье, о работе данного механизма в процессорах на базе ядра Cortex. В этом материале будет представлены основные понятия о DMA в ЦОС-процессорах серии 1967ВНхх, поскольку блок имеет существенные различия по сравнению с тем, что используется в микроконтроллерах на Cortex.

Архитектура DMA

Итак, в основном блок DMA одинаков для процессоров серии 1967, но все же есть свои особенности как для 028 так и для 044 процессора. В данном материале постараемся уточнить такие моменты.

Передача DMA может быть осуществлена между:

-внутренняя память процессора ↔ внутренняя память процессора;

-внутренняя память процессора ↔ внешняя память;

-внутренняя память процессора ↔ внешняя периферия;

-внешняя память процессора ↔ внешняя периферия;

-внешняя память процессора ↔ порт связи;

-порт связи ↔ порт связи;

-внутренняя память процессора ↔ внутренняя память другого процессора; (только для 1967ВН028)

-внутренняя память процессора ↔ хост-процессор; (только для 1967ВН028)

-ведущий на кластерной шине ↔ внутренняя память через AutoDMA; (только для 1967ВН028)

Каналы

Мы привыкли к тому, что для DMA необходимо указать адрес источника, адрес приёмника, добавить управляющее слово и можно запускать цикл (так работает DMA в контроллерах на Cortex). В ЦОС-процессорах DMA реализован иначе. Здесь управляющая структура DMA отдельно задается на приём и на передачу. Управляющую структуру в терминах документации принято обозначать как TCB. Но к этому вернёмся ниже по тексту.

В ЦОС серии 1967ВН всего есть 14 каналов. Приведём краткую таблицу для каждого процессора (также см. спецификации). Первые 4 канала (0-3) являются универсальными и полностью программируемыми, например, используются для пересылки память-память. Инициирование транзакции скорее всего подразумевает источник запроса, то есть в случае с данными каналами мы можем выставлять запрос программно.

Каналы 4-7 предназначены для устройств передачи. То есть данные из внутренней/внешний памяти отправляются в периферийный передатчик, соответственно (вероятно) условие возникновение запроса к dma - пустота передатчика.

Каналы 8-11 предназначены для устройств приёма. Все тоже самое, только в обратном направлении, предполагается, что условие возникновение запроса - наличие данных в приёмнике.

1967ВН028

Номер канала Источник данных Приёмник Инициирование транзакции
0-3 Программируется TCB Программируется TCB Программируется. Может использовать входы nDMAR0-3.
4-7 Программируется TCB Передатчик портов связи каналы 0-3 По запросу от периферии.
8-11 Приёмник портов связи каналы 0-3 Программируется TCB По запросу от периферии.
12 Запись данных внешним ведущим устройством Программируется TCB Всегда, при наличии данных во внутреннем буфере.
13 Запись данных внешним ведущим устройством Программируется TCB Всегда, при наличии данных во внутреннем буфере.

1967ВН044

Номер канала Источник данных Приёмник Инициирование транзакции
0-3 Программируется TCB Программируется TCB Программируется. Может использовать запрос устройства.
4-7 Программируется TCB Режим 1/2. Периферийное устройство/Адрес устройства задается в специальном регистре DCA По запросу от периферии.
8-11 Режим 1/2. Периферийное устройство/Адрес устройства задается в специальном регистре DCA Программируется TCB По запросу от периферии.
12 Запись данных цифровым коррелятором в регистр канала Программируется TCB Всегда, при наличии данных во внутреннем буфере.
13 Запись данных внешним ведущим устройством Программируется TCB Всегда, при наличии данных во внутреннем буфере.

Запросы

Каналы 0-3 предназначены для использования программных запросов и для запроса от внешних устройств с выводов nDMAR0-3 (для 044 можно использовать внешние выводы для всех каналов). Каналы же 4-11 предназначены для периферии. Если 1967ВН028 всё просто: все эти каналы предназначены для работы с передатчиками/приёмника link-порта, то в случае же с 1967ВН044 периферия представлена не только link-портами. Поэтому для того, чтобы указать DMA от какой периферии ожидать запрос, есть специальный регистр DMACFG, состоящий из двух 32 разрядных регистров DMACFGL и DMACFGH. Об этом подробнее описано в данной статье.

Управляющая структура TCB

Как уже было замечено выше блок управления передачей TCB представляет из себя квадрослово (128 бит), то есть 4 регистра по 32-бита. Именно в нём представлена информация для конфигурации канала DMA.

Квадролово TCB содержит 4 регистра: DI, DX, DY, DP.

Регистр DI

В регистр DI заносится адрес который может указывать на внутреннюю и внешнюю память. В случае если DMA программируется на передачу, то задается адрес, откуда взять данные (адрес источника), соответственно, если DMA настраивается на приём, то указывается адрес, куда положить их (адрес приёмника).

биты поле Описание
31..30 DI Содержит начальных адрес блока данных

Регистр DX

биты поле Описание
31..16 DXC Количество слов блока данных, которое необходимо передать
15..0 DXM Значение модификатора, используемое для изменения адреса после каждой транзакции

Регистр DY

Данный регистр имеет описание аналогичное регистру DX и используется вместе с ним, когда режим работы двумерного DMA разрешен. Если двумерный режим выключить, то необходимо оставить данное поле равным нулю.

Регистр DP

Основной регистр конфигурации управляющего слова. Здесь очень важно соблюдать определённые правила и ограничения при его программировании. Они приведены в документации. Когда канал завершает работу, поле TY переходит в состояние "канал выключен", то есть старшие биты сбрасываются.

биты поле Значения Описание
31..29 TY 000 - канал выключен
001 - линк порт
010 - внутренняя память (16/16)
011 - внутренняя память (22/10)
100 - внешняя память (16/16)
101 - внешнее устройство Flyby (только для 028, для 044 - резерв)
110 - загрузочное EPROM
111 - внешняя память
Тип обмена, выбор источника или приёмника
28 PR 1 - высокий
0 - обычный
Приоритет циклов обмена
27 2D 1 - двумерная посылка
0 - одномерная посылка
Включение режима 2-х мерной посылки
26..25 LEN 00 - резерв
01- слово 32 бита
10 - длинное слово 64 бита
11 - квадрослово 128 бит
Длина передаваемых данных (операнда) в одном цикле
24 INT 1 - разрешено
0- запрещено
Разрешение запроса прерывания после окончания работы канала
23 DRQ 1 - разрешено
0- запрещено
Разрешение анализа от внешнего запроса/запроса от периферии
22 CHEN 1 - разрешено
0- запрещено
Значение модификатора, используемое для изменения адреса после каждой транзакции
21..19 CHTG 000 – канал 8
001 – канал 9
010 – канал 10
011 – канал 11
100 – канал 4
101 – канал 5
110 – канал 6
111 – канал 7
Канал следующей цепочки. Поле имеет значение только для каналов 4-11
18..0 CHPT Указатель цепочки DMA. Поле включает в себя разряды 20-2 адреса внутренней памяти, где находится значение следующего регистра TCB

Пример

Рассмотрим следующий пример работы с DMA: просто скопируем массив данных из одной области памяти в другую. То есть нам необходимо работать с полностью программируемыми каналами 0-3. Для этого откроем описание библиотеки HAL и воспользуемся стандартной функцией для копирования данных с помощью DMA HAL_DMA_MemCopy32 (). В принципе, пример можно запускать на обоих процессорах - 028/044, только необходимо подключить соответствующие библиотеки.

Запуск на 1967ВН044. Ниже приведу листинг кода.

#include <hal_1967VN044.h>
#define N 1024
int data_tx32[N];
int data_rx32[N];

	int main(void)
{		int i;
		int errFlag;
		for (i=0; i < N; i++)
			data_tx32[i] =i ;
		errFlag = HAL_DMA_MemCopy32 (2, &data_tx32, &data_rx32, N);
	 	return 0;
}

Комментарии. В качестве параметров необходимо передать номер используемого канала DMA, указатель на массив передаваемых данных (его мы предварительно инициализируем последовательностью чисел от одного до N), указатель на массив, куда DMA сложит данные и количество самих данных. Функция возвращает флаг ошибки. Очень удобно, когда все делает одна функция, но для понимания работы DMA рассмотрим, что именно она делает. Итак, в первую очередь происходит объявление структуры управляющих данных DMA. И здесь кроется очень важный момент, который необходимо выделить.

Управляющая структура DMA должна быть выровнена на квадрослово.
uint32_t __attribute((aligned(4))) tcb_dcs[4];
uint32_t __attribute((aligned(4))) tcb_dcd[4];

Теперь посмотрим на инициализацию самой структуры. Стоит отметить тот момент, поскольку мы пользуемся 2 каналом DMA, здесь мы будем инициализировать 2 управляющие структуры TCB: источника (куда мы положим данные для передачи) и назначения (куда DMA сложит отправленные нами данные). В терминах спецификации квадрослово источника именуется DCS, а квадрослово приёмника или назначения - DCD. Рассмотрим DCS:

tcb_dcs[ 3 ] = 0;
HAL_DMA_InitMemType( ( uint32_t )src, tcb_dcs[ 3 ] );
tcb_dcs[ 0 ] = ( uint32_t ) src;
tcb_dcs[ 1 ] = ( size << 16 ) | 1;
tcb_dcs[ 2 ] = 0;
tcb_dcs[ 3 ] |= TCB_NORMAL;

Итак, для начала четвертый регистр DP сбрасывается. Затем с помощью макроса HAL_DMA_InitMemType проверяется, по какому адресу находится массив входных данных и в соответствии с ним задается поле TY регистра DP. В данном случае используется внутренняя память и поле TY=010;

#define TCB_INTMEM (0x40000000)

Затем в регистр DI заносится адрес массива данных для отправки. В регистр DX, в младшее полуслово заносим значение модификатора адреса DMA, используемое для изменения адреса после каждой транзакции, а в старшее полуслово заносим общее количество посылок, то есть размерность нашего массива для передачи. Регистр DY инициализируется нулём, поскольку двумерный DMA мы не используем. И в конце в регистр DP, к ранее заполненному полю TY, добавляем поле LEN =010, то есть указываем длину передаваемых данных операнда в одном цикле - 32 бита.

#define TCB_NORMAL (0x02000000)

Собственно, инициализация управляющей структуры назначения DMA DCD ничем не отличается, разве что в регистр DI заносится указатель на массив принимаемых данных.

tcb_dcd[ 3 ] = 0;
HAL_DMA_InitMemType( ( uint32_t )dst, tcb_dcd[ 3 ] );
tcb_dcd[ 0 ] = ( uint32_t ) dst;
tcb_dcd[ 1 ] = ( size << 16 ) | 1;
tcb_dcd[ 2 ] = 0;
tcb_dcd[ 3 ] |= TCB_NORMAL;   

Теперь сконфигурированные структуры с помощью функций HAL_DMA_WriteDCS () и HAL_DMA_WriteDCD () заносятся в память по соответствующем адресам DMA второго канала.

HAL_DMA_WriteDCD( ch_number, &tcb_dcd );
HAL_DMA_WriteDCS( ch_number, &tcb_dcs );

Можно запустить программу и поставить точку останова на функции HAL_DMA_WriteDCS( ch_number, &tcb_dcs ), то есть до записи структуры на отправку. И посмотреть, как прошла конфигурация принимающего блока DCD.

Красным выделен регистр - DI.

В нём, как мы видим, лежит указатель на массив, куда мы сложим принятые данные.

Зелёным - DX;

Синим - DY;

Фиолетовым - DP;

Посмотреть значение, записанное в управляющую структуру передатчика не получится, поскольку как только конфигурационное квадрослово будет записано по адресу источника DSC2, регистры DCS и DCD тут же меняют свое значение.

Получается, как только мы помещаем квадрослово источника на передачу DMA в соответствующий регистр DCS программируемых каналов 0-3, это и есть выставление программного запроса к DMA.

Это говорит о том, что используемый второй канал DMA отработал, и старшее поле TY регистра DP приняло значение 000, что соответствует выключенному каналу. И теперь можно увидеть, как DMA перенес наши данные из массива data_tx32 в массив data_rx32.

prog/spec/dma.txt · Последние изменения: 2019/08/21 14:33 — artem