diff --git a/sw/src/userspace/testje.c b/sw/src/userspace/testje.c deleted file mode 100644 index caa33be..0000000 --- a/sw/src/userspace/testje.c +++ /dev/null @@ -1,1436 +0,0 @@ -/* - * 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_TIMESTAMP_LO 0x0180 -#define REG_TIMESTAMP_HI 0x0184 -#define REG_TIMESTAMP_CLEAR 0x0188 -#define REG_ACQ_ADDR_START 0x0200 -#define REG_ACQ_ADDR_END 0x0204 -#define REG_ACQ_ADDR_LIMIT 0x0208 -#define REG_ACQ_ADDR_INTR 0x020c -#define REG_ACQ_ADDR_PTR 0x0210 -#define REG_ACQ_DMA_CTRL 0x0214 -#define REG_ACQ_INTR_CTRL 0x0218 -#define REG_ACQ_DMA_STATUS 0x021c -#define REG_ACQUISITION_EN 0x0220 -#define REG_RECORD_LENGTH 0x0224 -#define REG_DECIMATION_FACTOR 0x0228 -#define REG_SHIFT_STEPS 0x022c -#define REG_AVERAGING_EN 0x0230 -#define REG_CH4_MODE 0x0234 -#define REG_SIMULATE_ADC 0x0238 -#define REG_TRIGGER_MODE 0x0240 -#define REG_TRIGGER_DELAY 0x0244 -#define REG_TRIGGER_STATUS 0x0248 -#define REG_ADC_SAMPLE 0x0280 -#define REG_ADC23_SAMPLE 0x0284 -#define REG_ADC_RANGE_CLEAR 0x028c -#define REG_ADC0_MINMAX 0x0290 -#define REG_ADC1_MINMAX 0x0294 -#define REG_ADC2_MINMAX 0x0298 -#define REG_ADC3_MINMAX 0x029c -#define REG_TT_ADDR_START 0x0300 -#define REG_TT_ADDR_END 0x0304 -#define REG_TT_ADDR_LIMIT 0x0308 -#define REG_TT_ADDR_INTR 0x030c -#define REG_TT_ADDR_PTR 0x0310 -#define REG_TT_DMA_CTRL 0x0314 -#define REG_TT_INTR_CTRL 0x0318 -#define REG_TT_DMA_STATUS 0x031c -#define REG_TIMETAGGER_EN 0x0320 -#define REG_TIMETAGGER_MARK 0x0324 -#define REG_DIG_SIMULATE 0x0330 -#define REG_DIG_SAMPLE 0x0338 -#define REG_LED_STATE 0x0404 - - -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]; -}; - -enum puzzlefw_trigger_mode { - TRIG_NONE = 0, - TRIG_AUTO = 1, - TRIG_EXTERNAL = 2 -}; - - -/* - * 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); - } - - } -} - - -/* - * Return the current value of the timestamp counter. - */ -uint64_t puzzlefw_get_timestamp(struct puzzlefw_context *ctx) -{ - uint32_t vhi0, vlo, vhi; - vhi0 = puzzlefw_read_reg(ctx, REG_TIMESTAMP_HI); - vlo = puzzlefw_read_reg(ctx, REG_TIMESTAMP_LO); - vhi = puzzlefw_read_reg(ctx, REG_TIMESTAMP_HI); - if (vhi != vhi0) { - vlo = puzzlefw_read_reg(ctx, REG_TIMESTAMP_LO); - } - return (((uint64_t)vhi) << 32) | vlo; -} - - -/* - * Clear the timestamp counter. - */ -void puzzlefw_clear_timestamp(struct puzzlefw_context *ctx) -{ - puzzlefw_write_reg(ctx, REG_TIMESTAMP_CLEAR, 1); -} - - -/* - * Return the current trigger mode. - */ -enum puzzlefw_trigger_mode puzzlefw_get_trigger_mode( - struct puzzlefw_context *ctx) -{ - uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); - if ((v & 0x01) != 0) { - return TRIG_AUTO; - } - if ((v & 0x02) != 0) { - return TRIG_EXTERNAL; - } - return TRIG_NONE; -} - - -/* - * Set trigger mode. - */ -void puzzlefw_set_trigger_mode( - struct puzzlefw_context *ctx, - enum puzzlefw_trigger_mode mode) -{ - uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); - v &= 0xfc; - if (mode == TRIG_AUTO) { - v |= 0x01; - } - if (mode == TRIG_EXTERNAL) { - v |= 0x02; - } - puzzlefw_write_reg(ctx, REG_TRIGGER_MODE, v); -} - - -/* - * Return selected external trigger channel. - */ -int puzzlefw_get_trigger_ext_channel(struct puzzlefw_context *ctx) -{ - uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); - return ((v >> 4) & 7); -} - - -/* - * Select external trigger channel. - */ -void puzzlefw_set_trigger_ext_channel(struct puzzlefw_context *ctx, int channel) -{ - uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); - v &= 0x8f; - v |= (channel << 4); - puzzlefw_write_reg(ctx, REG_TRIGGER_MODE, v); -} - - -/* - * Return external trigger polarity. - */ -int puzzlefw_get_trigger_ext_falling(struct puzzlefw_context *ctx) -{ - uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); - return ((v & 0x80) != 0); -} - - -/* - * Select external trigger polarity. - */ -void puzzlefw_set_trigger_ext_falling(struct puzzlefw_context *ctx, int falling) -{ - uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); - v &= 0x7f; - if (falling) { - v |= 0x80; - } - puzzlefw_write_reg(ctx, REG_TRIGGER_MODE, v); -} - - -/* - * Force a single trigger event. - */ -void puzzlefw_trigger_force(struct puzzlefw_context *ctx) -{ - uint32_t v = puzzlefw_read_reg(ctx, REG_TRIGGER_MODE); - puzzlefw_write_reg(ctx, REG_TRIGGER_MODE, v | 0x100); -} - - -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); - - uint64_t ts = puzzlefw_get_timestamp(ctx); - printf(" timestamp = %llu\n", (unsigned long long)ts); - - v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_START); - printf(" acq_addr_start = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_END); - printf(" acq_addr_end = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_LIMIT); - printf(" acq_addr_limit = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_INTR); - printf(" acq_addr_intr = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_ACQ_ADDR_PTR); - printf(" acq_addr_ptr = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_ACQ_DMA_CTRL); - printf(" acq_dma_ctrl = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_ACQ_DMA_STATUS); - printf(" acq_dma_status = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_ACQ_INTR_CTRL); - printf(" acq_intr_ctrl = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_ACQUISITION_EN); - printf(" acquisition_en = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_SIMULATE_ADC); - printf(" simulate_adc = 0x%08x\n", v); - - enum puzzlefw_trigger_mode trigger_mode = puzzlefw_get_trigger_mode(ctx); - int trigger_channel = puzzlefw_get_trigger_ext_channel(ctx); - int trigger_falling = puzzlefw_get_trigger_ext_falling(ctx); - printf(" trigger mode = %s, channel=%d, falling=%d\n", - (trigger_mode == TRIG_AUTO) ? "auto" : - (trigger_mode == TRIG_EXTERNAL) ?"external" : - "none", - trigger_channel, - trigger_falling); - - v = puzzlefw_read_reg(ctx, REG_TRIGGER_DELAY); - printf(" trigger_delay = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_RECORD_LENGTH); - printf(" record_length = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_DECIMATION_FACTOR); - printf(" decimation_fac = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_SHIFT_STEPS); - printf(" shift_steps = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_AVERAGING_EN); - printf(" averaging_en = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_ADC_SAMPLE); - uint32_t adc_range = puzzlefw_read_reg(ctx, REG_ADC0_MINMAX); - printf(" channel 0 = %5u (min = %u, max = %u)\n", - v & 0xffff, - adc_range & 0xffff, - (adc_range >> 16) & 0xffff); - - adc_range = puzzlefw_read_reg(ctx, REG_ADC1_MINMAX); - printf(" channel 1 = %5u (min = %u, max = %u)\n", - (v >> 16) & 0xffff, - adc_range & 0xffff, - (adc_range >> 16) & 0xffff); - - v = puzzlefw_read_reg(ctx, REG_DIG_SAMPLE); - printf(" digital input = %d %d %d %d\n", - (v >> 3) & 1, (v >> 2) & 1, (v >> 1) & 1, v & 1); - - v = puzzlefw_read_reg(ctx, REG_TT_ADDR_START); - printf(" tt_addr_start = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_TT_ADDR_END); - printf(" tt_addr_end = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_TT_ADDR_LIMIT); - printf(" tt_addr_limit = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_TT_ADDR_INTR); - printf(" tt_addr_intr = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_TT_ADDR_PTR); - printf(" tt_addr_ptr = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_TT_DMA_CTRL); - printf(" tt_dma_ctrl = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_TT_DMA_STATUS); - printf(" tt_dma_status = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_TT_INTR_CTRL); - printf(" tt_intr_ctrl = 0x%08x\n", v); - - v = puzzlefw_read_reg(ctx, REG_TIMETAGGER_EN); - printf(" timetagger_en = 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_ACQ_DMA_CTRL, 0); - - // Setup DMA buffer. - puzzlefw_write_reg(ctx, REG_ACQ_ADDR_START, 0); - puzzlefw_write_reg(ctx, REG_ACQ_ADDR_END, ctx->dma_buf_size); - - // Set invalid limit to keep the writer blasting. - puzzlefw_write_reg(ctx, REG_ACQ_ADDR_LIMIT, 0xffffffff); - - // Initialize DMA writer. - puzzlefw_write_reg(ctx, REG_ACQ_DMA_CTRL, 2); - - // Enable DMA writer. - puzzlefw_write_reg(ctx, REG_ACQ_DMA_CTRL, 1); - - 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_ACQ_DMA_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, - int timetagger, - uint32_t read_pointer, - uint32_t wait_avail, - int timeout_ms) -{ - const uint32_t reg_addr_ptr = timetagger ? REG_TT_ADDR_PTR : REG_ACQ_ADDR_PTR; - const uint32_t reg_addr_intr = timetagger ? REG_TT_ADDR_INTR : REG_ACQ_ADDR_INTR; - const uint32_t reg_intr_ctrl = timetagger ? REG_TT_INTR_CTRL : REG_ACQ_INTR_CTRL; - - 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_addr_intr, addr_intr); - puzzlefw_write_reg(ctx, reg_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_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_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 = POLLIN; - - 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_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 | POLLIN)) != 0) { - // Connection closed, or client sent unexpected data. - // POLLHUP does not happen on Linux as long as our side of the - // connection remains open, but check it anyway to be sure. - 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, int timetagger) -{ - // Maximum block size per TCP send() call. - const uint32_t send_max_block = 65536; - - // Sleep until this number of bytes is available in the buffer. - const uint32_t wait_block_size = 4096; - - // Sleep at most this duration if there is insufficient data in the buffer. - const int timeout_ms = 10; - - // Reserve this number of bytes in the buffer to avoid ambiguous pointers. - const uint32_t pointer_margin = 4096; - - const uint32_t reg_dma_ctrl = timetagger ? REG_TT_DMA_CTRL : REG_ACQ_DMA_CTRL; - const uint32_t reg_dma_status = timetagger ? REG_TT_DMA_STATUS : REG_ACQ_DMA_STATUS; - const uint32_t reg_addr_start = timetagger ? REG_TT_ADDR_START : REG_ACQ_ADDR_START; - const uint32_t reg_addr_end = timetagger ? REG_TT_ADDR_END : REG_ACQ_ADDR_END; - const uint32_t reg_addr_limit = timetagger ? REG_TT_ADDR_LIMIT : REG_ACQ_ADDR_LIMIT; - const uint32_t reg_addr_ptr = timetagger ? REG_TT_ADDR_PTR : REG_ACQ_ADDR_PTR; - const uint32_t reg_intr_ctrl = timetagger ? REG_TT_INTR_CTRL : REG_ACQ_INTR_CTRL; - - - 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_ctrl, 0); - - // Initialize DMA write buffer. - puzzlefw_write_reg(ctx, reg_addr_start, 0); - puzzlefw_write_reg(ctx, reg_addr_end, ctx->dma_buf_size); - puzzlefw_write_reg(ctx, reg_addr_limit, ctx->dma_buf_size - pointer_margin); - - // Disable DMA writer interrupts; clear interrupt status. - puzzlefw_write_reg(ctx, reg_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 DMA writer. - puzzlefw_write_reg(ctx, reg_dma_ctrl, 2); - - // Enable DMA writer. - puzzlefw_write_reg(ctx, reg_dma_ctrl, 1); - - 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; - } - - // Determine number of bytes available. - uint32_t write_pointer = puzzlefw_read_reg(ctx, reg_addr_ptr); - uint32_t navail = (write_pointer >= read_pointer) ? - (write_pointer - read_pointer) : - (ctx->dma_buf_size + write_pointer - read_pointer); - - // Wait for enough data in the buffer, or timeout. - if (navail < wait_block_size) { - ret = wait_dma_data(ctx, conn, - timetagger, - read_pointer, - wait_block_size, - timeout_ms); - if (ret < 0) { - break; - } - if (ret == 2) { - // Connection closed. - ret = 0; - break; - } - - write_pointer = puzzlefw_read_reg(ctx, reg_addr_ptr); - } - - // 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. - 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_addr_limit, read_pointer - pointer_margin); - } - - navail -= block_size; - } - - if (ret < 0) { - break; - } - - if (ret == 2) { - // Connection closed. - ret = 0; - break; - } - - // Wrap read pointer at end of buffer. - 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_ctrl, 0); - - // Disable DMA writer interrupts; clear interrupt status. - puzzlefw_write_reg(ctx, reg_intr_ctrl, 2); - - return ret; -} - - -/* - * Run TCP server. - */ -int run_server(struct puzzlefw_context *ctx, int timetagger) -{ - const int tcp_port = timetagger ? 5002 : 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, timetagger); - - 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; - int tsclear = 0; - int acqon = 0; - int acqoff = 0; - int trigger = 0; - int trigauto = 0; - int trigext = 0; - int trignone = 0; - int trigfall = 0, trigrise = 0; - int set_trigdelay = 0, trigdelay = 0; - int set_reclen = 0, reclen = 0; - int set_decimate = 0, decimate = 0; - int set_shift = 0, shift_steps = 0; - int avgon = 0, avgoff = 0; - int simon = 0, simoff = 0; - int rangeclear = 0; - int set_digsim = 0, digsim = 0; - int set_timetagger = 0, timetagger_en = 0; - int marker = 0; - int ttserver = 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], "tsclear") == 0) { - tsclear = 1; - } else if (argc == 2 && strcmp(argv[1], "acqon") == 0) { - acqon = 1; - } else if (argc == 2 && strcmp(argv[1], "acqoff") == 0) { - acqoff = 1; - } else if (argc == 2 && strcmp(argv[1], "trigger") == 0) { - trigger = 1; - } else if (argc == 2 && strcmp(argv[1], "trigauto") == 0) { - trigauto = 1; - } else if (argc == 2 && strcmp(argv[1], "trigext") == 0) { - trigext = 1; - } else if (argc == 2 && strcmp(argv[1], "trignone") == 0) { - trignone = 1; - } else if (argc == 2 && strcmp(argv[1], "trigfall") == 0) { - trigfall = 1; - } else if (argc == 2 && strcmp(argv[1], "trigrise") == 0) { - trigrise = 1; - } else if (argc == 3 && strcmp(argv[1], "trigdelay") == 0) { - set_trigdelay = 1; - trigdelay = atoi(argv[2]); - } else if (argc == 3 && strcmp(argv[1], "reclen") == 0) { - set_reclen = 1; - reclen = atoi(argv[2]); - } else if (argc == 3 && strcmp(argv[1], "decimate") == 0) { - set_decimate = 1; - decimate = atoi(argv[2]); - } else if (argc == 3 && strcmp(argv[1], "shift") == 0) { - set_shift = 1; - shift_steps = atoi(argv[2]); - } else if (argc == 2 && strcmp(argv[1], "avgon") == 0) { - avgon = 1; - } else if (argc == 2 && strcmp(argv[1], "avgoff") == 0) { - avgoff = 1; - } else if (argc == 2 && strcmp(argv[1], "simon") == 0) { - simon = 1; - } else if (argc == 2 && strcmp(argv[1], "simoff") == 0) { - simoff = 1; - } else if (argc == 2 && strcmp(argv[1], "rangeclear") == 0) { - rangeclear = 1; - } else if (argc == 3 && strcmp(argv[1], "digsim") == 0) { - set_digsim = 1; - if (strcmp(argv[2], "off") == 0) { - digsim = -1; - } else { - digsim = atoi(argv[2]); - } - } else if (argc == 3 && strcmp(argv[1], "timetagger") == 0) { - set_timetagger = 1; - timetagger_en = atoi(argv[2]); - } else if (argc == 2 && strcmp(argv[1], "marker") == 0) { - marker = 1; - } else if (argc == 2 && strcmp(argv[1], "server") == 0) { - server = 1; - } else if (argc == 2 && strcmp(argv[1], "ttserver") == 0) { - ttserver = 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 tsclear\n" - " Clear timestamp counter.\n" - "\n" - " testje acqon\n" - " Enable acquisition.\n" - "\n" - " testje acqoff\n" - " Disable acquisition.\n" - "\n" - " testje trigger\n" - " Trigger once.\n" - "\n" - " testje trigauto\n" - " Enable continuous triggering.\n" - "\n" - " testje trigext\n" - " Enable external trigger.\n" - "\n" - " testje trignone\n" - " Disable triggering.\n" - "\n" - " testje trigrise\n" - " Trigger on rising edge.\n" - "\n" - " testje trigfall\n" - " Trigger on falling edge.\n" - "\n" - " testje trigdelay N\n" - " Set trigger delay N cycles.\n" - "\n" - " testje reclen N\n" - " Set record length N + 1.\n" - "\n" - " testje decimate N\n" - " Set decimation factor N + 1.\n" - "\n" - " testje shift N\n" - " Set shift-right by N.\n" - "\n" - " testje avgon\n" - " Enable averaging.\n" - "\n" - " testje avgoff\n" - " Disable averaging.\n" - "\n" - " testje simon\n" - " Use simulated ADC data.\n" - "\n" - " testje simoff\n" - " Use real ADC data.\n" - "\n" - " testje rangeclear\n" - " Clear min/max ADC sample monitor.\n" - "\n" - " testje digsim N|'off'\n" - " Set simulated digital input.\n" - "\n" - " testje timetagger MASK\n" - " Specify set of enabled time tagger channels.\n" - "\n" - " testje marker\n" - " Emit a marker in the time tagger stream.\n" - "\n" - " testje server\n" - " Open TCP port 5001 to stream ADC data.\n" - "\n" - " testje ttserver\n" - " Open TCP port 5002 to stream time tagger 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 (tsclear) { - puzzlefw_clear_timestamp(&ctx); - } - - if (acqon) { - puzzlefw_write_reg(&ctx, REG_ACQUISITION_EN, 1); - } - - if (acqoff) { - puzzlefw_write_reg(&ctx, REG_ACQUISITION_EN, 0); - } - - if (trigger) { - puzzlefw_trigger_force(&ctx); - } - - if (trigauto) { - puzzlefw_set_trigger_mode(&ctx, TRIG_AUTO); - } - - if (trigext) { - puzzlefw_set_trigger_mode(&ctx, TRIG_EXTERNAL); - } - - if (trignone) { - puzzlefw_set_trigger_mode(&ctx, TRIG_NONE); - } - - if (trigrise) { - puzzlefw_set_trigger_ext_falling(&ctx, 0); - } - - if (trigfall) { - puzzlefw_set_trigger_ext_falling(&ctx, 1); - } - - if (set_trigdelay) { - puzzlefw_write_reg(&ctx, REG_TRIGGER_DELAY, trigdelay); - } - - if (set_reclen) { - puzzlefw_write_reg(&ctx, REG_RECORD_LENGTH, reclen); - } - - if (set_decimate) { - puzzlefw_write_reg(&ctx, REG_DECIMATION_FACTOR, decimate); - } - - if (set_shift) { - puzzlefw_write_reg(&ctx, REG_SHIFT_STEPS, shift_steps); - } - - if (avgon) { - puzzlefw_write_reg(&ctx, REG_AVERAGING_EN, 1); - } - - if (avgoff) { - puzzlefw_write_reg(&ctx, REG_AVERAGING_EN, 0); - } - - if (simon) { - puzzlefw_write_reg(&ctx, REG_SIMULATE_ADC, 1); - } - - if (simoff) { - puzzlefw_write_reg(&ctx, REG_SIMULATE_ADC, 0); - } - - if (rangeclear) { - puzzlefw_write_reg(&ctx, REG_ADC_RANGE_CLEAR, 1); - } - - if (set_digsim) { - if (digsim < 0) { - puzzlefw_write_reg(&ctx, REG_DIG_SIMULATE, 0); - } else { - puzzlefw_write_reg(&ctx, REG_DIG_SIMULATE, 0x100 + digsim); - } - } - - if (set_timetagger) { - puzzlefw_write_reg(&ctx, REG_TIMETAGGER_EN, timetagger_en); - } - - if (marker) { - puzzlefw_write_reg(&ctx, REG_TIMETAGGER_MARK, 1); - } - - if (server) { - run_server(&ctx, 0); - } - - if (ttserver) { - run_server(&ctx, 1); - } - - puzzlefw_close(&ctx); - - return 0; -} - -/* end */