Разбор работы LVDS портов и DMA на отечественном DSP 1967ВН028

image-loader.svg

В продолжение моих заметок, посвященных разбору функционала процессора от фирмы Миландр 1967ВН28, рассмотрим способы общения с внешними устройствами. В данном процессоре есть возможности общения по следующим информационным каналам. Порты LINK (LVDS) и также имеется параллельный интерфейс, предназначенный для работы с внешней памятью. В данной заметке разберем работу с интерфейсом LINK. Этот интерфейс на отладочной плате выведен на разъемы DVI и процессор имеет всего 4 таких приемопередающих линий. Стоит отметить что этот интерфейс поддерживает одновременный прием и передачу (полный дуплекс). Чтобы эффективно работать с транспортировкой массива данных задействуем каналы DMA. Из одного канала DMA будем грузить данные в передающую линию интерфейса LINK и для первого опыта соединим передающий канал одного из LINKов с его же принимающей стороной, где уже другим каналом DMA организуем прием. Разъем DVI для работы с определенным LINK портом при определенной разрядности передачи выбирается исходя из представленных в паспорте отладочной платы таблиц 8 и 9. Таблички приведены ниже.

image-loader.svgimage-loader.svgimage-loader.svg

Стоит отметить что при некоторых вариантах соединения доступен только 1-битный формат приема/передачи, это связано с тем что не на все разъемы подведено по 4 линии данных.

Теперь перейдем ближе к делу, для начала стоит обратить внимание на конфигурационные порты. В спецификации на микросхему написано что за управлением приемником отвечает регистр LRCTLx, а для передатчика аналогичные функции выполняет регистр LTCTLx ниже приведено их описание.

ebe8bb8e7b215f1437ffa3d77ba390b2.PNGimage-loader.svgimage-loader.svg

Оба регистра не могут изменяться в процессе обмена. Запись в оба этих регистра возможна только при сброшенном нулевом разряде.

Так же необходимо сконфигурировать порт внешними сигналами.

image-loader.svg

На имеющейся у меня отладочной плате эти параметры устанавливаются движковыми переключателями SA14 и SA15. Я выставил TIMR0E в единицу, остальное по идее никак в данной ситуации не интересует.

Пойдем далее, было бы неплохо определиться с выбором канала LINK и каналов DMA. К сожалению, выбрать, бросив монетку у нас не получится ибо в зависимости от выбора LINKа мы можем либо иметь возможность передавать и в формате 4 бит и 1 бит, или только в формате 1 го бита.

Далее DMA, в этом процессоре DMA работает не так как например в Cortex, где мы просто указываем «откуда взять и куда положить», а потом добавляем управляющее слово которое пускает процесс. Здесь управляющая структура DMA отдельно задается на приём и на передачу. Управляющей структурой для DMA  являются регистры блока управления передачей TCB.

Как пишут в документации, каждый TCB (он же DCx) регистр имеет длину 128 бит и разделен на четыре 32-битных

— регистр индекса (DI);

— регистр X количества и приращения (DX);

— регистр Y количества и приращения (DY);

— регистр управления и указателя цепочки (DP);

image-loader.svg

Эти четыре регистра образуют 128 разрядный регистр TCB, в который можно загрузить настройки как выровненное счетверенное слово из внутренней памяти посредством цепочки (о том как работает цепочка будет сказано ниже) или с помощью ядра. Для инициирования нового обмена, после завершения текущего, программа должна записать новые параметры в регистры TCB.

Теперь перейдем к выбору LINK и каналов DMA. Все в той же спецификации на микросхему есть полезная табличка.

image-loader.svg

Так как цифра два — хорошая цифра, то решено выбрать второй LINK.  Сразу по табличке видим, что для его передатчика используется канал DMA 6, а для его приемника применяется канал 10. Так же по табличкам 8 и 9 видим что нам подходят разъемы XS4, XS5.

На всякий случай еще раз поясню что DC в табличке это те же самые группы регистров TCB — DI, DX, DY, DP.

Теперь подробнее про управляющие регистры DMA:

Регистр DI

Данный 32-разрядный регистр содержит начальный адрес блока данных и используется для прямого доступа к памяти. Он может указывать на адрес внешней памяти или внутренней памяти.

Регистр DX

Этот 32-разрядный регистр имеет два поля:

DXM — младшие биты (с 0-го по 15-й или с 0-го по 21-й) хранят значение модификатора адреса (DX_MODIFY), которое используется для изменения адреса после каждой транзакции;

DXC — старшие биты (с 16-го по 31-й или с 22-го по 31-й) хранят значение количества слов (DX_COUNT) в блоке данных, который необходимо передать. Например, если необходимо передать 16 слов данных, непрерывно размещенных в памяти, порциями по 4 слова в каждой передаче, то параметр DXC будет равен 16, а значение DXM будет равно 4. Длина операнда (порции данных) в регистре DP устанавливается равной счетверенному слову.

Существуют ограничения, которые следует принимать во внимание при программировании TCB:

 — указатель DI должен содержать адрес, выровненный на границе операнда, определяемого в регистре DP;

 —  Значение DXM должно быть кратно размеру операнда;

 —  Значение DXC должно быть кратным длине операнда.

