322 lines
7.7 KiB
C
322 lines
7.7 KiB
C
/*
|
|
* This file is part of the Xilinx DMA IP Core driver tools for Linux
|
|
*
|
|
* Copyright (c) 2016-present, Xilinx, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under BSD-style license (found in the
|
|
* LICENSE file in the root directory of this source tree)
|
|
*/
|
|
|
|
#define _BSD_SOURCE
|
|
#define _XOPEN_SOURCE 500
|
|
#include <assert.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "../xdma/cdev_sgdma.h"
|
|
|
|
#include "dma_utils.c"
|
|
|
|
static struct option const long_opts[] = {
|
|
{"device", required_argument, NULL, 'd'},
|
|
{"address", required_argument, NULL, 'a'},
|
|
{"aperture", required_argument, NULL, 'k'},
|
|
{"size", required_argument, NULL, 's'},
|
|
{"offset", required_argument, NULL, 'o'},
|
|
{"count", required_argument, NULL, 'c'},
|
|
{"data infile", required_argument, NULL, 'f'},
|
|
{"data outfile", required_argument, NULL, 'w'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{"verbose", no_argument, NULL, 'v'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
#define DEVICE_NAME_DEFAULT "/dev/xdma0_h2c_0"
|
|
#define SIZE_DEFAULT (32)
|
|
#define COUNT_DEFAULT (1)
|
|
|
|
|
|
static int test_dma(char *devname, uint64_t addr, uint64_t aperture,
|
|
uint64_t size, uint64_t offset, uint64_t count,
|
|
char *filename, char *);
|
|
|
|
static void usage(const char *name)
|
|
{
|
|
int i = 0;
|
|
|
|
fprintf(stdout, "%s\n\n", name);
|
|
fprintf(stdout, "usage: %s [OPTIONS]\n\n", name);
|
|
fprintf(stdout,
|
|
"Write via SGDMA, optionally read input from a file.\n\n");
|
|
|
|
fprintf(stdout, " -%c (--%s) device (defaults to %s)\n",
|
|
long_opts[i].val, long_opts[i].name, DEVICE_NAME_DEFAULT);
|
|
i++;
|
|
fprintf(stdout, " -%c (--%s) the start address on the AXI bus\n",
|
|
long_opts[i].val, long_opts[i].name);
|
|
i++;
|
|
fprintf(stdout, " -%c (--%s) memory address aperture\n",
|
|
long_opts[i].val, long_opts[i].name);
|
|
i++;
|
|
fprintf(stdout,
|
|
" -%c (--%s) size of a single transfer in bytes, default %d,\n",
|
|
long_opts[i].val, long_opts[i].name, SIZE_DEFAULT);
|
|
i++;
|
|
fprintf(stdout, " -%c (--%s) page offset of transfer\n",
|
|
long_opts[i].val, long_opts[i].name);
|
|
i++;
|
|
fprintf(stdout, " -%c (--%s) number of transfers, default %d\n",
|
|
long_opts[i].val, long_opts[i].name, COUNT_DEFAULT);
|
|
i++;
|
|
fprintf(stdout, " -%c (--%s) filename to read the data from.\n",
|
|
long_opts[i].val, long_opts[i].name);
|
|
i++;
|
|
fprintf(stdout,
|
|
" -%c (--%s) filename to write the data of the transfers\n",
|
|
long_opts[i].val, long_opts[i].name);
|
|
i++;
|
|
fprintf(stdout, " -%c (--%s) print usage help and exit\n",
|
|
long_opts[i].val, long_opts[i].name);
|
|
i++;
|
|
fprintf(stdout, " -%c (--%s) verbose output\n",
|
|
long_opts[i].val, long_opts[i].name);
|
|
i++;
|
|
|
|
fprintf(stdout, "\nReturn code:\n");
|
|
fprintf(stdout, " 0: all bytes were dma'ed successfully\n");
|
|
fprintf(stdout, " < 0: error\n\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int cmd_opt;
|
|
char *device = DEVICE_NAME_DEFAULT;
|
|
uint64_t address = 0;
|
|
uint64_t aperture = 0;
|
|
uint64_t size = SIZE_DEFAULT;
|
|
uint64_t offset = 0;
|
|
uint64_t count = COUNT_DEFAULT;
|
|
char *infname = NULL;
|
|
char *ofname = NULL;
|
|
|
|
while ((cmd_opt =
|
|
getopt_long(argc, argv, "vhc:f:d:a:k:s:o:w:", long_opts,
|
|
NULL)) != -1) {
|
|
switch (cmd_opt) {
|
|
case 0:
|
|
/* long option */
|
|
break;
|
|
case 'd':
|
|
/* device node name */
|
|
//fprintf(stdout, "'%s'\n", optarg);
|
|
device = strdup(optarg);
|
|
break;
|
|
case 'a':
|
|
/* RAM address on the AXI bus in bytes */
|
|
address = getopt_integer(optarg);
|
|
break;
|
|
case 'k':
|
|
/* memory aperture windows size */
|
|
aperture = getopt_integer(optarg);
|
|
break;
|
|
case 's':
|
|
/* size in bytes */
|
|
size = getopt_integer(optarg);
|
|
break;
|
|
case 'o':
|
|
offset = getopt_integer(optarg) & 4095;
|
|
break;
|
|
/* count */
|
|
case 'c':
|
|
count = getopt_integer(optarg);
|
|
break;
|
|
/* count */
|
|
case 'f':
|
|
infname = strdup(optarg);
|
|
break;
|
|
case 'w':
|
|
ofname = strdup(optarg);
|
|
break;
|
|
/* print usage help and exit */
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'h':
|
|
default:
|
|
usage(argv[0]);
|
|
exit(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (verbose)
|
|
fprintf(stdout,
|
|
"dev %s, addr 0x%lx, aperture 0x%lx, size 0x%lx, offset 0x%lx, "
|
|
"count %lu\n",
|
|
device, address, aperture, size, offset, count);
|
|
|
|
return test_dma(device, address, aperture, size, offset, count,
|
|
infname, ofname);
|
|
}
|
|
|
|
static int test_dma(char *devname, uint64_t addr, uint64_t aperture,
|
|
uint64_t size, uint64_t offset, uint64_t count,
|
|
char *infname, char *ofname)
|
|
{
|
|
uint64_t i;
|
|
ssize_t rc;
|
|
size_t bytes_done = 0;
|
|
size_t out_offset = 0;
|
|
char *buffer = NULL;
|
|
char *allocated = NULL;
|
|
struct timespec ts_start, ts_end;
|
|
int infile_fd = -1;
|
|
int outfile_fd = -1;
|
|
int fpga_fd = open(devname, O_RDWR);
|
|
long total_time = 0;
|
|
float result;
|
|
float avg_time = 0;
|
|
int underflow = 0;
|
|
|
|
if (fpga_fd < 0) {
|
|
fprintf(stderr, "unable to open device %s, %d.\n",
|
|
devname, fpga_fd);
|
|
perror("open device");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (infname) {
|
|
infile_fd = open(infname, O_RDONLY);
|
|
if (infile_fd < 0) {
|
|
fprintf(stderr, "unable to open input file %s, %d.\n",
|
|
infname, infile_fd);
|
|
perror("open input file");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (ofname) {
|
|
outfile_fd =
|
|
open(ofname, O_RDWR | O_CREAT | O_TRUNC | O_SYNC,
|
|
0666);
|
|
if (outfile_fd < 0) {
|
|
fprintf(stderr, "unable to open output file %s, %d.\n",
|
|
ofname, outfile_fd);
|
|
perror("open output file");
|
|
rc = -EINVAL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
posix_memalign((void **)&allocated, 4096 /*alignment */ , size + 4096);
|
|
if (!allocated) {
|
|
fprintf(stderr, "OOM %lu.\n", size + 4096);
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
buffer = allocated + offset;
|
|
if (verbose)
|
|
fprintf(stdout, "host buffer 0x%lx = %p\n",
|
|
size + 4096, buffer);
|
|
|
|
if (infile_fd >= 0) {
|
|
rc = read_to_buffer(infname, infile_fd, buffer, size, 0);
|
|
if (rc < 0 || rc < size)
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < count; i++) {
|
|
/* write buffer to AXI MM address using SGDMA */
|
|
rc = clock_gettime(CLOCK_MONOTONIC, &ts_start);
|
|
|
|
if (aperture) {
|
|
struct xdma_aperture_ioctl io;
|
|
|
|
io.buffer = (unsigned long)buffer;
|
|
io.len = size;
|
|
io.ep_addr = addr;
|
|
io.aperture = aperture;
|
|
io.done = 0UL;
|
|
|
|
rc = ioctl(fpga_fd, IOCTL_XDMA_APERTURE_W, &io);
|
|
if (rc < 0 || io.error) {
|
|
fprintf(stdout,
|
|
"#%d: aperture W ioctl failed %d,%d.\n",
|
|
i, rc, io.error);
|
|
goto out;
|
|
}
|
|
|
|
bytes_done = io.done;
|
|
} else {
|
|
rc = write_from_buffer(devname, fpga_fd, buffer, size,
|
|
addr);
|
|
if (rc < 0)
|
|
goto out;
|
|
|
|
bytes_done = rc;
|
|
}
|
|
|
|
rc = clock_gettime(CLOCK_MONOTONIC, &ts_end);
|
|
|
|
if (bytes_done < size) {
|
|
printf("#%d: underflow %ld/%ld.\n",
|
|
i, bytes_done, size);
|
|
underflow = 1;
|
|
}
|
|
|
|
/* subtract the start time from the end time */
|
|
timespec_sub(&ts_end, &ts_start);
|
|
total_time += ts_end.tv_nsec;
|
|
/* a bit less accurate but side-effects are accounted for */
|
|
if (verbose)
|
|
fprintf(stdout,
|
|
"#%lu: CLOCK_MONOTONIC %ld.%09ld sec. write %ld bytes\n",
|
|
i, ts_end.tv_sec, ts_end.tv_nsec, size);
|
|
|
|
if (outfile_fd >= 0) {
|
|
rc = write_from_buffer(ofname, outfile_fd, buffer,
|
|
bytes_done, out_offset);
|
|
if (rc < 0 || rc < bytes_done)
|
|
goto out;
|
|
out_offset += bytes_done;
|
|
}
|
|
}
|
|
|
|
if (!underflow) {
|
|
avg_time = (float)total_time/(float)count;
|
|
result = ((float)size)*1000/avg_time;
|
|
if (verbose)
|
|
printf("** Avg time device %s, total time %ld nsec, avg_time = %f, size = %lu, BW = %f \n",
|
|
devname, total_time, avg_time, size, result);
|
|
printf("%s ** Average BW = %lu, %f\n", devname, size, result);
|
|
}
|
|
|
|
out:
|
|
close(fpga_fd);
|
|
if (infile_fd >= 0)
|
|
close(infile_fd);
|
|
if (outfile_fd >= 0)
|
|
close(outfile_fd);
|
|
free(allocated);
|
|
|
|
if (rc < 0)
|
|
return rc;
|
|
/* treat underflow as error */
|
|
return underflow ? -EIO : 0;
|
|
}
|