1
votes

Hi I am trying to implement an FPGA accelerator to be integrated with an ARM processor by means of AXI bus. FPGA accelerator includes a DMA which aims to move input data (from memory) and output data (to memory). Everything is working as a bare metal application, but I have problems to do it under Linux.

The idea is that userspace processes must provide input data and must read output data. To cope with this I am writing a device driver but I am stuck at address translation from virtual address to physical address. When I give DMA the input and output base address I am able to provide just the virtual ones that is useless for my purpose, since I do not know how to translate it into the physical ones. As a result I read and write data from wrong memory locations. I also think that another difference from bare metal is that in linux data can be fragmented while in bare metal they are in contiguous memory regions.

Do you have any suggestion or reference to address these problems? Thanks

1

1 Answers

1
votes

First, a warning: don't try to use virt_to_phys, which is a common suggestion for this problem. The source code itself says that this function (actually a macro, but that's beside the point) should NOT be used for DMA or by device drivers.

What you should be using is a function from the dma_alloc family. The most likely candidate is void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag), which will do a few things for you:

  • allocate size bytes of pinned memory (pinning prevents the OS from swapping the memory out to disk)
  • assign a handle to dma_handle that can be cast to an unsigned integer and given to your hardware as a physical address for the memory
  • return a virtual address that you can use to access the memory from within your driver
  • assure that the memory is contiguous

Here are some other resources to take a look at, all of which are hosted on kernel.org:

Dynamic DMA Mapping Using the Generic Device
DMA Mapping Guide
Guide to the virt_to_phys and phys_to_virt functions (Remember that they shouldn't be used in this case - this is just for reference.)