PCIExpress 1.0 2.5GT/s analyzer на базе ПЛИС своими руками

На работе я занимаюсь созданием PCIExpress устройств на ПЛИС. Некоторые из ПЛИС имеют встроенное PCIExpress ядро, позволяющее работать с этим интерфейсом на уровне пользовательского приложения. ПЛИС серии ECP5UM фирмы Lattice использует Soft-IP Core для реализации протокола, написанный на языке HDL, а в микросхему лишь встроен блок, отвечающий за работу физического уровня. Мне пришла в голову идея попробовать сделать PCIExpress анализатор на базе этой микросхемы.

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

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

Физическая среда передачи данных

PCIExpress шина это физическое соединение типа точка-точка, состоящее из х1, х2, х4, х8, х12, х16 или х32 каналов связи. Один канал связи представляет собой две дифференциальные пары, работающие одна на приём, другая на передачу данных. То есть х1-канал работает по 4 проводникам, а каналу х-32 необходимо уже 128. Каналы связи всегда симметричны — то есть количество пар в обоих направлениях одинаково.
По каналу передаётся дифференциальный сигнал амплитудой от 800 мВ до 1200 мВ, постоянная составляющая сигнала может варьироваться от 0 до 3.6В на стороне передатчика. Передатчик отвязан по постоянному току от приёмника с помощью конденсаторов. Частота сигнала, передаваемого по каналам, зависит от версии PCIExpress-протокола (Таблица 1).

Версия протокола

Частота передачи

Скорость передачи данных

PCIe 1.0

2.5GHz

250MB/s

PCIe 2.0

5GHz

500МБ/с

PCIe 3.0

8GHz

985МБ/с

PCIe 4.0

16GHz

1969ГБ/с

PCIe 5.0

32GHz

3938ГБ/с

Таблица 1. Версии PCIExpress

Для синхронизации работы устройств, помимо сигналов с данными, по отдельной паре передаётся тактирующая частота 100 МГц.

Пакетная передача данных

Спецификация PCIE определяет многоуровневую архитектуру. Эти уровни состоят из физического уровня (physical layer), канального уровня (data link layer) и уровня транзакций (transaction layer) (Рисунок 1).

Рисунок 1. Логические уровни PCIExpress протоколаРисунок 1. Логические уровни PCIExpress протокола

Данные между устройствами передаются с помощью пакетов. Их содержимое формируется на уровне транзакций с использованием данных, полученных от ядра устройства и приложения. Такой пакет называется пакетом уровня транзакции (Transaction layer packet — TLP). Готовый пакет поступает на уровень канала данных, где к нему присоединяется дополнительная информация, необходимая для проверки ошибок, возникших при передаче. Затем пакет кодируется на физическом уровне и передаётся по каналу связи приёмнику.
Физический уровень приёмника декодирует полученный пакет и отправляет его по цепочке наверх. На канальном уровне этот пакет проверяется на отсутствие ошибок, и в случае успеха передаётся на уровень транзакций. Далее данные пакета обрабатываются таким образом, чтобы они могли быть приняты уровнем ядра и приложения.

Рассмотрим три основных типа пакетов PCIExpress протокола подробнее.

Transaction layer Packets (TLP) пакеты уровня транзакций

Приложение или ядро отправляет на уровень транзакций информацию, требуемую для сборки части TLP-пакета: заголовок и данные. Некоторые TLP-пакеты могут не содержать данных. Опционально возможно добавление дополнительного поля с End-to-End CRC (ECRC), которое может использоваться для проверки правильности доставки сообщения приложением.
На канальном уровне к этому пакету добавляется счётчик пакетов и Link CRC (LCRC). Результат посылается на физический уровень, где к нему добавляется старт- и стоп символы длинной в 1 байт каждый. Пакет кодируется и передаётся по доступному количеству дифференциальных пар.

Рисунок 2. Структура TLP пакетаРисунок 2. Структура TLP пакета

Приёмник проводит те же самые операции в обратном порядке. На каждом уровне отбрасываются соответствующие поля. И в конечном счёте данные приходят получателю.

Data Link Layer Packets (DLLP) пакеты уровня канала

