Как стать автором
Обновить

Комментарии 20

Первоисточник, наверное, забыли указать?
Как вы проверяете вашу «прошиву» на соответствие стандарту?
Статья стала попыткой совместить ту информацию, что я встретил в разных источниках, и полного, добуквенного эквивалента, как мне показалось, у неё нет. Конечно, сейчас всё достаточно быстро меняется, и я мог что-то пропустить.

Я не стану отрицать, что в немалой степени опирался на формат материалов от Eli Billauer, например, в части примеров пакетов. В списке использованных источников есть ссылка на его статью. В своё время он проделал неплохую работу. Однако когда я впервые разбирался, а как вообще передавать данные, только лишь этих материалов мне не хватило. К примеру, Eli толком не рассматривает, как быть, если векторов прерываний несколько; не рассматривает случаи с большими потками данных, где прерывания могут съесть приличную порцию процессорного времени. На мой взгляд, общую картину того, что должно быть сделано, какой алгоритм обмена с драйвером в целом, намного лучше обрисовывают здесь и здесь. Но, опять же, это лишь кусочки всей мозаики, которые дополняют друг друга. Источника, в котором было бы собрано всё и сразу, я пока не видел.

Верификацию в статье я не затрагивал. Часть вопросов по соответствию решает канальный уровень ядра внутри ПЛИС. Тем не менее, для проверки своей логики без BFM не обойтись. Когда я писал контроллер, мне не удалось найти BFM от Altera под мою версию Quartus. Из условно-бесплатных нашел такой вариант. Какие-то косяки логики всё равно пришлось долавливать через экспорт воздействий из SignalTap в файл для тестбенча.
В качестве первоисточника я подразумевал непосредственно спецификации от PCI-SIG.
Часть вопросов по соответствию решает канальный уровень ядра внутри ПЛИС.

На самом деле тестировать нужно на всех уровнях начиная с физики и выше.

Да, действительно, конкретный стандарт я не указал. Исправлю.


В целом, получается так, что physical layer, data link layer и даже часть transcation layer предоставляются как есть, от изготовителя ПЛИС. Они берут на себя отладку того, что предоставляют. На стороне разработчика остаётся только дополнить это логикой под конкретное приложение. Все потенциальные проблемы будут именно здесь.

НЛО прилетело и опубликовало эту надпись здесь
До того, чтобы задействовать CvP у меня, к сожалению, руки пока не дошли. На что еще стоит обратить внимание, кроме версии Quartus?
НЛО прилетело и опубликовало эту надпись здесь
Переносы ещё с 17 на 18 версию обрели пару затыков. Ну а для OpenCL там вообще много чего переделали.
Это особенность пятого циклона, на arria 10 CvP прекрасно жил в 17.1 (младшие не проверял, но и там походу тоже всё нормально).
Описаниями ядра, конечно же, пользовался. Получилось, как с очками на носу: очевидные документы, которые достаточно долго время были рядом, добавить позабыл.
Спасибо, что отметили!
Спасибо за статью. В целом, после работы с ядрами от Xilinx, все знакомо. За что зацепился взгляд:
Во-первых, длина запросов ограничена одним двойным словом (DWORD).
Это не так. Спецификация (данные из спецификации на ревизию 3.0) указывает, что количество DW в TLP может быть от 1 до 1024. Драйвер AHCI от Microsoft для Windows 7 (версию драйвера не помню за давностью лет) умеет посылать в PIO режиме запросы размером в 2 DW.
Первая группа особенностей связана со способами, которые позволяют сконфигурировать ПЛИС в течение 100 мс по требованиям PCIe.
Отсчет времени ведется от того момента, когда все напряжения питания выйдут на стабильный уровень. При работе с большими ПЛИС достаточно жесткий критерий.
Напоследок, разработчикам настоятельно рекомендуется использовать информацию о доступных кредитах для исходящих пакетов
Из ядра есть выводы с DLL, которые показывают наличие свободных буферов у устройства-партнера (Flow Control)?
Во-первых, длина запросов ограничена одним двойным словом (DWORD).
Это не так. Спецификация (данные из спецификации на ревизию 3.0) указывает, что количество DW в TLP может быть от 1 до 1024. Драйвер AHCI от Microsoft для Windows 7 (версию драйвера не помню за давностью лет) умеет посылать в PIO режиме запросы размером в 2 DW.

