Работа с внешней параллельной шиной на отечественном DSP процессоре 1967ВН028 фирмы Миландр
Отечественный процессор 1967ВН028 от фирмы Milandr предназначен в первую очередь для решения вычислительных задач и поэтому на его борту есть всего два способа обмена данными с внешним миром. Один из них — работа с LVDS LINK портами (разобран в прошлых статьях) и второй это внешняя параллельная шина.
Внешняя шина процессора 1967ВН028 включает в себя шину данных, с пропускной способностью 32 или 64 разрядов, 32-разрядную шину адреса и контрольные сигналы. Большинство контрольных сигналов являются двунаправленными, поскольку процессор может быть, как ведущим, так и ведомым на внешней шине.
Блок интерфейса внешней шины может быть сконфигурирован под различные настройки путем записи в конфигурационные регистры SYSCON и SDRCON внешнего порта. Установочные параметры этих двух регистров должны быть одинаковыми для всех процессоров в системе. Широковещательная запись рекомендована при инициализации регистров SYSCON и SDRCON. Доступ к регистру SYSCON возможен только в режиме супервизора. Для установки режима супервизора необходимо в регистре SQCTL установить бит NMOD. Регистр SYSCON также невозможно изменять во время передачи данных.
Описание регистра SYSCON из спецификации на микросхему:
Описание регистра SYSCON
Ширина шины задается в регистре SYSCON в соответствии со следующей таблицей из документации
Некоторые комбинации запрещены из-за того, что при их установке работа системы будет некорректной. Процессор не отслеживает запрещенные комбинации, и вся ответственность за их использование лежит на пользователе.
Протоколы обмена данными по внешней шине
Внешняя параллельная шина может работать по двум протоколам, конвейерному (синхронному) протоколу, и протоколу медленного устройства для шины (асинхронному).
Выбор протокола работы внешней шины происходит при установке значений на битах BNK#SLOW (HOSTSLOW).
В нашем примере мы разберем работу с конвейерным протоколом. Глубина конвейера для транзакций записи всегда равна 1, а глубину конвейера для транзакций чтения мы можем установить в поле PIPE регистра SYSCON для конкретной области памяти. Разряд IDLE устанавливается если есть несколько подчиненных устройств в одном банке памяти с целью предотвращения конфликта на шине данных между устройствами (фраза из документации, судя по всему речь про случай если на шине висит несколько процессоров с одинаковым ID).
После сброса регистр SYSCON имеет начальное значение равное 0×9067. Начальное значение определяет:
банк 0: медленный протокол, три состояния ожидания, включен холостой режим;
банк 1: конвейерный протокол, конвейер глубиной 1 цикл, нет состояний ожидания, включен холостой режим;
Хост: конвейерный протокол, конвейер глубиной два цикла, нет состояний ожидания, включен холостой режим.
Пользоваться внешней шиной мы можем либо с помощью DMA или же напрямую обращаясь ко внешнему адресу
Работа с внешней шиной посредством DMA
На борту у процессора 1967ВН028 имеются 14 каналов DMA. Из них только каналы с 0 по 3 имеют возможность задавать и источник данных и приемник данных. Остальные каналы либо со стороны источника, либо со стороны приемника закреплены за определенной периферией. Поэтому в выборе каналов для работы с внешней шиной мы ограничены только каналами с 0 по 4. В случае же приема от внешней шины используются также каналы 12 и 13 они же AutoDMA. Эти два канала поддерживают пересылку данных от внешнего ведущего устройства во внутреннюю память.
Существует две техники синхронизации этих устройств с DMA каналами процессора:
— Источник и приемник могут подтверждать запрос по внешним выводам nDMARx каждый раз при готовности передачи новых данных. Запросы аккумулируются в канале, и при их наличии транзакция запускается по соответствующему каналу. Возможно аккумулирование до 15 запросов.
— Источник, который может быть ведущим на кластерной шине может записывать данные в регистр AutoDMA. После того как данные записаны в регистр AutoDMA, канал AutoDMA передает принятые данные в соответствии с его настройками.
Механизм работы DMA в данном процессоре описан в моей прошлой статье ссылка.
Обращение к внешнему адресному пространству напрямую
Тут все довольно просто, мы должны обращаться к внутренней памяти внешнего имеющегося на шине процессора как к своей внутренней памяти только с небольшой припиской в адресе исходя из таблички, приведенной ниже.
Немного примеров
Из всего выше приведенного следует что через внешний порт можно передавать и читать данные двумя способами.
Способ первый. Обращение к внешнему адресному пространству напрямую
Как пример небольшой код, который зажжет индикацию на обоих процессорах размещенных на плате.
#include
.SECTION /DOUBLE64 /CHAR32 .data;
.ALIGN 4;
//Объявляем переменные
.var stack[100];//стек
.SECTION .program;
.ALIGN_CODE 4;
.GLOBAL _main;
_main:
SQCTL = 0x1200;;//переход в режим супервизора
j0=stack;;//инициализируем стек на 100 мест
j1=0x1f;;
FLAGREG=j1;;//индикация
xr1=syscon;;//настроим внешний параллельный порт
xr2=~(7<<19);;
xr1=r1 and r2;;
syscon=xr1;;
//теперь включим индикацию на процессоре ID1
j1=0x5f;;
j2=FLAGREG_LOC | P1_OFFSET_LOC;;
[j2+0]=j1;;
_main_loop:
nop;nop;nop;;
jump _main_loop;nop;;
_main.end:
Для того чтобы обращаться к адресному пространству другого процессора используем автозамены прописанные в заголовочном файле defts201.h, например чтоб обратиться к регистру в адресном пространстве процессора ID1 нужно к адресу требуемого регистра прибавить P1_OFFSET_LOC. Аналогично тому как
Способ второй. Использование внешних портов посредством DMA.
Код приемника:
#include
.SECTION /DOUBLE64 /CHAR32 .data;
.ALIGN 4;
.var stack[100];//стэк
.var rx_buffer_ID1[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
.var task_Tres[4]={0,0,0,0};
.SECTION .program;
.ALIGN_CODE 4;
.GLOBAL _main;
_main:
SQCTL = 0x1200;;//переход в режим супервизора
j0=stack;;//инициализируем стек на 100 мест
xr0 = rx_buffer_ID1;;
xr1 = 0x00100001;;
xR2 = 0x0;;
xr3 = 0x43000000;;
DC12 = xr3:0;;
j1=0x1f;;
FLAGREG=j1;;
idle;;
_main_loop:
nop;nop;nop;;
jump _main_loop;nop;;
_main.end:
Код передатчика:
#include
.SECTION /DOUBLE64 /CHAR32 .data;
.ALIGN 4;
.var stack[100];//стэк
.var task_T[4]={0,0,0,0};
.var tx_buffer_ID0[16]={0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7};
.var task_Tres[4]={0,0,0,0};
.SECTION .program;
.ALIGN_CODE 4;
.GLOBAL _main;
_main:
SQCTL = 0x1200;;//переход в режим супервизора
j0=stack;;//инициализируем стек на 100 мест
xr0 = tx_buffer_ID0;;
xr1 = 0x00100001;;
xR2 = 0x0;;
xr3 = 0x43000000;;
yr0 =0x00080074 +(5<<26);;//0x1F03E0Auto_DMA0;;//+MMS_ID1;;
yr1 = 0x00100001;;
yR2 = 0x0;;
yr3 = 0x83000000;;
DCS0 = xr3:0;;
DCD0 = yr3:0;;
j1=0x5f;;
FLAGREG=j1;;
idle;;
_main_loop:
nop;nop;nop;;
jump _main_loop;nop;;
_main.end: