Передача чтения DMA PCIe с ПК на ПЛИС

Я пытаюсь заставить работать передачу DMA между FPGA и машиной Linux x86_64.

На стороне ПК я выполняю эту инициализацию:

//driver probe
... 
pci_set_master(dev); //set endpoint as master
result = pci_set_dma_mask(dev, 0xffffffffffffffff); //set as 64bit capable
...

//read
pagePointer = __get_free_page(__GFP_HIGHMEM); //get 1 page
temp_addr = dma_map_page(&myPCIDev->dev,pagePointer,0,PAGE_SIZE,DMA_TO_DEVICE);
printk(KERN_WARNING "[%s]Page address: 0x%lx Bus address: 0x%lx\n",DEVICE_NAME,pagePointer,temp_addr);
writeq(cpu_to_be64(temp_addr),bar0Addr); //send address to FPGA
wmb();
writeq(cpu_to_be64(1),bar1Addr); //start trasnfer
wmb();

Адрес шины - это 64-битный адрес. На стороне FPGA TLP, который я отправляю для чтения 1 DW:

Fmt: "001"
Type: "00000"
R|TC|R|Attr|R|TH : "00000000"
TD|EP|Attr|AT : "000000"
Length : "0000000001"
Requester ID
Tag : "00000000"
Byte Enable : "00001111";
Address : (address from dma map page)

Завершение, которое я получаю от ПК, выглядит следующим образом:

Fmt: "000"
Type: "01010"
R|TC|R|Attr|R|TH : "00000000"
TD|EP|Attr|AT : "000000"
Length : "0000000000"
Completer ID
Compl Status|BCM : "0010"
Length : "0000000000";
Requester ID
Tag : "00000000"
R|Lower address : "00000000"

так что в основном завершение без данных и со статусом Unsupported Request. Я не думаю, что что-то не так с конструкцией TLP, но и со стороны водителя я не вижу никаких проблем. В ядре, которое я использую, включен отчет об ошибках PCIe, но я ничего не вижу в выводе dmesg. Что случилось? Или есть способ узнать, почему я получаю это неподдерживаемое завершение запроса?

Марко


person Cpu86    schedule 02.06.2015    source источник
comment
Вы можете сравнить свой код с другими открытыми драйверами PCIe, такими как Riffa 2.x или XilliBus, чтобы узнать, как использовать функцию ядра для DMA.   -  person Paebbels    schedule 03.06.2015


Ответы (1)


Это отрывок из одного из моих проектов (который работает!). Это VHDL и немного другой, но, надеюсь, он вам поможет:

-- First dword of TLP Header
tlp_header_0(31 downto 30)  <= "01";            -- Format = MemWr
tlp_header_0(29)                        <= '0' when pcie_addr(63 downto 32) = 0 else '1'; -- 3DW header or 4DW header
tlp_header_0(28 downto 24)  <= "00000";         -- Type
tlp_header_0(23)                        <= '0'; -- Reserved
tlp_header_0(22 downto 20)  <= "000";           -- Default traffic class
tlp_header_0(19)                        <= '0'; -- Reserved
tlp_header_0(18)                        <= '0'; -- No ID-based ordering
tlp_header_0(17)                        <= '0'; -- Reserved
tlp_header_0(16)                        <= '0'; -- No TLP processing hint
tlp_header_0(15)                        <= '0'; -- No TLP Digest
tlp_header_0(14)                        <= '0'; -- Not poisoned
tlp_header_0(13 downto 12)  <= "00";            -- No PCI-X relaxed ordering, no snooping
tlp_header_0(11 downto 10)  <= "00";            -- No address translation
tlp_header_0( 9 downto  0)  <= "00" & X"20";    -- Length = 32 dwords

-- Second dword of TLP Header
-- Bits 31 downto 16 are Requester ID, set by hardware PCIe core
tlp_header_1(15 downto 8)       <= X"00";   -- Tag, it may have to increment
tlp_header_1( 7 downto 4)       <= "1111";  -- Last dword byte enable
tlp_header_1( 3 downto 0)       <= "1111";  -- First dword byte enable

