ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Четверг
18 апреля
824499
Evgeny_CD, Архитектор (16.03.2018 21:42, просмотров: 2574)
[Очень простое процессорное ядро для FPGA. Удобное для ASM программирования] http://caxapa.ru/767218.html
Зачем. Ядро предназначено для управления аппаратными блоками и в минимальной степени на обработку информации и расчеты силами самого ядра. Идея. Пусть у нас есть 8 регистров. Или даже 4. Еще есть служебные регистры. Для них есть свое ALU - может только сложить-вычесть регистр с константой и поместить в то же место. Эти регистры используются только как указатели. Есть основное ALU, которое умеет выполнять все операции, но с особыми операндами. Все основные операции выполняются с данными в памяти. Команды 16 битные. 9 бит - 3 раза по 3 бита - кодирует операнд А, операнд Б и куда класть результат. остальное кодирует код операции и прочая. Команды имеют режим загрузки 12 битной константы в регистр (с дополнением 0), а также сложения-вычитания регистра с константой. Есть всякие автоинкременты-декременты. Шаг задается прямо в команде - байт, полуслово, слово, или значение из специального регистра. Скорость и растактовка. Пусть ядро работает на 100 Мгц - беспроблемная внутренняя тактовая для современных FPGA. Каждый такт оно обрабатывает 1 команду. Но хитро! команда - определить адрес операнда А. команда - определить адрес операнда Б команда - определить адрес результата команда - выполнить операцию в основном АЛУ с косвенной адресацией всех трех сущностей. Получаем 25М оп/сек для "настоящих" операций. Ассемблер. Самая изюминка. На самом деле это "язык среднего уровня" - ЯСУ. Вначале декларируются все переменные программы. Можно инициализировать. Адреса задаются автоматом или вручную. По умолчанию адреса идут подряд. Есть понятие группы переменных. Это не структура, это хитрее. Есть описание совмещенных переменных - переменные, которые могут занимать одну ячейку. Есть специальный класс виртуальных команд. Типа заменить переменную Var_Start на Var_Middle с инициализацией. Это значит, что с этого момента к Var_Start обратиться нельзя, но Var_Start и Var_Middle живут по одному адресу. Можно удалить группу переменных. И на их место заместить другие переменные - группой или последовательно. Широко используются макрокоманды. Указанная последовательность из 4-х команд программистом кодируется одной командой, и автоматически раскрывается в последовательность. Аналогично for, if и т.д. Удобная IDE сразу. + раскрывать и сворачивать макрокоманды. Карта памяти. И прочая. Синксис - а ля С, но без составных команд. a=b+c; Что все это дает. Простое ядро => простая HDL модель => будет быстро симулироваться вместе с HDL блока. Ресурсы FPGA. Оценка - ядро влезет в несколько сотен LE даже в 32 битном варианте. В простых случаях ему в качестве памяти кода хватит одного блока памяти Altera (512 х 16) и еще одного блока, либо вобще некоторое количество распределенной памяти кристалла для памяти данных. Преимущества подхода с точки зрения программирования. 1. Можно вводить кастомные команды, которые идеально подходя для целевой задачи. И которые будут идти быстрее любого готового процессора, несмотря на тормознутость нашего ядра. 2. Компилятор ЯСУ будет простым - дополнить его кастомными командами не так и сложно, можно для одного проекта сделать. 3. Полностью статическое распределение памяти. Предсказуемость времени исполнения. 4. Представляется, что язык будет удобнее голого асма. Последовательность разработки. Вот у нас есть идея блока и идея алгоритма управления. Берем SystemC, System Verilog и пишем поведенческое, несинтезируемое описание блока в виде черного ящика, который имеет сигналы наружу. Описывается только поведение сигналов, что внутри - насрать. Сращиваем блоки, моделируем. Понимаем корректность замысла блока и алгоритма. Делаем систему команд нашего ядра опять же в виде несинтезируемого описания на SystemC, System Verilog, но программу пишем из реальных команд. Симулируем, понимаем, хватило ли памяти и быстродействия. Потом начинаем дописывать модель блока в сторону синтезируемого решения. Постоянно моделируя для проверки. Test Driven Development в чистом виде. Модельный проет. Пусть мы делаем специализированный контроллер обмена по Ethernet с дублированием и реальным временем. У нас 2 или более внешних PHY, внутренняя буферная память и внешний процессор. Пусть каждый PHY управляется своим CPU. Тратится 4 блока памяти: - память команд - память данных - FIFO на вход - FIFO на выход Алгоритмы этого CPU крайне просты - одна скорость передачи и один режим обмена PHY. Опрашиваем MDIO - не сдох ли линк. При приеме пакета делаем засечку локального таймера по началу фрейма и кладем эту отметку в нужное место. Аналогично при отправке пакета вставляем отметку локального таймера и текущую поправку к времени мастер-таймера. И пусть у нас будет центральный процессор в чипе, который делит буферную память чипа, и управляет обменом внешним MCU с буферной памятью и двух подчиненных контроллеров. Также он синхронизирует таймер в FPGA с таймером процессора. Пусть у него тоже будет 4 блока памяти. Берем самый мелкий Cyclone 10 LP - 6272LE и 30x9k RAM. пусть у нас будет 3 Ethernet канала и протокол PRP -> 4x4 - 16 блоков памяти на 4 процессорных ядра. 14 блоков осталось, это 14к буферного ОЗУ. Если обмениваться пакетами по 1 кбайт, то это будет 14 пакетов. По 3 пакета на контроллер, и 5 пакетов для процессора. Хватит. А теперь почему надо делать на FPGA. Обмен по 3 интерфейсам сильно избыточный. Для надежности. В каждом принятом пакете мы считаем несколько CRC - Ethernet CRC пакета и CRC целевых данных. CRC целевых данных мы складываем в мелкий ассоциативный массив, который быстро дает ответ на вопрос - у каких пакетов целевые данные совпадают. Лишнее херим и передаем во внешний MCU ровно один пакет. С привязкой по времени и проч. При передаче аналогично - внешний контроллер загоняет один пакет, выходит 3 пакета по разным интерфейсам. Почему 3 пакета - потому что "типа RAID 6". Две линии в дублировании, третья - на ремонте. Чипов, к у которых 3 Ethernet контроллера, очень мало, либо они стоят немеренных денег. А уж 3 гигабитных - ваще. А нам гигабит ничего не мешает реализовать. PHY с GMII - это 25 сигналов 125 Мгц. Итого 75 сигналов. А у 10CL006YU256I7G их целых 176. Менее, чем за $9. Для гигабитного варианта разгрузка внешнего процессора будет очень большой. Гигабит важен не только для средней скорости, но и для задержки. В реальных системах он должен быть "полупустым", но чтобы быстро прокачивал синхропакет по сети. Касательно скорости нашего ядра. В рассмотренном примере данные приходят и уходят в PHY со скоростью 2 х 125Мбайт/сек, но управляющих событий там сильно меньше. 25 MIPS рассмотренной системы команд там точно хватит. Потому что данные не ходят через наше ядро. Они из PHY идут сразу в FIFO, если "открыт краник". А ядро только командует открыванием. А вот открывать и закрывать краник надо не чаще 1 488 096 раз в секунду (максимальное количество пакетов в секунду для гигабитного Ethernet). Итого, то, что описанным способом можно впихнуть в FPGA за $10, в готовом виде либо вообще будет отсутствовать, либо будет стоить космических денег (ieee 1588 v2 + PRP - это космос по цене).