2015-06-02 26 views
5

Sto cercando di ottenere il trasferimento DMA tra un FPGA e una macchina Linux x86_64.Trasferimento di lettura DMA PCIe da PC a FPGA

Sul lato PC che sto facendo questa inizializzazione:

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

L'indirizzo bus è un indirizzo 64bit. Sul lato FPGA TLP Mando per la lettura di 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) 

Il completamento che torno dal PC è:

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" 

in modo sostanzialmente un completamento senza dati e con lo stato Richiesta non supportata. Non penso che ci sia qualcosa di sbagliato nella costruzione del TLP ma non riesco a vedere alcun problema sul lato del driver. Il kernel che sto utilizzando ha la segnalazione errori PCIe abilitata ma non vedo nulla nell'output di dmesg. Cosa c'è che non va? Oppure, c'è un modo per scoprire perché ottengo il completamento Richiesta non supportata?

Marco

+0

È possibile confrontare il proprio codice con altri driver PCIe aperti come Riffa 2.xo XilliBus su come utilizzare la funzione kernel per DMA. – Paebbels

risposta

2

Questo è un estratto da uno dei miei disegni (che funziona!). E 'VHDL e un po' diverso, ma spero che vi aiuterà a:

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

Ignoriamo l'ovvia differenza che mi esibivo MemWr di 32 DWORD invece di leggere un valore DWORD. L'altra differenza, che mi ha causato problemi la prima volta che l'ho fatto, è che tu, , devi utilizzare l'intestazione 3DW se l'indirizzo è inferiore a 4 GB.

Ciò significa che è necessario controllare l'indirizzo che si ottiene dall'host e determinare se è necessario utilizzare l'intestazione 3DW (con solo LSB di indirizzo) o la modalità di intestazione 4DW completa.

A meno che non sia necessario trasferire una quantità di dati ingiustificata, è possibile impostare la maschera dell'indirizzo di dma su 32 bit per essere sempre nel caso 3DW, per impostazione predefinita Linux dovrebbe riservare molta memoria al di sotto dei 4 GB.

+0

Dalla pagina dma_map ricevo sempre un indirizzo a 64 bit, ecco perché sto usando un'intestazione 4DW. Se imposto la maschera dma su 32 bit, il kernel si blocca quando chiama dma_map_page. A proposito, io uso VHDL. – Cpu86

+1

Non si può essere sicuri di ciò, cosa succede se si esegue il codice su un sistema con meno di 4 GB di memoria fisica? Puoi usare 'dma_map_single' o' dma_map_coherent' invece di 'dma_map_page', almeno per testare? Probabilmente si blocca perché 'get_page' non è correlato a dma, quindi se ti dà una pagina> 4GB e richiedi una mappa su <4GB ... Il kernel ha anche una regione di memoria GFP_DMA, ma non sono sicuro che sia rilevante per sistema moderato in cui tutta la regione dovrebbe essere disponibile per DMA. –

+0

Hai ragione che non posso essere sicuro ogni volta. Solo nel mio caso stampo l'indirizzo del bus ed è sempre a 64 bit. Ho visto da qualche parte il GPF_DMA32 ma non è ben documentato. Il problema è che in futuro probabilmente avrei bisogno di mappare un'enorme quantità di memoria, anche> 4 GB. Hai mai provato a trasferire una pagina nella regione di memoria alta (> 4 GB) e utilizzando il TLP 4DW? Ha funzionato? – Cpu86