Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
В продолжение моих заметок, посвященных разбору функционала процессора от фирмы Миландр 1967ВН28, рассмотрим способы общения с внешними устройствами. В данном процессоре есть возможности общения по следующим информационным каналам. Порты LINK (LVDS) и также имеется параллельный интерфейс, предназначенный для работы с внешней памятью. В данной заметке разберем работу с интерфейсом LINK. Этот интерфейс на отладочной плате выведен на разъемы DVI и процессор имеет всего 4 таких приемопередающих линий. Стоит отметить что этот интерфейс поддерживает одновременный прием и передачу (полный дуплекс). Чтобы эффективно работать с транспортировкой массива данных задействуем каналы DMA. Из одного канала DMA будем грузить данные в передающую линию интерфейса LINK и для первого опыта соединим передающий канал одного из LINKов с его же принимающей стороной, где уже другим каналом DMA организуем прием. Разъем DVI для работы с определенным LINK портом при определенной разрядности передачи выбирается исходя из представленных в паспорте отладочной платы таблиц 8 и 9. Таблички приведены ниже.
Стоит отметить что при некоторых вариантах соединения доступен только 1-битный формат приема/передачи, это связано с тем что не на все разъемы подведено по 4 линии данных.
Теперь перейдем ближе к делу, для начала стоит обратить внимание на конфигурационные порты. В спецификации на микросхему написано что за управлением приемником отвечает регистр LRCTLx, а для передатчика аналогичные функции выполняет регистр LTCTLx ниже приведено их описание.
Оба регистра не могут изменяться в процессе обмена. Запись в оба этих регистра возможна только при сброшенном нулевом разряде.
Так же необходимо сконфигурировать порт внешними сигналами.
На имеющейся у меня отладочной плате эти параметры устанавливаются движковыми переключателями 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);
Эти четыре регистра образуют 128 разрядный регистр TCB, в который можно загрузить настройки как выровненное счетверенное слово из внутренней памяти посредством цепочки (о том как работает цепочка будет сказано ниже) или с помощью ядра. Для инициирования нового обмена, после завершения текущего, программа должна записать новые параметры в регистры TCB.
Теперь перейдем к выбору LINK и каналов DMA. Все в той же спецификации на микросхему есть полезная табличка.
Так как цифра два – хорошая цифра, то решено выбрать второй 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 – Это настроечный регистр задающий режим работы передатчика\приемника канала. На табличке ниже описание его разрядов.
Теперь о цепочке, как говорилось выше у регистров ТСВ есть вариант загрузки значения от ядра или из внутренней памяти посредством цепочки. В режиме цепочки при разрешении последующей передачи, процессор автоматически перезагружает ТСВ и выполняет следующее задание. Как видно из таблички мы можем работать в режиме однократной передачи установив бит 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:
Теперь посмотрим насколько все работает, запустив отладчик.
В массиве передачи лежат данные.
Массив, в который мы передаем изначально пуст
Когда же мы доходим до пустых операторов то там появляются передаваемые данные
Как видно передача произошла корректно. В следующий раз рассмотрим передачу по LVDS между процессорами, благо на отладочной плате у меня их два. А на этом закончу, спасибо за внимание.