Контроллер прямого доступа в память, он же DMA (Direct Memory Access) – механизм передачи данных без исполнения команд в ядре процессора. Это определение ни раз уже использовалось во всех источниках, в том числе и в соседней статье, о работе данного механизма в процессорах на базе ядра Cortex. В этом материале будет представлены основные понятия о DMA в ЦОС-процессорах серии 1967ВНхх, поскольку блок имеет существенные различия по сравнению с тем, что используется в микроконтроллерах на Cortex.
Итак, в основном блок 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 представляет из себя квадрослово (128 бит), то есть 4 регистра по 32-бита. Именно в нём представлена информация для конфигурации канала DMA.
Квадролово TCB содержит 4 регистра: DI, DX, DY, DP.
В регистр DI заносится адрес который может указывать на внутреннюю и внешнюю память. В случае если DMA программируется на передачу, то задается адрес, откуда взять данные (адрес источника), соответственно, если DMA настраивается на приём, то указывается адрес, куда положить их (адрес приёмника).
биты | поле | Описание |
---|---|---|
31..30 | DI | Содержит начальных адрес блока данных |
биты | поле | Описание |
---|---|---|
31..16 | DXC | Количество слов блока данных, которое необходимо передать |
15..0 | DXM | Значение модификатора, используемое для изменения адреса после каждой транзакции |
Данный регистр имеет описание аналогичное регистру DX и используется вместе с ним, когда режим работы двумерного DMA разрешен. Если двумерный режим выключить, то необходимо оставить данное поле равным нулю.
Основной регистр конфигурации управляющего слова. Здесь очень важно соблюдать определённые правила и ограничения при его программировании. Они приведены в документации. Когда канал завершает работу, поле 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. И здесь кроется очень важный момент, который необходимо выделить.
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 отработал, и старшее поле TY регистра DP приняло значение 000, что соответствует выключенному каналу. И теперь можно увидеть, как DMA перенес наши данные из массива data_tx32 в массив data_rx32.