Здесь я действительно сформулировал текст с ошибкой, неточно. Когда писал материал, то думал про BAR с шириной в 32 бита, так как сам использовал только такой вариант. В случае с BAR в 64 бита, длина запроса точно не может быть меньше 2 DW. Это нужно будет обязательно уточнить, спасибо!
Другая ситуация возникает, если мы формируем запрос с адресом, который не был выровнен по DWORD. В этом случае размер запроса становится больше: 2 DW для BAR 32 бита и 3 DW для BAR 64 бита. Но при этом в запросе поля byte enable выставлены так, что фактически внутри 2 DW или 3 DW полезные байты занимают меньше. Т.е. при смещенном адресе мы всё равно получаем не больше 1 DW для BAR 32 бита и не больше 2 DW для BAR 64 бита. Всё остальное оказывается мусором. По крайней мере, в моих экспериментах было так.

Первая группа особенностей связана со способами, которые позволяют сконфигурировать ПЛИС в течение 100 мс по требованиям PCIe.
Отсчет времени ведется от того момента, когда все напряжения питания выйдут на стабильный уровень. При работе с большими ПЛИС достаточно жесткий критерий.

Здесь я хотел обратить внимание именно на способы, которые помогают ПЛИС сконфигурироваться и вовремя активировать ядро, так как именно эта часть вызывала у меня наибольшие трудности. До PCIe я спокойно обходился простейшим Active Serial, где всё за меня делало готовое IP от производителя. Здесь же пришлось поломать голову над Fast Passive Parallel, посмотреть на Configuration over Protocol. В противном случае большие ПЛИС действительно не успевают прогрузиться. Но, наверное, всё равно стоит уточнить то, с какого события отсчитывать 100 мс. Учту.

Напоследок, разработчикам настоятельно рекомендуется использовать информацию о доступных кредитах для исходящих пакетов
Из ядра есть выводы с DLL, которые показывают наличие свободных буферов у устройства-партнера (Flow Control)?

Выводы для Flow Control есть. По умолчанию они показывают текущий лимит. Дело именно в том, что в самой документации на ядро сказано: «The Application Layer may track credits consumed and use the credit limit information to calculate the number of credits available». В дополнение к этому, примеры проектов с сайта производителя, которые я нашел, не использовали выводы в принципе. Да, в документации также сказано, что «Hard IP also checks the available credits before sending a request to the link, and if the Application Layer violates the available credits for a TLP it transmits, the Hard IP blocks that TLP and all future TLPs until credits become available». То есть, с одной стороны всё выглядит так, будто бы разработчик вообще может не беспокоиться о кредитах, и ядро само разберётся. С другой стороны, возникает ситуация, когда какой-то пакет забивает передающий канал. Ядро опустит флаг «tx_ready», и никакие другие пакеты пройти не смогут. Моя позиция в том, чтобы отслеживать выходы Flow Control и не допускать подобных заторов.

Upd: Забыл назвать еще одну причину, по которой выводы Flow Control стоит отслеживать. В разделе «Debugging» в документации есть такой подраздел как «Link Hangs in L0 State». В этой ситуации ядро опускает флаг «tx_ready» и не выходит из этого состояния, пока его не сбросят. Одна из возможных причин — «Flow control credit overflows».
С другой стороны, возникает ситуация, когда какой-то пакет забивает передающий канал. Ядро опустит флаг «tx_ready», и никакие другие пакеты пройти не смогут. Моя позиция в том, чтобы отслеживать выходы Flow Control и не допускать подобных заторов.
Спорная ситуация. Я не читал полностью спецификацию PCI-Express, но у меня сложилось мнение, что пока устройство не получит подтверждение на какой-то пакет, оно будет слать его вечно. И отслеживая выводы Flow Control, чего Вы добьетесь? Зная, что на DLL уровне осталось место под пару пакетов, прекратите формирование пакетов на TLP уровне? Так для этих целей и есть флаг tx_ready.
Аппаратные ядра PCI Express ПЛИС V-й серии фирмы Intel представлены в двух вариантах исполнения: с интерфейсом Avalon-MM и интерфейсом Avalon-ST. Последний, хотя и требует от разработчика больше усилий, позволяет получить наибольшую пропускную способность
Бегло посмотрел спецификацию по интерфейсам. Avalon-MM похож на AXI4, Avalon-ST похож на AXI4-Stream. В MM есть поддержка burst транзакций, пропускную способность должен обеспечивать. Его использование для взаимодействия с ядром PCI-Expess выглядит избыточным, поскольку весь обмен осуществляется пакетами, и формирование адресов для данных неуместно.
Спорная ситуация. Я не читал полностью спецификацию PCI-Express, но у меня сложилось мнение, что пока устройство не получит подтверждение на какой-то пакет, оно будет слать его вечно.

