/* * 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 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); } 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, 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_ACQ_ADDR_INTR, addr_intr); puzzlefw_write_reg(ctx, REG_ACQ_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_ACQ_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_ACQ_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_ACQ_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) { // 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; 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_ACQ_DMA_CTRL, 0); // Initialize DMA write buffer. puzzlefw_write_reg(ctx, REG_ACQ_ADDR_START, 0); puzzlefw_write_reg(ctx, REG_ACQ_ADDR_END, ctx->dma_buf_size); puzzlefw_write_reg(ctx, REG_ACQ_ADDR_LIMIT, ctx->dma_buf_size - pointer_margin); // Disable DMA writer interrupts; clear interrupt status. puzzlefw_write_reg(ctx, REG_ACQ_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_ACQ_DMA_CTRL, 2); // Enable DMA writer. puzzlefw_write_reg(ctx, REG_ACQ_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_ACQ_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, 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_ACQ_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_ACQ_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_ACQ_DMA_CTRL, 0); // Disable DMA writer interrupts; clear interrupt status. puzzlefw_write_reg(ctx, REG_ACQ_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; int tsclear = 0; int acqon = 0; int acqoff = 0; int trigger = 0; int trigauto = 0; int trignone = 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; 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], "trignone") == 0) { trignone = 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 == 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 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 trignone\n" " Disable triggering.\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 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 (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 (trignone) { puzzlefw_set_trigger_mode(&ctx, TRIG_NONE); } 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 (server) { run_server(&ctx); } puzzlefw_close(&ctx); return 0; } /* end */