Add userspace test program

This commit is contained in:
Joris van Rantwijk 2024-08-09 22:16:22 +02:00
parent 60800cce4b
commit c50dd84011
3 changed files with 970 additions and 0 deletions

10
os/16_build_userspace.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
set -e
. script_env
setup_toolchain
make -C src/userspace CROSS_COMPILE=arm-linux- clean
make -C src/userspace CROSS_COMPILE=arm-linux- all

16
os/src/userspace/Makefile Normal file
View File

@ -0,0 +1,16 @@
#
# Makefile to build user space software to run on the Red Pitaya.
#
CC = $(CROSS_COMPILE)gcc
CFLAGS = -Wall -O2
.PHONY: all
all: testje
testje: testje.c
.PHONY: clean
clean:
$(RM) -f testje

944
os/src/userspace/testje.c Normal file
View File

@ -0,0 +1,944 @@
/*
* testje.c
*
* Command-line program to test PuzzleFW firmware.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SOC_DEVICES_DIR "/sys/devices/soc0"
#define PUZZLEFW_REGS_SIZE 0x100000
#define REG_INFO 0x0000
#define REG_IRQ_ENABLE 0x0010
#define REG_IRQ_PENDING 0x0014
#define REG_DMA_EN 0x0100
#define REG_DMA_STATUS 0x0104
#define REG_DMA_CLEAR 0x0108
#define REG_DMA_ADDR_START 0x0200
#define REG_DMA_ADDR_END 0x0204
#define REG_DMA_ADDR_LIMIT 0x0208
#define REG_DMA_ADDR_INTR 0x020c
#define REG_DMA_ADDR_PTR 0x0210
#define REG_DMA_CHANNEL_CTRL 0x0214
#define REG_DMA_INTR_CTRL 0x0218
struct puzzlefw_context {
int uio_fd;
volatile uint32_t * regs;
volatile void * dma_buf;
size_t dma_buf_size;
size_t dma_transfer_size;
char device_name[128];
char uio_path[128];
};
/*
* Find PuzzleFW device in /sys filesystem.
* Initialize "device_name" attribute of context structure.
*
* Return 0 on success, -1 on error.
*/
static int puzzlefw_find_device_name(struct puzzlefw_context *ctx)
{
DIR *dirp;
struct dirent *dent;
dirp = opendir(SOC_DEVICES_DIR);
if (dirp == NULL) {
fprintf(stderr,
"ERROR: Can not access %s (%s)\n",
SOC_DEVICES_DIR, strerror(errno));
return -1;
}
/* Find entry matching "*.puzzlefw" */
while (1) {
errno = 0;
dent = readdir(dirp);
if (dent == NULL) {
break;
}
size_t namelen = strlen(dent->d_name);
if (namelen > 9
&& strcmp(dent->d_name + namelen - 9, ".puzzlefw") == 0) {
break;
}
}
if (dent == NULL && errno != 0) {
fprintf(stderr, "ERROR: Can not read %s (%s)\n",
SOC_DEVICES_DIR, strerror(errno));
closedir(dirp);
return -1;
}
closedir(dirp);
if (dent == NULL) {
fprintf(stderr, "ERROR: *.puzzlefw not found in %s\n",
SOC_DEVICES_DIR);
return -1;
}
int n = snprintf(ctx->device_name, sizeof(ctx->device_name),
"%s", dent->d_name);
if (n >= sizeof(ctx->device_name)) {
fprintf(stderr, "ERROR: Device name exceeds maximum length\n");
return -1;
}
return 0;
}
/*
* Find UIO device node for the PuzzleFW driver.
* Initialize "uio_path" and "dma_buf_size" attributes of context structure.
*
* Return 0 on success, -1 on error.
*/
static int puzzlefw_find_uio(struct puzzlefw_context *ctx)
{
FILE *f;
DIR *dirp;
struct dirent *dent;
char path[256];
char attrbuf[64];
int n = snprintf(path, sizeof(path),
"%s/%s/uio",
SOC_DEVICES_DIR, ctx->device_name);
if (n >= sizeof(path)) {
fprintf(stderr, "ERROR: Path name exceeds maximum length\n");
return -1;
}
dirp = opendir(path);
if (dirp == NULL) {
fprintf(stderr, "ERROR: Can not access %s (%s)\n",
path, strerror(errno));
return -1;
}
/* Find entry matching "uio*" */
while (1) {
errno = 0;
dent = readdir(dirp);
if (dent == NULL) {
break;
}
if (strlen(dent->d_name) > 3 && strncmp(dent->d_name, "uio", 3) == 0) {
break;
}
}
if (dent == NULL && errno != 0) {
fprintf(stderr, "ERROR: Can not read %s (%s)\n",
path, strerror(errno));
closedir(dirp);
return -1;
}
closedir(dirp);
if (dent == NULL) {
fprintf(stderr, "ERROR: No UIO node found in %s\n", path);
return -1;
}
/* Find name of /dev/uioN node. */
n = snprintf(ctx->uio_path, sizeof(ctx->uio_path),
"/dev/%s", dent->d_name);
if (n >= sizeof(ctx->uio_path)) {
fprintf(stderr, "ERROR: Device name exceeds maximum length\n");
return -1;
}
/* Read DMA buffer size. */
n = snprintf(path + strlen(path), sizeof(path) - strlen(path),
"/%s/maps/map1/size",
dent->d_name);
if (n >= sizeof(path)) {
fprintf(stderr, "ERROR: Path name exceeds maximum length\n");
return -1;
}
f = fopen(path, "r");
if (f == NULL) {
fprintf(stderr, "ERROR: Can not access %s (%s)\n",
path, strerror(errno));
return -1;
}
if (fgets(attrbuf, sizeof(attrbuf), f) == NULL) {
fprintf(stderr, "ERROR: Can not read %s (%s)\n",
path, strerror(errno));
fclose(f);
return -1;
}
fclose(f);
ctx->dma_buf_size = 0;
ctx->dma_buf_size = strtoul(attrbuf, NULL, 16);
if (ctx->dma_buf_size == 0) {
fprintf(stderr, "ERROR: Invalid value in %s\n", path);
return -1;
}
return 0;
}
/*
* Open PuzzleFW driver.
*
* Return 0 on success, -1 on error.
*/
int puzzlefw_open(struct puzzlefw_context *ctx)
{
ctx->uio_fd = -1;
ctx->regs = NULL;
ctx->dma_buf = NULL;
ctx->dma_transfer_size = 128;
if (puzzlefw_find_device_name(ctx) != 0) {
goto err_out;
}
if (puzzlefw_find_uio(ctx) != 0) {
goto err_out;
}
if (((ctx->dma_buf_size % ctx->dma_transfer_size) != 0) ||
(ctx->dma_buf_size < 8192)) {
fprintf(stderr, "ERROR: Invalid DMA buffer size (%zu)\n",
ctx->dma_buf_size);
goto err_out;
}
int fd = open(ctx->uio_path, O_RDWR | O_SYNC);
if (fd < 0) {
fprintf(stderr, "ERROR: Can not open %s (%s)\n",
ctx->uio_path, strerror(errno));
goto err_out;
}
/*
* Map FPGA user registers.
* Offset 0 tells the UIO to map the first address range,
* which corresponds to the registers.
*/
void *regs = mmap(NULL,
PUZZLEFW_REGS_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0);
if (regs == ((void*)-1)) {
fprintf(stderr, "ERROR: Can not mmap %s (%s)\n",
ctx->uio_path, strerror(errno));
goto err_close;
}
/*
* Map DMA buffer.
* Offset PAGE_SIZE tells the UIO to map the second address range,
* which corresponds to the DMA buffer.
*/
int page_size = getpagesize();
void *dma_buf = mmap(NULL,
ctx->dma_buf_size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
1 * page_size);
if (dma_buf == ((void*)-1)) {
fprintf(stderr, "ERROR: Can not mmap %s (%s)\n",
ctx->uio_path, strerror(errno));
goto err_unmap_regs;
}
ctx->uio_fd = fd;
ctx->regs = regs;
ctx->dma_buf = dma_buf;
return 0;
err_unmap_regs:
munmap(regs, PUZZLEFW_REGS_SIZE);
err_close:
close(fd);
err_out:
return -1;
}
/* Close PuzzleFW driver. */
void puzzlefw_close(struct puzzlefw_context *ctx)
{
if (ctx->uio_fd < 0 || ctx->regs == NULL || ctx->dma_buf == NULL) {
fprintf(stderr, "ERROR: Driver not open\n");
return;
}
if (munmap((void*)ctx->regs, PUZZLEFW_REGS_SIZE) < 0) {
fprintf(stderr, "ERROR: Can not unmap registers (%s)\n",
strerror(errno));
}
if (munmap((void*)ctx->dma_buf, ctx->dma_buf_size) < 0) {
fprintf(stderr, "ERROR: Can not unmap DMA buffer (%s)\n",
strerror(errno));
}
if (close(ctx->uio_fd) < 0) {
fprintf(stderr, "ERROR: Can not close %s (%s)\n",
ctx->uio_path, strerror(errno));
}
ctx->uio_fd = -1;
ctx->regs = NULL;
ctx->dma_buf = NULL;
}
/* Read from FPGA register. */
static inline uint32_t puzzlefw_read_reg(
struct puzzlefw_context *ctx,
unsigned long addr)
{
return ctx->regs[addr / 4];
}
/* Write to FPGA register. */
static inline void puzzlefw_write_reg(
struct puzzlefw_context *ctx,
unsigned long addr,
uint32_t v)
{
ctx->regs[addr / 4] = v;
}
/*
* Synchronize between register access and DMA buffer access.
*
* After reading from an FPGA register that indicates a DMA write
* operation has completed, call this function before reading from
* the DMA buffer.
*
* After writing to the DMA buffer, call this function before
* writing to the FPGA register that initiates a DMA read operation.
*/
static inline void puzzlefw_sync_dma(void)
{
asm volatile ("dsb" : : : "memory");
}
/*
* Copy data from uncached memory to normal memory.
*
* This function only supports copying multiples of 8 bytes,
* with source and destination pointers both 8-byte aligned.
*
* For large data blocks, this function copies ~ 230 MiB/s
* on Zynq-7000 with ARM running at 667 MHz and DDR running at 533 MHz.
*/
void puzzlefw_copy_from_dma(void *dst, volatile void *src, size_t len)
{
if (len < 256) {
memcpy(dst, (void*)src, len);
} else {
for (size_t i = 0; i < len / 64; i++) {
asm volatile (
"vldm %0!, {d0-d7} \n"
"vstm %1!, {d0-d7} \n"
: "+r" (src), "+r" (dst)
: : "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "memory"
);
}
if (len % 64 > 0) {
memcpy(dst, (void*)src, len % 64);
}
}
}
static void show_status(struct puzzlefw_context *ctx)
{
uint32_t v;
printf("PuzzleFW registers:\n");
v = puzzlefw_read_reg(ctx, REG_INFO);
printf(" info = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_IRQ_ENABLE);
printf(" irq_enable = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_IRQ_PENDING);
printf(" irq_pending = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_DMA_EN);
printf(" dma_en = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_DMA_STATUS);
printf(" dma_status = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_START);
printf(" dma_addr_start = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_END);
printf(" dma_addr_end = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_LIMIT);
printf(" dma_addr_limit = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_INTR);
printf(" dma_addr_intr = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_DMA_ADDR_PTR);
printf(" dma_addr_ptr = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_DMA_CHANNEL_CTRL);
printf(" dma_channel_ctrl = 0x%08x\n", v);
v = puzzlefw_read_reg(ctx, REG_DMA_INTR_CTRL);
printf(" dma_intr_ctrl = 0x%08x\n", v);
}
static void show_buf(struct puzzlefw_context *ctx)
{
printf("DMA buffer:\n");
for (int i = 0; i < 260; i++) {
unsigned int p = i * 32;
uint32_t b[8];
memcpy(b, ctx->dma_buf + p, 32);
printf("%04x: %08x %08x %08x %08x %08x %08x %08x %08x\n",
p, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
}
}
static void gen_data(struct puzzlefw_context *ctx)
{
for (int i = 0; i < 2080; i++) {
uint32_t v = (i & 0xffff) | (1 << (16 + i % 16));
memcpy(ctx->dma_buf + 4 * i, &v, 4);
}
}
#if 0
static void wait_irq(struct puzzlefw_context *ctx)
{
printf("Waiting for IRQ ...\n");
while (1) {
puzzlefw_write_reg(ctx, REG_IRQ_ENABLE, 1);
uint32_t v;
ssize_t k = read(ctx->uio_fd, &v, sizeof(v));
if (k != 4) {
fprintf(stderr, "ERROR: Can not read from UIO (%s)\n",
strerror(errno));
break;
}
printf("got %x interrupts\n", v);
puzzlefw_write_reg(ctx, REG_TEST_IRQ, 0);
}
}
#endif
static void blast_dma(struct puzzlefw_context *ctx)
{
printf("Starting DMA blaster ...\n");
// Disable DMA writer.
puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 0);
// Setup DMA buffer.
puzzlefw_write_reg(ctx, REG_DMA_ADDR_START, 0);
puzzlefw_write_reg(ctx, REG_DMA_ADDR_END, ctx->dma_buf_size);
// Set invalid limit to keep the writer blasting.
puzzlefw_write_reg(ctx, REG_DMA_ADDR_LIMIT, 0xffffffff);
// Initialize and enable DMA writer.
puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 3);
struct timespec tp;
tp.tv_sec = 10;
tp.tv_nsec = 0;
clock_nanosleep(CLOCK_MONOTONIC, 0, &tp, NULL);
// Disable DMA writer.
puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 0);
printf("Stopped DMA blaster\n");
}
/*
* Send all specified data to the socket.
*
* The socket must be in blocking mode.
*
* Returns:
* 0 if all data were written;
* 2 if the connection was closed;
* -1 if another error occurs.
*/
static int send_all(int conn, const void *buf, size_t len)
{
while (len > 0) {
ssize_t ret = send(conn, buf, len, MSG_NOSIGNAL);
if (ret < 0) {
if (errno == EINTR) {
continue;
}
if (errno == EPIPE || errno == ECONNRESET) {
return 2;
}
fprintf(stderr, "ERROR: send failed (%s)\n", strerror(errno));
return -1;
}
buf += ret;
len -= ret;
}
return 0;
}
/*
* Wait until the specified number of bytes are available in the DMA buffer,
* or the specified timeout expires,
* or the specified socket connection is closed.
*
* This function assumes that the DMA write channel is the only entity that
* triggers PuzzleFW interrupts. It also assumes that no other thread is
* interacting with PuzzleFW interrupts.
*
* Parameters:
* ctx: Device context.
* conn: Socket file descriptor.
* read_pointer: Read pointer within the DMA buffer.
* wait_avail: Number of bytes to wait for.
* This must be a multiple of the DMA transfer size.
* timeout_ms: Timeout in milliseconds.
*
* Returns:
* 0 if the specified number of bytes is available;
* 1 if timeout occurred;
* 2 if the connection was closed;
* -1 if a system error occurred.
*
* This function may sometimes return 0 when no data is available. This can
* happen due to a stale pending interrupt.
*/
static int wait_dma_data(
struct puzzlefw_context *ctx,
int conn,
uint32_t read_pointer,
uint32_t wait_avail,
int timeout_ms)
{
assert(wait_avail > 0);
assert(wait_avail % ctx->dma_transfer_size == 0);
// Enable DMA writer interrupt and clear pending interrupts.
uint32_t addr_intr = read_pointer + wait_avail;
if (addr_intr >= ctx->dma_buf_size) {
addr_intr -= ctx->dma_buf_size;
}
puzzlefw_write_reg(ctx, REG_DMA_ADDR_INTR, addr_intr);
puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 3);
// Check if data are already available.
// This is necessary to prevent a race condition when data becomes
// available just before the interrupt is enabled.
uint32_t write_pointer = puzzlefw_read_reg(ctx, REG_DMA_ADDR_PTR);
uint32_t navail =
(write_pointer >= read_pointer) ?
(write_pointer - read_pointer) :
(ctx->dma_buf_size + write_pointer - read_pointer);
if (navail >= wait_avail) {
// Data already available; disable DMA writer interrupts.
puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 2);
return 0;
}
// Enable interrupts from FPGA.
puzzlefw_write_reg(ctx, REG_IRQ_ENABLE, 1);
// Sleep until interrupt or timeout or connection closed.
struct pollfd fds[2];
fds[0].fd = ctx->uio_fd;
fds[0].events = POLLIN;
fds[1].fd = conn;
fds[1].events = 0;
int ret = poll(fds, 2, timeout_ms);
if (ret < 0) {
fprintf(stderr, "ERROR: poll failed (%s)\n", strerror(errno));
return -1;
}
// Disable DMA writer interrupt and clear pending interrupt.
puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 2);
if ((fds[0].revents & POLLIN) != 0) {
// Interrupt occurred.
int32_t intr_count;
read(ctx->uio_fd, &intr_count, sizeof(intr_count));
return 0;
}
if ((fds[1].revents & POLLHUP) != 0) {
// Connection closed.
return 2;
}
// Probably timeout then.
return 0;
}
/*
* Send data from DMA to TCP socket.
*
* Keep running until the TCP connection is closed.
*/
int transmit_dma_data(struct puzzlefw_context *ctx, int conn)
{
// Maximum block size per TCP send() call.
const uint32_t send_max_block = 65536;
// When buffer is empty, sleep until this amount becomes available.
const uint32_t wait_block_size = 4096;
// Reserve this number of bytes in the buffer to avoid ambiguous pointers.
const uint32_t pointer_margin = 1024;
// When buffer is empty, sleep at most this duration.
const int timeout_ms = 10;
assert(ctx->dma_buf_size >= 2 * wait_block_size);
assert(send_max_block % ctx->dma_transfer_size == 0);
assert(wait_block_size % ctx->dma_transfer_size == 0);
assert(pointer_margin % ctx->dma_transfer_size == 0);
// Disable AXI DMA.
puzzlefw_write_reg(ctx, REG_DMA_EN, 0);
// Disable DMA write channel.
puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 0);
// Initialize DMA write buffer.
puzzlefw_write_reg(ctx, REG_DMA_ADDR_START, 0);
puzzlefw_write_reg(ctx, REG_DMA_ADDR_END, ctx->dma_buf_size);
puzzlefw_write_reg(ctx, REG_DMA_ADDR_LIMIT,
ctx->dma_buf_size - pointer_margin);
// Disable DMA writer interrupts; clear interrupt status.
puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 2);
// Clear AXI DMA state.
puzzlefw_write_reg(ctx, REG_DMA_CLEAR, 1);
// Enable AXI DMA.
puzzlefw_write_reg(ctx, REG_DMA_EN, 1);
// Initialize and enable DMA writer.
puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 3);
uint32_t read_pointer = 0;
int ret;
while (1) {
// Check DMA status.
uint32_t status = puzzlefw_read_reg(ctx, REG_DMA_STATUS);
if ((status & 0x1e) != 0) {
// DMA error.
fprintf(stderr, "ERROR: DMA error, status=0x%08x\n", status);
ret = -1;
break;
}
// Check for data in buffer.
uint32_t write_pointer = puzzlefw_read_reg(ctx, REG_DMA_ADDR_PTR);
if (write_pointer == read_pointer) {
// Wait for new data.
ret = wait_dma_data(ctx, conn,
read_pointer,
wait_block_size,
timeout_ms);
if (ret < 0) {
break;
}
if (ret == 2) {
// Connection closed.
ret = 0;
break;
}
} else {
// Determine number of bytes available.
// If the write pointer wrapped around the end of the buffer,
// stop at the end of the buffer and process the rest in the next
// pass through the loop.
uint32_t navail =
(write_pointer >= read_pointer) ?
(write_pointer - read_pointer) :
(ctx->dma_buf_size - read_pointer);
// Make sure the CPU view of the DMA buffer is up to date
// relative to the state previously reported via registers.
puzzlefw_sync_dma();
// Transmit available data in blocks of 64 kB.
while (navail > 0) {
uint32_t block_size =
(navail < send_max_block) ? navail : send_max_block;
// Send data to socket.
ret = send_all(conn,
(void*)ctx->dma_buf + read_pointer, block_size);
if (ret != 0) {
break;
}
// Update read pointer and update DMA writer limit.
read_pointer += block_size;
if (read_pointer > pointer_margin) {
puzzlefw_write_reg(ctx, REG_DMA_ADDR_LIMIT,
read_pointer - pointer_margin);
}
navail -= block_size;
}
if (ret < 0) {
break;
}
if (ret == 2) {
// Connection closed.
ret = 0;
break;
}
if (read_pointer == ctx->dma_buf_size) {
read_pointer = 0;
}
}
}
// Disable AXI DMA.
puzzlefw_write_reg(ctx, REG_DMA_EN, 0);
// Disable DMA write channel.
puzzlefw_write_reg(ctx, REG_DMA_CHANNEL_CTRL, 0);
// Disable DMA writer interrupts; clear interrupt status.
puzzlefw_write_reg(ctx, REG_DMA_INTR_CTRL, 2);
return ret;
}
/*
* Run TCP server.
*/
int run_server(struct puzzlefw_context *ctx)
{
const int tcp_port = 5001;
// Create server socket.
int srv_sock = socket(AF_INET, SOCK_STREAM, 0);
if (srv_sock < 0) {
fprintf(stderr, "ERROR: socket (%s)\n", strerror(errno));
return -1;
}
int v = 1;
setsockopt(srv_sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v));
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(tcp_port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(srv_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr, "ERROR: bind (%s)\n", strerror(errno));
return -1;
}
if (listen(srv_sock, 1) < 0) {
fprintf(stderr, "ERROR: listen (%s)\n", strerror(errno));
return -1;
}
printf("Waiting for connection to port %d ...\n", tcp_port);
int conn = accept(srv_sock, NULL, NULL);
if (conn < 0) {
fprintf(stderr, "ERROR: accept (%s)\n", strerror(errno));
return -1;
}
printf("Connected, streaming data ...\n");
close(srv_sock);
int ret = transmit_dma_data(ctx, conn);
close(conn);
printf("Connection closed\n");
return ret;
}
int main(int argc, char **argv)
{
struct puzzlefw_context ctx;
int show = 0;
int showbuf = 0;
int gendata = 0;
int dmaon = 0;
int dmaoff = 0;
int dmaclear = 0;
int blastdma = 0;
int server = 0;
if (argc == 2 && strcmp(argv[1], "show") == 0) {
show = 1;
} else if (argc == 2 && strcmp(argv[1], "showbuf") == 0) {
showbuf = 1;
} else if (argc == 2 && strcmp(argv[1], "gendata") == 0) {
gendata = 1;
} else if (argc == 2 && strcmp(argv[1], "dmaon") == 0) {
dmaon = 1;
} else if (argc == 2 && strcmp(argv[1], "dmaoff") == 0) {
dmaoff = 1;
} else if (argc == 2 && strcmp(argv[1], "dmaclear") == 0) {
dmaclear = 1;
} else if (argc == 2 && strcmp(argv[1], "blastdma") == 0) {
blastdma = 1;
} else if (argc == 2 && strcmp(argv[1], "server") == 0) {
server = 1;
} else {
printf(
"Usage:\n"
" testje show\n"
" Show current register values.\n"
"\n"
" testje showbuf\n"
" Show current register values and part of buffer.\n"
"\n"
" testje gendata\n"
" Write test pattern to DMA buffer.\n"
"\n"
" testje dmaon\n"
" Enable DMA.\n"
"\n"
" testje dmaoff\n"
" Disable DMA.\n"
"\n"
" testje dmaclear\n"
" Clear DMA errors.\n"
"\n"
" testje blastdma\n"
" Run unlimited DMA into buffer for 10 seconds.\n"
"\n"
" testje server\n"
" Open TCP port 5001 to stream DMA data.\n"
"\n");
if (argc > 1) {
fprintf(stderr, "ERROR: Invalid command\n");
exit(1);
} else {
exit(0);
}
}
if (puzzlefw_open(&ctx) != 0) {
exit(1);
}
if (show || showbuf) {
show_status(&ctx);
}
if (showbuf) {
show_buf(&ctx);
}
if (gendata) {
gen_data(&ctx);
}
if (dmaon) {
puzzlefw_write_reg(&ctx, REG_DMA_EN, 1);
}
if (dmaoff) {
puzzlefw_write_reg(&ctx, REG_DMA_EN, 0);
}
if (dmaclear) {
puzzlefw_write_reg(&ctx, REG_DMA_CLEAR, 1);
}
if (blastdma) {
blast_dma(&ctx);
}
if (server) {
run_server(&ctx);
}
puzzlefw_close(&ctx);
return 0;
}
/* end */