2012-06-28 4 views
7

저는 FPGA 디바이스 (현재는 PCI 익스프레스를 통해 PC에 연결됨)를 CPU RAM에 직접 DMA 할 수있는 Linux 디바이스 드라이버를 작성하고 있습니다. 이는 상호 작용없이 발생해야하며 사용자 공간은 데이터에 액세스해야합니다. 일부 세부 정보 : - 실행 64 비트 페도라 14 - 시스템 RAM 8GB의이 - FPGA를 (사이클론 IV)이이 내가 수행 달성하기위한 시도에서 PCIe 카드FPGA를 CPU RAM에 직접 DMA 할 수있게 해주는 Linux 디바이스 드라이버

에 다음 를 - 위를 예약 memmap 6GB $ 2GB (부트되지 않습니다. mem = 2GB 추가). RAM의 상위 2GB가/proc/meminfo에 예약되어 있음을 볼 수 있습니다. - FPGA 레지스터를 읽고 쓸 수 있도록 매핑 된 BAR0이 작동합니다. - remap_pfn_range()를 사용하여 드라이버에 mmap 기능을 구현했습니다. - Use ioremap - 버퍼에 데이터를 쓰는 ioctl 호출을 추가했습니다. - 버퍼에 데이터를 쓰는 ioctl 호출을 만들어 mmap을 테스트하고 데이터가 사용자 공간에서 버퍼에 있는지 확인했습니다.

내가 직면 한 문제는 FPGA가 버퍼 주소로 데이터를 DMA하기 시작할 때 발생합니다. 나는 계속해서 PTE 에러를 얻는다. (DMAR로부터 :) 또는 아래의 코드로 다음 에러를 얻는다 : DMAR : [DMA Write] 요청 디바이스 [01 : 00.0] fault addr 186dc5000
DMAR : [fault reason 01] 현재 비트 in 루트 항목이 분명하다 DRHD : 처리 오류 상태 등록 3

여기 FPGA

에서 DMA에 기초가 0x1000 각 시간을 기준으로 첫 번째 줄 단위로 주소 내 초기화가()의 코드 :

#define IMG_BUF_OFFSET  0x180000000UL // Location in RAM (6GB) 
#define IMG_BUF_SIZE  0x80000000UL // Size of the Buffer (2GB) 

#define pci_dma_h(addr) ((addr >> 16) >> 16) 
#define pci_dma_l(addr) (addr & 0xffffffffUL) 

if((pdev = pci_get_device(FPGA_VEN_ID, FPGA_DEV_ID, NULL))) 
{ 
    printk("FPGA Found on the PCIe Bus\n"); 

    // Enable the device 
    if(pci_enable_device(pdev)) 
    { 
     printk("Failed to enable PCI device\n"); 
     return(-1); 
    } 
    // Enable bus master 
    pci_set_master(pdev); 

    pci_read_config_word(pdev, PCI_VENDOR_ID, &id); 
    printk("Vendor id: %x\n", id); 
    pci_read_config_word(pdev, PCI_DEVICE_ID, &id); 
    printk("Device id: %x\n", id); 
    pci_read_config_word(pdev, PCI_STATUS, &id); 
    printk("Device Status: %x\n", id); 
    pci_read_config_dword(pdev, PCI_COMMAND, &temp); 
    printk("Command Register : : %x\n", temp); 
    printk("Resources Allocated :\n"); 
    pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &temp); 
    printk("BAR0 : %x\n", temp); 

    // Get the starting address of BAR0 
    bar0_ptr = (unsigned int*)pcim_iomap(pdev, 0, FPGA_CONFIG_SIZE); 
    if(!bar0_ptr) 
    { 
    printk("Error mapping Bar0\n"); 
    return -1; 
    } 
    printk("Remapped BAR0\n"); 

    // Set DMA Masking 
    if(!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) 
    { 
    pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); 
    printk("Device setup for 64bit DMA\n"); 
    } 
    else if(!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) 
    { 
    pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); 
    printk("Device setup for 32bit DMA\n"); 
    } 
    else 
    { 
    printk(KERN_WARNING"No suitable DMA available.\n"); 
    return -1; 
    } 

    // Get a pointer to reserved lower RAM in kernel address space (virtual address) 
    virt_addr = ioremap(IMG_BUF_OFFSET, IMG_BUF_SIZE); 
    kernel_image_buffer_ptr = (unsigned char*)virt_addr; 
    memset(kernel_image_buffer_ptr, 0, IMG_BUF_SIZE); 
    printk("Remapped image buffer: 0x%p\n", (void*)virt_addr); 

}

unsigned long image_buffer; 
unsigned int low; 
unsigned int high; 

if(remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, 
        vma->vm_end - vma->vm_start, 
        vma->vm_page_prot)) 
{ 
    return(-EAGAIN); 
} 

image_buffer = (vma->vm_pgoff << PAGE_SHIFT); 

