Перейти к содержанию
    

Всем привет, уважаемые форумчане!

Возникла необходимость поднять на девборде PCIe 3x8. Референсный дизайн загружен и все работает.

Но теперь нужно сделать прием и отправку своих данных с/на хост. И вот тут то возникла куча вопросов и непониманий. Информации по теории PCIe полно, но никак не удается найти конкретики относительно ПЛИС...какой именно у меня должен быть порядок действий со стороны ПЛИС, чтобы организовать чтение/запись данных в ОЗУ хоста? таблица дискрипторов...кто её формирует, где она хранится?В голове каша и нет четкого понимания.

Прошу помощи у опытных в данной теме людей.

За любые ссылки, проекты, которые можно взять за основу, буду благодарен.

Изменено пользователем polyvyannyy

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Готов проконсультировать по всем вопросам касательно PCI-E. Насколько хватит моей скудной компетенции.

Только просьба, переименовать тему в сторону более информативного заголовка.

 

Вкратце: хост выделяет у себя кусочки памяти, затем делает их доступными для записи через PCI-E, попутно выясняя по какому адресу это будет видно через PCI-E. Эти адреса могут не совпадать. Все эти действия делаются API ядра операционной системы. Полученные адреса, например, можно засунуть через MMIO (т.е. BARы) в ПЛИС, а потом ПЛИС просто шлет пакеты, заполняя эти кусочки памяти. Можно для начала выделить 1 блок размером 4096.

 

На самом деле всё чудовищно просто, когда осваивал не мог понять/поверить что всё так просто.

 

http://xillybus.com/tutorials/pci-express-...utorial-guide-1

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Можно посмотреть на мой старый проект

http://ds-dev.ru/projects/ds-dma

 

Спасибо за ссылку!Буду изучать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Продублирую из лички сюда, чтобы осталось. Всё написанное - мои субъективные представления, подкрепленные небольшим опытом:

 

Основная проблема - нет понимания, какие я должен совершить действия в ПЛИС, что бы, отправить или принять кусочек данных с/на хост?

Вот включили питание ПЛИС-прошивка загрузилась-хост определил устройство PCI-E и выделил ему кусок памяти в своем ОЗУ. После записал в ПЛИС (как я понимаю в регистр/память? BAR0 базовый адрес. Для чего остальные BAR1-BAR5 не доконца понимаю). На этом завершается инициализация устройства PCI-E в системе и оно готово читать/писать данные с/на хост. Если в чем-то я не прав поправьте.

Вот после инициализации, что я должен сделать, что бы начать отправку данных на хост или как я узнаю, что хост подготовил для ПЛИС данные?

 

Есть отличная книга Jackson M., Budruk R., PCI Express Technology. Comprehensive Guide to Generations 1.x, 2.x, 3.0. Идеально для понимания этой шины на все 100%. Очень простым языком написана. Но в этой книге нет ли слова как работать от хоста.

 

Дело в том, что шина PCI шлет так называемые TLP-пакеты. На самом деле это обычные примитивные пакеты, не сложнее любого протокола для RS-232. Там чуток полей. Адресация BDF - bus device function. Как правило, PCI ядра позволяют получить пользователю со стороны ПЛИС то, на какой BDF село устройство. Если BDF получателя 0 - значит пакет идет в хост. Там есть еще поле адрес. Оно 32 бита может быть.

 

Можно заполнять 32-бит адрес, поле данных - и всё это точно придет в хост. Но... будет отброшено. Чтобы хост не отверг наши данные надо выделить кусочек памяти в ядре (например 4096 байт). Этот кусочек будет иметь некий стартовый адрес - это просто. Но дело в том, что на шине PCI оно будет иметь другой адрес - это результат нелепого устройства архитектуры x86, но ради универсальности между ARM MIPS x86, драйвера используют самый универсальный механизм. И для этого задействуют API ядра Windows/Linux чтобы установить привязку этого кусочка в памяти ядра и его же но на шине PCI. Вызвав функцию, мы получим адрес этого кусочка на шине. Как только ПЛИС узнает этот адрес на шине PCI и пошлет на него пакет, то оно вскоре очутится в этой памяти, и данные прочитает драйвер.

 

Как же в ПЛИС попадет этот адрес? Достаточно лишь выслать эти 4 байта. Для этого есть BAR-адреса:

00:14.2 Audio device: Advanced Micro Devices, Inc. [AMD/ATI] SBx00 Azalia (Intel HDA)
        Subsystem: Micro-Star International Co., Ltd. [MSI] Device f641
        Flags: bus master, slow devsel, latency 64, IRQ 16
        Memory at fcff4000 (64-bit, non-prefetchable) [size=16K]

