The DMA library provides a set of consistent routines across many of the EDT products, with simple yet powerful ring-buffered DMA (Direct Memory Access) capabilities.
A DMA transfer can be continuous or noncontinuous:
- For noncontinuous transfers, the an application may use edt_read() and edt_write(). Each edt_read() or edt_write() call performs one DMA transfer. These calls allocate kernel resources, during which time DMA transfers are interrupted.
- To perform continuous transfers, use the ring buffers – a set of buffers that applications can access continuously, reading and writing as required. When the last buffer in the set has been accessed, the application then cycles back to the first buffer. See edt_configure_ring_buffers() for a complete description of the configurable ring buffer parameters. See the sample programs simple_getdata.c and simple_putdata.c (in the installation directory) for examples of using the ring buffers.
- Note
- Always use the library calls edt_reg_read(), edt_reg_write(), edt_reg_or(), or edt_reg_and() to read or write hardware registers, rather than using ioctls.
Elements of EDT Interface Applications
Applications that perform continuous transfers typically include the following elements:
- The include statement:
- A call to edt_open() to open the device. This returns a handle which represents the EDT board in software. All subsequent calls will use this handle to access the board.
- Optionally, setup for writing a file or some other target for the data to be acquired.
- A call to edt_configure_ring_buffers() to configure the ring buffers.
- A call to start the DMA, such as edt_start_buffers().
- In a loop, a call to edt_wait_for_buffers()
- Data processing calls in the loop, as required.
- A call to edt_stop_buffers() followed by edt_close() to close the device.
- Appropriate settings in your Makefile or C workspace to link to the
libpcd
library.
Example:
#include <fcntl.h>
#include <unistd.h>
{
char *buf_ptr = NULL;
int outfd = open("outfile", 1) ;
{
write(outfd, buf_ptr, 1024*1024) ;
}
close(outfd);
return 0;
}
Applications that perform noncontinuous transfers typically include the following elements:
- The include statement:
- A call to edt_open() to open the device. This returns a handle which represents the EDT board in software. All subsequent calls will use this handle to access the board.
- Optionally, setup for writing a file or some other target for the data to be acquired..
- A call to edt_read() or edt_write() to cause one DMA transfer.
- Data processing calls, as required.
- A call to edt_close() to close the device.
- Appropriate settings in your Makefile or C workspace to link to the
libpcd
library.
Assuming that a multichannel FPGA configuration file has been loaded, this example opens a specific DMA channel with edt_open_channel():
#include <fcntl.h>
#include <unistd.h>
{
char buf[1024] = {0};
int numbytes = 0;
int outfd = open("outfile", 1);
while ((numbytes =
edt_read(edt_p, buf,
sizeof(buf))) > 0)
write(outfd, buf, numbytes);
close(outfd);
return 0;
}
You can use ring buffers for real-time data capture using a small number of buffers (typically 1 MB) configured in a round-robin data FIFO. During capture, the application must be able to transfer or process the data before data acquisition wraps around and overwrites the buffer currently being processed (an "overrun" condition). The example below shows real-time data capture using ring buffers, although it includes no error-checking. In this example, process_data(bufptr)
must execute in the same or less amount of time it takes DMA to fill a single buffer.
{
for (;;)
{
process_data(bufptr);
}
}
Multithreaded Programming
The EDT driver is thread-safe, with the following constraints:
- Because kernel DMA resources are allocated on a per-thread basis and must be allocated and released in the same thread, perform all DMA operations in the same thread as edt_open() and edt_close() with respect to each channel. Other threads can open the same channel concurrently with DMA, but must perform no DMA-related operations.
- To avoid undefined application or system behavior, or even system crashes, when exiting the program:
- Join all threads spawned by a main program with the main program after they exit and before the main program exits; or:
- If the main program does not wait for the child threads to exit, then any program that is run following the main program must wait for all the child threads to exit. This waiting period depends on system load and availability of certain system resources, such as a hardware memory management unit.