Skip to content

Commit

Permalink
Extend the transfer function to properly support VDMA.
Browse files Browse the repository at this point in the history
This resolves bperez77#62. The transfer function has been extended to properly
and fully support AXI VMDA. Before, the part of the driver that handled
single VDMA transfers (not looping video transfers) was hardcoded to a
1920x1080x4 frame. Now, the transfer function accepts a frame structure
that specified the height, width, and depth (pixels) of the image.

This require full cross-stack changes, so the driver, library, and
example applications have been updated. Additionally, this was used as
an opprotunity to refactor some of the driver code, since a frame
structure was created. Now, all parts of the codebase that interact with
VDMA use this frame structure.
  • Loading branch information
bperez77 committed Jun 22, 2018
1 parent 664aa62 commit d3f0966
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 89 deletions.
35 changes: 16 additions & 19 deletions driver/axidma_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,7 @@ struct axidma_transfer {

// VDMA specific fields (kept as union for extensability)
union {
struct {
int width; // Width of the image in pixels
int height; // Height of the image in lines
int depth; // Size of each pixel in bytes
} vdma_tfr;
struct axidma_video_frame frame; // Frame information for VDMA
};
};

Expand Down Expand Up @@ -227,10 +223,10 @@ static int axidma_prep_transfer(struct axidma_chan *axidma_chan,
dma_template.dst_start = sg_dma_address(&sg_list[0]);
dma_template.src_start = sg_dma_address(&sg_list[0]);
dma_template.dir = dma_dir;
dma_template.numf = dma_tfr->vdma_tfr.height;
dma_template.numf = dma_tfr->frame.height;
dma_template.frame_size = 1;
dma_template.sgl[0].size = dma_tfr->vdma_tfr.width *
dma_tfr->vdma_tfr.depth;
dma_template.sgl[0].size = dma_tfr->frame.width *
dma_tfr->frame.depth;
dma_template.sgl[0].icg = 0;
dma_txnd = dmaengine_prep_interleaved_dma(chan, &dma_template,
dma_flags);
Expand Down Expand Up @@ -505,10 +501,11 @@ int axidma_rw_transfer(struct axidma_device *dev,
tx_tfr.notify_signal = dev->notify_signal,
tx_tfr.process = get_current(),
tx_tfr.cb_data = &dev->cb_data[trans->tx_channel_id];
// FIXME: FIXME: FIXME: Temporary
tx_tfr.vdma_tfr.height = 1080;
tx_tfr.vdma_tfr.width = 1920;
tx_tfr.vdma_tfr.depth = 4;

// Add in the frame information for VDMA transfers
if (tx_chan->type == AXIDMA_VDMA) {
memcpy(&tx_tfr.frame, &trans->tx_frame, sizeof(tx_tfr.frame));
}

rx_tfr.sg_list = &rx_sg_list,
rx_tfr.sg_len = 1,
Expand All @@ -519,9 +516,11 @@ int axidma_rw_transfer(struct axidma_device *dev,
rx_tfr.notify_signal = dev->notify_signal,
rx_tfr.process = get_current(),
rx_tfr.cb_data = &dev->cb_data[trans->rx_channel_id];
rx_tfr.vdma_tfr.height = 1080;
rx_tfr.vdma_tfr.width = 1920;
rx_tfr.vdma_tfr.depth = 4;

// Add in the frame information for VDMA transfers
if (tx_chan->type == AXIDMA_VDMA) {
memcpy(&rx_tfr.frame, &trans->rx_frame, sizeof(rx_tfr.frame));
}

// Prep both the receive and transmit transfers
rc = axidma_prep_transfer(tx_chan, &tx_tfr);
Expand Down Expand Up @@ -564,9 +563,7 @@ int axidma_video_transfer(struct axidma_device *dev,
.channel_id = trans->channel_id,
.notify_signal = dev->notify_signal,
.process = get_current(),
.vdma_tfr.width = trans->width,
.vdma_tfr.height = trans->height,
.vdma_tfr.depth = trans->depth,
.frame = trans->frame,
};

// Allocate an array to store the scatter list structures for the buffers
Expand All @@ -578,7 +575,7 @@ int axidma_video_transfer(struct axidma_device *dev,
}

// For each frame, setup a scatter-gather entry
image_size = trans->width * trans->height * trans->depth;
image_size = trans->frame.width * trans->frame.height * trans->frame.depth;
for (i = 0; i < transfer.sg_len; i++)
{
rc = axidma_init_sg_entry(dev, transfer.sg_list, i,
Expand Down
143 changes: 106 additions & 37 deletions examples/axidma_benchmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h> // Strlen function

#include <fcntl.h> // Flags for open()
#include <sys/stat.h> // Open() system call
Expand All @@ -47,7 +48,7 @@
* Internal Definitons
*----------------------------------------------------------------------------*/

// The size of data to send per transfer (1080p image, 7.24 MB)
// The size of data to send per transfer (1080p image, 7.24 MiB)
#define IMAGE_SIZE (1920 * 1080)
#define DEFAULT_TRANSFER_SIZE ((int)(IMAGE_SIZE * sizeof(int)))

Expand All @@ -69,33 +70,40 @@ static void print_usage(bool help)
FILE* stream = (help) ? stdout : stderr;
double default_size;

fprintf(stream, "Usage: axidma_benchmark [-v] [-t <DMA tx channel>] "
"[-r <DMA rx channel>] [-i <Tx transfer size (MB)>] "
"[-b <Tx transfer size (bytes)>] [-o <Rx transfer size (MB)] "
"[-s <Rx transfer size (bytes)>] [-n <number transfers>]\n");
fprintf(stream, "Usage: axidma_benchmark [-v] [-t <(V)DMA tx channel>] "
"[-r <(V)DMA rx channel>] [-i <Tx transfer size (MiB)>] "
"[-b <Tx transfer size (bytes)>] [-f <Tx frame size (HxWxD)>] "
"[-o <Rx transfer size (MiB)>] [-s <Rx transfer size (bytes)>] "
"[-g <Rx frame size (HxWxD)>] [-n <number transfers>]\n");
if (!help) {
return;
}

default_size = BYTE_TO_MB(DEFAULT_TRANSFER_SIZE);
default_size = BYTE_TO_MIB(DEFAULT_TRANSFER_SIZE);
fprintf(stream, "\t-v:\t\t\t\tUse the AXI VDMA channels instead of AXI DMA "
"ones for the transfer.\n");
fprintf(stream, "\t-t <DMA tx channel>:\t\t\tThe device id of the DMA "
"channel to use for transmitting the data to the PL fabric.\n");
fprintf(stream, "\t-r <DMA rx channel>:\t\t\tThe device id of the DMA "
"channel to use for receiving the the data from the PL fabric.\n");
fprintf(stream, "\t-i <transmit transfer size (MB)>:\tThe size of the data "
"transmit over the DMA on each transfer. Default is %0.2f MB.\n",
default_size);
fprintf(stream, "\t-b <transmit transfer size (bytes)>:\tThe size of the "
fprintf(stream, "\t-i <transmit transfer size (MiB)>:\tThe size of the "
"data transmit over the DMA on each transfer. Default is %0.2f "
"MiB.\n", default_size);
fprintf(stream, "\t-b <Tx transfer size (bytes)>:\tThe size of the "
"data transmit over the DMA on each transfer. Default is %d "
"bytes.\n", DEFAULT_TRANSFER_SIZE);
fprintf(stream, "\t-o <receive transfer size (MB)>:\tThe size of the data "
"to receive from the DMA on each transfer. Default is %0.2f MB.\n",
fprintf(stream, "\t-f <Tx frame size (height x width x depth)>:\tThe size "
"of the frame to transmit over VDMA on each transfer, where the "
"depth is in bytes.");
fprintf(stream, "\t-o <Rx transfer size (MiB)>:\tThe size of the data "
"to receive from the DMA on each transfer. Default is %0.2f MiB.\n",
default_size);
fprintf(stream, "\t-s <receive transfer size (bytes)>:\tThe size of the "
fprintf(stream, "\t-s <Rx transfer size (bytes)>:\tThe size of the "
"data to receive from the DMA on each transfer. Default is %d "
"bytes.\n", DEFAULT_TRANSFER_SIZE);
fprintf(stream, "\t-g <Rx frame size (height x width x depth)>:\tThe size "
"of the frame to receive over VDMA on each transfer, where the "
"depth is in bytes.");
fprintf(stream, "\t-n <number transfers>:\t\t\tThe number of DMA transfers "
"to perform to do the benchmark. Default is %d transfers.\n",
DEFAULT_NUM_TRANSFERS);
Expand All @@ -105,21 +113,29 @@ static void print_usage(bool help)
/* Parses the command line arguments overriding the default transfer sizes,
* and number of transfer to use for the benchmark if specified. */
static int parse_args(int argc, char **argv, int *tx_channel, int *rx_channel,
size_t *tx_size, size_t *rx_size, int *num_transfers, bool *use_vdma)
size_t *tx_size, struct axidma_video_frame *tx_frame, size_t *rx_size,
struct axidma_video_frame *rx_frame, int *num_transfers, bool *use_vdma)
{
double double_arg;
int int_arg;
char option;
bool tx_frame_specified, rx_frame_specified;

// Set the default data size and number of transfers
*use_vdma = false;
*tx_channel = -1;
*rx_channel = -1;
*tx_size = DEFAULT_TRANSFER_SIZE;
tx_frame->height = -1;
tx_frame->width = -1;
tx_frame->depth = -1;
*rx_size = DEFAULT_TRANSFER_SIZE;
rx_frame->height = -1;
rx_frame->width = -1;
rx_frame->depth = -1;
*num_transfers = DEFAULT_NUM_TRANSFERS;

while ((option = getopt(argc, argv, "vt:r:i:b:o:s:n:h")) != (char)-1)
while ((option = getopt(argc, argv, "vt:r:i:b:f:o:s:g:n:h")) != (char)-1)
{
switch (option)
{
Expand Down Expand Up @@ -151,7 +167,7 @@ static int parse_args(int argc, char **argv, int *tx_channel, int *rx_channel,
print_usage(false);
return -EINVAL;
}
*tx_size = MB_TO_BYTE(double_arg);
*tx_size = MIB_TO_BYTE(double_arg);
break;

// Parse the transmit transfer size argument
Expand All @@ -163,13 +179,29 @@ static int parse_args(int argc, char **argv, int *tx_channel, int *rx_channel,
*tx_size = int_arg;
break;

// Parse the transmit frame size option
case 'f':
if (strlen(optarg) == 0) {
fprintf(stderr, "The -f option requires an argument.\n");
print_usage(false);
return -EINVAL;
} else if (parse_resolution(option, optarg, &tx_frame->height,
&tx_frame->width, &tx_frame->depth) < 0) {
print_usage(false);
return -EINVAL;
}
*tx_size = tx_frame->height * tx_frame->width * tx_frame->depth;
tx_frame_specified = true;

break;

// Parse the receive transfer size argument
case 'o':
if (parse_double(option, optarg, &double_arg) < 0) {
print_usage(false);
return -EINVAL;
}
*rx_size = MB_TO_BYTE(double_arg);
*rx_size = MIB_TO_BYTE(double_arg);
break;

// Parse the receive transfer size argument
Expand All @@ -181,6 +213,22 @@ static int parse_args(int argc, char **argv, int *tx_channel, int *rx_channel,
*rx_size = int_arg;
break;

// Parse the receive frame size option
case 'g':
if (strlen(optarg) == 0) {
fprintf(stderr, "The -g option requires an argument.\n");
print_usage(false);
return -EINVAL;
} else if (parse_resolution(option, optarg, &rx_frame->height,
&rx_frame->width, &rx_frame->depth) < 0) {
print_usage(false);
return -EINVAL;
}
*rx_size = rx_frame->height * rx_frame->width * rx_frame->depth;
rx_frame_specified = true;

break;

// Parse the number of transfers argument
case 'n':
if (parse_int(option, optarg, &int_arg) < 0) {
Expand Down Expand Up @@ -214,6 +262,11 @@ static int parse_args(int argc, char **argv, int *tx_channel, int *rx_channel,
return -EINVAL;
}

if (*use_vdma && (!tx_frame_specified || !rx_frame_specified)) {
fprintf(stderr, "Error: If -v is specified, then both -f and -g must "
"also be specified.\n");
return -EINVAL;
}

return 0;
}
Expand Down Expand Up @@ -331,16 +384,17 @@ static int verify_data(char *tx_buf, char *rx_buf, size_t tx_buf_size,
}

static int single_transfer_test(axidma_dev_t dev, int tx_channel, void *tx_buf,
int tx_size, int rx_channel, void *rx_buf, int rx_size)
int tx_size, struct axidma_video_frame *tx_frame, int rx_channel,
void *rx_buf, int rx_size, struct axidma_video_frame *rx_frame)
{
int rc;

// Initialize the buffer region we're going to transmit
init_data(tx_buf, rx_buf, tx_size, rx_size);

// Perform the DMA transaction
rc = axidma_twoway_transfer(dev, tx_channel, tx_buf, tx_size,
rx_channel, rx_buf, rx_size, true);
rc = axidma_twoway_transfer(dev, tx_channel, tx_buf, tx_size, tx_frame,
rx_channel, rx_buf, rx_size, rx_frame, true);
if (rc < 0) {
return rc;
}
Expand All @@ -349,15 +403,15 @@ static int single_transfer_test(axidma_dev_t dev, int tx_channel, void *tx_buf,
return verify_data(tx_buf, rx_buf, tx_size, rx_size);
}


/*----------------------------------------------------------------------------
* Benchmarking Test
*----------------------------------------------------------------------------*/

/* Profiles the transfer and receive rates for the DMA, reporting the throughput
* of each channel in MB/s. */
* of each channel in MiB/s. */
static int time_dma(axidma_dev_t dev, int tx_channel, void *tx_buf, int tx_size,
int rx_channel, void *rx_buf, int rx_size, int num_transfers)
struct axidma_video_frame *tx_frame, int rx_channel, void *rx_buf,
int rx_size, struct axidma_video_frame *rx_frame, int num_transfers)
{
int i, rc;
struct timeval start_time, end_time;
Expand All @@ -369,8 +423,8 @@ static int time_dma(axidma_dev_t dev, int tx_channel, void *tx_buf, int tx_size,
// Perform n transfers
for (i = 0; i < num_transfers; i++)
{
rc = axidma_twoway_transfer(dev, tx_channel, tx_buf, tx_size,
rx_channel, rx_buf, rx_size, true);
rc = axidma_twoway_transfer(dev, tx_channel, tx_buf, tx_size, tx_frame,
rx_channel, rx_buf, rx_size, rx_frame, true);
if (rc < 0) {
fprintf(stderr, "DMA failed on transfer %d, not reporting timing "
"results.\n", i+1);
Expand All @@ -383,15 +437,15 @@ static int time_dma(axidma_dev_t dev, int tx_channel, void *tx_buf, int tx_size,

// Compute the throughput of each channel
elapsed_time = TVAL_TO_SEC(end_time) - TVAL_TO_SEC(start_time);
tx_data_rate = BYTE_TO_MB(tx_size) * num_transfers / elapsed_time;
rx_data_rate = BYTE_TO_MB(rx_size) * num_transfers / elapsed_time;
tx_data_rate = BYTE_TO_MIB(tx_size) * num_transfers / elapsed_time;
rx_data_rate = BYTE_TO_MIB(rx_size) * num_transfers / elapsed_time;

// Report the statistics to the user
printf("DMA Timing Statistics:\n");
printf("\tElapsed Time: %0.2f s\n", elapsed_time);
printf("\tTransmit Throughput: %0.2f Mb/s\n", tx_data_rate);
printf("\tReceive Throughput: %0.2f Mb/s\n", rx_data_rate);
printf("\tTotal Throughput: %0.2f Mb/s\n", tx_data_rate + rx_data_rate);
printf("\tTransmit Throughput: %0.2f MiB/s\n", tx_data_rate);
printf("\tReceive Throughput: %0.2f MiB/s\n", rx_data_rate);
printf("\tTotal Throughput: %0.2f MiB/s\n", tx_data_rate + rx_data_rate);

return 0;
}
Expand All @@ -410,16 +464,27 @@ int main(int argc, char **argv)
char *tx_buf, *rx_buf;
axidma_dev_t axidma_dev;
const array_t *tx_chans, *rx_chans;
struct axidma_video_frame transmit_frame, *tx_frame, receive_frame, *rx_frame;

// Check if the user overrided the default transfer size and number
if (parse_args(argc, argv, &tx_channel, &rx_channel, &tx_size, &rx_size,
&num_transfers, &use_vdma) < 0) {
if (parse_args(argc, argv, &tx_channel, &rx_channel, &tx_size,
&transmit_frame, &rx_size, &receive_frame, &num_transfers,
&use_vdma) < 0) {
rc = 1;
goto ret;
}
printf("AXI DMA Benchmark Parameters:\n");
printf("\tTransmit Buffer Size: %0.2f Mb\n", BYTE_TO_MB(tx_size));
printf("\tReceive Buffer Size: %0.2f Mb\n", BYTE_TO_MB(rx_size));
if (!use_vdma) {
printf("\tTransmit Buffer Size: %0.2f MiB\n", BYTE_TO_MIB(tx_size));
printf("\tReceive Buffer Size: %0.2f MiB\n", BYTE_TO_MIB(rx_size));
} else {
printf("\tTransmit Buffer Size: %dx%dx%d (%0.2f MiB)\n",
transmit_frame.height, transmit_frame.width, transmit_frame.depth,
BYTE_TO_MIB(tx_size));
printf("\tReceive Buffer Size: %dx%dx%d (%0.2f MiB)\n",
receive_frame.height, receive_frame.width, receive_frame.depth,
BYTE_TO_MIB(rx_size));
}
printf("\tNumber of DMA Transfers: %d transfers\n\n", num_transfers);

// Initialize the AXI DMA device
Expand Down Expand Up @@ -448,9 +513,13 @@ int main(int argc, char **argv)
if (use_vdma) {
tx_chans = axidma_get_vdma_tx(axidma_dev);
rx_chans = axidma_get_vdma_rx(axidma_dev);
tx_frame = &transmit_frame;
rx_frame = &receive_frame;
} else {
tx_chans = axidma_get_dma_tx(axidma_dev);
rx_chans = axidma_get_dma_rx(axidma_dev);
tx_frame = NULL;
rx_frame = NULL;
}
if (tx_chans->len < 1) {
fprintf(stderr, "Error: No transmit channels were found.\n");
Expand All @@ -474,16 +543,16 @@ int main(int argc, char **argv)

// Transmit the buffer to DMA a single time
rc = single_transfer_test(axidma_dev, tx_channel, tx_buf, tx_size,
rx_channel, rx_buf, rx_size);
tx_frame, rx_channel, rx_buf, rx_size, rx_frame);
if (rc < 0) {
goto free_rx_buf;
}
printf("Single transfer test successfully completed!\n");

// Time the DMA eingine
printf("Beginning performance analysis of the DMA engine.\n\n");
rc = time_dma(axidma_dev, tx_channel, tx_buf, tx_size, rx_channel, rx_buf,
rx_size, num_transfers);
rc = time_dma(axidma_dev, tx_channel, tx_buf, tx_size, tx_frame,
rx_channel, rx_buf, rx_size, rx_frame, num_transfers);

free_rx_buf:
axidma_free(axidma_dev, rx_buf, rx_size);
Expand Down
Loading

0 comments on commit d3f0966

Please sign in to comment.