Продублирую из лички сюда, чтобы осталось. Всё написанное - мои субъективные представления, подкрепленные небольшим опытом:Цитата
Основная проблема - нет понимания, какие я должен совершить действия в ПЛИС, что бы, отправить или принять кусочек данных с/на хост?
Вот включили питание ПЛИС-прошивка загрузилась-хост определил устройство 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