-- Third and fourth dwords of TLP Header, fourth is *not* sent when pcie_addr is 32 bits
tlp_header_2    <= std_logic_vector(pcie_addr(31 downto  0)) when pcie_addr(63 downto 32) = 0 else std_logic_vector(pcie_addr(31 downto 0));
tlp_header_3    <= std_logic_vector(pcie_addr(31 downto  0));

Давайте проигнорируем очевидную разницу в том, что я выполнял MemWr из 32 двойных слов вместо чтения двойного слова. Другое отличие, которое вызвало у меня проблемы в первый раз, когда я сделал это, заключается в том, что вам нужно использовать заголовок 3DW, если адрес меньше 4 ГБ.

Это означает, что вы должны проверить адрес, который вы получаете от хоста, и определить, нужно ли вам использовать заголовок 3DW (только с младшими битами адреса) или полный режим заголовка 4DW.

Если вам не нужно передавать нечестивый объем данных, вы можете установить маску адреса dma на 32 бита, чтобы она всегда была в случае 3DW, Linux по умолчанию должен зарезервировать много места в памяти ниже 4 ГБ.

person Jonathan Drolet    schedule 03.06.2015
comment
С dma_map_page я всегда получаю 64-битный адрес, поэтому я использую заголовок 4DW. Если я установлю маску dma на 32 бита, ядро ​​выйдет из строя при вызове dma_map_page. Кстати, я использую VHDL. - person Cpu86; 03.06.2015
comment
Вы не можете быть уверены в этом, что, если вы запустите свой код в системе с физической памятью менее 4 ГБ? Можете ли вы использовать dma_map_single или dma_map_coherent вместо dma_map_page хотя бы для тестирования? Вероятно, происходит сбой, поскольку get_page не имеет отношения к dma, поэтому, если он дает вам страницу ›4 ГБ и запрашивает карту на‹ 4 ГБ ... Ядро также имеет область памяти GFP_DMA, но я не уверен, что это актуально для современной системы где весь регион должен быть доступен для DMA. - person Jonathan Drolet; 03.06.2015
comment
Вы правы, что я не могу быть уверенным каждый раз. Просто в моем случае я печатаю адрес шины и всегда 64 бита. Я где-то видел GPF_DMA32, но плохо документированный. Проблема в том, что в будущем мне, вероятно, придется отображать огромный объем памяти, даже ›4 ГБ. Вы когда-нибудь пытались перенести страницу в область верхней памяти (›4 ГБ) с использованием 4DW TLP? Это сработало? - person Cpu86; 03.06.2015
comment
Итак, я попробовал с dma_map_single, и он работает со страницей в регионе highmem. Адрес шины совершенно другой. С dma_map_page я получаю что-то вроде 0xFFFFFFF110A80000, а с dma_map_single что-то вроде 0x0000000110A80000. Что-то не так в реализации. Благодарность - person Cpu86; 03.06.2015
comment
Хорошо, я только что нашел огромную ошибку. dma_map_single хочет, чтобы адрес был вторым параметром, а dma_map_page ожидает struct page *! Это виновник неисправности. - person Cpu86; 03.06.2015
comment
Да, адрес 0xFFxx будет виртуальной памятью, тогда как FPGA нуждается в физическом адресе (если вы не используете VTd), который равен 0 для размера памяти. Я не думаю, что большая память имеет отношение к 64-битной архитектуре. Память DMA из dma_map_* является непрерывной и, скорее всего, выйдет из строя, если вы попросите 4 ГБ. В этом случае вы должны реализовать scatter-gather и использовать dma_map_sg, что усложняет задачу. - person Jonathan Drolet; 03.06.2015
comment
Большая память актуальна, если у вас более 4 ГБ, и ваше устройство может адресовать более 32 бит. Скаттер уже использую, но по-своему. - person Cpu86; 03.06.2015