Выбор длины полей модификатора и количества определяется типом обмена, задаваемым полем TY в регистре DP.

Регистр DY –применяется при реализации двумерной передачи, но он нам сейчас не понадобится, потому опустим.

Регистр DP — Это настроечный регистр задающий режим работы передатчика\приемника канала. На табличке ниже описание его разрядов.

image-loader.svgimage-loader.svgimage-loader.svg

Теперь о цепочке, как говорилось выше у регистров ТСВ есть вариант загрузки значения от ядра или из внутренней памяти посредством цепочки. В режиме цепочки при разрешении последующей передачи, процессор автоматически перезагружает ТСВ и выполняет следующее задание. Как видно из таблички мы можем работать в режиме однократной передачи установив бит CHEN в ноль. В этом режиме DMA выполнит прием или передачу блока данных в соответствии с настройками регистров DI и DX. В случае если разрешено прерывание сгенерирует прерывание по завершению работы и остановится.

Если же есть необходимость передать не один блок данных, или принимать непрерывный поток данных, то стоит разрешить загрузку следующей цепочки настроек для TCB. Это реализуется установлением бита CHEN в единицу, также необходимо записать в первые 18 битов регистра DP указатель на область памяти в которой хранятся настройки для следующей цепочки. При таком раскладе по завершению работы канал DMA будет сразу получать новые настройки и продолжать работу.

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

Применим все это дело теперь на практике. Сначала реализуем однократную передачу блока данных от процессора к самому себе.

Сначала выделим память под буфер приема и буфер передачи

.SECTION /DOUBLE64 /CHAR32 .data;
.ALIGN 4;
.var arrt[8]={1,2,3,4,5,6,7,8};//массив передачи
.var arrr[8]={0,0,0,0,0,0,0,0};//массив приема

Далее выключим все LVDS порты и каналы DMA

.SECTION .program;
.ALIGN_CODE 4;
.GLOBAL _main;
_main:
SQCTL = 0x1200;;//переход в режим супервизора 
//выключим для начала линки
j0=0;;
LRCTL0=j0;;
LRCTL1=j0;;
LRCTL2=j0;;
LRCTL3=j0;;
LtCTL2=j0;;
//и их dma каналы
xr0=0;xr1=0;;
xr2=0;xr3=0;;
dc11=xr3:0;;
dc10=xr3:0;;
dc9 =xr3:0;;
dc8 =xr3:0;;
dc7=xr3:0;;

Теперь включаем Link передатчика, а затем выдержав драматическую паузу включаем приемник. Это делается, с целью чтобы приемник до того, как окажется в надежных руках передатчика не наелся помех.

j0=(1<<4)+(4<<5);;//формат 4 бит
LtCTL2=j0;;
j0=(1<<4);;//формат 4 бит
LRCTL2=j0;;
lc0 = 10;;
dly_p1: if nlc0e, jump dly_p1;; // подождем
//включим передатчик
j0=1+(1<<4)+(4<<5);;
LtCTL2=j0;;
lc0 = 100;;
dly_p2: if nlc0e, jump dly_p2;; 
//Включим приемник
j0=1+(1<<4);;
LRCTL2=j0;;

Настраиваем и включаем DMA приемника и передатчика. Бит CHEN остается в нуле, т.к. нас сейчас интересует однократный прием/передача. Первые 18 битов регистра DP (xr3) так же оставляем нулевыми, так как указатель на следующую цепочку нам сейчас не нужен.

//Настраиваем и включаем DMA приемника
xr0 = arrr;;//указатель на массив данных
xr1=0x80004;;//сообщаем что будет 8 слов по 4 байта
xr2=0;;
xr3=0x40000000/*выбираем внутреннюю память*/+(3<<19)/*выбираем номер порта линк и направление
*/ +(3<<25);;/*ставим длину данных*/
dc10=xr3:0;;
//Настраиваем и включаем DMA передатчика
xr0=arrt;;//указатель на массив данных
xr1=0x80004;;//сообщаем что будет 8 слов по 4 байта
xr2=0;;
xr3=0x40000000/*выбираем внутреннюю память*/+ (7<<19)/*выбираем номер порта линк и направление
*/ + (3<<25);;/*ставим длину данных*/
dc6=xr3:0;;

Далее останавливаемся в бесконечном цикле

_main_loop:
nop;nop;nop;;
nop;nop;nop;;
nop;nop;nop;;
nop;nop;nop;;
nop;nop;nop;;
nop;nop;nop;;
nop;nop;nop;;
nop;nop;nop;;
nop;nop;nop;;
jump _main_loop;nop;;
_main.end:

Теперь посмотрим насколько все работает, запустив отладчик.

В массиве передачи лежат данные.

image-loader.svg

Массив, в который мы передаем изначально пуст

image-loader.svg

Когда же мы доходим до пустых операторов то там появляются передаваемые данные

image-loader.svg

Как видно передача произошла корректно. В следующий раз рассмотрим передачу по LVDS между процессорами, благо на отладочной плате у меня их два. А на этом закончу, спасибо за внимание.

© Habrahabr.ru