From c50dd8401114f75a0413fdd687b328d5e1fb1240 Mon Sep 17 00:00:00 2001 From: Joris van Rantwijk Date: Fri, 9 Aug 2024 22:16:22 +0200 Subject: [PATCH] Add userspace test program --- os/16_build_userspace.sh | 10 + os/src/userspace/Makefile | 16 + os/src/userspace/testje.c | 944 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 970 insertions(+) create mode 100755 os/16_build_userspace.sh create mode 100644 os/src/userspace/Makefile create mode 100644 os/src/userspace/testje.c diff --git a/os/16_build_userspace.sh b/os/16_build_userspace.sh new file mode 100755 index 0000000..7bf7ef5 --- /dev/null +++ b/os/16_build_userspace.sh @@ -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 + diff --git a/os/src/userspace/Makefile b/os/src/userspace/Makefile new file mode 100644 index 0000000..7db6d73 --- /dev/null +++ b/os/src/userspace/Makefile @@ -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 + diff --git a/os/src/userspace/testje.c b/os/src/userspace/testje.c new file mode 100644 index 0000000..244df15 --- /dev/null +++ b/os/src/userspace/testje.c @@ -0,0 +1,944 @@ +/* + * testje.c + * + * Command-line program to test PuzzleFW firmware. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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 */