Помимо пакетов с данными, на канальном уровне осуществляется приём-передача пакетов, необходимых для управления потоком, подтверждения доставки данных (acknowledge/ no acknowledge) и управления питанием шины. Такой пакет состоит из поля данных уровня канала и контроля CRC. На физическом уровне DLLP-пакет так же дополняется старт- и стоп байтом.

Рисунок 3. Структура DLLP пакетаРисунок 3. Структура DLLP пакета

На стороне приёмника такой пакет после декодирования на физическом уровне попадает на уровень канала, и не транслируется на уровень транзакций. Доступ к пакетам уровня канала доступен пользователю лишь косвенно — через конфигурирование PCIExpress — ядра и в виде регистров или сигналов статуса шины.

Physical Layer Packets (PLP) пакеты физического уровня

Ещё один вид пакетов — пакеты физического уровня. Они генерируются на физическом уровне протокола передатчика, и обрабатываются на том же уровне на стороне приёмника. PLP-пакеты очень просты по своей структуре, и состоят только из заголовка и трёх или более байт, которые определяют структуру пакета и содержат дополнительную информацию.

Рисунок 4. PLP - пакетРисунок 4. PLP — пакет

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

Физический уровень протокола PCIExpress Gen 1 и Gen 2

Описание полной логики работы PCIExpress выходит за рамки одной статьи. Но для объяснения принципа работы анализатора рассмотрим структуру физического уровня протокола. На рисунке 5 отображена упрощённая блок-схема. На нем показаны следующие элементы приёмника и передатчика:

  • Буферы пакетов (Tx Buffer/ Rx Buffer)

  • Мультиплексоры (Mux)

  • Логика упаковки/ распаковки байт (Byte (Un-)stripping)

  • Скремблер и дескремблер

  • 8b/10b кодер и энкодер

  • Сериализатор — десериализатор.

Рисунок 5. Структурная схема физического уровня протоколаРисунок 5. Структурная схема физического уровня протокола

TLP и DLLP пакеты попадают из канального уровня в буффер физического уровня. С помощью мультиплексора пакет дополняется стартовым и стоповым символами, которые используются приёмником для определения границ пакета в потоке. Готовый фрейм (пакет и управляющие старт-стоп символы) отправляется логике, которая разбивает байты на отдельные каналы, в случае, если протокол работает более чем по одному каналу (х2, х4 и т.д.).
Все байты во фрейме, за исключением управляющих, скремблируются (скремблирование можно отключать в настройках PCIExpress) и дополняются двумя дополнительными битами в 8b/10b кодере. В 8b/10b коде получается расширить поток данных управляющими символами. Таким образом, байты на входе кодера получают специальный управляющий К-флаг, по которому в нашем случае определяются старт- и стоп байты во фрейме.

D или K символ

HEX байт

отдельные биты

название байта

CRD-

CRD+

Data (D)

6A

011 01010

D10.3

010101 1100

010101 0011

Data (D)

1B

000 11011

D27.0

110110 0100

001001 1011

Data (D)

F7

111 10111

D23.7

111010 0001

000101 1110

Control (K)

F7

111 10111

K23.7

111010 1000

000101 0111

Control (K)

BC

101 11100

K28.5

001111 1010

110000 0101

Таблица 2. Пример 8b/10b кодирования

Как уже говорилось, К-Символы в потоке данных сигнализируют о границах пакета. К таким символам относятся:

Название байта

HEX значение

Функция

K23.7

0xF7

PAD; used in framing and link width and lane ordering negotiations

K27.7

0xFB

Start TLP; Marks the start of a transaction layer packet

K28.0

0×1C

Skip; used for compensating for different bit rates

K28.1

0×30

Fast Training Sequence; Used within a ordered set to exit from L0s to L0

K28.2

0×5C

Start DLLP; marks the start of a data link layer packet

K28.3

0×7C

Idle; used in the electrical idle ordered set

K28.5

0xBC

Comma; used for lane and link initialization and management

K28.7

0xFC

Electrical Idle Exit; Reserved in 2.5 GT/s

K29.7

0xFD

End; marks the end of a TLP packet or DLLP packet

K30.7

0xFE

End Bad; marks the end of nullified TLP

Таблица 3. К-символы, используемые в PCIExpress-протоколе

Кодированный фрейм поступает на вход сериалайзера и через драйвер дифференциальной шины на выход устройства.

