Все началось с того, когда, пролистывая datasheet контролера Atmel SAM3X, который стоит на небезызвестной плате Arduino Due, я наткнулся на интересную весч.
На тот момент я не стал разбираться, что это такое. Да и вскоре забыл.
Немного позже, я разрабатывал программу для ArduinoDue и мне критически не хватало оперативной памяти для моих нужд. Я задумал поставить внешнюю с параллельным интерфейсом, но куда ее подключать? — К GPIO? — тогда я и вспомнил про эту штуку.
Заходим на forum.arduino.cc
Опа! 😉 Кто-то уже состряпал библиотеку.
Теория
Что это вообще — «параллельный интерфейс/шина«? В самом простом представлении: куча параллельно идущих проводов, каждый из которых несет бит информации.
Первое — шина данных.
Шина данных — самое главное, несколько проводников(бит), передающих информацию в двух (или одном) направлении. Количество этих жил будет равно ширине передаваемой единице информации. Если мы хотим передать байт, то это будет восемь проводов. Это также влияет на скорость передачи: мы можем сделать шины шире и передавать сразу несколько байт. Однако, имея только шину данных — мы можем передать устройству одну единицу этой информации.
Второе — шина адреса.
Шина адреса — грубо говоря, указатель на ячейку, куда мы хотим передать/забрать информацию по шине данных. Обычно она всегда шире, чем шина адреса, так как количество информации, которое мы сможем передать, будет: 2 ^ ШиринаАдреса * ШиринаДанных = .бит. В случае с 16бит шиной адреса и 8бит шиной данных, мы сможем передать 65536байт.
Чтобы точно понять принцип, давайте сократим до 3Bit Адрес и 1Bit Данные. Наглядно:
Стрелка — селектор, он управляется адресом, позволяя выбрать ячейку, с которой будет происходить работа по шине данных. Ячейка может быть каким-то регистром устройства, в случае с памятью у нас таких ячеек будет огромное количество. Любой параллельной памяти присуща линейность, что уравнивает скорости доступа к ячейкам.
Третье — управляющие сигналы.
Основные управляющие:
- WE -разрешение записи
- RE -разрешение чтения
- CS0..n -выбор устройства
Все три используют по одному биту. Если WE, то передаем информацию, если RE, то принимаем информацию. CSn — это разрешает взаимодействовать с устройством, что позволяет отключить его от всей шины. Тут у нас происходит разделение на главное устройство (одно) (Master) и ведомое(-ые) (Slave). Что же это дает? — Используя CS управляющие, мы можем подключить несколько устройств по одной шине! 🙂 Но, с каждым устройством — у нас будет уходить по одному пину CS.
Подобное используется в наших ПК. Данная система не имеет схем шифрования, сжатия — все сделано наиболее просто, а все простое — быстрое, но часто громоздкое. :-\ Скорость передачи будет напрямую зависеть от максимальной частоты I/O устройств. Если она 100MHz, а ширина данных — 8бит, то получаем 100МегаБайт/сек. 🙂 Таким может похвастаться не каждый HDD.
Устройство внешней шины контроллера
У контроллера ATSAM3X блок управления шиной состоит из нескольких частей.
SMC контроллер дает нам 24бита адреса и 16бит данных. NAND Flash — позволяет подключать флэш nand память, а также дает нам несколько линий CS и разрешения чтения/записи.
ECC — контролирует остальные сигналы.
При всем при этом, весь этот воз подключен к общей шине ЦП, что дает нам возможность обращаться напрямую к внешнему интерфейсу. То есть, мы можем просто послать данные на определенный адрес и он автоматом появится на внешней шине, причем со всеми необходимыми сигналами и установленными задержками. Это очень полезно при подключении доп. ОЗУ: при переполнении родного ОЗУ, данные пойдут на внешнее без участия пользователя.
1 2 3 4 5 | uint8_t *addr = (uint8_t*)0x6xxxxxxx; ……… addr[x] = 128; //Отправим на внешнюю шину ячейку x (это ж указатель) i = addr[x]; //Прочитаем что-то memcpy(foo, addr, 978); //Или даже пересылки |
Изобразить схему работы можно так:
Все задержки между сигналами — условные, тк. у каждого устройства они свои и настраиваются в SMC контроллере.
Максимальная скорость передачи, которую я смог получить (без DMA) = 36МегаБайт/сек, при восьмибитной шине.
Верните наши пины!
Теперь перейдем к основной части: железу. Используем нашу шину по минимуму: 8bit Данные, 16bit Адрес + пара управляющих. Первое, что нужно сделать — сопоставить пины Arduino с выводами SAM3X.
Теперь появляются проблемы. A6 — вообще никуда не выведен, а это седьмой бит адреса. Ничего не остается как припаять напрямую к микросхеме проводник:
C A9 — немного проще, так как он находится на RX светодиоде:
С RE пином нельзя вообще что-либо сделать, так как он закорочен с A5 где-то на плате под микросхемой. 🙁 Зачем? — без понятия…
Вообще, RE пин — является противоположностью WE, соответственно, на крайний случай, можно поставить инвертор. Однако, во многих устройствах используется только WE: 1 — записываем, 0 — читаем. Так что все не так плохо.
Остальные пины разбросаны по всей плате, их соответствия вы можете найти по картинке:
Схема и плата
Следующее действие — разработка платы переходика: пины Arduino -> параллельная шина. Но сперва определитесь, какое устройство вы будете подключать к МК — от этого зависит корректность работы всей передающей линии. Здесь частота ~40MHz, поэтому согласованием линий и средством от помех не следует пренебрегать! Подробнее об этом можно почитать там — /.
Все выведено на колодки. Я поставил SMD резисторы 89Ом последовательно в линии для уменьшения паразитных характеристик и защитой от КЗ. Вся плата «надевается» на Arduino Due, получается бутерброд.
В качестве устройства, с которым будет работать МК, может выступать микросхема ОЗУ или какая-нибудь флэш память:
Причем максимальный возможный объем при данной конфигурации: 64кб на каждый CS пин. Могу посоветовать CY7C1019DV33-10ZSXI или IS62WV51216BLL.
Но мы рассмотрим видеопроцессор из этой статьи или просто ПЛИС EMP240. Тип подключения точно такой же как и у ОЗУ.
Концепт:
Передача данных будет происходить в одну сторону без замудрений: установили адрес, установили данные, записали.
Так как шлейф 90мм, нам не нужно согласовывать линию, но индуктивность/емкость/сопротивление проводников нужно учитывать. Также лучше использовать IDE 80PIN и чередуйте землю/сигнал.
Со стороны ПЛИС ничего особенного нету, шлейф приключается напрямую к EPM240. Конфигурация портов очевидная:
- ADDRESS — INPUT
- DATA — INPUT (HI-Z)
- WE — INPUT
Тест
Во-первых, убедитесь, что вы подключили все пины правильно.
Во-вторых, попробуйте подать на одну линию меандр с МК. Следите, чтобы на выходе сигнал имел минимально возможное искажение. Не должно быть выбросов, отражений и прочих гадостей.
Иначе — колдуйте с резисторами.
Важное замечание: проверять работу шины нужно осциллографом, так как частоты там высокие, а мультиметор их не поймает. Когда проблемы с железами решены, переходим к программной части.
Идем на arduino.cc и стаскиваем с форума библиотеку ArduinoDueParallel, здесь несколько функций:
- Parallel.begin(данные, cs, адрес, RE пин, WE пин);
Инициализация. Все вполне понятно, только нужно поставить false у readEnable, тк этот пин закорочен.
- Parallel.setAddressSetupTiming(циклы до WE, циклы до CS и WE, циклы до RE, циклы до CS и RE);
Здесь нужно смотреть документацию к устройству, которое вы подключаете, у каждого разные задержки. В случае с ПЛИС — почти везде нужно поставить 0. 1цикл = 11ns.
- Parallel.setPulseTiming(длит. WE, длит. CS WE, длит. RE, длит. CS RE);
То же самое, что и выше.
- Parallel.setCycleTiming(цикл Записи всего, цикл Чтения всего);
- Parallel.write(адрес, данные);
- uint8_t Paralle.read(адрес);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <Parallel.h> uint8_t* gram = (uint8_t*)0×60000000; //Адрес видеопроцессора void setup() { Parallel.begin(PARALLEL_BUS_WIDTH_8, PARALLEL_CS_NONE, 16, false, true); Parallel.setCycleTiming(1,1); Parallel.setPulseTiming(0,0,0,0); Parallel.setAddressSetupTiming(0,0,0,0); } void loop() { memset(gram, 255, 57344); //Заполняем весь экран белым } |
В этом примере CS вообще не используетеся, но внешнюю адресацию нужно знать:
Мы просто создаем указатель на той или иной адрес и работаем как хотим! ЦП принимает их как родную память, только с меньшей скоростью доступа. 🙂
Беленький 🙂
Заключение
Получившееся устройство выглядит так:
Что ж, внешняя параллельная шина весьма полезная весч. Можно вести параллельную обработку информации на нескольких устройствах, подключив ПЛИС, ОЗУ или еще чего-нибудь. Кстати, пересылать данные между двумя устройствами (CS на каждом) можно да же через memcpy, но при этом скорость пересылки падает в ~1.2раза. 🙂
Наконец, все эти SPI, Uart и прочая периферия, подключены к ЦП по принципу идентичному SMC контроллеру с внешней шиной: можно неплохо расширить возможности МК.
Иное возможное использование
1. Параллельный ЦАП или другой преобразователь
Я имею ввиду отдать адрес и данные или что-то одно под разряды преобразователя. Глупо, но работать будет 😀
2. Подключить к FPGA (или CPLD)
Применение найти можно.
3. Дополнительные интерфейсы
Подключить по такому же принципу, как в самом МК подключены все контроллеры. Возможно понадобиться несколько дискретной логики, но это не страшно.
4. Подключить к ISA
Тут, конечно, без сторонних компонентов не обойтись.
5. Соединить две Arduino Due!
Файлы