Работа с внешней параллельной шиной на отечественном DSP процессоре 1967ВН028 фирмы Миландр

a9371419fb94d806ff8b9e58a590fbaf.png

Отечественный процессор 1967ВН028 от фирмы Milandr предназначен в первую очередь для решения вычислительных задач и поэтому на его борту есть всего два способа обмена данными с внешним миром. Один из них — работа с LVDS LINK портами (разобран в прошлых статьях) и второй это внешняя параллельная шина.

Внешняя шина процессора 1967ВН028 включает в себя шину данных, с пропускной способностью 32 или 64 разрядов, 32-разрядную шину адреса и контрольные сигналы. Большинство контрольных сигналов являются двунаправленными, поскольку процессор может быть, как ведущим, так и ведомым на внешней шине.

Блок интерфейса внешней шины может быть сконфигурирован под различные настройки путем записи в конфигурационные регистры SYSCON и SDRCON внешнего порта. Установочные параметры этих двух регистров должны быть одинаковыми для всех процессоров в системе. Широковещательная запись рекомендована при инициализации регистров SYSCON и SDRCON. Доступ к регистру SYSCON возможен только в режиме супервизора. Для установки режима супервизора необходимо в регистре SQCTL установить бит NMOD. Регистр SYSCON также невозможно изменять во время передачи данных.

Описание регистра SYSCON из спецификации на микросхему:

c09877a2635049f8803a5c03462e86cb.pngfcd9c9e49b0f1dbd0bd8016b82128f90.pngОписание регистра SYSCONОписание регистра SYSCON

Ширина шины задается в регистре SYSCON в соответствии со следующей таблицей из документации

4916c0dc673752b0e45a549ee509a034.png

Некоторые комбинации запрещены из-за того, что при их установке работа системы будет некорректной. Процессор не отслеживает запрещенные комбинации, и вся ответственность за их использование лежит на пользователе.

Протоколы обмена данными по внешней шине

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

Выбор протокола работы внешней шины происходит при установке значений на битах 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 в данном процессоре описан в моей прошлой статье ссылка.

Обращение к внешнему адресному пространству напрямую

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

6e569dac48d61c1501a8a54615a3ad12.png

Немного примеров

Из всего выше приведенного следует что через внешний порт можно передавать и читать данные двумя способами.

Способ первый. Обращение к внешнему адресному пространству напрямую

Как пример небольшой код, который зажжет индикацию на обоих процессорах размещенных на плате.

#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:

© Habrahabr.ru