В приёмнике фрейм проходит те же шаги, но в обратном порядке. Дифференциальный сигнал со входа устройства преобразуется драйвером и подаётся на десериалайзер. 10-битный сигнал расшифровывается 8b/10b декодером, где вычленяются управляющие байты и байты потока данных. D-символы потока данных поступают на дескремблер, а К-символы синхронизируют его работу. Отдельные байты разных физических каналов упаковываются в один пакет, фильтруются по типу, и поступают в буффер обмена с верхним, канальным уровнем.

Описание и принцип работы анализатора

Это, конечно, очень поверхностное описание работы протокола. Но оно даёт приблизительное представление о том, как проще всего создать анализатор. Надо «вклиниться» в линии передачи между устройствами, расшифровать поток и полученные пакеты передать на обработку программе.

Как уже упоминалось в начале статьи ПЛИС серии ECP5UM фирмы Lattice имеют в своём распоряжении до четырёх PCS-блоков (Physical Coding Sublayer). PCS-блок это устройство, умеющее работать с физическими уровнями PCIE, GbE, SGMII и прочими протоколами. Помимо дифференциального драйвера и сериалайзера-десериалайзера, он умеет декодировать поток 8b10b.
ECP5UM работает с версией протокола Gen 1, а ECP5UMG — с версией протокола Gen 1 и Gen 2.
Подключив Rx и Tx линии, соединяющие два PCIExpress устройства, к приёмникам PCS ПЛИС, можно декодировать поток, получая фреймы физического, канального уровней, и уровня транзакций.
Логику работы устройства можно создать таким образом, чтобы фильтровать ненужные пакеты, настраивать триггеры на определённые события, сохранять данные и время их получения/отправки.

Для проверки работоспособности этой идеи отлично подходит отладочная плата LFE5UM5G-85F-EVN. В начале 2022 года её можно было заказать на digikey.com примерно за 90 евро. Все входы и выходы PCS у ФПГА на этой плате подключены к внешним разъёмам SMA. Для подключения устройства к PCIExpress-шине, была разработана плата-мезонин.

Плата-мезонин сделана достаточно просто. На ней установлены три усилителя, SMA-разъёмы для соединения с отладочной платой. Для подключения PCIExpress устройств используется плоский кабель с дифференциальными парами серии SL8802 от 3М. Кабель распаивается с одной стороны на плату-мезонин, а с другой стороны его можно монтировать на различные адаптеры — к примеру стандартный PCIExpress, или miniPCIE. В моём случае это разъём, к которому можно подключать устройства, разрабатываемые на нашей фирме.

Рисунок 6. Отладочная плата с установленной платой-мезонинРисунок 6. Отладочная плата с установленной платой-мезонин

На рисунке 6 виден первый прототип — отладочная плата с ECP5UMG и установленная на неё плата-мезонин. Плоский кабель, распаяный на плате, смонтирован на разъём, с помощью которого скоммутированны PCIExpress Root Complex и Endpoint устройства. Три пары плоского кабеля подключены к RX, TX и CLK линиям интерфейса, одна пара кабеля свободна. Коаксиальные кабели соединяют выходы усилителя и входы приёмников PCS ПЛИС.
Через штырьковый разъём на плату-мезонин подаётся питание усилителей, а так же сигналы их включения-выключения.

Рисунок 7. Блок-схема прототипа анализатораРисунок 7. Блок-схема прототипа анализатора

На рисунке 7 представлена блок-схема данного устройства. В качестве усилителя был выбран ограничивающий усилитель ONET1191P от Texas Instruments. Усилитель может работать со скоростью до 11.3 ГБс, чего более чем достаточно для подобного эксперимента. Схема и трассировка делались в программе KiCad, исходные файлы можно найти в Git-репозитории. Печатная платы и трафарет для паяльной пасты заказывались на сайте JLCPCB. Производство пяти плат обошлись мне менее 7 евро, трафарет стоил около 6 евро. В итоге цена с доставкой получилась порядка 23 евро.

Реализация в ПЛИС

Проект

Основной задачей ПЛИС является декодирование физического интерфейса двух каналов TX и RX шины. Анализ же полученных данных лучше проводить на компьютере или микроконтроллере. Устройство на ПЛИС получает байты с PCS-блока, определяет границы и тип пакетов, и сохраняет их для дальнейшей обработки.