Видно что size=16K. Эти адреса примечательны тем, что вызвав пару команд в драйвере станет возможным затем вызывать в Linux readl/writel и таким образом обмениваться с устройством пакетами по 4 байта данных. Можно лишь по 4 байта за раз и это очень медленно. Но удобно чтобы прочитать регистр статуса или еще для чего то, например выслать адрес буфера.

 

Поэтому настроив работу через BAR, как в любом драйвере PCI, мы можем прислать 4 байта адреса в ПЛИС, и ПЛИС сможет писать в этот буферок пакетиками по 64/128/256 байт. И так за несколько пакетов заполнить весь буфер.

 

Но как хост поймет что ПЛИС заполнило буфер??? Для этого есть прерывания. В PCI-E есть очень удобный механизм MSI - message signaled interrupts. Это значит вместо дергания ножки, устройство высылает пакетик о том что случилось прерывания. Драйвер это увидит и проснется, чтобы скопировать данные пользователю, или что-то другое с ними сделать. Так работает механизм DMA. Все что я описал - это он и есть.

 

Я повторюсь, что всё это чудовищно просто. Надо лишь уловить суть этой примитивной логики работы PCI-E и DMA. Я год разбирался с этим всем делом. Но я возился не с тем что там всё сложно или мудрено. А возился именно с тем, чтобы выяснить что как надо делать. Сейчас я умею работать с PCI-E на Altera Lattice и Xilinx. Потому что освоив ПЛИС одного производителя, всё PCI-E ядра оказались очень похожими, хотя видно что их делали разные разработчики.

 

1) настраиваем BAR (можем читать-писать по 4 байта туда сюда) 2) выделяем блочек 4К и отображаем на PCI 3) регистрируем обработчик прерывания 4) шлем этот адрес через BAR 5) ПЛИС высылает серию 64 пакетов по 64 байта = 4096 6) ПЛИС дергает ножку MSI у PCI ядра 7) драйвер проснулся и готов работать с этими 4К данных от ПЛИС.

 

Вот пример моего драйвера - использовал для замера скорости записи от ПЛИС: http://paste.org.ru/?0bqfgq

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

"1) настраиваем BAR (можем читать-писать по 4 байта туда сюда)" - я так понимаю, означает получение устройством базового адреса на шине PCIe от ОС. Этот адрес записывается в BAR?

"2) выделяем блочек 4К" - выделением занимется драйвер?

"и отображаем на PCI" т.е приводим соответствие адресов на шине PCIe адресам в ОЗУ хоста?

"3) регистрируем обработчик прерывания" не понял?кто этим занимается?драйвер?

"4) шлем этот адрес через BAR " какой этот адрес? и что значит "шлем этот адрес через BAR"?

"5) ПЛИС высылает серию 64 пакетов по 64 байта = 4096" т.е. объем переданных данных от ПЛИС к хосту равен объему выделеной в ОЗУ памяти. А если при выделенном объеме = 4096 байта, мне нужно чтобы

драйвер обработал, скажем 256 байт, я могу его пнуть раньше, чем заполнятся все 4096 байта?

"6) ПЛИС дергает ножку MSI у PCI ядра" это и есть механизм посылки специализированного сообщения прерывания? Если так, как мне инициировать передачу такого сообщения?

1) Эти адреса, если не ошибаюсь, назначает BIOS, либо ОС на ранних этапах запуска. Устройство просто говорит, хочу пару регионов на 4К и на 256К например. А уже при старте ОС либо BIOS смотрит где там лучше выделить в адресном пространстве. После того как выделится, драйвер может буквально несколькими командами получить эти адреса себе в распоряжение (MMIO) и общаться посылками от 1 до 4 байт по этим адресам с нужными смещениями. Я вижу роль этого MMIO и BARов в качестве "регистрового интерфейса" устройства, чтобы получать статусы, передавать что-то небольшое. Раз 4К регион/диапазон, значит 1024 слова по 4 байт могут быть адресованы.

2) Выделяет драйвер в пространстве адресов ядра - ну просто кусок памяти, обычный такой, чтобы хранить какие-то данные. Драйвер, выполнив волшебную комманду, сможет узнать по какому адресу будет "видеть" ПЛИС этот кусочек. Он может быть другим, он может меняться от запуска к запуску системы или драйвера. Вот через MMIO (пункт 1) мы и сообщим эти 4 байта адреса ПЛИСине, куда она может TLP пакеты присылать. И они будут записываться в ОЗУ и ядро эти данные увидит - а нам это и надо, чтобы драйвер от ПЛИС получал данные.