Похоже, что в первые несколько раз, когда я читал спецификацию, эту часть я упустил. В памяти хорошо отложился REPLAY_TIMER — счетчик времени, по которому передатчик решает, что пора повторно отправлять пакет. Но вот что происходит, когда приемник несколько раз отправляет обратно NACK, я не осознал. Выходит, что существует счетчик повторов REPLAY_NUM. Если счетчик повторов превышен, тогда канальный уровень просит физический уровень повторить инициализацию канала (подразумеваю retraining).
Выходит, что здесь Вы правы, и ядро будет отправлять пакет вечно. Теперь мне стала более понятна ситуация, в которой ПК вис, когда я формировал ответы на чтение с ошибочным адресом.

И отслеживая выводы Flow Control, чего Вы добьетесь? Зная, что на DLL уровне осталось место под пару пакетов, прекратите формирование пакетов на TLP уровне? Так для этих целей и есть флаг tx_ready.

Если, к примеру, у меня есть исходящий ответ на чтение, то я отправлю его, пока счетчики кредитов для запросов записи обновляются через DLLP.
Я столкнулся с тем, что хотя флаг «tx_st_ready» был в единице, счетчик кредитов был меньше, чем размер пакета, который я отправлял ядру. В результате ядро роняло флаг «tx_st_ready» в ноль и держало его так 100 тактов и даже дольше. Пока я не отладил проверку Flow Control, проблема не ушла. Конечно, я не исключаю, что это в принципе была проблема моей логики как таковой, и другой разработчик не столкнется с моей ситуацией в принципе.

Тем не менее, подраздел «Link Hangs in L0 State» внутри раздела «Debugging» описывает случай переполнения счетчика кредитов. Цитата для Cyclone V:
Determine if the credit field associated with the current TLP type in the tx_cred bus is less than the requested credit value. When insufficient credits are available, the core waits for the link partner to release the correct credit type. Sufficient credits may be unavailable if the link partner increments credits more than expected, creating a situation where the Cyclone V Hard IP for PCI Express IP Core credit calculation is out-of-sync with the link partner.

Я могу трактовать это только так, что за выходами Flow Control всё же стоит следить.
По моему здесь некоторая путаница. Flow control это backpressure — т.е. пакет, для которого закончились кредиты, по линии просто не посылается, и ACK, NACK dllp пакеты тут соответственно не задействуются. А вот если пакет был послан, то передающий контроллер ждет ACK или NACK.

И далее как уже тут упоминалось — если передающий контроллер принял NACK или не получил никакого ответа за таймаут REPLAY_TIMER, то три раза повторяет отправку этого же пакета. Если же и после трех отправок повторилась ситуация — то retraining link.

Не буду вдаваться в сложности — есть потраченные кредиты передатчиком (Credits_Consumed), а есть предоставленные приемником (Credits_Limit). Отправить больше чем предоставили нельзя. Есть отдельные кредиты для заголовка и для данных (payload). Т.е. может получиться так, что можно отправить много коротких пакетов, а один длинный отправить нельзя из-за нехватки кредитов для payload.
Насколько я понял документацию на ядро, проблема в двух местах.