Чтобы проверить, насколько хорошо устройство будет синхронизироваться и декодировать PCIExpress-шину, была написана простая прошивка, которая по нажатию кнопки на отладочной плате записывает TLP-пакеты в Block-RAM. Затем эти данные считываются из памяти и показываются на временной диаграмме встроенного в прошивку анализатора.

В ECP5UMG, установленном на отладочной плате, есть два блока DCU (Dual Channel Unit), каждый из DCU состоит из двух блоков PCS. В проекте используется два приёмника (канал 1 и канал 2) одного блока DCU. Так же DCU-блок умеет синхронизировать свою работу с тактовой частотой, подающейся либо с CLK-контактов самого блока, либо с тактовой частотой, подающейся непосредственно из ПЛИС. На отладочной плате CLK-сигналы второго DCU-блока подключены к установленному на плате внешнему тактовому генератору 200 МГц. Этот сигнал, преобразованный через PLL в 125 МГц, подаётся на блок DCU. Поэтому один из каналов на плате мезонин, предназначенный для подключения к clk-линии PCIExpress устройства, не используется.

Рисунок 8. Блок-схема дизайна ПЛИСРисунок 8. Блок-схема дизайна ПЛИС

На рисунке 8 показана упрощённая схема тестового проекта. С выхода PCS принимается восьмибитная параллельная шина данных и К-Флаг 8b10b декодера. Это соответствует трём нижним блокам физического уровня приёмника PCIExpress, показанным на рисунке 5 (Rx, Serial-to-parallel, 8b/10b Decoder). Данные поступают на вход дескремблера. Поскольку тестовое устройство работает лишь с одной линией PCIExpress интерфейса, функция Byte Un-stripping не нужна. Дескремблированный поток идёт на вход анализатора. Анализатор определяет по К-флагу начало и конец пакета, а по заголовку его тип. Для простоты тестирования в первой версии учитываются и сохраняются только TLP-пакеты. После нажатия кнопки на отладочной плате, анализатор запускает счётчик тактов, с помощью которого можно определять время получения пакетов (timestamp), и ожидает TLP-заголовок. Как только на шине происходит запись или чтение данных, анализатор перехватывает пакеты и записывает во внутреннюю память ПЛИС.

Самый простой и быстрый способ посмотреть полученные данные это использование встроенного логического анализатора ПЛИС. Для ПЛИС Lattice и среды разработки Lattice Diamond такой анализатор называется Reveal Analyzer. Этот блок, встраиваемый в прошивку, позволяет сохранять выбранные сигналы в память, считывать их с помощью JTAG-программатора, а затем показывать на временной диаграмме в среде разработки. Использование такого анализатора в дизайне не самый хороший тон. Но для быстрого прототипирования и отладки этот подход всё же имеет право на жизнь.

После приёма нескольких пакетов, работа анализатора останавливается, полученные данные считываются из памяти, и с помощью Reveal Analyzer’а показываются на временной диаграмме.

Тестирование устройства

Результаты работы прототипа сравнивались с работой профессионального PCIExpress-анализатора Teledyne Lecroy Summit T24.

В качестве подопытного кролика выступал сферический ARM-CPU (PCIExpres Root Complex) в вакууме, подключенный к не менее сферическому PCIExpress Endpoint устройству на базе ECP5UM-ПЛИС.

Подключив настоящий анализатор и настроив его триггер на приём TLP-пакетов, прочитал несколько байт из PCIExpress устройства:

0x68020000: 01000000 00000001 00000000 00000000  * ................ *
0x68020010: 00008422 00000000 00000000 00000000  * "............... *

Здесь адреса 0×68020000 и 0×68020010 это физические адреса одного из BAR-регистров Endpoint устройства.

Lecroy PCIExpress Analyzer после выполнения операции записал следующее:

Рисунок 9. Запись транзакций PCIExpress шины с помощью Lecroy PCIExpress analyzerРисунок 9. Запись транзакций PCIExpress шины с помощью Lecroy PCIExpress analyzer

На рисунке 9 можно увидеть 4 пакета: два «downstream» пакета с запросом на чтение от RC (Root Complex) к EP (Endpoint). И два «upstream» пакета с ответом в направлении EP>RC. Остальные пакеты (DLLP, SKIP, …), которые передаются по шине, были отфильтрованы.