3) Драйвер регистрирует, что когда возникнет MSI прерывание 0, то чтобы вызывалась некоторая функция. А в этой функции можно пробуждать драйвер от ожидания новых данных. Данные можно отбросить, куда-то скопировать, передать в пользовательскую программу.

4) См. пункт 2 - если мы установили что наш кусочек 4К в ядре имеет некий адрес на шине (иной), то мы должны каким-то образом об этом сказать ПЛИСине. Лучше чем MMIO (пункт 1) для этого ничего нет.

5) Да, можно и раньше. Можно вообще только 4 байта прислать и пнуть - и вот они наши 4 байта прислались. Можно 8 байт и т.д. Главное не слать данные между границ страниц памяти, они могут быть 4К, а может иной размер быть настроен в ОС.

6) В любом ядре есть MSI вход. Достаточно просто фронтик на 1-битовом входе сделать и через некоторое время снять (обычно есть сигнал ACK для этого). А оно уже само пошлет что нужно и куда нужно.

 

1.Как функционирует система в режиме gather-scatter? Как и кто формирует таблицу дескрипторов и на основании чего она формируется?где хранится?

2.В каких случаях лучше использовать режима gather-scatter, а в каких целесообразнее иной режим?

3.Если хост отправляет данные в PCIe устройство, то он сначала записывает весь объем данных к себе в ОЗУ, а потом пинает устройство и говорит - "данные готовы-забирай". Или же хост напрямую отправляет данные в устройство?

4.Есть ли механизмы переназначения адресов BAR без перезагрузки системы (например перезаливка прошвики ПЛИС без перезагрузки системы)?

5. Вы сказали, что имеете опыт работы с Альтерой может Вы знаете почему в примерах Alter-ы для Arria 10 нет доступа к BAR0, а BAR4 и выход DMA подключены к одной шине?и как инициировать передачу на хост для сборки Avalon-MM DMA?

1) Честно говоря, не использовал я этот scatter-gather. Если я не ошибаюсь, и правильно понимаю как это работает, я делаю аналог этого механизма своими руками.

Суть, видимо, вот в чем, не ручаюсь за 100% правильность. В пространстве ядра (не знаю как в процессорах с IOMMU) нет трансляции адресов вроде бы, стало быть проблема фрагментации стоит в полный рост. В общем случае, две последовательно идущие страницы памяти могут и не найтись, всё что гарантировано - это одна страница памяти (например 4К). Вот можно навыделять например 1024 таких разбросанных по адресам (scattered) страниц по 4К и получить нужные 4М памяти. Вот эти 1024 адреса можно прислать в ПЛИС и она будет заполнять одну за другой. Видимо дескриптор это мудреное название для записи в воображаемой таблице, где слева номер страницы (первая вторая ... последняя) а справа адрес 4 байта данного кусока по 4К (=1 стр памяти).

2) Какие иные режимы Вы знаете? :)

3) Не могу подсказать, с отправкой потока данных в ПЛИС не работал. Но не обязательно всё сразу в драйвер из программы пользователя загребать. Можно и по частям. Но я чисто предполагаю что именно так - мы заполнили кусочки данными для ПЛИС и сказали ПЛИС - выгребай. И ПЛИС шлет пачку TLP запросов типа MRd (memory read + ожидание ClpD с данными в ответ).

4) Не знаю :) Reset наше всё. Удобнее работать, когда устройство в еще один комп воткнуто. Оно ведь виснуть будет на первых порах, как же без этого :)

5) С шиной Avalon не работал, равно как и PCI-E + Nios. Я использовал вариант ядра PCI-E без Nios. Все пакеты делал сам в своей логике. Так что не могу подсказать по тонкостям реализации Altera.

Хотя то, через что я работал, кажется звалось Avalon-ST (stream), довольно простой интерфейс. Но видимо сильно различается с MM.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

AVR, а вы проводили функциональное моделирование в симуляторе вашего PCIe устройства? И по гайду сразу в синтез и оно работает без вопросов?

 

Я правильно понимаю, что ваша реализация - на Альтере? Какую микросхему использовали? Какие параметры линка (ген, ширина)? Каких скоростей удалось достичь? Или это вам было не нужно (трафик небольшой)?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

AVR, а вы проводили функциональное моделирование в симуляторе вашего PCIe устройства? И по гайду сразу в синтез и оно работает без вопросов?