if(0 > check_mem_region(IMG_BUF_OFFSET, IMG_BUF_SIZE)) 
{ 
    printk("Failed to check region...memory in use\n"); 
    return -1; 
} 

request_mem_region(IMG_BUF_OFFSET, IMG_BUF_SIZE, DRV_NAME); 

// Get the bus address from the virtual address above 
//dma_page = virt_to_page(addr); 
//dma_offset = ((unsigned long)addr & ~PAGE_MASK); 
//dma_addr = pci_map_page(pdev, dma_page, dma_offset, IMG_BUF_SIZE, PCI_DMA_FROMDEVICE);  
//dma_addr = pci_map_single(pdev, image_buffer, IMG_BUF_SIZE, PCI_DMA_FROMDEVICE); 
//dma_addr = IMG_BUF_OFFSET; 
//printk("DMA Address: 0x%p\n", (void*)dma_addr); 

// Write start or image buffer address to the FPGA 
low = pci_dma_l(image_buffer); 
low &= 0xfffffffc; 
high = pci_dma_h(image_buffer); 
if(high != 0) 
    low |= 0x00000001; 

*(bar0_ptr + (17024/4)) = 0; 

//printk("DMA Address LOW : 0x%x\n", cpu_to_le32(low)); 
//printk("DMA Address HIGH: 0x%x\n", cpu_to_le32(high)); 
*(bar0_ptr + (4096/4)) = cpu_to_le32(low); //2147483649; 
*(bar0_ptr + (4100/4)) = cpu_to_le32(high); 
*(bar0_ptr + (17052/4)) = cpu_to_le32(low & 0xfffffffe);//2147483648; 

printk("Process Read Command: Addr:0x%x Ret:0x%x\n", 4096, *(bar0_ptr + (4096/4))); 
printk("Process Read Command: Addr:0x%x Ret:0x%x\n", 4100, *(bar0_ptr + (4100/4))); 
printk("Process Read Command: Addr:0x%x Ret:0x%x\n", 17052, *(bar0_ptr + (17052/4))); 
return(0); 

당신이 제공 할 수있는 모든 도움을 주셔서 감사합니다 :

여기 내 mmap에 코드입니다.

답변

5

직접 TLP 패킷을 쓰는 RTL 코드를 제어합니까, 아니면 사용중인 DMA 엔진과 PCIe BFM (버스 기능 모델)을 지정할 수 있습니까? 시뮬레이터에서 패킷은 어떻게 생겼을까요? 대부분의 알맞은 BFM은 PCIe 하드웨어 캡처 시스템을 사용하여 사후 배치를 찾지 않고이를 포착해야합니다.

상위 2GB RAM을 대상으로하려면 장치에서 2DW (64 비트) 주소를 전송해야합니다. 당신의 Fmt/Type에있는 비트들이 이것을하기 위해 설정되어 있습니까? 오류가있는 주소는 마스킹 된 32 비트 버스 주소처럼 보이므로이 수준의 항목이 잘못된 것일 수 있습니다. 또한 PCIe는 빅 엔디안이기 때문에 대상 주소를 PCIe 장치 끝점에 쓸 때주의해야합니다. Fmt가 잘못된 경우 대상 주소의 하위 바이트가 페이로드로 떨어질 수 있습니다. 다시 한 번 적절한 BFM이 결과 패킷 길이 불일치를 알아 내야합니다.

최근 마더 보드/최신 CPU를 사용하는 경우 PCIe 엔드 포인트는 PCIe AER (고급 오류보고)를 수행해야하므로 최신 Centos/RHEL 6.3을 실행하는 경우 dmesg 엔드 포인트 오류 보고서를 받아야합니다. 이것은 보고서가 DW의 첫 번째 소수를 특수 캡처 레지스터에 캡처하므로 수신 된 TLP를 검토 할 수 있으므로 매우 유용합니다.

커널 드라이버에서 장치의 페이지에 쓸 수 있도록 mmu를 프로그래밍하지 않았으므로 DMA 마스크를 설정하는 것으로 충분하지 않습니다.pci_alloc_consistent()의 구현을 보면이 목표를 달성하기 위해 무엇을 불러야하는지 확인할 수 있습니다.

+0

"PCIe가 빅 엔디안이기 때문에"또한 PCI가 리틀 엔디안이라는 인상하에있었습니다 – cha5on

2

이유를 찾고 있다면 다음과 같이 보입니다. 커널에 DMA_REMAPPING 플래그가 기본적으로 활성화되어 있으므로 IOMMU 컨텍스트/도메인 항목이 장치에 프로그래밍되어 있지 않으므로 IOMMU가 위의 오류를 발생시킵니다.

커널 명령 행에서 intel_iommu = off를 사용하거나 IOMMU를 디바이스의 우회 모드로 설정할 수 있습니다. 감사합니다, Samir

관련 문제