С помощью программы можно рассмотреть эти пакеты подробнее. К примеру, пакет под номером 0 выглядит следующим образом:

Рисунок 10. Пакет №0 - Memory ReadРисунок 10. Пакет №0 — Memory Read

Здесь видно контрольные символы 0xFB (Байт 0) и 0xFD (Байт 23), означающие начало и конец пакета, которые генерируются на физическом уровне протокола. Байты под номерами 1, 2 и с 19 по 22 принадлежат к канальному уровню, и представляют собой Seqence Number и LCRC соответственно. Байты с 3 по 14 являются заголовком пакета TLP уровня. Расшифровку заголовка TLP пакета видно на рисунке 9. В нашем случае это пакет с запросом на чтение (MRd32) четырёх байт данных по адресу 0×68020000.

На этот запрос endpoint отвечает пакетом (Packet 2 на рисунке 9):

Рисунок 11. Пакет №2 - Completion with dataРисунок 11. Пакет №2 — Completion with data

Как и в предыдущем случае, пакет начинается и заканчивается контрольными байтами, далее идут байты канального уровня. Байты с 3 по 14 представляют собой заголовок TLP-пакета. Его расшифровка так же видна на рисунке 9: это Comletion with Data (CplD) — ответ c данными на Memory Read-запрос. Данные передаются в байтах с 15 по 18. Сравнивая их с тем, что было получено в консоли, можно как раз увидеть единичку по младшему адресу.

Попробуем прочитать эти же данные, подключив то же PCIExpress устройство к прототипу анализатора.

Окно Reveal Analyzer после захвата данных выглядит так:

Рисунок 12. Lattice Reveal AnalyzerРисунок 12. Lattice Reveal Analyzer

data_ch_1 и data_ch_2 — это каналы RX и TX данных PCIExpress шины. Data_ena_tx — триггер, по которому начинается захват данных, mem_data_out_rx35 и mem_data_out_tx35 — флаги, указывающие на timestamp пакета. Поскольку данные с каналов проходят через регистр, они задержаны относительно флага timestamp на один такт. Это видно из рисунка ниже.

Рисунок 13. Первые два пакетаРисунок 13. Первые два пакета

На рисунке 13 можно подробнее рассмотреть полученные данные. Здесь видно запрос от RC (Downstream) и ответ EP (Upstream). Сравнивая рисунок 13 с рисунками 10 и 11 можно увидеть те же самые байты, что были получены с помощью Lecroy анализатора.

Благодаря тому, что работа анализируемого устройства каждый раз при включении идентична, удалось добиться полного соответствия записанных разными анализаторами данных. Совпадают даже порядковые номера и LCRC-суммы пакетов канального уровня.

Такая проверка позволила сравнить результаты декодирования PCIExpress шины и удостовериться в правильности работы создаваемого анализатора.

Выводы и дальнейшие планы

Этот прототип создавался максимально упрощённо. Мне было интересно проверить концепцию — возможно ли на базе недорогого ПЛИС сделать PCIExpress анализатор. Результатами эксперимента я остался доволен: устройство работает достаточно стабильно, показывает идентичные результаты даже не смотря на далеко не идеальное «железо», и имеет на мой взгляд хороший потенциал.

Следующим шагом мне хотелось бы сделать более завершённое устройство. Для начала планирую использовать Raspberry Pi Zero (на отладочной плате есть стандартный разъём для подключения малинки), чтобы иметь возможность управлять устройством и получать данные через SPI-интерфейс. На этом этапе можно отладить работу FPGA-логики: настройку триггеров, фильтров, запись данных в память.

Далее можно создать отдельное устройство с ПЛИС и, к примеру, микроконтроллером STM32. Пока что связь ПЛИС и микроконтроллера я предполагаю сделать через flexible external memory controller. То есть регистры и память в ПЛИСе будут отражены непосредственно в памяти микроконтроллера. Анализируемые данные могут передаваться по USB, Ethernet или с помощью других популярных интерфейсов на компьютер.

В итоге должно получится достаточно простое, но в то же время функциональное устройство. За счёт конструктивных особенностей его можно будет легко адаптировать под разные типы разъёмов (PCIExpress, miniPCIE). А благодаря невысокой стоимости компонентов, такой прибор смогут позволить себе многие: фрилансеры, радиолюбители и студенты.

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

© Habrahabr.ru