Что Lattice что Altera - PCI-E заработало без вопросов, без всяких моделирований. Хотя я оборачивал ядро во враппер, и моделировал логику работы самого устройства в Modelsim - так было удобнее, чем ковыряться в железе. Но по шине, повторюсь, никакой возни не было.

 

Я правильно понимаю, что ваша реализация - на Альтере? Какую микросхему использовали? Какие параметры линка (ген, ширина)? Каких скоростей удалось достичь? Или это вам было не нужно (трафик небольшой)?

Пока что скромно - 1.0 x1, скорость получал 170+ Мбайт/с от ПЛИС в хост.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А Flow Control как-то обрабатываете? Или трафик такой, что гарантировано проблем не возникает?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1) Эти адреса, если не ошибаюсь, назначает BIOS, либо ОС на ранних этапах запуска. Устройство просто говорит, хочу пару регионов на 4К и на 256К например. А уже при старте ОС либо BIOS смотрит где там лучше выделить в адресном пространстве. После того как выделится, драйвер может буквально несколькими командами получить эти адреса себе в распоряжение (MMIO) и общаться посылками от 1 до 4 байт по этим адресам с нужными смещениями. Я вижу роль этого MMIO и BARов в качестве "регистрового интерфейса" устройства, чтобы получать статусы, передавать что-то небольшое. Раз 4К регион/диапазон, значит 1024 слова по 4 байт могут быть адресованы.

2) Выделяет драйвер в пространстве адресов ядра - ну просто кусок памяти, обычный такой, чтобы хранить какие-то данные. Драйвер, выполнив волшебную комманду, сможет узнать по какому адресу будет "видеть" ПЛИС этот кусочек. Он может быть другим, он может меняться от запуска к запуску системы или драйвера. Вот через MMIO (пункт 1) мы и сообщим эти 4 байта адреса ПЛИСине, куда она может TLP пакеты присылать. И они будут записываться в ОЗУ и ядро эти данные увидит - а нам это и надо, чтобы драйвер от ПЛИС получал данные.

3) Драйвер регистрирует, что когда возникнет MSI прерывание 0, то чтобы вызывалась некоторая функция. А в этой функции можно пробуждать драйвер от ожидания новых данных. Данные можно отбросить, куда-то скопировать, передать в пользовательскую программу.

4) См. пункт 2 - если мы установили что наш кусочек 4К в ядре имеет некий адрес на шине (иной), то мы должны каким-то образом об этом сказать ПЛИСине. Лучше чем MMIO (пункт 1) для этого ничего нет.

5) Да, можно и раньше. Можно вообще только 4 байта прислать и пнуть - и вот они наши 4 байта прислались. Можно 8 байт и т.д. Главное не слать данные между границ страниц памяти, они могут быть 4К, а может иной размер быть настроен в ОС.

6) В любом ядре есть MSI вход. Достаточно просто фронтик на 1-битовом входе сделать и через некоторое время снять (обычно есть сигнал ACK для этого). А оно уже само пошлет что нужно и куда нужно.

 

 

1) Честно говоря, не использовал я этот scatter-gather. Если я не ошибаюсь, и правильно понимаю как это работает, я делаю аналог этого механизма своими руками.

Суть, видимо, вот в чем, не ручаюсь за 100% правильность. В пространстве ядра (не знаю как в процессорах с IOMMU) нет трансляции адресов вроде бы, стало быть проблема фрагментации стоит в полный рост. В общем случае, две последовательно идущие страницы памяти могут и не найтись, всё что гарантировано - это одна страница памяти (например 4К). Вот можно навыделять например 1024 таких разбросанных по адресам (scattered) страниц по 4К и получить нужные 4М памяти. Вот эти 1024 адреса можно прислать в ПЛИС и она будет заполнять одну за другой. Видимо дескриптор это мудреное название для записи в воображаемой таблице, где слева номер страницы (первая вторая ... последняя) а справа адрес 4 байта данного кусока по 4К (=1 стр памяти).

2) Какие иные режимы Вы знаете? :)

3) Не могу подсказать, с отправкой потока данных в ПЛИС не работал. Но не обязательно всё сразу в драйвер из программы пользователя загребать. Можно и по частям. Но я чисто предполагаю что именно так - мы заполнили кусочки данными для ПЛИС и сказали ПЛИС - выгребай. И ПЛИС шлет пачку TLP запросов типа MRd (memory read + ожидание ClpD с данными в ответ).

4) Не знаю :) Reset наше всё. Удобнее работать, когда устройство в еще один комп воткнуто. Оно ведь виснуть будет на первых порах, как же без этого :)

