litex/misoclib/com/litepcie/software/linux/user/litepcie_util.c

260 lines
7.0 KiB
C

/*
* LitePCIe utilities
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <time.h>
#include "litepcie.h"
#include "cutils.h"
#include "config.h"
#include "csr.h"
#include "flags.h"
#include "litepcie_lib.h"
static inline uint32_t seed_to_data(uint32_t seed)
{
#if 1
/* more random but slower */
return seed * 0x31415976 + 1;
#else
/* simplify debug: just copy the counter */
return seed;
#endif
}
static void write_pn_data(uint32_t *dst, int count, uint32_t *pseed)
{
int i;
uint32_t seed;
seed = *pseed;
for(i = 0; i < count; i++) {
dst[i] = seed_to_data(seed);
seed++;
}
*pseed = seed;
}
/* Return the number of errors */
static int check_pn_data(const uint32_t *tab, int count,
uint32_t *pseed)
{
int i, errors;
uint32_t seed;
errors = 0;
seed = *pseed;
for(i = 0; i < count; i++) {
if (tab[i] != seed_to_data(seed)) {
errors++;
}
seed++;
}
*pseed = seed;
return errors;
}
#define MAX_SHIFT_OFFSET 128
/* test DMA with a buffer size of buf_size bytes in loopback
mode. */
void dma_test(LitePCIeState *s, int buf_size, int buf_count, BOOL is_loopback)
{
int is_first, tx_buf_num, buf_num_cur, buf_num_next;
struct litepcie_ioctl_dma_wait dma_wait;
int buf_stats_count; /* statistics */
int64_t last_time;
uint32_t tx_seed, rx_seed;
int buf_rx_count, first_rx_buf, rx_errors, shift, d, tx_underflows;
litepcie_dma_start(s, buf_size, buf_count, is_loopback);
is_first = 1;
buf_num_cur = 0; /* next buffer to receive */
/* PN data TX and RX state */
tx_seed = MAX_SHIFT_OFFSET;
rx_seed = 0;
buf_rx_count = 0;
first_rx_buf = 1;
/* statistics */
buf_stats_count = 0;
last_time = litepcie_get_time_ms();
rx_errors = 0;
shift = 0;
tx_underflows = 0;
for(;;) {
/* wait until at least one buffer is received */
dma_wait.timeout = 1000; /* 1 second timeout */
dma_wait.tx_wait = FALSE;
dma_wait.tx_buf_num = -1; /* not used */
if (is_first) {
dma_wait.rx_buf_num = -1; /* don't wait, just get the last
received buffer number */
} else {
dma_wait.rx_buf_num = sub_mod_int(buf_num_cur, 1, buf_count);
}
/* wait until the current buffer number is different from
dma_wait.buf_num */
if (ioctl(s->litepcie_fd, LITEPCIE_IOCTL_DMA_WAIT, &dma_wait) < 0) {
perror("LITEPCIE_IOCTL_DMA_WAIT");
}
if (is_first) {
buf_num_cur = dma_wait.rx_buf_num;
is_first = 0;
}
buf_num_next = add_mod_int(dma_wait.rx_buf_num, 1, buf_count);
while (buf_num_cur != buf_num_next) {
/* write the TX data 4/10 of a DMA cycle in the future */
tx_buf_num = add_mod_int(buf_num_cur, 4*buf_count/10, buf_count);
d = sub_mod_int(tx_buf_num, buf_num_next, buf_count);
if (d >= (buf_count / 2)) {
/* we are too late in writing data, which necessarily
gives read errors. */
tx_underflows++;
}
write_pn_data((uint32_t *)(s->dma_tx_buf +
tx_buf_num * s->dma_tx_buf_size),
s->tx_buf_size >> 2, &tx_seed);
if (buf_rx_count >= 4*buf_count/10) {
const uint32_t *rx_buf;
int rx_buf_len;
rx_buf = (uint32_t *)(s->dma_rx_buf + buf_num_cur * s->dma_rx_buf_size);
rx_buf_len = s->rx_buf_size >> 2;
if (first_rx_buf) {
uint32_t seed;
/* find the initial shift */
for(shift = 0; shift < 2 * MAX_SHIFT_OFFSET; shift++) {
seed = rx_seed + shift;
rx_errors = check_pn_data(rx_buf, rx_buf_len, &seed);
if (rx_errors <= (rx_buf_len / 2)) {
rx_seed = seed;
break;
}
}
if (shift == 2 * MAX_SHIFT_OFFSET) {
printf("Cannot find initial data\n");
exit(1);
} else {
printf("RX shift = %d\n",
-(shift - MAX_SHIFT_OFFSET));
}
first_rx_buf = 0;
} else {
/* count the number of errors */
rx_errors += check_pn_data(rx_buf, rx_buf_len, &rx_seed);
}
} else {
buf_rx_count++;
}
buf_num_cur = add_mod_int(buf_num_cur, 1, buf_count);
/* statistics */
if (++buf_stats_count == 10000) {
int64_t duration;
duration = litepcie_get_time_ms() - last_time;
printf("%0.1f Gb/sec %0.1f bufs/sec tx_underflows=%d errors=%d\n",
(double)buf_stats_count * buf_size * 8 / ((double)duration * 1e6),
(double)buf_stats_count * 1000 / (double)duration,
tx_underflows, rx_errors);
last_time = litepcie_get_time_ms();
buf_stats_count = 0;
tx_underflows = 0;
rx_errors = 0;
}
}
}
litepcie_dma_stop(s);
}
void dma_loopback_test(void)
{
LitePCIeState *s;
s = litepcie_open(LITEPCIE_FILENAME);
if (!s) {
fprintf(stderr, "Could not init driver\n");
exit(1);
}
dma_test(s, 16*1024, DMA_BUFFER_COUNT, TRUE);
litepcie_close(s);
}
void dump_version(void)
{
LitePCIeState *s;
s = litepcie_open(LITEPCIE_FILENAME);
if (!s) {
fprintf(stderr, "Could not init driver\n");
exit(1);
}
printf("sysid=0x%x\n", litepcie_readl(s, CSR_IDENTIFIER_SYSID_ADDR));
printf("frequency=%d\n", litepcie_readl(s, CSR_IDENTIFIER_FREQUENCY_ADDR));
litepcie_close(s);
}
void help(void)
{
printf("usage: litepcie_util cmd [args...]\n"
"\n"
"available commands:\n"
"dma_loopback_test test DMA loopback operation\n"
"version return fpga version\n"
);
exit(1);
}
int main(int argc, char **argv)
{
const char *cmd;
int c;
for(;;) {
c = getopt(argc, argv, "h");
if (c == -1)
break;
switch(c) {
case 'h':
help();
break;
default:
exit(1);
}
}
if (optind >= argc)
help();
cmd = argv[optind++];
if (!strcmp(cmd, "dma_loopback_test")) {
dma_loopback_test();
} else if (!strcmp(cmd, "version")) {
dump_version();
} else {
help();
}
return 0;
}