2015-06-02 3 views
5

Ich versuche, DMA-Transfer arbeiten zwischen einem FPGA und einem x86_64 Linux-Rechner.DMA PCIe lesen Übertragung von PC zu FPGA

Auf der PC-Seite Ich mache diese Initialisierung:

//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(); 

Die Bus-Adresse ist eine 64-Bit-Adresse. Auf der Seite FPGA die TLP Ich sende von 1 DW für das Auslesen:

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) 

Die Fertigstellung, die ich vom PC zurück ist:

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" 

so dass im Grunde ein Abschluss ohne Daten und mit dem Status Nicht unterstützte Anfrage. Ich glaube nicht, dass etwas an der Konstruktion des TLP falsch ist, aber ich sehe auch kein Problem auf der Treiberseite. Der Kernel, den ich verwende, hat die PCIe-Fehlerberichterstattung aktiviert, aber ich sehe nichts in der dmesg-Ausgabe. Was ist los? Oder gibt es eine Möglichkeit zu finden, warum ich diese nicht unterstützte Anfrage Completion bekomme?

Marco

+0

Sie könnten Ihren Code mit anderen offenen PCIe-Treibern wie Riffa 2.x oder XilliBus vergleichen, um die Kernel-Funktion für DMA zu verwenden. – Paebbels

Antwort

2

Dies ist ein Auszug aus einem meiner Entwürfe (die funktioniert!). Es ist VHDL und etwas anders, aber hoffentlich wird es Ihnen helfen:

-- 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)); 

Lassen Sie sich den offensichtlichen Unterschied ignorieren, dass ich MemWr von 32 D-Worte statt Lesen ein dword durchführt. Der andere Unterschied, der mir beim ersten Mal Probleme verursachte, ist, dass Sie 3DW-Header verwenden müssen, wenn die Adresse unter 4GB liegt.

Das bedeutet, dass Sie die Adresse überprüfen müssen, die Sie vom Host erhalten und bestimmen, ob Sie den 3DW-Header (mit nur LSBs der Adresse) oder den vollständigen 4DW-Header-Modus verwenden müssen.

Sofern Sie nicht gottlose Datenmengen übertragen müssen, können Sie die dma-Adressmaske auf 32 Bit setzen, um immer im 3DW-Fall zu sein. Linux sollte standardmäßig viel Speicherplatz unter 4 GB reservieren.

+0

Von der dma_map_page bekomme ich immer eine 64-Bit-Adresse, deshalb verwende ich einen 4DW-Header. Wenn ich die DMA-Maske auf 32 Bit setze, stürzt der Kernel beim Aufruf von dma_map_page ab. BTW ich benutze VHDL. – Cpu86

+1

Sie können nicht sicher sein, was passiert, wenn Sie Ihren Code auf einem System mit weniger als 4 GB physikalischem Speicher ausführen? Kannst du 'dma_map_single' oder' dma_map_coherent' anstelle von 'dma_map_page' benutzen, zumindest um es zu testen? Es stürzt wahrscheinlich ab, da die 'get_page' nichts mit dma zu tun hat, also wenn es eine Seite> 4GB gibt und eine Map auf <4GB anfordert ... Der Kernel hat auch eine GFP_DMA Speicherregion, aber ich bin mir nicht sicher, ob das relevant ist modernes System, in dem alle Regionen für DMA verfügbar sein sollen. –

+0

Sie haben Recht, dass ich nicht jedes Mal sicher sein kann. Nur in meinem Fall drucke ich die Busadresse und habe immer 64Bits. Ich habe irgendwo den GPF_DMA32 gesehen, aber nicht gut dokumentiert. Das Problem ist, dass ich wahrscheinlich in Zukunft eine riesige Menge an Speicher abbilden müsste, sogar> 4 GB. Haben Sie schon einmal versucht, eine Seite im Bereich hoher Arbeitsspeicher (> 4 GB) zu übertragen und 4DW TLP zu verwenden? Hat es funktioniert? – Cpu86