5) С шиной Avalon не работал, равно как и PCI-E + Nios. Я использовал вариант ядра PCI-E без Nios. Все пакеты делал сам в своей логике. Так что не могу подсказать по тонкостям реализации Altera.

Хотя то, через что я работал, кажется звалось Avalon-ST (stream), довольно простой интерфейс. Но видимо сильно различается с MM.

 

 

Спасибо большое за столь развернутые ответы. Теперь буду приводить мысли в порядок и пробовать)Уверен, будут еще вопросы ;)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А Flow Control как-то обрабатываете? Или трафик такой, что гарантировано проблем не возникает?

Нужно следить за статусами "кредитов", т.е. емкости приемных буферов например ближайшего бриджа или непосредственно хоста - всё это PCI-E ядро само обновляет запросами и сообщает пользователю в ПЛИС. А со стороны драйвера просто такой трафик в моей задаче, что оно само собой разруливается, но в других ситуациях следить надо конечно.

 

Спасибо большое за столь развернутые ответы. Теперь буду приводить мысли в порядок и пробовать)Уверен, будут еще вопросы

С радостью отвечу на все последующие вопросы, на какие смогу.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нужно следить за статусами "кредитов", т.е. емкости приемных буферов например ближайшего бриджа или непосредственно хоста - всё это PCI-E ядро само обновляет запросами и сообщает пользователю в ПЛИС. А со стороны драйвера просто такой трафик в моей задаче, что оно само собой разруливается, но в других ситуациях следить надо конечно.

Да, вот меня больше волнует ситуация, когда не запись из ПЛИС на хост, а чтение потока данных из системной памяти в ПЛИС - нужно очень аккуратно выдавать запросы на запись, чтобы комлешны не забили входные буфера. Такие ситуации хотелось бы помоделировать. Только не очень понятно, каким образом эмулируется на функциональном моделировании физический уровень - ясно, что там нет смысла трансиверы изображать, но хотя бы их функциональность надо. В доках Xilinx упоминается некий PIPE - Physical Interface for PCI Express, который, вроде, и берёт на себя эту задачу. Но что это и как использовать, пока не знаю. Кто в курсе темы, поделитесь опытом, советами?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, вот меня больше волнует ситуация, когда не запись из ПЛИС на хост, а чтение потока данных из системной памяти в ПЛИС - нужно очень аккуратно выдавать запросы на запись, чтобы комлешны не забили входные буфера. Такие ситуации хотелось бы помоделировать. Только не очень понятно, каким образом эмулируется на функциональном моделировании физический уровень - ясно, что там нет смысла трансиверы изображать, но хотя бы их функциональность надо. В доках Xilinx упоминается некий PIPE - Physical Interface for PCI Express, который, вроде, и берёт на себя эту задачу. Но что это и как использовать, пока не знаю. Кто в курсе темы, поделитесь опытом, советами?

Почему нельзя дождаться прерывания от ПЛИС о том, что чтение завершено. К тому же, нужно так делать устройство, чтобы на стороне ПЛИС ничего не стопорилось, это ведь реально. А ничего, что ПЛИС и есть инициатор этих чтений? Не успевает, значит следующие запросы будут с задержкой.

Или я что-то упустил???

 

Иными словами, не собрались ли Вы бороться с несуществующей проблемой? :)

Там все эти возможные стопоры и задержки естественным образом уходят. Или у x86 есть механизм, чтобы хост сам формировал запросы копирования из ОЗУ на шину?

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Проблема может возникнуть из-за того, что если накидать запросов на чтение - а такой запрос - это простейший TLP, состоящий из заголовка, - так, что в ответ будет валиться большое число комлешнов. По стандарту PCIe реквестер чтения должен заявлять бесконечное число кредитов для ответных комплешнов, поэтому хост будет пытаться честно наливать поток в соответствии с запросами. И если накидать таких запросов (non-posted транзакций), то в ответ повалится неслабая пачка CplD, которая забьёт буфера и всё обломается по таймауту.

 

Чтобы не доводить до этого, реквестер (ПЛИС) должен метать запросы, контролируя кредиты приёмника. Конечно, можно данные качать порциями - сделал запросы так, чтобы гарантировано не переполнились приёмные буфера, и пока их не вычерпал, новые не слать. Но тогда будет простой на шине и скорости не достичь. Поэтому тут нужно в динамике следить за кредитами и слать запросы так, чтобы и кредитов хватало, и пауз не было. Вот это представляется нетривиальной задачей и её бы хотелось погонять в симуляторе.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Но тогда будет простой на шине и скорости не достичь

Мне кажется, всё там как надо будет достигаться. Невелик простой. Столько сколько нужно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...