Первое — это счетчики потребленных кредитов (CREDITS_CONSUMED) и счетчики пределов/лимитов (CREDITS_LIMIT). В документации на ядро явно сказано, что счетчик лимитов в ядре может не соответствовать действительности («Sufficient credits may be unavailable if the link partner increments credits more than expected»).
Если мы обратимся к спецификации PCIe, то увидим, что доступные кредиты считаются как «лимит – (потребленные кредиты + кредиты для текущего пакета)». Пакет отправится только тогда, когда разница между лимитом и потреблением будет равна 2^[Field Size] / 2.
Полагаю, что в случае, который описан в “Debugging”, разница между лимитом и потреблением окажется отрицательной. В этой ситуации, так как счетчики беззнаковые, в реальности получаем, что разница превышает 2^[Field Size] / 2. Например, если для данных текущее значение счетчиков лимита — это 0x3D, а число потребленных кредитов — 0x3F, тогда число доступных кредитов — это FFE. Для кредитов данных 2^[Field Size] / 2 — это 7FF/2 = 3FF. Т.е. получается, что канальный уровень не может отправить пакет и должен ждать, пока счетчик лимитов вырастет.
А теперь предположим, что для приемной стороны текущие лимиты достигли максимума. Будет ли она отправлять передатчику (в нашем случае, ядру) пакет DLLP с новыми пределами? Как я понимаю, в этом случае приемник ничего отправлять не будет, так как для него лимиты уже на максимуме. Для ядра при этом лимиты оказываются меньше, чем нужно. Выходит, что имеем затор до тех пор, пока не обнулим счетчики ядра. Так как счетчики кредитов работают от времени инициализации линка, то единственный способ обнулить их — повторять инициализацию.

Второе — это буфер пакетов, куда попадает всё, что мы подаем на пользовательский интерфейс ядра Avalon-ST. Может оказаться, что “tx_st_ready” связан лишь с флагом “almost_full” буфера ядра. Допустим, мы хотим закинуть в ядро пакет в 128 байт. Счетчики кредитов говорят, что для 128 байт кредитов недостаточно, и при этом произошла рассинхронизация лимитов. Флаг “tx_st_ready” находится в “1”, потому что место в буфере ядра еще есть. Мы вливаем в ядро пакет, и на этом всё останавливается. Ядро ждет кредитов, которые не увеличатся, так как счетчики врут. Флаг “tx_st_ready” висит в “0”, потому что места в буфере больше нет.

Пакет в описанном случае действительно никуда не посылается, канальный уровень следует правилу. Проблема именно в том, как работает конкретная реализация ядра PCIe.
А теперь предположим, что для приемной стороны текущие лимиты достигли максимума. Будет ли она отправлять передатчику (в нашем случае, ядру) пакет DLLP с новыми пределами? Как я понимаю, в этом случае приемник ничего отправлять не будет, так как для него лимиты уже на максимуме. Для ядра при этом лимиты оказываются меньше, чем нужно. Выходит, что имеем затор до тех пор, пока не обнулим счетчики ядра. Так как счетчики кредитов работают от времени инициализации линка, то единственный способ обнулить их — повторять инициализацию.


При достижении максимального значения Credits_Limit происходит rollover. То же самое для Credits_Consumed. Поскольку они unsigned, разница будет правильной. Retraining тут не нужен.

Я взглянул на описание tx_st_ready. Да, скорее всего он и есть almost_full сигнал.
Под максимумом я подразумевал число кредитов, которое доступно передатчику в принципе. То есть, то число, что получается сразу же после инициализации, когда CREDITS_CONSUMED = 0, а CREDIT_LIMIT — это то, что выделила приемная сторона, CREDITS_ALLOCATED.

Допустим, после инициализации линка имеем CREDIT_LIMIT = 0x3DE кредитов для данных. CREDITS_CONSUMED = 0. Теперь отправляем 1 пакет в 256 байт и останавливаемся. Имеем CREDITS_CONSUMED = 256/16 = 16 или 0x10. Когда приемная сторона возвращает кредиты, CREDIT_LIMIT становится равен 0x3EE. CREDIT_LIMIT — CREDITS_CONSUMED = 0x3EE — 0x10 = 0x3DE. И больше, чем 0x3DE приемник не выделит.

А теперь представим, что на стороне ядра PCIe счетчик CREDIT_LIMIT вдруг рассинхронизировался, число доступных кредитов (CREDIT_LIMIT — CREDITS_CONSUMED) стало 0xFFE. Приемник же имеет (CREDITS_ALLOCATED — CREDITS_RECEIVED) = 0x3DE. Передатчик ядра ждёт, пока CREDIT_LIMIT не увеличится. Приемник же не увеличивает CREDIT_LIMIT, потому что больше, чем 0x3DE кредитов данных он выделить не может.

По крайней мере, в моём представлении ситуация из раздела «Debugging» оказывается именно такой.
Что ж, ситуация, когда устройство принявшее пакет, возвращает неверное количество кредитов теоретически возможна. Поскольку помехи на линии не могут исказить кредиты (DLLP пакет защищен CRC), то это грубейшая аппаратная ошибка в логике работы (как синий экран windows).
Тогда нужен ресет линка.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории