#define _XOPEN_SOURCE 500
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <termios.h>
#include <errno.h>
#include <ctype.h>
#include <math.h>
#include <arpa/inet.h>
#include "libwr2rf/io.h"
#include "libwr2rf/host.h"
#include "libwr2rf/board.h"
#include "libwr2rf/api.h"
#include "wr2rf_vme_regs.h"
#include "wr2rf_init_regs.h"
#include "wr2rf_rfnco_regs.h"
#include "wb_uart.h"
#include "regs.h"

/* [0x10]: REG Hardware Info Register */
#define SYSC_REG_HWIR 0x00000010

/* [0x0]: REG Core Reset Register */
#define WRC_CPU_CSR_REG_RESET 0x00000000
/* [0x4]: REG Core Upload Address Register */
#define WRC_CPU_CSR_REG_UADDR 0x00000004
/* [0x8]: REG Core Upload Data Register */
#define WRC_CPU_CSR_REG_UDATA 0x00000008

struct xlat_tab {
  const char *name;
  unsigned val;
};

static unsigned
xlat_get_value(const struct xlat_tab *tab, const char *name, unsigned err)
{
  const struct xlat_tab *e;

  for (e = tab; e->name; e++)
    if (strcmp(e->name, name) == 0)
      return e->val;
  return err;
}

static const char *
xlat_get_name(const struct xlat_tab *tab, unsigned val, const char *err)
{
  const struct xlat_tab *e;
  for (e = tab; e->name; e++)
    if (e->val == val)
      return e->name;
  return err;
}

static uint32_t
libwr2rf_framerxtx_read32(struct libwr2rf_dev *dev, unsigned off)
{
        return libwr2rf_16x32_read32 (dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_FRAMERXTX + off);
}

static void
libwr2rf_framerxtx_write32(struct libwr2rf_dev *dev, unsigned off, uint32_t val)
{
  //printf("writel %08x at %04x\n", val, off);
        libwr2rf_16x32_write32(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_FRAMERXTX + off, val);
}

static unsigned
get_wrs_rxframe_counter (struct libwr2rf_dev *dev)
{
  return libwr2rf_read16
    (dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRS_RXFRAME_COUNTER);
}

static void
disp_wrs_rxframe (struct libwr2rf_dev *dev, unsigned count)
{
  printf ("RX frame count: %04x, ctrl: %04x, ftw: %04x%04x%04x\n",
          count,
          libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                          + WR2RF_INIT_REGS_WRS_RXFRAME_CTRL),
          libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                          + WR2RF_INIT_REGS_WRS_RXFRAME_FTW + 2),
          libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                          + WR2RF_INIT_REGS_WRS_RXFRAME_FTW + 4),
          libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                          + WR2RF_INIT_REGS_WRS_RXFRAME_FTW + 6));
}

static void
ftw_show (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned prev = ~0;

  while (1)
    {
      unsigned count = get_wrs_rxframe_counter (dev);
      if (count != prev) {
        disp_wrs_rxframe (dev, count);
        prev = count;
      }
    }
}

static void
framerxtx_last (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned count;
  unsigned short time_lsb;
  unsigned short time_msb;
  unsigned tai;
  unsigned cycles;

  printf ("NCO forced: %x\n",
          !!(libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_LOC_OR_WRS)
             & WR2RF_INIT_REGS_NCO_LOC_OR_WRS_PARAMS_SEL));

  count = libwr2rf_read16
          (dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRS_RXFRAME_COUNTER);

  printf ("RX frame count: %04x, ctrl: %04x, ftw: %04x%04x%04x\n",
          count,
          libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                         + WR2RF_INIT_REGS_WRS_RXFRAME_CTRL),
          libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                         + WR2RF_INIT_REGS_WRS_RXFRAME_FTW + 2),
          libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                         + WR2RF_INIT_REGS_WRS_RXFRAME_FTW + 4),
          libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                         + WR2RF_INIT_REGS_WRS_RXFRAME_FTW + 6));
  printf ("RX frame counter: %04x\n",
          libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRS_RXFRAME_COUNTER));

  time_lsb = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRS_RXFRAME_RFTIMESTAMP+2);
  time_msb = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRS_RXFRAME_RFTIMESTAMP);
  tai      = time_msb >> 12;
  cycles   = ((time_msb & 0xfff) << 16) | time_lsb;
  printf ("RX frame rftimestamp:   secs=%02d cycles=0x%08x\n", tai, cycles);

  time_lsb = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRS_RXFRAME_RXWRTIMESTAMP+2);
  time_msb = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRS_RXFRAME_RXWRTIMESTAMP);
  tai      = time_msb >> 12;
  cycles   = ((time_msb & 0xfff) << 16) | time_lsb;
  printf ("RX frame rxwrtimestamp: secs=%02d cycles=0x%08x\n", tai, cycles);

  time_lsb = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRS_RXFRAME_TXWRTIMESTAMP+2);
  time_msb = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRS_RXFRAME_TXWRTIMESTAMP);
  tai      = time_msb >> 12;
  cycles   = ((time_msb & 0xfff) << 16) | time_lsb;
  printf ("RX frame txwrtimestamp: secs=%02d cycles=0x%08x\n", tai, cycles);
}

static void
framerxtx_framerxtx (struct libwr2rf_dev *dev, int argc, char **argv)
{
  printf ("RFFRame version:   %08x\n", libwr2rf_framerxtx_read32 (dev, 0x00));
  printf ("Status:            %08x\n", libwr2rf_framerxtx_read32 (dev, 0x04));
  printf ("TX_Period:         %08x\n", libwr2rf_framerxtx_read32 (dev, 0x08));
  printf ("rx_out_data_time:  %08x\n", libwr2rf_framerxtx_read32 (dev, 0x0c));
  printf ("simrf_ctl:         %08x\n", libwr2rf_framerxtx_read32 (dev, 0x10));
  printf ("rfsim_scyc_len:    %08x\n", libwr2rf_framerxtx_read32 (dev, 0x14));
  printf ("rfsim_scyc_inc:    %08x\n", libwr2rf_framerxtx_read32 (dev, 0x18));
  printf ("rfsim_scyc_ctrl:   %08x\n", libwr2rf_framerxtx_read32 (dev, 0x1c));
  printf ("dbg_rx_rfm_or_rfs: %08x\n", libwr2rf_framerxtx_read32 (dev, 0x20));
  printf ("dbg_tx_rfm_or_rfs: %08x\n", libwr2rf_framerxtx_read32 (dev, 0x24));
  printf ("ftw_sim_off msb:   %08x\n", libwr2rf_framerxtx_read32 (dev, 0x28));
  printf ("ftw_sim_off lsb:   %08x\n", libwr2rf_framerxtx_read32 (dev, 0x2c));
}

static void
framerxtx_send1 (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned long long ftw_h1 = 0x16B24A8FB6ULL;
    unsigned control_1 = 0;
    unsigned control_2 = 0;
    unsigned pulse = 0;
    unsigned i;

    for (i = 1; i < argc; i++) {
        if (strcmp(argv[i], "reset") == 0) {
            control_1 |= 1;
        } else if (strcmp(argv[i], "pulse") == 0) {
            control_1 |= 1;
            pulse = 1;
        } else if (isdigit(argv[i][0])) {
            ftw_h1 = strtoull(argv[i], NULL, 0);
        } else {
            printf ("usage: %s [FTW] [reset] [pulse]\n", argv[0]);
            return;
        }
    }

    //printf ("Sending RFframe with FTW_H1 = %016llx\n", ftw_h1);
    libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TXFRAME_FTW_H1_MAIN + 2, ftw_h1 >> 32);
    libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TXFRAME_FTW_H1_MAIN + 4, ftw_h1 >> 16);
    libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TXFRAME_FTW_H1_MAIN + 6, ftw_h1 >> 0);
    libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TXFRAME_CONTROL, control_1);

    /* SCR: tx dbg=2 (simulation), ftype=1 (RFm), send-once.  */
    libwr2rf_framerxtx_write32 (dev, 0x04, (1 << 3) | (0 << 1) | (1 << 0));

    if (pulse) {
        libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TXFRAME_CONTROL, control_2);
        libwr2rf_framerxtx_write32 (dev, 0x04, (1 << 3) | (0 << 1) | (1 << 0));
    }
}

static void
framerxtx_send (struct libwr2rf_dev *dev, int argc, char **argv)
{
        /* TX period: every second  */
        libwr2rf_framerxtx_write32 (dev, 0x08, 62500000);

        /* SCR: tx dbg=1 (counter), ftype=1 (RFm).  */
        libwr2rf_framerxtx_write32 (dev, 0x04, (1 << 9) | (1 << 3) | (1 << 1));
}

static void
framerxtx_ramp (struct libwr2rf_dev *dev, int argc, char **argv)
{
    char fname[16]  = "framerxtx_send1";
    char ftw_h1[16] = "0x1687012c85";
    char cmd[10]    = "pulse";
    char *args[3]   = {fname, ftw_h1, cmd};
    unsigned long ftw_min  = 0x1687012c85;
    unsigned long ftw_max  = 0x16bdadeee1;
    unsigned long ftw_step = 0x5f32;
    unsigned long ftw      = ftw_min;
    unsigned step_up  = 1;

    framerxtx_send1(dev, 3, args);
    while (0) {
        if (step_up) {
            ftw += ftw_step;
            if (ftw >= ftw_max)
                step_up = 0;
        } else {
            ftw -= ftw_step;
            if (ftw <= ftw_min)
                step_up = 1;
        }
        sprintf(ftw_h1, "0x%lx", ftw);
        framerxtx_send1(dev, 2, args);
//        nanosleep(&snooze, NULL);
        printf("0x%012lx\n", ftw);
    }
}


static void
framerxtx_simul (struct libwr2rf_dev *dev, int argc, char **argv)
{
    /* One frame every 20us (50_000 frame/sec)
       Pattern:
       * sync on PPS,
       * 600ms flat bottom (Frf=198.51 Mhz, ftw=0x16_8701_2c85
       len=30000
       * ramp-up to Frf=200.392Mhz
       len=37640, inc=0x5f32
       * flat-top: Frf=200.392Mhz, ftw=0x16_bdad_eee1
       len~=32460, (last 10 with reset)
    */
    char fname[16]  = "framerxtx_send1";
    char ftw_h1[16] = "0x1687012c85";
    char cmd[10]    = "pulse";
    char *args[3]   = {fname, ftw_h1, cmd};
    framerxtx_send1(dev, 3, args);

    /* FTW_SIM_OFFSET: low.  */
    libwr2rf_framerxtx_write32 (dev, 0x28, 0x16);
    libwr2rf_framerxtx_write32 (dev, 0x2c, 0x87012c85);

    /* SIMRF CTRL: CLEN=0, SCID=0  */
    libwr2rf_framerxtx_write32 (dev, 0x10, 0x00);

    /* Set sub cycle length and increment.  */
    libwr2rf_framerxtx_write32 (dev, 0x14, 30000);
    libwr2rf_framerxtx_write32 (dev, 0x18, 0);
    libwr2rf_framerxtx_write32 (dev, 0x1c, 0);

    /* SIMRF CTRL: CLEN=0, SCID=1  */
    libwr2rf_framerxtx_write32 (dev, 0x10, 0x10);

    /* Set sub cycle length and increment.  */
    libwr2rf_framerxtx_write32 (dev, 0x14, 37640);
    libwr2rf_framerxtx_write32 (dev, 0x18, 0x5f32);
    libwr2rf_framerxtx_write32 (dev, 0x1c, 0);

    /* SIMRF CTRL: CLEN=0, SCID=2  */
    libwr2rf_framerxtx_write32 (dev, 0x10, 0x20);

    /* Set sub cycle length and increment.  */
    libwr2rf_framerxtx_write32 (dev, 0x14, 10000 /*32450*/);
    libwr2rf_framerxtx_write32 (dev, 0x18, 0);
    libwr2rf_framerxtx_write32 (dev, 0x1c, 0);

    /* SIMRF CTRL: CLEN=0, SCID=3  */
    libwr2rf_framerxtx_write32 (dev, 0x10, 0x30);

    /* Set sub cycle length and increment.  */
    libwr2rf_framerxtx_write32 (dev, 0x14, 8);
    libwr2rf_framerxtx_write32 (dev, 0x18, 0);
    libwr2rf_framerxtx_write32 (dev, 0x1c, 1);

    /* SIMRF CTRL: CLEN=4, SCID=0, CPPSSTART  */
    libwr2rf_framerxtx_write32 (dev, 0x10, (1 << 12) | 0x04);

    /* TX period: every 20us */
    libwr2rf_framerxtx_write32 (dev, 0x08, 62500000 / 50000);

    /* SCR: tx dbg=2 (simul), ftype=1 (RFm).  */
    libwr2rf_framerxtx_write32 (dev, 0x04, (1 << 9) | (1 << 3) | (2 << 1));
}

static void
nco_source (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned ctrl;

  ctrl = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_LOC_OR_WRS);

  if (argc == 1
      || strcmp (argv[1], "status") == 0)
    {
      printf ("CTRL: %04x, NCO local(=1) network(=0): %x\n",
              ctrl,
              !!(ctrl & WR2RF_INIT_REGS_NCO_LOC_OR_WRS_PARAMS_SEL));
    }
  else if (strcmp (argv[1], "network") == 0)
    {
      ctrl &= ~WR2RF_INIT_REGS_NCO_LOC_OR_WRS_PARAMS_SEL;
      libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_LOC_OR_WRS, ctrl);
    }
  else if (strcmp (argv[1], "local") == 0)
    {
      ctrl |= WR2RF_INIT_REGS_NCO_LOC_OR_WRS_PARAMS_SEL;
      libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_LOC_OR_WRS, ctrl);
    }
  else
    printf ("unknown source sub-command %s\n", argv[1]);
}

static void
nco_reset (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned  nco_ctrl_addr;

    if (argc != 1)
        goto usage;

    nco_ctrl_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_NCO_RESET_CTRL;
    libwr2rf_write16(dev, nco_ctrl_addr, 0x7);
    return;

usage:
    printf ("usage: %s\n", argv[0]);
}

static void
nco_reset_delay (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int      ch = 0;
  unsigned addr;
  unsigned cdelay = 0;
  unsigned fdelay = 0;
  unsigned odelay = 0;
  unsigned val;

  if (argc != 2 && argc != 3 && argc != 4 && argc != 5)
      goto usage;

  if (strcmp(argv[1], "1") == 0)
      ch = 1;
  if (strcmp(argv[1], "2") == 0)
      ch = 2;

  if (ch != 1 && ch != 2)
      goto usage;
  else if (argc == 3)
      cdelay = strtoul(argv[2], NULL, 0);
  else if (argc == 4) {
      cdelay = strtoul(argv[2], NULL, 0);
      fdelay = strtoul(argv[3], NULL, 0);
  } else if (argc == 5) {
      cdelay = strtoul(argv[2], NULL, 0);
      fdelay = strtoul(argv[3], NULL, 0);
      odelay = strtoul(argv[4], NULL, 0);
  }

  addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_RF + WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY;
  printf("Your cdelay=%d fdelay=%d odelay=%d\n", cdelay, fdelay, odelay);

  if (argc == 3) {
      val    = libwr2rf_read16(dev, addr + WR2RF_INIT_RF_REGS_RF1);
      cdelay = (cdelay << WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_CDELAY_SHIFT) &
                          WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_CDELAY_MASK;
      fdelay = val & WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_FDELAY_MASK;
      odelay = val & WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_ODELAY_MASK;
      val    = odelay | fdelay | cdelay;

      if (ch == 1) {
          libwr2rf_write16(dev, addr + WR2RF_INIT_RF_REGS_RF1, val);
      } else {
          libwr2rf_write16(dev, addr + WR2RF_INIT_RF_REGS_RF2, val);
      }
  } else if (argc == 4) {
      val    = libwr2rf_read16(dev, addr + WR2RF_INIT_RF_REGS_RF1);
      cdelay = (cdelay << WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_CDELAY_SHIFT) &
                          WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_CDELAY_MASK;
      fdelay = (fdelay << WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_FDELAY_SHIFT) &
                          WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_FDELAY_MASK;
      odelay = val & WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_ODELAY_MASK;
      val    = odelay | fdelay | cdelay;

      printf ("writing nco_reset to: %02x\n", val);
      if (ch == 1)
          libwr2rf_write16(dev, addr + WR2RF_INIT_RF_REGS_RF1, val);
      else
          libwr2rf_write16(dev, addr + WR2RF_INIT_RF_REGS_RF2, val);
  } else if (argc == 5) {
      cdelay = (cdelay << WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_CDELAY_SHIFT) &
                          WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_CDELAY_MASK;
      fdelay = (fdelay << WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_FDELAY_SHIFT) &
                          WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_FDELAY_MASK;
      odelay = (odelay << WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_ODELAY_SHIFT) &
                          WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_ODELAY_MASK;
      val    = odelay | fdelay | cdelay;

      printf ("writing nco_reset to: %02x\n", val);
      if (ch == 1)
          libwr2rf_write16(dev, addr + WR2RF_INIT_RF_REGS_RF1, val);
      else
          libwr2rf_write16(dev, addr + WR2RF_INIT_RF_REGS_RF2, val);
  }

  val    = libwr2rf_read16(dev, addr + WR2RF_INIT_RF_REGS_RF1);
  cdelay = (val & WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_CDELAY_MASK) >>
                  WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_CDELAY_SHIFT;
  fdelay = (val & WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_FDELAY_MASK) >>
                  WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_FDELAY_SHIFT;
  odelay = (val & WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_ODELAY_MASK) >>
                  WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_ODELAY_SHIFT;

  printf ("RF1 T1 sync delay: %02d cycles (16ns) and %01d nsec odelay=%d\n", cdelay, fdelay, odelay);

  val    = libwr2rf_read16(dev, addr + WR2RF_INIT_RF_REGS_RF2);
  cdelay = (val & WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_CDELAY_MASK) >>
                  WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_CDELAY_SHIFT;
  fdelay = (val & WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_FDELAY_MASK) >>
                  WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_FDELAY_SHIFT;
  odelay = (val & WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_ODELAY_MASK) >>
                  WR2RF_INIT_RF_CH_REGS_T1_SYNC_PROGDELAY_ODELAY_SHIFT;
  printf ("RF2 T1 sync delay: %02d cycles (16ns) and %01d nsec odelay=%d\n", cdelay, fdelay, odelay);
  return;

usage:
  printf ("usage: %s 1|2 [course_delay_in_cycles] [fine_delay_in_nsec] \n", argv[0]);
}


static void
rf_reset_offset (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int      ch    = 0;
  unsigned addr  = 0;
  unsigned delay = 0;

  if (argc != 2 && argc != 3 && argc != 4 && argc != 5)
      goto usage;

  addr += WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_RF;
  if (strcmp(argv[1], "1") == 0) {
      ch = 1;
      addr += WR2RF_INIT_RF_REGS_RF1;
  }
  if (strcmp(argv[1], "2") == 0) {
      ch = 2;
      addr += WR2RF_INIT_RF_REGS_RF2;
  }

  if (ch != 1 && ch != 2)
      goto usage;
  else if (argc == 3)
      delay = strtoul(argv[2], NULL, 0);

  addr += WR2RF_INIT_RF_CH_REGS_RST_CDELAY;
  printf("Your delay=%d\n", delay);

  if (argc == 3) {
      libwr2rf_write16(dev, addr, delay);
  }

  delay    = libwr2rf_read16(dev, addr);
  printf ("RF%d Reset Delay Offset =  %02d cycles (16ns)\n", ch, delay);

  return;

usage:
  printf ("usage: %s 1|2 [offset_delay_in_cycles] \n", argv[0]);
}

static void
nco_reset_ctrl (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_NCO_RESET_CTRL;
    unsigned v    = libwr2rf_read16(dev, addr);
    unsigned set  = 0;

    if (argc != 1 && argc != 3)
        goto usage;

    if (argc == 1) {
        printf ("nco_reset_ctrl=%02x \n", libwr2rf_read16(dev, addr));
        return;
    }

    if (strcmp(argv[2], "on") == 0)
        set = 1;
    else if  (strcmp(argv[2], "off") == 0)
        set = 0;
    else
        goto usage;

    if (strcmp (argv[1], "dds_mask") == 0) {
        if (set == 1)
            v = v | WR2RF_CTRL_REGS_NCO_RESET_CTRL_MASK_DDS;
        else
            v = v & ~WR2RF_CTRL_REGS_NCO_RESET_CTRL_MASK_DDS;
    } else if (strcmp (argv[1], "rf1nco_mask") == 0) {
        if (set == 1)
            v = v | WR2RF_CTRL_REGS_NCO_RESET_CTRL_MASK_RFNCO_CH1;
        else
            v = v & ~WR2RF_CTRL_REGS_NCO_RESET_CTRL_MASK_RFNCO_CH1;
    } else if (strcmp (argv[1], "rf2nco_mask") == 0) {
        if (set == 1)
            v = v | WR2RF_CTRL_REGS_NCO_RESET_CTRL_MASK_RFNCO_CH2;
        else
            v = v & ~WR2RF_CTRL_REGS_NCO_RESET_CTRL_MASK_RFNCO_CH2;
    } else if (strcmp (argv[1], "rf1_t1_mask") == 0) {
        if (set == 1)
            v = v | WR2RF_CTRL_REGS_NCO_RESET_CTRL_MASK_RF1_TRIG1;
        else
            v = v & ~WR2RF_CTRL_REGS_NCO_RESET_CTRL_MASK_RF1_TRIG1;
    } else if (strcmp (argv[1], "rf2_t1_mask") == 0) {
        if (set == 1)
            v = v | WR2RF_CTRL_REGS_NCO_RESET_CTRL_MASK_RF2_TRIG1;
        else
            v = v & ~WR2RF_CTRL_REGS_NCO_RESET_CTRL_MASK_RF2_TRIG1;
    } else if (strcmp (argv[1], "soft_nco_reset")        == 0) {
        if (set == 1) // autoclr register
            v = v | WR2RF_CTRL_REGS_NCO_RESET_CTRL_SOFT;
    } else {
        printf ("Unexpected input:\n");
        goto usage;
    }

    libwr2rf_write16(dev, addr, v);
    printf ("nco_reset_ctrl=%02x \n", libwr2rf_read16(dev, addr));
    return;

usage:
    printf ("usage: %s [dds_mask | rf1nco_mask | rf1nco_mask | rf1_t1_mask | rf2_t1_mask | soft_nco_reset] [on|off] \n",
            argv[0]);
}

static void
nco_wrctrl (struct libwr2rf_dev *dev, int argc, char **argv)
{
        unsigned long  ctrl;
        unsigned short before;
        unsigned short after;

        if (argc != 2) {
                printf("Expected %s <val>\n", argv[0]);
                return;
        } else {
                ctrl = strtoul(argv[1], NULL, 0);

                before = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_CTRL);
                libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_CTRL, ctrl);
                libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_UPDATE,
                                 WR2RF_INIT_REGS_NCO_UPDATE_VALID);
                after  = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_CTRL);
                printf("before=%04x after=%04x expected=%04lx %s\n",
                       before, after, ctrl, (ctrl != after ? "ERROR" : ""));
        }
}

static void
nco_lcfg (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned long long   ftw    = 0x16B24A8FB6;
    unsigned long long   LO0    = 0x1C9BA5E300000;//223.5 MHz
    unsigned long long   LO1    = 0x1C9BA5E300000;//223.5 MHz
    unsigned off                = 0;
    unsigned harmonic           = 4620;
    unsigned control            = RFNCO_CONTROL_SOFTLOAD;
    unsigned nco_ftw_addr       = RFNCO_FTW_H1;
    unsigned nco_harmonic_addr  = RFNCO_INPUTS_H;
    unsigned nco_LO0_addr       = RFNCO_FTW_LO0;
    unsigned nco_LO1_addr       = RFNCO_FTW_LO1;
    unsigned nco_ftw_on_addr        = RFNCO_FTW_ON;
    unsigned nco_ftw_offsetfsk_addr = RFNCO_INPUTS_OFFSETFSK;
    unsigned nco_ftw_slip_addr      = RFNCO_FTW_SLIP;
    unsigned nco_control_addr       = RFNCO_CONTROL;

    if (argc == 2 || argc == 3) {
        if (strcmp (argv[1], "1") == 0)
            off = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_RFNCO;
        else if (strcmp (argv[1], "2") == 0)
            off = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_RFNCO;
        else {
            printf ("Received unexpected channel (got %s)\n", argv[1]);
            return;
        }
        if (argc == 3)
            ftw  = strtoull(argv[2], NULL, 0);
    } else {
        printf ("usage: %s 1|2 [FTW] \n", argv[0]);
        printf("Unexpected arguments\n");
        return;
    }

    nco_ftw_addr           += off;
    nco_LO0_addr           += off;
    nco_LO1_addr           += off;
    nco_harmonic_addr      += off;
    nco_ftw_on_addr        += off;
    nco_ftw_offsetfsk_addr += off;
    nco_ftw_slip_addr      += off;
    nco_control_addr       += off;

    printf("Supplied ftw=0x%016llx\n", ftw);
    // write ftw
    libwr2rf_16x32_write32(dev, nco_ftw_addr+0, ftw);
    libwr2rf_16x32_write32(dev, nco_ftw_addr+4, ftw>>32);

    libwr2rf_16x32_write32(dev, nco_LO0_addr+0, LO0);
    libwr2rf_16x32_write32(dev, nco_LO0_addr+4, LO0>>32);
    libwr2rf_16x32_write32(dev, nco_LO1_addr+0, LO1);
    libwr2rf_16x32_write32(dev, nco_LO1_addr+4, LO1>>32);

    libwr2rf_16x32_write32(dev, nco_harmonic_addr, harmonic);
    libwr2rf_16x32_write32(dev, nco_control_addr,  control);

    printf("nco_ftw_addr=%x nco_L00_addr=%x nco_LO1_addr=%x nco_harmonic_addr=%x nco_control_addr=%x\n",
           nco_ftw_addr, nco_LO0_addr, nco_LO1_addr, nco_harmonic_addr, nco_control_addr );

    printf("ftw=%08x.%08x LO=%08x.%08x L1=%08x.%08x harmonic=%08x control=%08x \n",
           libwr2rf_16x32_read32(dev, nco_ftw_addr+4),
           libwr2rf_16x32_read32(dev, nco_ftw_addr+0),
           libwr2rf_16x32_read32(dev, nco_LO0_addr+4),
           libwr2rf_16x32_read32(dev, nco_LO0_addr+0),
           libwr2rf_16x32_read32(dev, nco_LO1_addr+4),
           libwr2rf_16x32_read32(dev, nco_LO1_addr+0),
           libwr2rf_16x32_read32(dev, nco_harmonic_addr),
           libwr2rf_16x32_read32(dev, nco_control_addr) );
    printf("ftw_on=%08x.%08x offset_fsk=%08x.%08x ftw_slip=%08x \n",
           libwr2rf_16x32_read32(dev, nco_ftw_on_addr+4),
           libwr2rf_16x32_read32(dev, nco_ftw_on_addr+0),
           libwr2rf_16x32_read32(dev, nco_ftw_offsetfsk_addr+4),
           libwr2rf_16x32_read32(dev, nco_ftw_offsetfsk_addr+0),
           libwr2rf_16x32_read32(dev, nco_ftw_slip_addr));

}

static void
set_sw_rxframe(struct libwr2rf_dev *dev, unsigned long long ftw, unsigned ctrl)
{
    unsigned addr_rfframe_ftw    = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_H1_FTW ;
    unsigned addr_rfframe_ctrl   = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_CTRL;
    unsigned addr_rfframe_update = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_UPDATE;

    libwr2rf_write16(dev, addr_rfframe_ftw,    ftw>>48);
    libwr2rf_write16(dev, addr_rfframe_ftw+2,  ftw>>32);
    libwr2rf_write16(dev, addr_rfframe_ftw+4,  ftw>>16);
    libwr2rf_write16(dev, addr_rfframe_ftw+6,  ftw>>0);
    libwr2rf_write16(dev, addr_rfframe_ctrl,   ctrl);
    libwr2rf_write16(dev, addr_rfframe_update, 1);
}

static void
nco_lcfg_update (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned ctrl                = 0x7;
    unsigned long long  ftw      = 0x16B24A8FB6;

    if (argc != 1 && argc !=3)
        goto usage;

    if (argc == 3) {
        ctrl = strtoul(argv[1], NULL, 0);
        ftw  = strtoull(argv[2], NULL, 0);
    }
    set_sw_rxframe(dev, ftw, ctrl);
    return;

usage:
    printf ("Usage: %s [ctrl ftw]\n", argv[0]);
}

static void
nco_wrcfg (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned long long   LO0    = 0x1C9BA5E300000;//223.5 MHz
    unsigned long long   LO1    = 0x1C9BA5E300000;//223.5 MHz
    unsigned off                = 0;
    unsigned harmonic           = 4620;
    unsigned control            = RFNCO_CONTROL_SELFTWH1 | RFNCO_CONTROL_EXTRESETENABLE | RFNCO_CONTROL_SELLOAD |
                                  RFNCO_CONTROL_SELWRFTWH1;
    unsigned nco_harmonic_addr  = RFNCO_INPUTS_H;
    unsigned nco_LO0_addr       = RFNCO_FTW_LO0;
    unsigned nco_LO1_addr       = RFNCO_FTW_LO1;
    unsigned nco_ftw_on_addr        = RFNCO_FTW_ON;
    unsigned nco_ftw_offsetfsk_addr = RFNCO_INPUTS_OFFSETFSK;
    unsigned nco_ftw_slip_addr      = RFNCO_FTW_SLIP;
    unsigned nco_control_addr       = RFNCO_CONTROL;

    if (argc == 2) {
        if (strcmp (argv[1], "1") == 0)
            off = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_RFNCO;
        else if (strcmp (argv[1], "2") == 0)
            off = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_RFNCO;
        else {
            printf ("Received unexpected channel (got %s)\n", argv[1]);
            return;
        }
    } else {
        printf ("usage: %s 1|2 \n", argv[0]);
        return;
    }

    nco_LO0_addr      += off;
    nco_LO1_addr      += off;
    nco_harmonic_addr += off;
    nco_ftw_on_addr   += off;
    nco_ftw_offsetfsk_addr += off;
    nco_ftw_slip_addr += off;
    nco_control_addr  += off;

    libwr2rf_16x32_write32(dev, nco_LO0_addr+0, LO0);
    libwr2rf_16x32_write32(dev, nco_LO0_addr+4, LO0>>32);
    libwr2rf_16x32_write32(dev, nco_LO1_addr+0, LO1);
    libwr2rf_16x32_write32(dev, nco_LO1_addr+4, LO1>>32);

    libwr2rf_16x32_write32(dev, nco_harmonic_addr, harmonic);
    libwr2rf_16x32_write32(dev, nco_control_addr,  control);

    printf("LO=%08x.%08x L1=%08x.%08x harmonic=%08x control=%08x \n",
           libwr2rf_16x32_read32(dev, nco_LO0_addr+4),
           libwr2rf_16x32_read32(dev, nco_LO0_addr+0),
           libwr2rf_16x32_read32(dev, nco_LO1_addr+4),
           libwr2rf_16x32_read32(dev, nco_LO1_addr+0),
           libwr2rf_16x32_read32(dev, nco_harmonic_addr),
           libwr2rf_16x32_read32(dev, nco_control_addr) );
    printf("ftw_on=%08x.%08x offset_fsk=%08x.%08x ftw_slip=%08x \n",
           libwr2rf_16x32_read32(dev, nco_ftw_on_addr+4),
           libwr2rf_16x32_read32(dev, nco_ftw_on_addr+0),
           libwr2rf_16x32_read32(dev, nco_ftw_offsetfsk_addr+4),
           libwr2rf_16x32_read32(dev, nco_ftw_offsetfsk_addr+0),
           libwr2rf_16x32_read32(dev, nco_ftw_slip_addr));

}

static void
nco_status (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned off                    = WR2RF_VME_REGS_CTRL;
    unsigned nco_ftw_addr           = RFNCO_FTW;
    unsigned nco_harmonic_addr      = RFNCO_INPUTS_H;
    unsigned nco_phaseh1_addr       = RFNCO_PHASES_H1;
    unsigned nco_phaseh1main_addr   = RFNCO_PHASES_H1MAIN;
    unsigned nco_phaseif0_addr      = RFNCO_PHASES_IF0;
    unsigned nco_phaseif1_addr      = RFNCO_PHASES_IF1;
    unsigned nco_ftw_on_addr        = RFNCO_FTW_ON;
    unsigned nco_ftw_offsetfsk_addr = RFNCO_INPUTS_OFFSETFSK;
    unsigned nco_ftw_slip_addr      = RFNCO_FTW_SLIP;
    unsigned nco_ftw_lo0_addr       = RFNCO_FTW_LO0;
    unsigned nco_ftw_lo1_addr       = RFNCO_FTW_LO1;
    unsigned nco_control_addr       = RFNCO_CONTROL;
    unsigned nco_status_addr        = RFNCO_STATUS;
    unsigned nco_pres_latch_addr    = RFNCO_PRESENTLATCH;
    unsigned nco_coef_addr          = RFNCO_READBACK_COEF;
    unsigned nco_tw_addr            = RFNCO_READBACK_TW;
    unsigned nco_phaseH1Comp_addr   = RFNCO_READBACK_PHASEH1COMP;

    if (argc == 2) {
        if (strcmp (argv[1], "1") == 0)
            off += WR2RF_CTRL_REGS_RF1_RFNCO;
        else if (strcmp (argv[1], "2") == 0)
            off += WR2RF_CTRL_REGS_RF2_RFNCO;
        else {
            printf ("Received unexpected channel (got %s)\n", argv[1]);
            return;
        }
    } else {
        printf ("usage: %s 1|2 \n", argv[0]);
        return;
    }

     nco_ftw_addr         += off;
     nco_harmonic_addr    += off;
     nco_phaseh1_addr     += off;
     nco_phaseh1main_addr += off;
     nco_phaseif0_addr    += off;
     nco_phaseif1_addr    += off;
     nco_ftw_on_addr      += off;
     nco_ftw_offsetfsk_addr += off;
     nco_ftw_slip_addr    += off;
     nco_ftw_lo0_addr     += off;
     nco_ftw_lo1_addr     += off;
     nco_control_addr     += off;
     nco_status_addr      += off;
     nco_pres_latch_addr  += off;
     nco_coef_addr        += off;
     nco_tw_addr          += off;
     nco_phaseH1Comp_addr += off;

    printf ("RFNCO_FTW=%08x.%08x \n",
            libwr2rf_16x32_read32(dev, nco_ftw_addr+4),
            libwr2rf_16x32_read32(dev, nco_ftw_addr+0) );
    printf ("RFNCO_FTW_H=%08x \n",
            libwr2rf_16x32_read32(dev, nco_harmonic_addr+0) );
    printf ("RFNCO_PHASEH1=%08x.%08x \n",
            libwr2rf_16x32_read32(dev, nco_phaseh1_addr+4),
            libwr2rf_16x32_read32(dev, nco_phaseh1_addr+0) );
    printf ("RFNCO_PHASEH1MAIN=%08x.%08x \n",
            libwr2rf_16x32_read32(dev, nco_phaseh1main_addr+4),
            libwr2rf_16x32_read32(dev, nco_phaseh1main_addr+0) );
    printf ("RFNCO_PHASE_IF0=%08x.%08x \n",
            libwr2rf_16x32_read32(dev, nco_phaseif0_addr+4),
            libwr2rf_16x32_read32(dev, nco_phaseif0_addr+0) );
    printf ("RFNCO_PHASE_IF1=%08x.%08x \n",
            libwr2rf_16x32_read32(dev, nco_phaseif1_addr+4),
            libwr2rf_16x32_read32(dev, nco_phaseif1_addr+0) );
    printf ("RFNCO_CONTROL=%08x \n",
            libwr2rf_16x32_read32(dev, nco_control_addr+0) );
    printf ("RFNCO_STATUS=%08x \n",
            libwr2rf_16x32_read32(dev, nco_status_addr+0) );
    printf ("RFNCO_PRESENT_LATCH=%08x \n",
            libwr2rf_16x32_read32(dev, nco_pres_latch_addr+0) );
    printf ("Readback struct\n");
    printf ("  readback.coef=%08x \n",
            libwr2rf_16x32_read32(dev, nco_coef_addr+0) );
    printf ("  readback.tw=%08x \n",
            libwr2rf_16x32_read32(dev, nco_tw_addr+0) );
    printf ("  readback.phaseH1Comp=%08x.%08x \n",
            libwr2rf_16x32_read32(dev, nco_phaseH1Comp_addr+4),
            libwr2rf_16x32_read32(dev, nco_phaseH1Comp_addr+0) );
    printf("ftw_on=%08x.%08x offset_fsk=%08x.%08x ftw_slip=%08x ftw_lo0=%08x.%08x ftw_lo1=%08x.%08x \n",
           libwr2rf_16x32_read32(dev, nco_ftw_on_addr+4),
           libwr2rf_16x32_read32(dev, nco_ftw_on_addr+0),
           libwr2rf_16x32_read32(dev, nco_ftw_offsetfsk_addr+4),
           libwr2rf_16x32_read32(dev, nco_ftw_offsetfsk_addr+0),
           libwr2rf_16x32_read32(dev, nco_ftw_slip_addr),
           libwr2rf_16x32_read32(dev, nco_ftw_lo0_addr+4),
           libwr2rf_16x32_read32(dev, nco_ftw_lo0_addr+0),
           libwr2rf_16x32_read32(dev, nco_ftw_lo1_addr+4),
           libwr2rf_16x32_read32(dev, nco_ftw_lo1_addr+0)
           );

    for (int i = 0; i < 512; i = i + 4)
        printf ("[%02x]=%08x\n",
                i, libwr2rf_16x32_read32(dev, off + i) );
}

static void
nco_cablecomp (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned cablecomp_addr = RFNCO_INPUTS_DELAYCOMP;
  unsigned cablecomp      = 0;
  unsigned off;

    if (argc == 2 || argc == 3) {
        if (strcmp (argv[1], "1") == 0)
            off = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_RFNCO;
        else if (strcmp (argv[1], "2") == 0)
            off = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_RFNCO;
        else {
            printf ("Received unexpected channel (got %s)\n", argv[1]);
            return;
        }
        if (argc == 3)
            cablecomp  = strtoul(argv[2], NULL, 0);
    } else {
        printf ("usage: %s 1|2 [cablecomp] \n", argv[0]);
        printf("Unexpected arguments\n");
        return;
    }

    cablecomp_addr += off;
    printf ("RFNCO_CABLECOMP: addr=%08x, value=%08x, off=%08x \n", cablecomp_addr, cablecomp, off );

    if (argc == 3)
        libwr2rf_16x32_write32(dev, cablecomp_addr, cablecomp);

    printf ("RFNCO_CABLECOMP=%08x \n", libwr2rf_16x32_read32(dev, cablecomp_addr) );
}

static void
svmup_pll_sel (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned short after;
  unsigned short before;
  unsigned short pll;

  before = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_SVEC_MUP_CTRL);
  after  = before;
  pll    = !!(before & WR2RF_INIT_REGS_SVEC_MUP_CTRL_BCLK_RFCLK_SEL);

  if (! pll)
      after = after | WR2RF_INIT_REGS_SVEC_MUP_CTRL_BCLK_RFCLK_SEL;
  else
      after = after & ~WR2RF_INIT_REGS_SVEC_MUP_CTRL_BCLK_RFCLK_SEL;

  libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_SVEC_MUP_CTRL,
                   after);
  after = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_SVEC_MUP_CTRL);
  printf("svmup_pll_sel: before=%04x after=%04x\n", before, after);
}

static void
read_init_reg (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned long addr;
  unsigned long data;

  if (argc != 2) {
      printf("Expected %s <val>\n", argv[0]);
      exit(EXIT_FAILURE);
  } else {
      addr = strtoul(argv[1], NULL, 0);
      data = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + addr);

      printf("read_init_reg: addr: %04x, data: %04x \n", (unsigned)addr, (unsigned)data);
  }
}

static void
write_init_reg (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned long addr;
  unsigned long data;

  if (argc != 3) {
      printf("Expected %s <val>\n", argv[0]);
      exit(EXIT_FAILURE);
  } else {
      addr = strtoul(argv[1], NULL, 0);
      data = strtoul(argv[2], NULL, 0);

      libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + addr, data);
  }
}

static void
wrc_print_status (unsigned status)
{
  printf ("wrc status: %02x, link: %u, time: %u, ",
          status,
          (status & WR2RF_INIT_REGS_WRC_STATUS_LINKUP) ? 1 : 0,
          (status & WR2RF_INIT_REGS_WRC_STATUS_TIME_VALID) ? 1 : 0);
}

static void
wrc_get_time (struct libwr2rf_dev *dev,
              unsigned *tai_h,
              unsigned *tai_l,
              unsigned *cyc)
{
        unsigned tai[3];
        unsigned tcyc[2];

        do {
                tai[2] = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                                     + WR2RF_INIT_REGS_WRC_TAI + 6);
                tai[1] = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                                         + WR2RF_INIT_REGS_WRC_TAI + 4);
                tai[0] = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                                         + WR2RF_INIT_REGS_WRC_TAI + 2);
        } while (tai[2] != libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                                           + WR2RF_INIT_REGS_WRC_TAI + 6));
        do {
                tcyc[0] = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                                          + WR2RF_INIT_REGS_WRC_CYCLES + 0);
                tcyc[1] = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                                          + WR2RF_INIT_REGS_WRC_CYCLES + 2);
        }
        while (tcyc[0] != libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                                          + WR2RF_INIT_REGS_WRC_CYCLES));
        *tai_h = tai[0];
        *tai_l = (tai[1] << 16) | tai[2];
        *cyc = (tcyc[0] << 16) | tcyc[1];
}

static void
wrc_print_time (struct libwr2rf_dev *dev)
{
  unsigned tai_h;
  unsigned tai_l;
  unsigned cyc;

  wrc_get_time(dev, &tai_h, &tai_l, &cyc);
  printf ("tai: %04x%08x, cycles: %08x", tai_h, tai_l, cyc);
}

static void
wrc_time (struct libwr2rf_dev *dev, int argc, char **argv)
{
        unsigned status;

        status = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRC_STATUS);
        wrc_print_status (status);
        if (1 || (status & WR2RF_INIT_REGS_WRC_STATUS_TIME_VALID))
                wrc_print_time(dev);
        else
                printf ("(invalid)");
        putchar('\n');
}

/* Read and print WR link/time status.  */
static void
wrc_read_print_status (struct libwr2rf_dev *dev)
{
  unsigned status;
  status = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRC_STATUS);
  wrc_print_status (status);
  wrc_print_time(dev);
  putchar('\n');
}

static void
wrc_wait (struct libwr2rf_dev *dev, int argc, char **argv)
{
    while (1) {
        wrc_read_print_status (dev);
        if (libwr2rf_wrc_time_valid(dev) == 0)
            break;
        sleep(1);
    }
}

static void
wrc_cycles (struct libwr2rf_dev *dev, int argc, char **argv)
{
        unsigned cyc[2];

        while (1) {
            cyc[0] = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                                     + WR2RF_INIT_REGS_WRC_CYCLES + 0);
            cyc[1] = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT
                                     + WR2RF_INIT_REGS_WRC_CYCLES + 2);
            printf ("cycles: %08x\n", (cyc[0] << 16) | cyc[1]);
        }
}

static uint32_t vuart_read32(struct libwr2rf_dev *dev, int reg)
{
        return libwr2rf_wrc_read32(dev, 0x500 + reg);
}


static void vuart_write32(struct libwr2rf_dev *dev, uint32_t value, int reg)
{
        libwr2rf_wrc_write32(dev, 0x500 + reg, value);
}

static int8_t wr_vuart_rx(struct libwr2rf_dev *dev)
{
        int rdr = vuart_read32(dev, UART_REG_HOST_RDR );
        return (rdr & UART_HOST_RDR_RDY) ? UART_HOST_RDR_DATA_R(rdr) : -1;
}

/**
 * It transmits a single byte
 * @param[in] vuart token from dev_map()
 */
static void wr_vuart_tx(struct libwr2rf_dev *dev, char data)
{
        do {
                int sr = vuart_read32(dev, UART_REG_SR );
                if (sr == -1)
                        exit(1);
                /* If the rx byte is not present (and therefore cannot be
                   read), it could be written.  */
                if (!(sr & UART_SR_RX_RDY))
                        break;
        } while (1);

        vuart_write32(dev, UART_HOST_TDR_DATA_W(data), UART_REG_HOST_TDR );
}

static void
vuart (struct libwr2rf_dev *dev, int argc, char **argv)
{
        struct termios old_tios, new_tios;
        struct pollfd pfd;
        int ret;
        char rx, tx;
        unsigned res;


        //libwr2rf_writel(WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRC_PAGE0, 0x40004);
        //libwr2rf_writel(WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRC_PAGE1, 0x10000);

        libwr2rf_be_write32(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRC_PAGE0, 0x40000);

        res = libwr2rf_wrc_read32(dev, 0x400 + SYSC_REG_HWIR);
        printf ("HWIR: %08x\n", res);

        fprintf(stderr, "[press C-a to exit]\n");

        tcgetattr(0, &old_tios);
        memcpy (&new_tios, &old_tios, sizeof (struct termios));

        new_tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
                              | INLCR | IGNCR | ICRNL | IXON);
        new_tios.c_oflag &= ~OPOST;
        new_tios.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
        new_tios.c_lflag |= ISIG; /* Keep C-c, C-z */
        tcflush(0, TCIFLUSH);
        tcsetattr(0, TCSANOW, &new_tios);

        pfd.fd = 0;
        pfd.events = POLLIN;

        while(1) {
                ret = poll (&pfd, 1, 10);
                if (ret == 0)
                        ;
                else if (ret > 0 && (pfd.revents & POLLIN)) {
                        /* The user wrote something */
                        ret = read(0, &tx, 1);
                        if (ret != 1) {
                                fprintf(stderr, "nothing to read. Port disconnected?\n");
                                break;
                        }
                        /* If the user character is C-a, then kill */
                        if(tx == 0x01)
                                break;

                        wr_vuart_tx(dev, tx);
                }
                else
                        break;

                /* Print all the incoming charactes */
                while (1) {
			int r;

                        rx = wr_vuart_rx(dev);
                        if (rx == -1)
                                break;
                        r = write(1, &rx, 1);
			if (r < 0)
				break;
                }
        }

        tcsetattr(0, TCSANOW, &old_tios);
}

static void
wrc_mac (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned char mac[6];
  unsigned i;

  if (libwr2rf_get_mac(dev, mac) != 0) {
    printf ("error while reading MAC addres\n");
    return;
  }

  printf ("MAC: ");
  for (i = 0; i < 6; i++)
    {
      if (i != 0)
        putchar(':');
      printf("%02x", mac[i]);
    }
  putchar('\n');
}

static void
fw_dump (struct libwr2rf_dev *dev, int argc, char **argv)
{
        unsigned addr;
        unsigned len = 0x2000;
        unsigned j;

        if (argc > 1)
                len = strtoul (argv[1], NULL, 0);

        printf ("WRC memory dump of 0x%06x words:\n", len);

        for (addr = 0; addr < len; addr += 16) {
                if ((addr & 0xfff) == 0)
                        libwr2rf_be_write32(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRC_PAGE1,
                                            addr & (~0xfff));
                printf ("%06x:", addr);
                for (j = 0; j < 16; j += 4) {
                        unsigned v = libwr2rf_wrc_read32(dev, 0x1000 + ((addr+j) & 0xfff));
                        printf (" %08x", v);
                }
                printf("\n");
        }
}


static unsigned char *
load_binary_file(const char *filename, long *size)
{
  int i;
  long sz;
  unsigned char *buf;
  FILE *f;

  f = fopen(filename, "r");
  if (!f) {
    fprintf(stderr, "%s: %s\n", filename, strerror(errno));
    return NULL;
  }

  if (fseek(f, 0, SEEK_END) != 0) {
    fprintf(stderr, "%s: %s\n", filename, strerror(errno));
    fclose(f);
    return NULL;
  }

  sz = ftell(f);
  *size = sz;

  fseek(f, 0, SEEK_SET);

  buf = malloc((sz + 3) & ~3);
  if (!buf) {
    fprintf(stderr, "loading %s: %s\n", filename, strerror(errno));
    fclose(f);
    return NULL;
  }

  i = fread(buf, sz, 1, f);
  fclose(f);
  if (i < 0) {
    fprintf(stderr, "reading %s: %s\n", filename, strerror(errno));
    free(buf);
    return NULL;
  }
  if (i != 1) {
    fprintf(stderr, "%s: short read\n", filename);
    free(buf);
    return NULL;
  }

  return buf;
}

static void
load_wrs_firmware (struct libwr2rf_dev *dev, const char *filename)
{
  unsigned char *buf;
  long sz;

  buf = load_binary_file(filename, &sz);
  if (buf == NULL)
    return;

  /* Reset uRV.  */
  libwr2rf_wrc_write32(dev, 0xb00 + WRC_CPU_CSR_REG_RESET, 1 << 0);

  /* Load.  */
  for (unsigned addr = 0; addr < sz; addr += 4) {
    uint32_t v;

    libwr2rf_wrc_write32(dev, 0xb00 + WRC_CPU_CSR_REG_UADDR, addr >> 2);

    /* Use BE.  */
    v = (buf[addr + 3] << 0)
      | (buf[addr + 2] << 8)
      | (buf[addr + 1] << 16)
      | (buf[addr + 0] << 24);
    libwr2rf_wrc_write32(dev, 0xb00 + WRC_CPU_CSR_REG_UDATA, v);
  }

  /* Unreset.  */
  libwr2rf_wrc_write32(dev, 0xb00 + WRC_CPU_CSR_REG_RESET, 0);

  free (buf);
}

static void
fw_load (struct libwr2rf_dev *dev, int argc, char **argv)
{
  if (argc != 2) {
    printf ("fw-load: need filename\n");
    return;
  }
  load_wrs_firmware(dev, argv[1]);
}

static void
pll_dump (struct libwr2rf_dev *dev, int argc, char **argv)
{
        /* Simple test.  */
        libwr2rf_pll_spi_init(dev);

        /* TODO:
         * check REV/PART (Reg 16)
         * check NO_REF = 0, NO_VCO = 0 (presence of clocks)
         * configure status
         */

        /* Note: Reg 16 is REV/PART.  */
        for (unsigned i = 0; i <= 0x16; i++)
                printf ("pll regs %02x: %02x\n", i, libwr2rf_pll_read(dev, i));
}

static void
ocxo (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned v;

  if (argc == 1) {
    v = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_OCXO_CTRL);
    printf ("ocxo status: 0x%04x\n", v);
    v = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_OCXO_UPTIME);
    printf ("ocxo uptime: 0x%04x (%u)\n", v, v);
    v = libwr2rf_get_ocxo_current_ma(dev);
    printf ("ocxo current: %u mA\n", v);
  }
  else if (argc == 2 && strcmp (argv[1], "off") == 0) {
    libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_OCXO_CTRL, 0);
  }
  else if (argc == 2 && strcmp (argv[1], "on") == 0) {
    libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_OCXO_CTRL,
                     WR2RF_INIT_REGS_OCXO_CTRL_ENABLE);
  }
  else {
    printf ("usage: %s [on|off]\n", argv[0]);
    return;
  }
}

#define XADC_FLAG_TEMP 1  /* Value is a temperature */
#define XADC_FLAG_POWR 2  /* Value is a voltage (unipolar) */
#define XADC_FLAG_OCXO_SENSE 3 /* ocxo current */
struct xadc_regs_desc {
  const char *name;
  unsigned flag;
};

#define NBR_XADC_REGS 0x60
static const struct xadc_regs_desc xadc_regs[] =
  {
   /* 0x00 */
   { "temp", XADC_FLAG_TEMP},
   { "vccint", XADC_FLAG_POWR},
   { "vccaux", XADC_FLAG_POWR},
   { "vp/vn", 0},
   { "vrefp", 0},
   { "vrefn", 0},
   { "vccbram", XADC_FLAG_POWR},
   { NULL, 0},

   /* 0x08 */
   { "supply-A-off", 0},
   { "ADC-A-off", 0},
   { "ADC-A-gain", 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},

   /* 0x10 */
   { NULL, 0},
   { NULL, 0},
   { "vaux2", XADC_FLAG_OCXO_SENSE},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},

   /* 0x18 */
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},

   /* 0x20 */
   { "max-temp", XADC_FLAG_TEMP},
   { "max-vccint", XADC_FLAG_POWR},
   { "max-vccaux", XADC_FLAG_POWR},
   { "max-vccbram", XADC_FLAG_POWR},
   { "min-temp", XADC_FLAG_TEMP},
   { "min-vccint", XADC_FLAG_POWR},
   { "min-vccaux", XADC_FLAG_POWR},
   { "min-vccbram", XADC_FLAG_POWR},

   /* 0x28 */
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},

   /* 0x30 */
   { "supply-B-off", 0},
   { "ADC-B-off", 0},
   { "ADC-B-gain", 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},

   /* 0x38 */
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { "flag", 0},

   /* 0x40 */
   { "config-0", 0},
   { "config-1", 0},
   { "config-2", 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},

   /* 0x48 */
   { "seq-sel-0", 0},
   { "seq-sel-1", 0},
   { "seq-avg-0", 0},
   { "seq-avg-1", 0},
   { "seq-inp-0", 0},
   { "seq-inp-1", 0},
   { "seq-tim-0", 0},
   { "seq-tim-1", 0},

   /* 0x50 */
   { "temp-up", XADC_FLAG_TEMP},
   { "vccint-up", XADC_FLAG_POWR},
   { "vccaux-up", XADC_FLAG_POWR},
   { "ot-lim", XADC_FLAG_TEMP},
   { "temp-lo", XADC_FLAG_TEMP},
   { "vccint-lo", XADC_FLAG_POWR},
   { "vccaux-lo", XADC_FLAG_POWR},
   { "ot-rst", XADC_FLAG_TEMP},

   /* 0x58 */
   { "vccbram-up", XADC_FLAG_POWR},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
   { "vccbram-lo", XADC_FLAG_POWR},
   { NULL, 0},
   { NULL, 0},
   { NULL, 0},
  };

static void
xadc_dump (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_XADC;
  unsigned i;

  for (i = 0; i < NBR_XADC_REGS; i++) {
    const struct xadc_regs_desc *d = &xadc_regs[i];
    if (d->name) {
      unsigned val = libwr2rf_read16(dev, addr + (i << 1));
      printf ("xadc[%02x] %12s: %04x", i, d->name, val);
      switch (d->flag) {
      case 0:
        break;
      case XADC_FLAG_TEMP:
        /* See ug480 eq 2-6 p24 */
        val >>= 4;
        printf (" %.02f deg-C", (val * 503.975) / 4096 - 273.15);
        break;
      case XADC_FLAG_POWR:
        /* See ug480 eq 2-7 p 26 */
        printf (" %1.02f V", (val >> 4) / 4096.0 * 3.0);
        break;
      case XADC_FLAG_OCXO_SENSE:
        /* See EDA-04387-V2 Sheet 24 (p43) Power for OCXO */
        /* V= 40 * 0.05 Ohm * ocxo_cur */
        {
          double v = (val >> 4) / 4096.0;
          double a = v / 2.0;
          printf (" %1.03f V -> %1.03f A", v, a);
        }
        break;
      default:
        break;
      }
      printf("\n");
    }
  }
}

static void
xadc_reg (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned base_addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_XADC;
  unsigned reg;

  if (argc < 2 || argc > 3) {
    printf ("usage: %s reg [val]\n", argv[0]);
    return;
  }

  reg = strtoul(argv[1], NULL, 0);

  if (argc == 2) {
    unsigned val;
    val = libwr2rf_read16(dev, base_addr + (reg << 1));
    printf ("xadc[%02x] = %04x\n", reg, val);
  }
  else {
    unsigned val;

    val = strtoul(argv[2], NULL, 0);
    libwr2rf_write16(dev, base_addr + (reg << 1), val);
    printf ("xadc[%02x] <- %04x\n", reg, val);
  }
}

static void
mmcm_status (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_CLOCK_STATUS;
  unsigned long val = libwr2rf_read16(dev, addr);

  printf ("clock status: 0x%04lx\n", val);
  printf ("mmcm locked: 0x%04lx\n",
          val & WR2RF_INIT_REGS_CLOCK_STATUS_MMCM_LOCKED);
  printf ("shift busy: 0x%04lx\n",
          val & WR2RF_INIT_REGS_CLOCK_STATUS_SHIFT_BUSY);
}

static void
pll_status (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_PLL_STATUS;
  unsigned long val = libwr2rf_read16(dev, addr);

  printf ("pll status: 0x%04lx\n", val);
  printf ("error:  0x%04lx\n",
          val & WR2RF_INIT_REGS_PLL_STATUS_ERROR);
  printf ("locked: 0x%04lx\n",
          val & WR2RF_INIT_REGS_PLL_STATUS_LOCKED);
}

static void
pll_init (struct libwr2rf_dev *dev, int argc, char **argv)
{
    if (libwr2rf_enable_check_ocxo(dev) != 0)
        printf ("Warning: OCXO is not warmed-up\n");
    libwr2rf_clock_init(dev);
}

static void
dds_reset (struct libwr2rf_dev *dev, int argc, char **argv)
{
        unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_DDS_CTRL;
        unsigned val;

        if (argc == 1) {
                val = libwr2rf_read16(dev, addr);
                printf ("dds ctrl: 0x%04x\n", val);
        }
        else if (argc == 2) {
                if (strcmp (argv[1], "0") == 0)
                    libwr2rf_dds_reset(dev, 0);
                else if (strcmp (argv[1], "1") == 0)
                    libwr2rf_dds_reset(dev, 1);
                else {
                    printf ("incorrect reset value (got %s)\n", argv[1]);
                    return;
                }
        }
        else
                printf ("usage: %s [0|1]\n", argv[0]);
}

static void
dds_init (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned io_update = 0;

    libwr2rf_dds_reset_pulse(dev);
    if (argc == 2) {
        io_update = strtoul(argv[1], NULL, 0);
        libwr2rf_dds_init(dev, io_update);
    }
}

struct dds_regs {
  const char *name;
  unsigned nbits;
  unsigned addr;
};

static const struct dds_regs dds_regs[] =
  {
   { "cfr1", 32, 0 },
   { "cfr2", 32, 1 },
   { "cfr3", 32, 2 },
   { "dac ", 32, 3 },
   { "iour", 32, 4 },
   { "ftw ", 32, 7 },
   { "pow ", 16, 8 },
   { "asf ", 32, 9 },
   { "sync", 32, 0xa },
   { "rmpl", 64, 0xb },
   { "rmps", 64, 0xc },
   { "rmpr", 32, 0xd },
   { "prf0", 64, 0xe },
   { "prf1", 64, 0xf },
   { "prf2", 64, 0x10 },
   { "prf3", 64, 0x11 },
   { "prf4", 64, 0x12 },
   { "prf5", 64, 0x13 },
   { "prf6", 64, 0x14 },
   { "prf7", 64, 0x15 },
   { "ram ", 32, 0x16 },
   { NULL, 0, 0 }
  };


static void
dds_dump (struct libwr2rf_dev *dev, int argc, char **argv)
{
        for (const struct dds_regs *r = dds_regs; r->name; r++) {
                uint64_t v = libwr2rf_dds_read(dev, r->addr, r->nbits);
                printf ("%s [@%02x] = %llx (%d bits)\n",
                        r->name, r->addr, (unsigned long long)v, r->nbits);
        }
}

static void
dds_sync_cal (struct libwr2rf_dev *dev, int argc, char **argv)
{
    printf ("To calibrate the timing for the DDS sync signal's arrival time at the DDS, the DDS must be operating\n");

    libwr2rf_dds_sync_calibrate (dev, 1);
}

static void
dds_sync_set (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned rx_del;
    unsigned val_del;

    if (argc != 3)
        goto usage;

    rx_del  = strtoul(argv[1], NULL, 0);
    val_del = strtoul(argv[2], NULL, 0);
    if (rx_del >= 32 || val_del >= 16)
        goto usage;
    libwr2rf_dds_configure_sync(dev, 1, rx_del, val_del);
    return;

usage:
    printf("usage %s sync_receiver_delay(31-0) sync_validation_delay(15-0)\n", argv[0]);
}


static void
dds_sync_status (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned dds_status_addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_DDS_STATUS;

    printf("dds sync status=%04x\n",
           libwr2rf_read16(dev, dds_status_addr));
}

static void
dds_ioupdate (struct libwr2rf_dev *dev, int argc, char **argv)
{
    libwr2rf_dds_ioupdate(dev);
}

static void
dds_freq (struct libwr2rf_dev *dev, int argc, char **argv)
{
    if (argc != 2) {
        printf ("usage: %s dds_freq\n", argv[0]);
        return;
    }

    uint64_t freq = strtoull(argv[1], NULL, 0);
    libwr2rf_dds_freq_init(dev, freq, 1);

    return;
}


static void
set_rfout (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned rf;
  unsigned cfg;

  if (argc != 3) {
    printf ("usage: set-rfout 1|2 mix|dds|gnd\n");
    return;
  }

  if (strcmp(argv[1], "1") == 0)
    rf = 1;
  else if (strcmp(argv[1], "2") == 0)
    rf = 2;
  else {
    printf ("bad rf channel (got %s)\n", argv[1]);
    return;
  }

  if (strcmp(argv[2], "mix") == 0)
    cfg = LIBWR2RF_RF_OUT_MIXER;
  else if (strcmp(argv[2], "dds") == 0)
    cfg = LIBWR2RF_RF_OUT_DDS;
  else if (strcmp(argv[2], "gnd") == 0)
    cfg = LIBWR2RF_RF_OUT_GND;
  else {
    printf ("bad rfout mode (got %s)\n", argv[2]);
    return;
  }

  if (libwr2rf_set_rf_out(dev, rf, cfg) != 0)
      printf ("error while calling set_rd_out\n");
}

static void
wrs_dump (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned i;
    unsigned size = 0xa4;
    unsigned res;
    libwr2rf_be_write32(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRC_PAGE0, 0x48000);

    for (i = 0; i <= size; i = i + 4) {
        res = libwr2rf_wrc_read32(dev, i);
        printf("WRS[%02x]=%08x\n", i, res);
    }
}

static void
wrs_fixedlat (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned val;

    if (argc != 2) {
        val = libwr2rf_get_wrs_fixed_latency(dev);
        printf ("usage: %s [val] \n    (current val = %d)\n", argv[0], val);
        return;
    }

    val = strtoul(argv[1], NULL, 0);
    libwr2rf_set_wrs_fixed_latency (dev, val);
}

static void
wrs_reset_rxstats (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = 0x4; // STREAMERS_RX_CFG5 - fixed latency, # of 8ns clock cycles

    libwr2rf_be_write32(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_WRC_PAGE0, 0x48000);
    libwr2rf_wrc_write32(dev, addr, 1);
}

static void
wrs_tolat (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned val;

    if (argc != 2) {
        val = libwr2rf_get_wrs_timeout_latency(dev);
        printf ("usage: %s [val] \n   (current val = %d)\n", argv[0], val);
        return;
    }

    val = strtoul(argv[1], NULL, 0);
    libwr2rf_set_wrs_timeout_latency (dev, val);
}

static void
ext_ref (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_PIN_CTRL;
    unsigned v    = 0;

    if (argc == 3) {
        if (strcmp (argv[1], "10m_in") == 0)
            v = v & ~WR2RF_INIT_REGS_PIN_CTRL_EXT_10M_DIR;
        else if (strcmp (argv[1], "10m_out") == 0)
            v = v |  WR2RF_INIT_REGS_PIN_CTRL_EXT_10M_DIR;
        else {
            goto usage;
        }
        if (strcmp (argv[1], "pps_in") == 0)
            v = v & ~WR2RF_INIT_REGS_PIN_CTRL_EXT_PPS_DIR;
        else if (strcmp (argv[1], "pps_out") == 0)
            v = v |  WR2RF_INIT_REGS_PIN_CTRL_EXT_PPS_DIR;
        else {
            goto usage;
        }
        libwr2rf_write16 (dev, addr, v);
    }

    v = libwr2rf_read16 (dev, addr);
    printf ("ext-10m dir: %01x (0:in, 1:out)\n", !!(v & WR2RF_INIT_REGS_PIN_CTRL_EXT_10M_DIR) );
    printf ("ext-pps dir: %01x (0:in, 1:out)\n", !!(v & WR2RF_INIT_REGS_PIN_CTRL_EXT_PPS_DIR) );
    return;

usage:
    printf ("usage: ext-ref [10m_in|10m_out] [pps_in|pps_out]\n");
}

static void
hwver (struct libwr2rf_dev *dev, int argc, char **argv)
{
  printf ("fw version: 0x%04x\n", libwr2rf_read_fwversion(dev));
}

static void
hwinfo (struct libwr2rf_dev *dev, int argc, char **argv)
{
  printf ("std version: 0x%08x\n", libwr2rf_be_read32(dev, HWINFO_STDVERSION));
  printf ("ident:       0x%04x\n", libwr2rf_read16(dev, HWINFO_IDENT));
  printf ("firmware version: 0x%08x\n", libwr2rf_be_read32(dev, HWINFO_FIRMWAREVERSION));
  printf ("memmap version:   0x%08x\n", libwr2rf_be_read32(dev, HWINFO_MEMMAPVERSION));
  printf ("echo:             0x%08x\n", libwr2rf_be_read32(dev, HWINFO_ECHO));
  printf ("set standard:     0x%08x\n", libwr2rf_be_read32(dev, HWINFO_SETSTANDARD));
}

static void
nco_ipinfo (struct libwr2rf_dev *dev, int argc, char **argv)
{
  struct libwr2rf_nco_ipinfo_t ipinfo;

  libwr2rf_read_nco_ipinfo(dev, &ipinfo);

  printf ("std version: 0x%08x\n", ipinfo.std_ver);
  printf ("ident:       0x%04x\n", ipinfo.ident);
  printf ("firmware version: 0x%08x\n", ipinfo.fw_ver);
  printf ("memmap version:   0x%08x\n", ipinfo.mem_map_ver);
}

static void
reconfigure (struct libwr2rf_dev *dev, int argc, char **argv)
{
  libwr2rf_write16(dev, WR2RF_INIT_REGS_RECONFIGURE, 0xa37c);
}

static int
parse_dac_channel (const char *arg)
{
  if (strcmp(arg, "1") == 0)
    return DAC1_CS;
  if (strcmp(arg, "2") == 0)
    return DAC2_CS;
  printf ("bad rf channel (got %s)\n", arg);
  return -1;
}

static void
dac_reset (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int ch;
  unsigned rf;
  unsigned addr;
  unsigned val;

  if (argc != 2 && argc != 3) {
    printf ("usage: dac-init 1|2 [0|1|pulse]\n");
    return;
  }

  ch = parse_dac_channel(argv[1]);
  if (ch < 0)
    return;

  if (ch == DAC1_CS)
    rf = WR2RF_INIT_RF_REGS_RF1;
  else if (ch == DAC2_CS)
    rf = WR2RF_INIT_RF_REGS_RF2;
  else
    abort();

  addr = WR2RF_VME_REGS_INIT
    + WR2RF_INIT_REGS_RF + rf + WR2RF_INIT_RF_CH_REGS_COMMON;

  val = libwr2rf_read16(dev, addr);

  if (argc == 2) {
    printf ("value:      0x%04x\n", val);
    printf ("mixer_en:    %04x\n",
            (unsigned)(val & WR2RF_INIT_RF_CH_REGS_COMMON_MIXER_EN));
    printf ("mux_sel:     %04x\n",
            (unsigned)(val & WR2RF_INIT_RF_CH_REGS_COMMON_MUX_SEL_MASK));
    printf ("iqdac_reset: %04x\n",
            (unsigned)(val & WR2RF_INIT_RF_CH_REGS_COMMON_IQDAC_RESET));
  }
  else {
    if (strcmp (argv[2], "0") == 0) {
      val &= ~WR2RF_INIT_RF_CH_REGS_COMMON_IQDAC_RESET;
      libwr2rf_write16(dev, addr, val);
    }
    else if (strcmp (argv[2], "1") == 0) {
      val |= WR2RF_INIT_RF_CH_REGS_COMMON_IQDAC_RESET;
      libwr2rf_write16(dev, addr, val);
    }
    else if (strcmp (argv[2], "pulse") == 0) {
      val |= WR2RF_INIT_RF_CH_REGS_COMMON_IQDAC_RESET;
      libwr2rf_write16(dev, addr, val);
      val &= ~WR2RF_INIT_RF_CH_REGS_COMMON_IQDAC_RESET;
      libwr2rf_write16(dev, addr, val);
    }
    else {
      printf ("unknown sub-command for dac-reset (get %s)\n", argv[2]);
    }
  }
}

static void
dac_xdds (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned long ftw = 0xabadcafe;
    unsigned      dds_ftw_addr;
    unsigned      dds_ftw_update_addr;
    unsigned      ch;

    if (argc != 2 && argc != 3)
        goto usage_msg;

    ch = parse_dac_channel(argv[1]);
    if (ch != 1 && ch != 2)
        goto usage_msg;

    if (ch == 1) {
        dds_ftw_addr        = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_DDS_FTW;
        dds_ftw_update_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_DDS_FTW_VALID;
    } else {
        dds_ftw_addr        = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_DDS_FTW;
        dds_ftw_update_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_DDS_FTW_VALID;
    }

    if (argc == 3) {
        ftw = strtoul(argv[2], NULL, 0);

        libwr2rf_write16(dev, dds_ftw_addr + 6, ftw>>48);
        libwr2rf_write16(dev, dds_ftw_addr + 4, ftw>>32);
        libwr2rf_write16(dev, dds_ftw_addr + 2, ftw>>16);
        libwr2rf_write16(dev, dds_ftw_addr + 0, ftw>>0);

        libwr2rf_write16(dev, dds_ftw_update_addr, 0x0001);
        libwr2rf_write16(dev, dds_ftw_update_addr, 0x0);
    }

    printf ("DAC XDDS FTW: ftw: %04x%04x%04x%04x\n",
            libwr2rf_read16(dev, dds_ftw_addr + 6),
            libwr2rf_read16(dev, dds_ftw_addr + 4),
            libwr2rf_read16(dev, dds_ftw_addr + 2),
            libwr2rf_read16(dev, dds_ftw_addr + 0));
    return;

usage_msg:
    printf ("usage: %s 1|2 ftw\n", argv[0]);
    return;
}

static void
dac_iqsetpoint (struct libwr2rf_dev *dev, int argc, char **argv)
{
    int ch;
    unsigned igain_addr;
    unsigned qgain_addr;

    if (argc != 2 && argc != 4) {
        printf ("usage: %s 1|2 [i_setpoint q_setpoint]\n", argv[0]);
        return;
    }

    ch = parse_dac_channel(argv[1]);
    if (ch < 1 || ch > 2)
        return;


    if (argc == 4) {
      unsigned update_addr;
      unsigned igain;
      unsigned qgain;

      if (ch == DAC1_CS) {
        igain_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_IQDAC_IGAIN_ARM;
        qgain_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_IQDAC_QGAIN_ARM;
        update_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_IQDAC_PHASE_UPDATE;
      } else {
        igain_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_IQDAC_IGAIN_ARM;
        qgain_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_IQDAC_QGAIN_ARM;
        update_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_IQDAC_PHASE_UPDATE;
      }

      igain = strtoul(argv[2], NULL, 0);
      qgain = strtoul(argv[3], NULL, 0);
      libwr2rf_write16(dev, igain_addr, igain);
      libwr2rf_write16(dev, qgain_addr, qgain);
      libwr2rf_write16(dev, update_addr, WR2RF_CTRL_REGS_RF2_IQDAC_PHASE_UPDATE_VALID);
      printf("Setting RF Channel %d i_setpoint=%04x q_setpoint=%04x\n", ch, igain, qgain);
    } else {
      unsigned igain;
      unsigned qgain;
      unsigned phase;
      unsigned phase_addr;

      if (ch == DAC1_CS) {
        igain_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_IQDAC_IGAIN;
        qgain_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_IQDAC_QGAIN;
        phase_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_IQDAC_PHASE;
      } else {
        igain_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_IQDAC_IGAIN;
        qgain_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_IQDAC_QGAIN;
        phase_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_IQDAC_PHASE;
      }

      igain = libwr2rf_read16(dev, igain_addr);
      qgain = libwr2rf_read16(dev, qgain_addr);
      phase = libwr2rf_read16(dev, phase_addr);
      printf("RF Channel %d i_setpoint=%04x q_setpoint=%04x, phase=%u\n", ch, igain, qgain, phase);

      float igainf = (short)igain;
      float qgainf = (short)qgain;
      float rad = atan2f (qgainf, igainf);
      float amp = hypotf(igainf, qgainf);
      printf ("Real phase=%3.1f gain=%04x\n",
              rad * 180.0 / M_PI, (unsigned short)amp);
    }
}

static void
dac_iq_phase (struct libwr2rf_dev *dev, int argc, char **argv)
{
    int ch;
    float phase;
    float amp;
    int err;

    if (argc != 3 && argc != 4) {
        printf ("usage: %s 1|2 phase [amp]\n", argv[0]);
        return;
    }

    ch = parse_dac_channel(argv[1]);
    if (ch < 1 || ch > 2)
        return;

    phase = strtof(argv[2], NULL);
    if (argc > 3)
      amp = strtof(argv[3], NULL);
    else
      amp = 0.5;

    err = libwr2rf_dac_phase_amp_ext(dev, ch, amp, phase, 1);

    if (err != 0)
      printf("error while setting phase+amp\n");
}

static void
dac_iqctrl (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned iqctrl_addr = 0;
    unsigned ctrl = 0;
    unsigned ch = 0;
    char     str[10];

    void get_iqdac_sel(char *str, unsigned ctrl) {
        if (ctrl & 1)
            sprintf(str,"gnd");
        else if (ctrl & 2)
            sprintf(str,"dds");
        else
            sprintf(str,"rfnco");
    }

    if (argc != 3 && argc != 2) {
        printf ("usage: %s 1|2 [rfnco|dds|gnd] \n", argv[0]);
        return;
    }

    ch = parse_dac_channel(argv[1]);
    if (ch != 1 && ch != 2)
    {
        printf ("Unknown channel\n");
        printf ("usage: %s 1|2 [rfnco|dds|gnd] \n", argv[0]);
        return;
    }

    if (ch == 1) {
        iqctrl_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_IQDAC_CTRL;
    } else {
        iqctrl_addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_IQDAC_CTRL;
    }

    if (argc == 3) {
        if (strcmp(argv[2], "rfnco") == 0)
            ctrl = 0x4;
        else if (strcmp(argv[2], "dds") == 0)
            ctrl = 0x2;
        else if (strcmp(argv[2], "gnd") == 0)
            ctrl = 0x1;
        else {
            printf ("bad dac_iqctrl mode (got %s)\n", argv[2]);
            return;
        }
        libwr2rf_write16(dev, iqctrl_addr, ctrl);
    }

    ctrl = libwr2rf_read16(dev, iqctrl_addr);
    get_iqdac_sel(str, ctrl);
    printf("RF Channel %d dac_iqctrl=%04x %s\n", ch, ctrl, str);
}

static void
dac_init (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int ch;
  if (argc != 2) {
    printf ("usage: dac-init 1|2\n");
    return;
  }

  ch = parse_dac_channel(argv[1]);
  if (ch < 0)
    return;

  libwr2rf_dac_init (dev, ch);
}

static void
dac_validate (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int ch;
  if (argc != 2) {
    printf ("usage: dac-validate 1|2\n");
    return;
  }

  ch = parse_dac_channel(argv[1]);
  if (ch < 0)
    return;

  libwr2rf_dac_validate_timing (dev, ch);
}


static void
dac_tman (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned ch;
    unsigned val;

    if (argc != 3) {
        printf ("usage: dac-tman 1|2 val\n");
        return;
    }

    ch = parse_dac_channel(argv[1]);
    if (ch < 0)
        return;

    val = strtoul(argv[2], NULL, 0);
    libwr2rf_dac_write(dev, ch, DAC_REG_TIMING_ADJ, val);
}

static void
dac_power (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned ch;
    unsigned val;

    if (argc != 3) {
        printf ("usage: dac-power 1|2 val\n");
        return;
    }

    ch = parse_dac_channel(argv[1]);
    if (ch < 0)
        return;

    val = strtoul(argv[2], NULL, 0);
    libwr2rf_dac_write(dev, ch, DAC_REG_POWER_DOWN, val);
}

static void
dac_fsc (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned ch;
    unsigned dac1_fsc;
    unsigned dac2_fsc;


    if (argc!=2 && argc!=4) {
        printf ("usage: dac-fsc 1|2 dac1_fsc1 dac2_fsc2 \n");
        return;
    }

    ch = parse_dac_channel(argv[1]);
    if (ch < 0)
        return;

    if (argc == 4) {
        dac1_fsc = strtoul(argv[2], NULL, 0);
        dac2_fsc = strtoul(argv[3], NULL, 0);
        libwr2rf_dac_write(dev, ch, DAC_REG_DAC1_FSC_LO, dac1_fsc);
        libwr2rf_dac_write(dev, ch, DAC_REG_DAC1_FSC_HI, dac1_fsc>>8);
        libwr2rf_dac_write(dev, ch, DAC_REG_DAC2_FSC_LO, dac2_fsc);
        libwr2rf_dac_write(dev, ch, DAC_REG_DAC2_FSC_HI, dac2_fsc>>8);
    }

    dac1_fsc = (libwr2rf_dac_read(dev, ch, DAC_REG_DAC1_FSC_LO)) |
               (libwr2rf_dac_read(dev, ch, DAC_REG_DAC1_FSC_HI)  << 8);
    dac2_fsc = (libwr2rf_dac_read(dev, ch, DAC_REG_DAC2_FSC_LO)) |
               (libwr2rf_dac_read(dev, ch, DAC_REG_DAC2_FSC_HI)  << 8);

    printf("RF channel %d: DAC_FSC1=%04x DAC_FSC2=%04x\n", ch, dac1_fsc, dac2_fsc);
}

static void
dac_param (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int ch;

  if (argc < 2) {
    printf ("usage: dac-param 1|2 CMD\n");
    return;
  }

  ch = parse_dac_channel(argv[1]);
  if (ch < 0)
    return;

  if (argc == 3 && strcmp (argv[2], "signed") == 0) {
    libwr2rf_dac_write(dev, ch, DAC_REG_DATA_CONTROL, 0x00);
  }
  else if (argc == 3 && strcmp (argv[2], "unsigned") == 0) {
    libwr2rf_dac_write(dev, ch, DAC_REG_DATA_CONTROL, 0x80);
  }
  else {
    printf ("usage: dac-param 1|2 CMD\n");
    printf ("where CMD is\n");
    printf (" signed\n");
    printf (" unsigned\n");
  }
  return;
}


static void
dac_timing (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int ch;
  int res;

  if (argc != 2) {
    printf ("usage: dac-init 1|2\n");
    return;
  }

  ch = parse_dac_channel(argv[1]);
  if (ch < 0)
    return;

  res = libwr2rf_dac_port_timing(dev, ch);
  if (res != 0) {
    printf ("error: cannot set timing for dac %u\n", ch);
    return;
  }
}

static void
dac_timing_set (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int ch;
  unsigned res;

  if (argc != 3)
      goto usage;

  ch = parse_dac_channel(argv[1]);
  if (ch < 0)
      goto usage;

  res = strtoul(argv[2], NULL, 0);
  if (res >= 32)
      goto usage;
  libwr2rf_dac_set_timing_delay(dev, ch, res);
  return;

usage:
  printf ("usage: dac-timing-set 1|2 SMP[31:0] \n");
}

static const struct xlat_tab dac_regs[] =
  {
   { "spi_control ", DAC_REG_SPI_CONTROL  },
   { "data_control", DAC_REG_DATA_CONTROL },
   { "power_down  ", DAC_REG_POWER_DOWN   },
   { "setup_hold  ", DAC_REG_SETUP_HOLD   },
   { "timing_adj  ", DAC_REG_TIMING_ADJ   },
   { "seek        ", DAC_REG_SEEK         },
   { "mix_mode    ", DAC_REG_MIX_MODE     },
   { "dac1_fsc_lo ", DAC_REG_DAC1_FSC_LO  },
   { "dac1_fsc_hi ", DAC_REG_DAC1_FSC_HI  },
   { "auxdac1_lo  ", DAC_REG_AUXDAC1_LO   },
   { "auxdac1_hi  ", DAC_REG_AUXDAC1_HI   },
   { "dac2_fsc_lo ", DAC_REG_DAC2_FSC_LO  },
   { "dac2_fsc_hi ", DAC_REG_DAC2_FSC_HI  },
   { "auxdac2_lo  ", DAC_REG_AUXDAC2_LO   },
   { "auxdac2_hi  ", DAC_REG_AUXDAC2_HI   },
   { "version     ", DAC_REG_VERSION      },
   { NULL, 0}
  };

static void
dac_dump (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned ch;

  if (argc != 2) {
    printf ("usage: dac-dump 1|2\n");
    return;
  }

  if (strcmp(argv[1], "1") == 0)
    ch = DAC1_CS;
  else if (strcmp(argv[1], "2") == 0)
    ch = DAC2_CS;
  else {
    printf ("bad rf channel (got %s)\n", argv[1]);
    return;
  }

  for (const struct xlat_tab *r = dac_regs; r->name; r++) {
          unsigned val = libwr2rf_dac_read(dev, ch, r->val);
          printf ("%s [@%02x] = 0x%02x\n", r->name, r->val, val);
  }
}

static void
dac_ram (struct libwr2rf_dev *dev, int argc, char **argv)
{
  if (argc == 4 && strcmp (argv[1], "load") == 0) {
    unsigned sig;
    FILE *f;
    unsigned addr;

    if (strcmp (argv[2], "i") == 0)
      sig = 1;
    else if (strcmp (argv[2], "q") == 0)
      sig = 2;
    else {
      printf ("incorrect ram (expect i or q, got %s)\n", argv[2]);
      return;
    }
    /* Stop playing.  */
    libwr2rf_write16(dev, WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_IQDAC_RAM_PLAY, 0);

    /* Read file.  */
    f = fopen(argv[3], "rb");
    if (f == NULL) {
      printf ("cannot open file %s\n", argv[3]);
      return;
    }
    for (addr = 0; ; addr++) {
      unsigned short v;
      if (fread(&v, sizeof (v), 1, f) != 1)
        break;
      /* Write.  */
      libwr2rf_write16
        (dev, WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_IQDAC_RAM_ADDR, addr);
      libwr2rf_write16
        (dev, WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_IQDAC_RAM_DATA, v);
      libwr2rf_write16
        (dev, WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_IQDAC_RAM_WRITE, sig);
    }
    printf ("%u values written\n", addr);
    fclose (f);
    return;
  }
  else if (argc == 2 && strcmp (argv[1], "play") == 0) {
    libwr2rf_write16
      (dev, WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_IQDAC_RAM_PLAY, 3);
    return;
  }
  else {
    printf ("usage: dac-ram load i|q FILE\n");
    return;
  }
}

static void
iodelay_init (struct libwr2rf_dev *dev, int argc, char **argv)
{
    libwr2rf_iodelay_init(dev);
}

static void
disp_tmg(struct libwr2rf_dev *dev)
{
    unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TMG;
    unsigned val;
    unsigned i;

    val = libwr2rf_read16(dev, addr);
    printf ("Complete TMG register: tmg[15:0]=%04x\n", val);
    printf (" tmg io term :");
    for (i = 0; i < 4; i++)
      printf (" %u:%u",
              i+1, (val >> (WR2RF_INIT_REGS_TMG_IO_TERM_SHIFT + i)) & 1);
    printf ("\n");
    printf (" tmg io oe   :");
    for (i = 0; i < 4; i++)
      printf (" %u:%u",
              i+1, (val >> (WR2RF_INIT_REGS_TMG_IO_OE_SHIFT + i)) & 1);
    printf ("\n");

    printf (" tmg clk term:");
    for (i = 0; i < 2; i++)
      printf (" %u:%u",
              i+1, (val >> (WR2RF_INIT_REGS_TMG_CLK_TERM_SHIFT + i)) & 1);
    printf ("\n");
    printf (" tmg clk oe  :");
    for (i = 0; i < 2; i++)
      printf (" %u:%u",
              i+1, (val >> (WR2RF_INIT_REGS_TMG_CLK_OE_SHIFT + i)) & 1);
    printf ("\n");
    printf ("\n");
}

static void
tmgio_oe (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TMG;
    unsigned bval = libwr2rf_read16(dev, addr);
    unsigned val;

    if (argc != 1 && argc != 2) {
        printf ("usage: tmgio_oe val, e.g. val=0xf tmgio_oe[3:0] enabled, val=0x1 then only tmgio_oe[0] is enabled\n");
        return;
    }

    if (argc == 2) {
        val = strtoul(argv[1], NULL, 0);

        bval = bval & ~WR2RF_INIT_REGS_TMG_IO_OE_MASK;
        bval = bval | ((val << WR2RF_INIT_REGS_TMG_IO_OE_SHIFT) & WR2RF_INIT_REGS_TMG_IO_OE_MASK);
        libwr2rf_write16(dev, addr, bval);
    }

    disp_tmg(dev);
}

static void
tmgio_term (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TMG;
    unsigned bval = libwr2rf_read16(dev, addr);
    unsigned val;

    if (argc != 1 && argc != 2) {
        printf ("usage: tmgio_term val, e.g. val=0xf tmgio_term[3:0] enabled, val=0x1 then only tmgio_term[0] is enabled\n");
        return;
    }

    if (argc == 2) {
        val = strtoul(argv[1], NULL, 0);

        bval = bval & ~WR2RF_INIT_REGS_TMG_IO_TERM_MASK;
        bval = bval | ((val << WR2RF_INIT_REGS_TMG_IO_TERM_SHIFT) & WR2RF_INIT_REGS_TMG_IO_TERM_MASK);
        libwr2rf_write16(dev, addr, bval);
    }

    disp_tmg(dev);
}

static void
tmgclk_term (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TMG;
    unsigned bval = libwr2rf_read16(dev, addr);
    unsigned val;

    if (argc != 1 && argc != 2) {
        printf ("usage: tmgclk_term val, e.g. val=0x3 tmgclk_term[1:0] enabled, val=0x1 then only tmgclk_term[0] is enabled\n");
        return;
    }

    if (argc == 2) {
        val = strtoul(argv[1], NULL, 0);

        bval = bval & ~WR2RF_INIT_REGS_TMG_CLK_TERM_MASK;
        bval = bval | ((val << WR2RF_INIT_REGS_TMG_CLK_TERM_SHIFT) & WR2RF_INIT_REGS_TMG_CLK_TERM_MASK);
        libwr2rf_write16(dev, addr, bval);
    }

    disp_tmg(dev);
}

static void
tmgclk_oe (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TMG;
    unsigned bval = libwr2rf_read16(dev, addr);
    unsigned val;

    if (argc != 1 && argc != 2) {
        printf ("usage: tmgclk_oe val, e.g. val=0x3 tmgclk_oe[1:0] enabled, val=0x1 then only tmgclk_oe[0] is enabled\n");
        return;
    }

    if (argc == 2) {
        val = strtoul(argv[1], NULL, 0);

        bval = bval & ~WR2RF_INIT_REGS_TMG_CLK_OE_MASK;
        bval = bval | ((val << WR2RF_INIT_REGS_TMG_CLK_OE_SHIFT) & WR2RF_INIT_REGS_TMG_CLK_OE_MASK);
        libwr2rf_write16(dev, addr, bval);
    }

    disp_tmg(dev);
}

static unsigned
parse_lemo_str2reg(const char *str)
{
    if (strcmp(str, "tmgio1") == 0)
        return WR2RF_INIT_REGS_TMGIO1;
    else if (strcmp(str, "tmgio2") == 0)
        return WR2RF_INIT_REGS_TMGIO2;
    else if (strcmp(str, "tmgio3") == 0)
        return WR2RF_INIT_REGS_TMGIO3;
    else if (strcmp(str, "tmgio4") == 0)
        return WR2RF_INIT_REGS_TMGIO4;
    else if (strcmp(str, "tmgclk1") == 0)
        return WR2RF_INIT_REGS_TMGCLK1;
    else if (strcmp(str, "tmgclk2") == 0)
        return WR2RF_INIT_REGS_TMGCLK2;
    else
        return 0;
}

static unsigned
parse_lemo_str2sel(const char *str)
{
    unsigned val;

    if (strcmp(str, "none") == 0)
        val = 0;
    else if (strcmp(str, "tmgio1") == 0)
        val = 1;
    else if (strcmp(str, "tmgio2") == 0)
        val = 2;
    else if (strcmp(str, "tmgio3") == 0)
        val = 4;
    else if (strcmp(str, "tmgio4") == 0)
        val = 8;
    else if (strcmp(str, "tmgclk1") == 0)
        val = 16;
    else if (strcmp(str, "tmgclk2") == 0)
        val = 32;
    else
        val = 0;

    val = val << 1;

    return val;
}

static unsigned
parse_startsel_str2reg(const char *str)
{
    if (strcmp(str, "1.1") == 0)
        return WR2RF_INIT_REGS_LEMO_RF1_T1_START;
    else if (strcmp(str, "1.2") == 0)
        return WR2RF_INIT_REGS_LEMO_RF1_T2_START;
    else if (strcmp(str, "2.1") == 0)
        return WR2RF_INIT_REGS_LEMO_RF2_T1_START;
    else if (strcmp(str, "2.2") == 0)
        return WR2RF_INIT_REGS_LEMO_RF2_T2_START;
    else
        return 0;
}

static unsigned
parse_stopsel_str2reg(const char *str)
{
    if (strcmp(str, "1.1") == 0)
        return WR2RF_INIT_REGS_LEMO_RF1_T1_STOP;
    else if (strcmp(str, "1.2") == 0)
        return WR2RF_INIT_REGS_LEMO_RF1_T2_STOP;
    else if (strcmp(str, "2.1") == 0)
        return WR2RF_INIT_REGS_LEMO_RF2_T1_STOP;
    else if (strcmp(str, "2.2") == 0)
        return WR2RF_INIT_REGS_LEMO_RF2_T2_STOP;
    else
        return 0;
}

static const struct xlat_tab dbg_reg[] =
  {
   { "none", 0},
   { "nco_reset",  1},
   { "rf1_nco_reset_cdelayed",  2},
   { "rf2_nco_reset_cdelayed",  4},
   { "rf1_frev",  8},
   { "rf1_frev_main",  0x10},
   { "rf2_frev",  0x20},
   { "rf2_frev_main",  0x40},
   { "wrs_rx_valid",  0x80},
   { "wrs_tx_valid",  0x100},
   { "gpio1",  0x200},
   { "gpio2",  0x400},
   { "rf_rst",  0x800},
   { "softstop",  0x1000},
   { "softstart",  0x2000},
   { NULL, 0}
  };

static void
lemo_dbg_sel (struct libwr2rf_dev *dev, int argc, char **argv)
{
    const struct xlat_tab *xlat;
    unsigned addr = WR2RF_VME_REGS_INIT;
    unsigned lemo = 0;
    unsigned val  = 0;
    unsigned i;

    if (argc != 1 && argc != 3) {
        goto usage;
    }

    if (argc == 3) {
        lemo = parse_lemo_str2reg(argv[1]);
        if (lemo == 0) {
            goto usage;
        }
        val = xlat_get_value(dbg_reg, argv[2], ~0U);
        if (val == ~0U) {
            goto usage;
        }

        libwr2rf_write16(dev, addr + lemo, val);
    }

    for (i = 0; i < 6; i++) {
        val = libwr2rf_read16(dev, addr + WR2RF_INIT_REGS_TMGIO1 + i*2);
        printf("lemo%d_osel=%x %s\n",
               i+1, val, xlat_get_name(dbg_reg, val, "??"));
    }
    return;

usage:
    printf("Usage: lemo-dbg-sel lemo_name dbg_name\n");
    printf("lemo_names: tmgio1, tmgio2, tmgio3, tmgio4 tmgclk1, tmgclk2 \n");
    printf("dbg_names:  ");
    for (xlat = dbg_reg; xlat->name; xlat++)
      printf (" %s", xlat->name);
    putchar('\n');
}

static void
lemo_gpio (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_LEMO;
    unsigned lemo;
    unsigned val;
    unsigned bit;

    if (argc != 2 && argc != 3) {
        goto usage;
    }

    if (strcmp(argv[1], "gpio1") == 0)
        lemo = 0;
    else if (strcmp(argv[1], "gpio2") == 0)
        lemo = 1;
    else
        goto usage;

    val = libwr2rf_read16(dev, addr);

    if (argc == 3) {
        bit = strtoul (argv[2], NULL, 0) & 1;
        val &= ~(1 << lemo);
        val |= (bit << lemo);
        libwr2rf_write16(dev, addr, val);
    }
    else
        printf ("gpio: %u\n", (val >> lemo) & 1);

    return;

usage:
    printf ("usage: %s gpio1|gpio2 [0 | 1] \n", argv[0]);
}

static void
lemo_ext_sel (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_LEMO_EXT;
    unsigned lemo;

    if (argc != 1 && argc != 2)
        goto usage;

    lemo = strtoul (argv[1], NULL, 0) & 0x3f;
    libwr2rf_write16(dev, addr, lemo);
    lemo = libwr2rf_read16(dev, addr);

    printf ("lemo_ext: 0x%02x\n", lemo);

    return;

usage:
    printf ("usage: %s [sel] \n", argv[0]);
}

static void
lemo_start_sel (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = WR2RF_VME_REGS_INIT;
    unsigned sel  = 0;
    unsigned vtu;

    if (argc != 2 && argc != 3) {
        goto usage;
    }

    vtu  = parse_startsel_str2reg(argv[1]);
    if (vtu == 0) {
      printf ("%s: bad vtu name (%s)\n", argv[0], argv[1]);
      goto usage;
    }

    if (argc == 3) {
        sel  = parse_lemo_str2sel(argv[2]);
        libwr2rf_write16(dev, addr + vtu, sel);
    }
    return;

usage:
    printf("Usage: lemo-start-sel VTU LEMO\n");
    printf("LEMO: none, tmgio1, tmgio2, tmgio3, tmgio4, tmgclk1, tmgclk2\n");
}

static void
lemo_stop_sel (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned addr = WR2RF_VME_REGS_INIT;
    unsigned sel = 0;
    unsigned vtu;

    if (argc != 2 && argc != 3) {
        goto usage;
    }

    vtu  = parse_stopsel_str2reg(argv[1]);
    if (vtu == 0) {
      printf ("%s: bad vtu name (%s)\n", argv[0], argv[1]);
      goto usage;
    }

    if (argc == 3) {
        sel  = parse_lemo_str2sel(argv[2]);
        libwr2rf_write16(dev, addr + vtu, sel);
    }

    printf("%s = %02x, addr=%x\n",
           argv[1], libwr2rf_read16(dev, addr + vtu), addr + vtu);

    return;
usage:
    printf("Usage: lemo-stop-sel VTU LEMO\n");
    printf("LEMO: none, tmgio1, tmgio2, tmgio3, tmgio4, tmgclk1, tmgclk2\n");
}

/* Simple line buffer to format output.  */
#define STRB_MAXLEN 80
struct strb {
  unsigned len;
  char buf[STRB_MAXLEN];
};

static void
strb_init (struct strb *strb)
{
  strb->len = 0;
}

static void
strb_printf(struct strb *strb, const char *format, ...)
{
  va_list args;
  size_t res;

  va_start(args, format);

  if (strb->len >= STRB_MAXLEN)
    return;
  res = vsnprintf(strb->buf + strb->len, STRB_MAXLEN - strb->len,
                  format, args);
  if (res < 0)
    return;
  strb->len += res;
  va_end(args);
}

static void
strb_pad(struct strb *strb, unsigned col)
{
  while (strb->len < col)
    strb->buf[strb->len++] = ' ';
}

static void
strb_puts(struct strb *strb)
{
  if (strb->len >= STRB_MAXLEN)
    strb->buf[STRB_MAXLEN - 1] = 0;
  else
    strb->buf[strb->len] = 0;
  fputs (strb->buf, stdout);
  putc ('\n', stdout);
  strb->len = 0;
}

static void
lemo_disp_oe_term(struct strb *strb, unsigned lemo, unsigned oe, unsigned term)
{
  if ((oe >> lemo) & 1)
    strb_printf (strb, " OUT-EN");
  if ((term >> lemo) & 1)
    strb_printf (strb, " 50ohm");
}

static void
lemo_disp_dbg(struct libwr2rf_dev *dev, struct strb *strb, unsigned lemo)
{
  unsigned val;

  val = libwr2rf_read16
    (dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TMGIO1 + lemo*2);
  strb_printf(strb, "out: ");
  if (val == (1 << LIBWR2RF_LEMO_DBG_WRS_SOFTSTOP)) {
    unsigned vtu;

    strb_printf(strb, "softstop");

    vtu = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_SOFTSTOP);

    switch (vtu) {
    case LIBWR2RF_RF1T1_SOFTSTOP_SEL:
      strb_printf(strb, "(1.1)");
      break;
    case LIBWR2RF_RF1T2_SOFTSTOP_SEL:
      strb_printf(strb, "(1.2)");
      break;
    case LIBWR2RF_RF2T1_SOFTSTOP_SEL:
      strb_printf(strb, "(2.1)");
      break;
    case LIBWR2RF_RF2T2_SOFTSTOP_SEL:
      strb_printf(strb, "(2.2)");
      break;
    case LIBWR2RF_SOFTSTOP_NONE_SEL:
      strb_printf(strb, "(none)");
      break;
    default:
      strb_printf(strb, "(bad)");
      break;
    }
  }
  else if (val == (1 << LIBWR2RF_LEMO_DBG_WRS_SOFTSTART)) {
    unsigned vtu;

    strb_printf(strb, "softstart");

    vtu = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_SOFTSTART);

    switch (vtu) {
    case LIBWR2RF_RF1T1_SOFTSTART_SEL:
      strb_printf(strb, "(1.1)");
      break;
    case LIBWR2RF_RF1T2_SOFTSTART_SEL:
      strb_printf(strb, "(1.2)");
      break;
    case LIBWR2RF_RF2T1_SOFTSTART_SEL:
      strb_printf(strb, "(2.1)");
      break;
    case LIBWR2RF_RF2T2_SOFTSTART_SEL:
      strb_printf(strb, "(2.2)");
      break;
    case LIBWR2RF_SOFTSTART_NONE_SEL:
      strb_printf(strb, "(none)");
      break;
    default:
      strb_printf(strb, "(bad)");
      break;
    }
  } else
    strb_printf(strb, "%s", xlat_get_name(dbg_reg, val, "??"));
}

static void
lemo_disp_start_stop(struct libwr2rf_dev *dev, struct strb *strb,
                     unsigned lemo, const unsigned off[])
{
  unsigned val;
  unsigned i;
  unsigned num;

  num = 0;
  for (i = 0; i < 4; i++) {
    val = libwr2rf_read16 (dev, WR2RF_VME_REGS_INIT + off[i]);
    if (val & (1 << lemo)) {
      strb_printf (strb, " %u.%u", 1 + (i >> 1), 1 + (i & 1));
      num++;
    }
  }
}

static void
lemo_disp_start(struct libwr2rf_dev *dev, struct strb *strb, unsigned lemo)
{
  static const unsigned start_off[] =
    {
     WR2RF_INIT_REGS_LEMO_RF1_T1_START,
     WR2RF_INIT_REGS_LEMO_RF1_T2_START,
     WR2RF_INIT_REGS_LEMO_RF2_T1_START,
     WR2RF_INIT_REGS_LEMO_RF2_T2_START
    };

  strb_printf (strb, "start:");
  lemo_disp_start_stop(dev, strb, lemo, start_off);
}

static void
lemo_disp_stop(struct libwr2rf_dev *dev, struct strb *strb, unsigned lemo)
{
  static const unsigned stop_off[] =
    {
     WR2RF_INIT_REGS_LEMO_RF1_T1_STOP,
     WR2RF_INIT_REGS_LEMO_RF1_T2_STOP,
     WR2RF_INIT_REGS_LEMO_RF2_T1_STOP,
     WR2RF_INIT_REGS_LEMO_RF2_T2_STOP
    };

  strb_printf (strb, "stop: ");
  lemo_disp_start_stop(dev, strb, lemo, stop_off);
}

static void
lemo_show (struct libwr2rf_dev *dev, int argc, char **argv)
{
  static const char * const lemos_name[] =
    {
     "tmgio1 ", "tmgio2 ", "tmgio3 ", "tmgio4 ",
     "tmgclk1", "tmgclk2"
    };
  unsigned tmg_term;
  unsigned tmg_oe;
  unsigned val;
  struct strb strb;
  int i;

  strb_init (&strb);

  /* Read term+oe.  */
  val = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_TMG);

  tmg_term = (val >> WR2RF_INIT_REGS_TMG_IO_TERM_SHIFT) & 0x0f;
  tmg_oe   = (val >> WR2RF_INIT_REGS_TMG_IO_OE_SHIFT) & 0x0f;

  tmg_term |= ((val >> WR2RF_INIT_REGS_TMG_CLK_TERM_SHIFT) & 0x03) << 4;
  tmg_oe   |= ((val >> WR2RF_INIT_REGS_TMG_CLK_OE_SHIFT) & 0x03) << 4;

  for (i = 4; i >= 0; i -= 2) {
    strb_printf (&strb, "%s", lemos_name[i]);
    lemo_disp_oe_term(&strb, i, tmg_oe, tmg_term);
    strb_pad (&strb, 40);
    strb_printf (&strb, "%s", lemos_name[i + 1]);
    lemo_disp_oe_term(&strb, i + 1, tmg_oe, tmg_term);
    strb_puts (&strb);
    lemo_disp_dbg(dev, &strb, i);
    strb_pad (&strb, 40);
    lemo_disp_dbg(dev, &strb, i + 1);
    strb_puts (&strb);
    lemo_disp_start(dev, &strb, i);
    strb_pad (&strb, 40);
    lemo_disp_start(dev, &strb, i + 1);
    strb_puts (&strb);
    lemo_disp_stop(dev, &strb, i);
    strb_pad (&strb, 40);
    lemo_disp_stop(dev, &strb, i + 1);
    strb_puts (&strb);
    putchar ('\n');
  }
}

static void
disp_vtudiag(struct libwr2rf_dev *dev, unsigned addr)
{
  unsigned v;
  unsigned window;

  v = libwr2rf_read16(dev, addr + VTUDIAG_REGS_CONTROL);
  window = v & VTUDIAG_REGS_CONTROL_WINDOW_MASK;
  printf ("control: 0x%04x\n", v);
  printf (" enable: 0x%04x\n", (unsigned)(v & VTUDIAG_REGS_CONTROL_ENABLE));
  printf (" window: 0x%04x\n", (unsigned)(v & window));

  v = libwr2rf_read16(dev, addr + VTUDIAG_REGS_GENERATION);
  printf ("generation: 0x%04x\n", v);

  v = libwr2rf_be_read32(dev, addr + VTUDIAG_REGS_FREQ);
  printf ("freq:    0x%04x %u", v, v);
  switch (window) {
  case 0x00:
    printf (" per 5us, freq=%f Mhz", (double)v / 5.0);
    break;
  case 0x02:
    printf (" per 500us, freq=%f Khz", (double)v * 2.0);
    break;
  case 0x04:
    printf (" per 50ms, freq=%f hz", (double)v * 20.0);
    break;
  case 0x05:
    printf (" per 5sec, freq=%f hz", (double)v / 5.0);
    break;
  case 0x06:
    printf (" per 20sec, freq=%f hz", (double)v / 20.0);
    break;
  }
  printf("\n");

  v = libwr2rf_be_read32(dev, addr + VTUDIAG_REGS_COUNTER);
  printf ("counter: 0x%08x (%u)\n", v, v);
}

/* vtu diag subcommand.  diag command at argv[1].  */
static void
vtudiag (struct libwr2rf_dev *dev, unsigned addr, int argc, char **argv)
{
  if (argc == 1) {
    disp_vtudiag(dev, addr);
  }
  else if ((argc == 2 || argc == 3) && strcmp (argv[1], "en") == 0) {
    unsigned window;

    if (argc == 2)
      window = libwr2rf_read16(dev, addr + VTUDIAG_REGS_CONTROL)
        & VTUDIAG_REGS_CONTROL_WINDOW_MASK;
    else
      if (strcmp(argv[2], "5us") == 0)
        window = 0;
      else if (strcmp(argv[2], "500us") == 0)
        window = 2;
      else if (strcmp(argv[2], "50ms") == 0)
        window = 4;
      else if (strcmp(argv[2], "5s") == 0)
        window = 5;
      else if (strcmp(argv[2], "20s") == 0)
        window = 6;
      else {
        printf ("unknown window; expect 5us|500us|50ms|5s|20s, got %s\n",
                argv[2]);
        return;
      }
    libwr2rf_write16(dev, addr + VTUDIAG_REGS_CONTROL,
                     window | VTUDIAG_REGS_CONTROL_ENABLE);
  }
  else if (argc == 2 && strcmp (argv[1], "off") == 0) {
    unsigned window;

    window = libwr2rf_read16(dev, addr + VTUDIAG_REGS_CONTROL)
      & VTUDIAG_REGS_CONTROL_WINDOW_MASK;
    libwr2rf_write16(dev, addr + VTUDIAG_REGS_CONTROL, window);
  }
  else {
    printf("unknown command %s\n", argv[1]);
    printf("commands are:\n");
    printf(" en  [5us|500us|50ms|5s|20s]    to enable diagnostics\n");
    printf(" off                            to disable diagnostics\n");
    printf("current values are displayed if no command\n");
    return;
  }
}

static void
rf_diag (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned ch;
  unsigned addr;

  if (argc < 2) {
    printf ("usage: rf-diag 1|2 CMD\n");
    return;
  }

  ch = parse_dac_channel(argv[1]);
  if (ch < 0)
    return;

  if (ch == 1)
    addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF1_VTUS
      + WR2RF_RFTRIGGER_REGS_RF_DIAG;
  else
    addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_RF2_VTUS
      + WR2RF_RFTRIGGER_REGS_RF_DIAG;

  vtudiag(dev, addr, argc - 1, &argv[1]);
}

#define BADADDR 0xffffffff

static unsigned
parse_vtu (const char *arg,
           unsigned base, unsigned rf1, unsigned rf2, unsigned t1, unsigned t2)
{
  unsigned addr;

  printf ("base=%08x rf1=%08x rf2=%08x t1=%08x t2=%08x \n",
          base, rf1, rf2, t1, t2 );
  addr = base;
  if (arg[0] == '1')
    addr += rf1;
  else if (arg[0] == '2')
    addr += rf2;
  else {
    printf("bad vtu rf number (expect RF.TR, got %s)\n", arg);
    return BADADDR;
  }
  if (arg[1] != '.') {
    printf("bad vtu separator (expect RF.TR, got %s)\n", arg);
    return BADADDR;
  }
  if (arg[2] == '1')
    addr += t1;
  else if (arg[2] == '2')
    addr += t2;
  else {
    printf("bad vtu trigger number (expect RF.TR, got %s)\n", arg);
    return BADADDR;
  }
  return addr;
}

static void
vtu (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned addr;

  addr = 0;
  if (argc > 1) {
    addr = parse_vtu(argv[1],
                     WR2RF_VME_REGS_CTRL,
                     WR2RF_CTRL_REGS_RF1_VTUS, WR2RF_CTRL_REGS_RF2_VTUS,
                     WR2RF_RFTRIGGER_REGS_T1, WR2RF_RFTRIGGER_REGS_T2);
    if (addr == BADADDR)
      return;
  }

  if (argc == 2) {
    libwr2rf_vtu_dump_by_addr(dev, addr);
    printf ("\n"
            "vtu diag:\n");
    disp_vtudiag(dev, addr + TRIGUNIT_REGS_TRIGDIAG);
  }
  else if (argc >= 3 && strcmp(argv[2], "diag") == 0) {
    vtudiag(dev, addr + TRIGUNIT_REGS_TRIGDIAG, argc - 2, &argv[2]);
  }
  else if (argc == 4 && strcmp(argv[2], "b") == 0) {
    uint64_t v;

    v = strtoul (argv[3], NULL, 0);
    libwr2rf_be_write64(dev, addr + TRIGUNIT_REGS_BVALUEOFFLINE, v);
  }
  else if (argc == 4 && strcmp(argv[2], "ht") == 0) {
    uint64_t v;

    v = strtoul (argv[3], NULL, 0);
    libwr2rf_be_write64(dev, addr + TRIGUNIT_REGS_HTVALUEOFFLINE, v);
  }
  else if (argc == 4 && strcmp(argv[2], "w") == 0) {
    uint64_t v;

    v = strtoul (argv[3], NULL, 0);
    libwr2rf_be_write64(dev, addr + TRIGUNIT_REGS_WVALUEOFFLINE, v);
  }
  else if (argc == 4 && strcmp(argv[2], "mode") == 0) {
    unsigned v;
    int mode;

    mode = libwr2rf_vtu_get_mode_by_name(argv[3]);
    if (mode == -1) {
      printf ("bad vtu mode, expect:");
      for (v = 0; ; v++) {
        const char *name = libwr2rf_vtu_get_mode_by_index(v);
        if (name == NULL)
          break;
        printf (" %s", name);
      }
      printf ("\n");
      return;
    }
    v = libwr2rf_read16(dev, addr + TRIGUNIT_REGS_CONFIGOFFLINE);
    v = v & ~TRIGUNIT_REGS_CONFIGOFFLINE_MODE_MASK;
    v |= mode << TRIGUNIT_REGS_CONFIGOFFLINE_MODE_SHIFT;
    libwr2rf_write16(dev, addr + TRIGUNIT_REGS_CONFIGOFFLINE, v);
  }
  else if (argc == 3 && strcmp(argv[2], "valid") == 0) {
    unsigned v;
    v = libwr2rf_read16(dev, addr + TRIGUNIT_REGS_CONFIGOFFLINE);
    v |= TRIGUNIT_REGS_CONFIGOFFLINE_VALID;
    libwr2rf_write16(dev, addr + TRIGUNIT_REGS_CONFIGOFFLINE, v);
  }
  else if (argc == 4 && strcmp(argv[2], "reset") == 0) {
    unsigned v;
    v = libwr2rf_read16(dev, addr + TRIGUNIT_REGS_CONTROL);
    if (strcmp (argv[3], "0") == 0)
      v &= ~TRIGUNIT_REGS_CONTROL_VTURESET;
    else if (strcmp (argv[3], "1") == 0)
      v |= TRIGUNIT_REGS_CONTROL_VTURESET;
    else {
      printf ("bad reset value, expect 0 or 1, got %s\n", argv[3]);
      return;
    }
    libwr2rf_write16(dev, addr + TRIGUNIT_REGS_CONTROL, v);
  }
  else if (argc == 3 && strcmp(argv[2], "softstart") == 0) {
    unsigned v;
    v = libwr2rf_read16(dev, addr + TRIGUNIT_REGS_CONTROL);
    v |= TRIGUNIT_REGS_CONTROL_STARTSOFTTRIGGER;
    libwr2rf_write16(dev, addr + TRIGUNIT_REGS_CONTROL, v);
  }
  else if (argc == 3 && strcmp(argv[2], "softstop") == 0) {
    unsigned v;
    v = libwr2rf_read16(dev, addr + TRIGUNIT_REGS_CONTROL);
    v |= TRIGUNIT_REGS_CONTROL_STOPSOFTTRIGGER;
    libwr2rf_write16(dev, addr + TRIGUNIT_REGS_CONTROL, v);
  }

  else {
    printf("usage: vtu RF.TR            Display registers\n");
    printf("usage: vtu RF.TR diag ...   Diagnostic\n");
    printf("usage: vtu RF.TR b VAL      Set offline bvalue\n");
    printf("usage: vtu RF.TR ht VAL     Set offline htvalue\n");
    printf("usage: vtu RF.TR w VAL      Set offline wvalue\n");
    printf("usage: vtu RF.TR mode M     Set offline mode\n");
    printf("usage: vtu RF.TR valid      Set offline config valid bit\n");
    printf("usage: vtu RF.TR reset 0|1  Set vtu reset\n");
    printf("usage: vtu RF.TR softstart  Manually start vtu\n");
    printf("usage: vtu RF.TR softstop   Manually stop vtu\n");
    return;
  }
}

static void
vtu_watch (struct libwr2rf_dev *dev, int argc, char **argv)
{
  enum state { IDLE, STARTED, RUNNING, UNKNOWN };
  static const char * const state_names[] = { "idle   ",
                                              "started",
                                              "running",
                                              "stopped"};
  enum state prev[2], cur[2];
  unsigned base = WR2RF_VME_REGS_CTRL;
  unsigned long long prev_time;
  unsigned rxframe_prev = ~0U;
  unsigned i;
  unsigned flag_verbose = 0;
  unsigned flag_rxframe = 0;
  unsigned flag_wait = 0;

  if (argc < 2) {
    printf ("usage: %s 1|2 [v] [rx] [w]\n", argv[0]);
    return;
  }

  if (strcmp(argv[1], "1") == 0)
    base += WR2RF_CTRL_REGS_RF1_VTUS;
  else if (strcmp(argv[1], "2") == 0)
    base += WR2RF_CTRL_REGS_RF2_VTUS;
  else {
    printf ("bad VTU unit\n");
    return;
  }

  for (i = 2; i < argc; i++)
    if (strcmp(argv[i], "v") == 0)
      flag_verbose++;
    else if (strcmp(argv[i], "rx") == 0)
      flag_rxframe++;
    else if (strcmp(argv[i], "w") == 0)
      flag_wait++;
    else {
      printf ("%s: unknown option %s\n", argv[0], argv[i]);
      return;
    }


  cur[0] = 0xff;
  cur[1] = 0xff;
  {
    unsigned tai_h;
    unsigned tai_l;
    unsigned cyc;
    wrc_get_time(dev, &tai_h, &tai_l, &cyc);
    prev_time = (cyc * 16) / 1000 + (tai_l * 1000000ULL);
  }

  while (1) {
    int i;

    if (flag_rxframe) {
      unsigned rxframe_count = get_wrs_rxframe_counter (dev);
      if (rxframe_count != rxframe_prev) {
        disp_wrs_rxframe (dev, rxframe_count);
        rxframe_prev = rxframe_count;
      }
    }

    for (i = 0; i < 2; i++) {
      unsigned v;
      unsigned addr;

      prev[i] = cur[i];

      if (i == 0)
        addr = base + WR2RF_RFTRIGGER_REGS_T1;
      else
        addr = base + WR2RF_RFTRIGGER_REGS_T2;

      v = libwr2rf_read16(dev, addr + TRIGUNIT_REGS_STATUS);

      if (v & TRIGUNIT_REGS_STATUS_IDLE)
        cur[i] = IDLE;
      else if (v & TRIGUNIT_REGS_STATUS_STARTED)
        cur[i] = STARTED;
      else if (v & TRIGUNIT_REGS_STATUS_RUNNING)
        cur[i] = RUNNING;
      else
        cur[i] = UNKNOWN;

      if (flag_verbose && cur[i] == UNKNOWN) {
        printf ("VTU%u status=0x%04x\n", i + 1, v);
        libwr2rf_vtu_dump_by_addr(dev, addr);
      }
    }

    if (cur[0] != prev[0] || cur[1] != prev[1]) {
      unsigned tai_h;
      unsigned tai_l;
      unsigned cyc;
      unsigned long long ntime, delta, delta_ms;
      wrc_get_time(dev, &tai_h, &tai_l, &cyc);
      printf ("VTU T1: %s   T2: %s  ",
              state_names[cur[0]],
              state_names[cur[1]]);
      ntime = (cyc * 16) / 1000 + (tai_l * 1000000ULL);
      delta = ntime - prev_time;
      delta_ms = delta / 1000;
      printf ("tai: %08x, cyc: %07x +%6ums%03uus\n",
              tai_l, cyc,
              (unsigned)delta_ms, (unsigned)(delta - delta_ms * 1000));
      prev_time = ntime;
    }

    if (flag_wait)
      usleep(flag_wait);
  }
}


static unsigned
parse_api_vtu (const char *arg)
{
  if (arg == NULL) {
    printf ("missing VTU id\n");
    return BADADDR;
  }

  if (strcmp(arg, "0") == 0
      || strcmp (arg, "1.1") == 0)
    return LIBWR2RF_TRIG_ID_RF_1_TRIG_1;
  else if (strcmp(arg, "1") == 0
      || strcmp (arg, "1.2") == 0)
    return LIBWR2RF_TRIG_ID_RF_1_TRIG_2;
  else if (strcmp(arg, "2") == 0
      || strcmp (arg, "2.1") == 0)
    return LIBWR2RF_TRIG_ID_RF_2_TRIG_1;
  else if (strcmp(arg, "3") == 0
      || strcmp (arg, "2.2") == 0)
    return LIBWR2RF_TRIG_ID_RF_2_TRIG_2;
  else {
    printf ("incorrect VTU id (%s)\n", arg);
    printf ("expect 1.1/1.2/2.1/2.2 or 0/1/2/3\n");
    return BADADDR;
  }
}

static void
api_vtu_softstart (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned id;

  id = parse_api_vtu(argv[1]);
  if (id == BADADDR)
    return;

  if (libwr2rf_vtu_softstart (dev, id) != 0)
    printf ("ERROR\n");
}

static void
api_vtu_softstop (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned id;

  id = parse_api_vtu(argv[1]);
  if (id == BADADDR)
    return;

  if (libwr2rf_vtu_softstop (dev, id) != 0)
    printf ("ERROR\n");
}

static void
api_vtu_reset (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned id;
  unsigned reset;

  if (argc != 3) {
    printf ("usage: %s VTU-ID 0|1\n", argv[0]);
    return;
  }

  id = parse_api_vtu(argv[1]);
  if (id == BADADDR)
    return;

  reset = strtoul(argv[2], NULL, 0);

  if (libwr2rf_vtu_reset (dev, id, reset) != 0)
    printf ("ERROR\n");
}

static void
api_lemo_dbg_sel (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned lemo;
    unsigned dbg;

    if (argc != 3) {
        printf ("usage: %s lemo dbg \n", argv[0]);
        return;
    }

    lemo = strtoul(argv[1], NULL, 0);
    dbg  = strtoul(argv[2], NULL, 0);

    libwr2rf_configure_lemo_debug(dev, lemo, dbg);
}

static void
api_softstop_sel (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned sel  = 0;

    if (argc > 1) {
        if (strcmp(argv[1],"1.1") == 0)
            sel = LIBWR2RF_RF1T1_SOFTSTOP_SEL;
        else if (strcmp(argv[1],"1.2") == 0)
            sel = LIBWR2RF_RF1T2_SOFTSTOP_SEL;
        else if (strcmp(argv[1],"2.1") == 0)
            sel = LIBWR2RF_RF2T1_SOFTSTOP_SEL;
        else if (strcmp(argv[1],"2.2") == 0)
            sel = LIBWR2RF_RF2T2_SOFTSTOP_SEL;
        else if (strcmp(argv[1],"none") == 0)
            sel = LIBWR2RF_SOFTSTOP_NONE_SEL;
        else {
            printf("Usage: %s [RF.TU] or none\n", argv[0]);
            return;
        }
    }

    libwr2rf_configure_softstop_sel(dev, sel);
}

static void
api_softstart_sel (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned sel  = 0;

    if (argc > 1) {
        if (strcmp(argv[1],"1.1") == 0)
            sel = LIBWR2RF_RF1T1_SOFTSTART_SEL;
        else if (strcmp(argv[1],"1.2") == 0)
            sel = LIBWR2RF_RF1T2_SOFTSTART_SEL;
        else if (strcmp(argv[1],"2.1") == 0)
            sel = LIBWR2RF_RF2T1_SOFTSTART_SEL;
        else if (strcmp(argv[1],"2.2") == 0)
            sel = LIBWR2RF_RF2T2_SOFTSTART_SEL;
        else if (strcmp(argv[1],"none") == 0)
            sel = LIBWR2RF_SOFTSTART_NONE_SEL;
        else {
            printf("Usage: %s [RF.TU] or none\n", argv[0]);
            return;
        }
    }

    libwr2rf_configure_softstart_sel(dev, sel);
}

static void
api_vtu_infinite (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned id;
  unsigned b;
  unsigned ht;

  if (argc != 4) {
    printf ("usage: %s VTU-ID B HT\n", argv[0]);
    return;
  }

  id = parse_api_vtu(argv[1]);
  if (id == BADADDR)
    return;

  b = strtoul(argv[2], NULL, 0);
  ht = strtoul(argv[3], NULL, 0);

  if (libwr2rf_vtu_program_infinite (dev, id, b, ht) != 0)
    printf ("ERROR\n");
}

static void
api_vtu_highfreq (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned id;
  unsigned b;
  unsigned div;

  if (argc != 4) {
    printf ("usage: %s VTU-ID B DIV\n", argv[0]);
    return;
  }

  id = parse_api_vtu(argv[1]);
  if (id == BADADDR)
    return;

  b = strtoul(argv[2], NULL, 0);
  div = strtoul(argv[3], NULL, 0);

  if (libwr2rf_vtu_program_highfreq (dev, id, b, div) != 0)
    printf ("ERROR\n");
}

static void
api_vtu_window (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned id;
  unsigned b;
  unsigned ht;
  unsigned win;

  if (argc != 5) {
    printf ("usage: %s VTU-ID B ht win\n", argv[0]);
    return;
  }

  id = parse_api_vtu(argv[1]);
  if (id == BADADDR)
    return;

  b   = strtoul(argv[2], NULL, 0);
  ht  = strtoul(argv[3], NULL, 0);
  win = strtoul(argv[4], NULL, 0);

  if (libwr2rf_vtu_program_window (dev, id, b, ht, win) != 0)
    printf ("ERROR\n");
}

static void
api_vtu_invalidate (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned id;

  if (argc != 2) {
    printf ("usage: %s VTU-ID\n", argv[0]);
    return;
  }

  id = parse_api_vtu(argv[1]);
  if (id == BADADDR)
    return;

  if (libwr2rf_vtu_program_invalidate (dev, id) != 0)
    printf ("ERROR\n");
}

static void
api_vtu_sync_delays (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned cdelay = 0;
    unsigned fdelay = 0;
    unsigned odelay = 0;
    unsigned cal;
    unsigned id;

    if (argc != 3)
        goto usage;

    id = strtoul (argv[1], NULL, 0);
    if (id != 1 && id != 2)
        goto usage;

    cal = strtoul (argv[2], NULL, 0);
    if (cal != 0 && cal != 1)
        goto usage;

    if (libwr2rf_vtu_sync_delays (dev, id, &cdelay, &fdelay, &odelay, cal) != 0)
        printf("ERROR\n");
    else
        printf("VTU=%d.1 cdelay=%d(x16ns), fdelay=%d(x1ns) odelay=%d(x78ps)\n",
               id, cdelay, fdelay, odelay);
    return;

usage:
    printf ("usage: %s RF_CH[1|2] Calibrated_Delays[0|1] \n", argv[0]);
}


static void
vtu_out (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned addr     = 0;
  unsigned csr_addr = 0;
  unsigned del_addr = WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_RF +
                      WR2RF_INIT_RF_REGS_RF_TRIGGER_FF_DELAY;

  if (argc > 1) {
    addr = parse_vtu(argv[1],
                     WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_RF,
                     WR2RF_INIT_RF_REGS_RF1, WR2RF_INIT_RF_REGS_RF2,
                     WR2RF_INIT_RF_CH_REGS_CH,
                     WR2RF_INIT_RF_CH_REGS_CH + WR2RF_INIT_RF_CH_REGS_CH_SIZE);
    if (addr == BADADDR)
      return;
  }

  csr_addr = addr + WR2RF_INIT_RF_CH_REGS_CH_CSR;
  printf("csr_addr=%04x\n", csr_addr);

  if (argc == 2) {
    unsigned v;
    unsigned d;

    v = libwr2rf_read16(dev, csr_addr);
    printf ("rf-ch-csr:   0x%04x [@ 0x%08x]\n", v, csr_addr);
    printf (" delay_oen:  0x%04x\n",
            (unsigned)(v & WR2RF_INIT_RF_CH_REGS_CH_CSR_DELAY_OEN));
    d = libwr2rf_read16(dev, del_addr);
    printf (" delay_val:  0x%04x\n", (unsigned)(d));
    printf (" shape_n:    0x%04x\n",
            (unsigned)(v & WR2RF_INIT_RF_CH_REGS_CH_CSR_MUX_SEL));
    printf (" trig_reset: 0x%04x\n",
            (unsigned)(v & WR2RF_INIT_RF_CH_REGS_CH_CSR_TRIG_RST));
    printf (" trig_latch: 0x%04x\n",
            (unsigned)(v & WR2RF_INIT_RF_CH_REGS_CH_CSR_DELAY_LATCH));
  }
  else if (argc == 4 && strcmp (argv[2], "delay_oen") == 0) {
    unsigned v;

    v = libwr2rf_read16(dev, csr_addr);
    if (strcmp (argv[3], "0") == 0)
      v &= ~WR2RF_INIT_RF_CH_REGS_CH_CSR_DELAY_OEN;
    else if (strcmp (argv[3], "1") == 0)
      v |= WR2RF_INIT_RF_CH_REGS_CH_CSR_DELAY_OEN;
    else {
      printf ("bad value.  Expect 0|1, got %s\n", argv[2]);
      return;
    }
    libwr2rf_write16(dev, csr_addr, v);
  }
  else if (argc == 4 && strcmp (argv[2], "shape_n") == 0) {
    unsigned v;

    v = libwr2rf_read16(dev, csr_addr);
    if (strcmp (argv[3], "on") == 0)
      v &= ~WR2RF_INIT_RF_CH_REGS_CH_CSR_MUX_SEL;
    else if (strcmp (argv[3], "off") == 0)
      v |= WR2RF_INIT_RF_CH_REGS_CH_CSR_MUX_SEL;
    else {
      printf ("bad value.  Expect on|off, got %s\n", argv[2]);
      return;
    }
    libwr2rf_write16(dev, csr_addr, v);
  }
  else if (argc == 4 && strcmp (argv[2], "trig_reset") == 0) {
    unsigned v;

    v = libwr2rf_read16(dev, csr_addr);
    if (strcmp (argv[3], "0") == 0)
      v &= ~WR2RF_INIT_RF_CH_REGS_CH_CSR_TRIG_RST;
    else if (strcmp (argv[3], "1") == 0)
      v |= WR2RF_INIT_RF_CH_REGS_CH_CSR_TRIG_RST;
    else {
      printf ("bad value.  Expect 0|1, got %s\n", argv[2]);
      return;
    }
    libwr2rf_write16(dev, csr_addr, v);
  } else if (argc == 4 && strcmp (argv[2], "delay_val") == 0) {
    unsigned v = strtoul (argv[3], NULL, 0);
    unsigned b = libwr2rf_read16(dev, csr_addr);
    unsigned n = 0;

    n = b |  WR2RF_INIT_RF_CH_REGS_CH_CSR_DELAY_LATCH;
    libwr2rf_write16(dev, csr_addr, n);
    libwr2rf_write16(dev, del_addr, v);
    n = b & ~WR2RF_INIT_RF_CH_REGS_CH_CSR_DELAY_LATCH;
    libwr2rf_write16(dev, csr_addr, n);
    n = b |  WR2RF_INIT_RF_CH_REGS_CH_CSR_DELAY_LATCH;
    libwr2rf_write16(dev, csr_addr, n);
  } else {
    printf ("usage: %s RF.TR [CMD]\n", argv[0]);
    printf ("commands are:\n");
    printf (" delay_oen  0|1 [0: enable, 1: disable]\n");
    printf (" shape_n    0|1 [0: on,     1: off]\n");
    printf (" trig_reset 0|1\n");
    printf (" delay_val val\n");
  }
}

static void
vtu_odelay (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned baddr = 0;
    unsigned addr  = 0;
    unsigned v     = 0;
    unsigned hc    = 0;

    if (argc > 1) {
        baddr = parse_vtu(argv[1],
                          WR2RF_VME_REGS_CTRL,
                          WR2RF_CTRL_REGS_RF1_VTUS, WR2RF_CTRL_REGS_RF2_VTUS,
                          WR2RF_RFTRIGGER_REGS_T1, WR2RF_RFTRIGGER_REGS_T2);
        if (baddr == BADADDR)
            goto usage;
    }

    if (argc != 2 && argc != 4 ) {
        goto usage;
    }
    if (argc == 4) {
        hc = strtoul (argv[2], NULL, 0);
        v  = strtoul (argv[3], NULL, 0);
        printf ("Your delay request: hc=%01x, odelay=0x%02x (~%d ps)\n", hc, v, v*78);
        //if ((hc != 0 && hc != 1) || v > 31) {
        //    goto usage;
        //}
        addr = baddr + TRIGUNIT_REGS_TRIGOHCDELAY;
        libwr2rf_write16(dev, addr, hc);
        addr = baddr + TRIGUNIT_REGS_TRIGODELAY;
        libwr2rf_write16(dev, addr, v);
    }

    addr = baddr + TRIGUNIT_REGS_TRIGOHCDELAY;
    hc = libwr2rf_read16(dev, addr);
    addr = baddr + TRIGUNIT_REGS_TRIGODELAY;
    v = libwr2rf_read16(dev, addr);
    printf ("VTU %s: hc=%01x, odelay=%02x (~%d ps)\n", argv[1], hc, v, v*78);
    return;

usage:
    printf ("usage: %s RF.TU [hc=0|1] [delay_odelay_units] #(78 ps steps for odelay value) \n", argv[0]);
}

static void
api_init (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned verbose = 1;
  const char *fw = NULL;
  unsigned i;

  for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "--fw") == 0 && i + 1 < argc) {
      fw = argv[i+1];
      i++;
    }
    else if (strcmp(argv[i], "-q") == 0) {
      verbose = 0;
    }
    else {
      printf ("usage: %s [-q] [--fw FIRMWARE]\n", argv[0]);
      return;
    }
  }

  /* Recommended way.  */
  if (libwr2rf_check_fwversion(dev) != 0) {
    printf ("error: incorrect FW version, get: 0x%04x\n", libwr2rf_read_fwversion(dev));
    return;
  }

  /* Command pll-init.  */
  if (libwr2rf_enable_check_ocxo(dev) != 0) {
    printf ("warning: OCXO may not be warm\n");
    /* But continue.  */
  }
  libwr2rf_clock_init(dev);

  /* Command fw-load */
  if (fw) {
    load_wrs_firmware(dev, fw);
  }

  /* Wait until WR is ready.  */
  while (1) {
      if (verbose)
          wrc_read_print_status (dev);
      if (libwr2rf_wrc_time_valid(dev) == 0)
          break;
      sleep(1);
  }

  if (libwr2rf_api_init (dev) != 0)
    printf ("ERROR\n");
}

static void
api_vtu_start_lemo (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int res;
  unsigned long id;
  unsigned long lemo;

  if (argc != 3) {
    printf ("usage: %s VTU-ID LEMO-ID\n", argv[0]);
    return;
  }

  id = parse_api_vtu (argv[1]);
  if (id == BADADDR)
    return;

  lemo = strtoul (argv[2], NULL, 0);
  res = libwr2rf_vtu_start_lemo(dev, id, lemo);
  if (res != 0)
    printf ("failed (status=%d)\n", res);
}

static void
api_vtu_stop_lemo (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int res;
  unsigned long id;
  unsigned long lemo;

  if (argc != 3) {
    printf ("usage: %s VTU-ID LEMO-ID\n", argv[0]);
    return;
  }

  id = parse_api_vtu (argv[1]);
  if (id == BADADDR)
    return;

  lemo = strtoul (argv[2], NULL, 0);
  res = libwr2rf_vtu_stop_lemo(dev, id, lemo);
  if (res != 0)
    printf ("failed (status=%d)\n", res);
}

static void
api_vtu_output_ff_delay (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int res;
  unsigned long id;
  unsigned long delay;

  if (argc != 3) {
    printf ("usage: %s VTU-ID DELAY\n", argv[0]);
    return;
  }
  id = parse_api_vtu (argv[1]);
  if (id == BADADDR)
    return;

  delay = strtoul (argv[2], NULL, 0);
  res = libwr2rf_vtu_output_ff_delay(dev, id, delay);
  if (res != 0)
    printf ("failed (status=%d)\n", res);
}

static void
api_vtu_output_pad_delay (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int res;
  unsigned long id;
  unsigned long hc;
  unsigned long odelay;

  if (argc != 4) {
    printf ("usage: %s VTU-ID half_cycle ODELAY\n", argv[0]);
    return;
  }
  id = parse_api_vtu (argv[1]);
  if (id == BADADDR)
    return;

  hc     = strtoul (argv[2], NULL, 0);
  odelay = strtoul (argv[3], NULL, 0);
  res = libwr2rf_vtu_output_pad_delay(dev, id, hc, odelay);
  if (res != 0)
    printf ("failed (status=%d)\n", res);
}


static void
api_vtu_output_enable (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int res;
  unsigned long id;
  unsigned long enable;


  if (argc != 3) {
    printf ("usage: %s VTU-ID ENABLE\n", argv[0]);
    return;
  }
  id = parse_api_vtu (argv[1]);
  if (id == BADADDR)
    return;
  enable = strtoul (argv[2], NULL, 0);
  res = libwr2rf_vtu_output_enable(dev, id, enable);
  if (res != 0)
    printf ("failed (status=%d)\n", res);
}

static void
api_vtu_output_shaper (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int res;
  unsigned long id;
  unsigned long enable;

  if (argc != 3) {
    printf ("usage: %s VTU-ID ENABLE\n", argv[0]);
    return;
  }
  id = parse_api_vtu (argv[1]);
  if (id == BADADDR)
    return;
  enable = strtoul (argv[2], NULL, 0);
  res = libwr2rf_vtu_output_shaper(dev, id, enable);
  if (res != 0)
    printf ("failed (status=%d)\n", res);
}

static void
api_vtu_output_reset (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int res;
  unsigned long id;
  unsigned long enable;

  if (argc != 3) {
    printf ("usage: %s VTU-ID RST\n", argv[0]);
    return;
  }
  id = parse_api_vtu (argv[1]);
  if (id == BADADDR)
    return;
  enable = strtoul (argv[2], NULL, 0);
  res = libwr2rf_vtu_output_reset(dev, id, enable);
  if (res != 0)
    printf ("failed (status=%d)\n", res);
}

static void
api_vtu_status (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned res;

  if (libwr2rf_vtu_global_status_read(dev, &res) != 0) {
    printf ("failed\n");
    return;
  }
  printf ("status: %04x ", res);
  if (res & LIBWR2RF_GBL_RF1T1_START)
    printf (" rf1t1-start");
  if (res & LIBWR2RF_GBL_RF1T2_START)
    printf (" rf1t2-start");
  if (res & LIBWR2RF_GBL_RF2T1_START)
    printf (" rf2t1-start");
  if (res & LIBWR2RF_GBL_RF2T2_START)
    printf (" rf2t2-start");
  printf("\n");
}

static void
api_vtu_fault (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned res;
  unsigned long id;

  if (argc != 2) {
    printf ("usage: %s VTU-ID\n", argv[0]);
    return;
  }
  id = parse_api_vtu (argv[1]);
  if (id == BADADDR)
    return;

  if (libwr2rf_vtu_fault_read(dev, id, &res) != 0) {
    printf ("failed\n");
    return;
  }
  printf ("status: %04x ", res);
  if (res & TRIGUNIT_REGS_STATUS_MISSIDLE)
    printf (" miss-idle");
  if (res & TRIGUNIT_REGS_STATUS_MISSVALID)
    printf (" miss-valid");
  if (res & TRIGUNIT_REGS_STATUS_MISSREADY)
    printf (" miss-ready");
  printf("\n");
}

static void
api_vtu_flags (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned flags;
  unsigned long id;
  unsigned i;

  if (argc < 2) {
    printf ("usage: %s VTU-ID [force-valid] [start-as-stop]\n", argv[0]);
    return;
  }
  id = parse_api_vtu (argv[1]);
  if (id == BADADDR)
    return;

  flags = 0;
  for (i = 2; i < argc; i++)
    if (!strcmp(argv[i], "force-valid"))
      flags |= LIBWR2RF_VTU_FLAGS_FORCE_VALID_OFFLINE;
    else if (!strcmp(argv[i], "start-as-stop"))
      flags |= LIBWR2RF_VTU_FLAGS_START_AS_STOP;
    else {
      printf ("unknown vtu flag '%s'\n", argv[i]);
      break;
    }

  if (libwr2rf_vtu_set_flags(dev, id, flags) != 0)
    printf ("failed\n");
}

static unsigned
parse_api_diag (const char *arg)
{
  if (arg == NULL) {
    printf ("missing DIAG id\n");
    return BADADDR;
  }

  if (strcmp(arg, "rf-1") == 0)
    return LIBWR2RF_DIAG_ID_RF_1;
  else if (strcmp(arg, "rf-2") == 0)
    return LIBWR2RF_DIAG_ID_RF_2;
  else if (strcmp(arg, "trig-1.1") == 0
           || strcmp (arg, "1.1") == 0)
    return LIBWR2RF_TRIG_ID_RF_1_TRIG_1;
  else if (strcmp(arg, "trig-1.2") == 0
           || strcmp (arg, "1.2") == 0)
    return LIBWR2RF_TRIG_ID_RF_1_TRIG_2;
  else if (strcmp(arg, "trig-2.1") == 0
           || strcmp (arg, "2.1") == 0)
    return LIBWR2RF_TRIG_ID_RF_2_TRIG_1;
  else if (strcmp(arg, "trig-2.2") == 0
           || strcmp (arg, "2.2") == 0)
    return LIBWR2RF_TRIG_ID_RF_2_TRIG_2;
  else {
    printf ("incorrect DIAG id (%s)\n", arg);
    printf ("expect rf-1/rf-2/trig-1.1/trig-1.2/trig-2.1/trig-2.2\n");
    return BADADDR;
  }
}

static void
api_diag_enable (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned id;
  unsigned window;
  unsigned gen;

  if (argc != 3) {
    printf ("usage: %s DIAG-ID WINDOW(us)\n", argv[0]);
    return;
  }

  id = parse_api_diag(argv[1]);
  if (id == BADADDR)
    return;
  window = strtoul(argv[2], NULL, 0);

  if (libwr2rf_enable_diag (dev, id, window, &gen) != 0) {
    printf ("ERROR\n");
    return;
  }
  printf ("generation: %u\n", gen);
}

static void
api_diag_read (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned id;
  unsigned freq;
  unsigned count;
  unsigned gen;

  if (argc != 2) {
    printf ("usage: %s DIAG-ID\n", argv[0]);
    return;
  }

  id = parse_api_diag(argv[1]);
  if (id == BADADDR)
    return;

  if (libwr2rf_read_diag (dev, id, &freq, &count, &gen) != 0) {
    printf ("ERROR\n");
    return;
  }
  printf ("generation: %u\n", gen);
  printf ("window count: %u\n", freq);
  printf ("total count: %u\n", count);
}

static void
api_rfnco_read32 (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned rfnco_idx = 0;
    unsigned addr_off  = 0; // bytes
    unsigned max_off   = 4096; // max address space for rfnco

    if (argc != 3)
        goto usage;

    rfnco_idx = strtoul(argv[1], NULL, 0);
    addr_off  = strtoul(argv[2], NULL, 0);

    if (rfnco_idx != 1 && rfnco_idx != 2)
        goto usage;
    if (addr_off >= max_off)
        goto usage;

    printf ("addr=%03x val=%08x \n", addr_off, libwr2rf_rfnco_read32 (dev, rfnco_idx, addr_off));

    return;

usage:
    printf ("usage: %s rfnco rfnco_addr_in_bytes\n", argv[0]);
}

static void
api_rfnco_write32 (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned rfnco_idx = 0;
    unsigned addr_off  = 0; // bytes
    unsigned max_off   = 4096;
    unsigned val       = 0;

    if (argc != 4)
        goto usage;

    rfnco_idx = strtoul(argv[1], NULL, 0);
    addr_off  = strtoul(argv[2], NULL, 0);
    val       = strtoul(argv[3], NULL, 0);

    if (rfnco_idx != 1 && rfnco_idx != 2)
        goto usage;
    if (addr_off >= max_off)
        goto usage;

    libwr2rf_rfnco_write32 (dev, rfnco_idx, addr_off, val);
    return;

usage:
    printf ("usage: %s rfnco rfnco_addr_in_bytes val\n", argv[0]);
}

static void
api_nco_reset_delay (struct libwr2rf_dev *dev, int argc, char **argv)
{
  int      ch;
  unsigned cdelay;
  unsigned fdelay;
  unsigned odelay;

  if (argc != 5) {
    printf ("usage: %s CH COARSE FINE ODELAY\n", argv[0]);
    return;
  }

  ch = strtoul(argv[1], NULL, 0);
  cdelay = strtoul(argv[2], NULL, 0);
  fdelay = strtoul(argv[3], NULL, 0);
  odelay = strtoul(argv[4], NULL, 0);

  if (libwr2rf_vtu_nco_reset_delay(dev, ch, cdelay, fdelay, odelay) != 0)
    printf ("ERROR\n");
}

static void
api_nco_lcfg (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned           lcfg       = 0;
  unsigned long long ftw_rfnco1 = 0;
  unsigned long long ftw_rfnco2 = 0;

  if (argc != 4 && argc != 2) {
    printf ("usage: %s lcfg_enable [ftw_rfnco1 ftw_rfnco2]\n", argv[0]);
    return;
  }

  lcfg = strtoul(argv[1],  NULL, 0);
  if (argc == 4) {
      ftw_rfnco1  = strtoull(argv[2], NULL, 0);
      ftw_rfnco2  = strtoull(argv[3], NULL, 0);
  }

  printf("lcfg=%d ftw1=%llx ftw2=%llx\n", lcfg, ftw_rfnco1, ftw_rfnco2);
  if (libwr2rf_nco_lcfg(dev, lcfg, ftw_rfnco1, ftw_rfnco2) != 0)
    printf ("ERROR\n");
}


static void
api_nco_reset_ignore (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned val;

  if (argc != 2) {
    printf ("usage: %s 0|1\n", argv[0]);
    return;
  }

  val = strtoul(argv[1], NULL, 0);

  libwr2rf_set_nco_reset_ignore(dev, val);
}

static void
api_dds_ioupdate_delay (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned fdelay;
  unsigned odelay;

  if (argc != 3)
      goto usage;

  fdelay = strtoul(argv[1], NULL, 0);
  odelay = strtoul(argv[2], NULL, 0);

  if (fdelay > 15 || odelay > 31)
      goto usage;

  if (libwr2rf_ioupdate_delay(dev, fdelay, odelay) != 0)
    printf ("ERROR\n");

  return;
usage:
  printf ("usage: %s fdelay odelay\n", argv[0]);
  printf (" fdelay in 0..15;  odelay in 0..31\n");
}

static void
api_rfframerx_ts (struct libwr2rf_dev *dev, int argc, char **argv)
{
    uint64_t tai    = 0;
    uint32_t cycles = 0;

    if (argc != 2)
        goto usage;

    unsigned ts_nco_reset = strtoul(argv[1], NULL, 0);
    if (ts_nco_reset != 0 && ts_nco_reset != 1)
        goto usage;

    if (libwr2rf_rfframerx_ts(dev, ts_nco_reset, &tai, &cycles))
        printf ("ERROR\n");


    printf("Timestamp %s: 0x%012lx 0x%08x\n", ts_nco_reset == 1 ? "nco_reset rfframe" : "last frame", tai, cycles);

    return;

usage:
    printf ("Usage: %s [0|1], where 1 indicates the rx'd frame must contain nco_reset\n", argv[0]);
}

static void
api_tmgclk (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned lemo;
    unsigned oe;
    unsigned term;

    if (argc != 4) {
        printf ("usage: %s LEMO OE TERM\n", argv[0]);
        return;
    }

    lemo = strtoul(argv[1], NULL, 0);
    oe   = strtoul(argv[2], NULL, 0);
    term = strtoul(argv[3], NULL, 0);

    if (libwr2rf_configure_tmgclk(dev, lemo, oe, term))
        printf ("ERROR\n");
}

static void
api_tmgio (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned lemo;
    unsigned oe;
    unsigned term;

    if (argc != 4) {
        printf ("usage: %s LEMO OE TERM\n", argv[0]);
        return;
    }

    lemo = strtoul(argv[1], NULL, 0);
    oe   = strtoul(argv[2], NULL, 0);
    term = strtoul(argv[3], NULL, 0);

    if (libwr2rf_configure_tmgio(dev, lemo, oe, term))
        printf ("ERROR\n");
}

static void
api_wr_leapsec (struct libwr2rf_dev *dev, int argc, char **argv)
{
    unsigned leapsec;
    if (libwr2rf_wr_leapsec(dev, &leapsec))
        printf ("ERROR\n");

    printf("WR leapsec val=%d\n", leapsec);
}

static void
api_vtu_timestamps (struct libwr2rf_dev *dev, int argc, char **argv)
{
    int           res;
    unsigned long id;
    uint64_t      tai_first;
    uint32_t      cycles_first;
    uint64_t      tai_last;
    uint32_t      cycles_last;

    if (argc != 2) {
        printf ("usage: %s VTU-ID\n", argv[0]);
        return;
    }

    id = parse_api_vtu (argv[1]);
    if (id == BADADDR)
        return;

    res = libwr2rf_vtu_timestamps(dev, id, &tai_first, &cycles_first, &tai_last, &cycles_last);
    if (res != 0)
        printf ("%s: failed (status=%d)\n", __FUNCTION__, res);

    printf ("%s:\n", __FUNCTION__);
    printf("FIRST TAI: %010lu, cycles: %08u \n", tai_first, cycles_first);
    printf("LAST  TAI: %010lu, cycles: %08u \n", tai_last,  cycles_last);
}

static void
calib_dds_ioupdate (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned addr = WR2RF_VME_REGS_CTRL + WR2RF_CTRL_REGS_NCO_RESET_CTRL;
  unsigned v    = libwr2rf_read16(dev, addr);

  printf ("configuring rf1 out to dds\n");
  libwr2rf_set_rf_out(dev, 1, LIBWR2RF_RF_OUT_DDS);

  v |= WR2RF_CTRL_REGS_NCO_RESET_CTRL_SOFT;

  printf ("generating ioupdate signals every 200ms\n");
  while (1) {
    libwr2rf_write16(dev, addr, v);
    usleep (200000);
  }
}

static void
calib_vtu (struct libwr2rf_dev *dev, int argc, char **argv)
{
  unsigned ch;

  if (argc != 2) {
    printf ("usage: %s [1|2]\n", argv[0]);
    return;
  }

  ch = strtoul(argv[1], NULL, 0);
  ch = (ch - 1) * 2;

  printf ("configuring rf1 out to mixer\n");
  libwr2rf_set_rf_out(dev, 1, LIBWR2RF_RF_OUT_MIXER);

  printf ("setting NCO source to local\n");
  {
    unsigned ctrl = libwr2rf_read16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_LOC_OR_WRS);
    ctrl |= WR2RF_INIT_REGS_NCO_LOC_OR_WRS_PARAMS_SEL;
    libwr2rf_write16(dev, WR2RF_VME_REGS_INIT + WR2RF_INIT_REGS_NCO_LOC_OR_WRS, ctrl);
  }

  printf ("clear VTU FF reset\n");
  libwr2rf_vtu_output_reset (dev, ch + 0, 0);
  libwr2rf_vtu_output_reset (dev, ch + 1, 0);

  while (1) {
    libwr2rf_vtu_softstop (dev, ch + 0);

    if (libwr2rf_vtu_program_infinite (dev, ch + 1, 16, 4620) != 0)
      printf ("ERROR program infinite vtu %u\n", ch + 1);
    libwr2rf_vtu_softstart (dev, ch + 1);

    if (libwr2rf_vtu_program_infinite (dev, ch + 0, 16, 4620) != 0)
      printf ("ERROR program infinite vtu %u\n", ch + 0);
    libwr2rf_vtu_softstart (dev, ch + 0);

    set_sw_rxframe(dev, 0x16B24A8FB6, 7);

    usleep(500000);
    libwr2rf_vtu_softstop (dev, ch + 1);
  }
}

static void cmd_help (struct libwr2rf_dev *dev, int argc, char **argv);

struct cmds {
  const char *name;
  void (*cmd)(struct libwr2rf_dev *dev, int argc, char **argv);
  const char *help;
};

static struct cmds cmds[] =
  {
   { "help", cmd_help, "show help" },
   { "hwver", hwver, "print hw version" },
   { "hwinfo", hwinfo, "print hw info" },
   { "nco-ipinfo", nco_ipinfo, "print NCO ip info" },
   { "reconfigure", reconfigure, "reset the fpga" },
   { "api-init", api_init, "init the board"},
   { "api-dds-ioupdate-delay", api_dds_ioupdate_delay, "Sets the delay from the FPGA for the dds ioupdate signal" },
   { "api-rfframerx-ts", api_rfframerx_ts, "Displays the timestamp for received RF frames" },
   { "api-vtu-start-lemo", api_vtu_start_lemo, "select start lemo for a vtu"},
   { "api-vtu-stop-lemo", api_vtu_stop_lemo, "select stop lemo for a vtu"},
   { "api-vtu-output-ff-delay", api_vtu_output_ff_delay, "set vtu output delay chip value"},
   { "api-vtu-output-pad-delay", api_vtu_output_pad_delay, "set vtu output delay from FPGA to ff"},
   { "api-vtu-output-enable", api_vtu_output_enable, "enable vtu output delay"},
   { "api-vtu-output-shaper", api_vtu_output_shaper, "enable vtu output shaper"},
   { "api-vtu-output-reset", api_vtu_output_reset, "force reset vtu output"},
   { "api-vtu-softstart", api_vtu_softstart, "software start of a vtu"},
   { "api-vtu-softstop", api_vtu_softstop, "software stop of a vtu"},
   { "api-vtu-reset", api_vtu_reset, "control vtu reset"},
   { "api-vtu-infinite", api_vtu_infinite, "program vtu in infinite mode"},
   { "api-vtu-highfreq", api_vtu_highfreq, "program vtu in highfreq mode"},
   { "api-vtu-window", api_vtu_window, "program vtu in window mode"},
   { "api-vtu-invalidate", api_vtu_invalidate, "invalidate vtu program"},
   { "api-vtu-sync-delays", api_vtu_sync_delays, "Retrieve the reference or current delays that are applied to nco_reset for a VTU sync." },
   { "api-vtu-status", api_vtu_status, "get vtu global status"},
   { "api-vtu-fault", api_vtu_fault, "get vtu global fault"},
   { "api-vtu-flags", api_vtu_flags, "set vtu flags (force-valid)" },
   { "api-lemo-dbg-sel", api_lemo_dbg_sel, "Select the debug for the front panel lemos"},
   { "api-softstart-sel", api_softstart_sel, "configure which vtu soft start can be output to lemo"},
   { "api-softstop-sel", api_softstop_sel, "configure which vtu soft stop can be output to lemo"},
   { "api-diag-enable", api_diag_enable, "configure diag unit"},
   { "api-diag-read", api_diag_read, "read diag unit"},
   { "api-rfnco-read32", api_rfnco_read32, "Perform a 32b read access via the RFNCO's AXI i/f"},
   { "api-rfnco-write32", api_rfnco_write32, "Perform a 32b write access via the RFNCO's AXI i/f"},
   { "api-nco-reset-delay", api_nco_reset_delay, "set nco reset delay"},
   { "api-nco-lcfg", api_nco_lcfg, "configures the RFNCO to output a local fixed frequency." },
   { "api-tmgio", api_tmgio, "set lemo io output enable and termination"},
   { "api-tmgclk", api_tmgclk, "set lemo clk output enable and termination"},
   { "api-wr-leapsec", api_wr_leapsec, "retrieve the current leapsec information"},
   { "api-vtu-timestamps", api_vtu_timestamps, "Return timestamps of the first and last vtu trigger output pulses after the VTU has been started"},
   { "api-nco-reset-ignore", api_nco_reset_ignore, "ignore (or not) nco reset"},
   { "ftw-show", ftw_show, "show received ftw" },
   { "ftw-last", framerxtx_last, "display last ftw" },
   { "ftw-framerxtx", framerxtx_framerxtx, "display framerxtx registers" },
   { "ftw-send1", framerxtx_send1, "send one frame" },
   { "ftw-send", framerxtx_send, "send counter frames" },
   { "ftw-simul", framerxtx_simul, "send a ramp" },
   { "ftw-ramp", framerxtx_ramp, "send a ramp" },
   { "nco-source", nco_source, "select nco source" },
   { "nco-reset", nco_reset, "force a nco reset from software" },
   { "nco-reset-delay", nco_reset_delay, "Delay the application of nco reset into the T1 trigger units until stable RF is present. 1->128 cycles at 16ns each" },
   { "rf-reset-offset", rf_reset_offset, "Set the rf reset offset delay wrt nco_reset_cdelayed" },
   { "nco-reset-ctrl", nco_reset_ctrl, "Provides masks to prevent nco_reset being distributed to DDS and RFNCOs"},
   { "nco-wrctrl", nco_wrctrl, "write nco ctrl register" },
   { "nco-lcfg", nco_lcfg, "directly configures the rfnco for local (debug) register access" },
   { "nco-lcfg-update", nco_lcfg_update, "mimics an RFframe off the network in local registers" },
   { "nco-wrcfg", nco_wrcfg, "configure the nco for network (operational) operation" },
   { "nco-status", nco_status, "Display some registers from the RFNCO" },
   { "nco-cablecomp", nco_cablecomp, "Sets the cable delay compensation value for the RFNCO"},
   { "pllsel", svmup_pll_sel, "toggle pll_sel"},
   { "read_init_reg", read_init_reg, "write init ctrl register"},
   { "write_init_reg", write_init_reg, "write init ctrl register"},
   { "wr-time", wrc_time, "display wrc time & status" },
   { "wr-cycles", wrc_cycles, "display wrc cycles (continuously)" },
   { "wr-wait", wrc_wait, "wait until wrc is sync-ed"},
   { "fw-dump", fw_dump, "dump WRC firmware"},
   { "fw-load", fw_load, "load WRC firmware"},
   { "vuart", vuart, "connect to WRC vuart"},
   { "wr-mac", wrc_mac, "print mac address"},
   { "ocxo", ocxo, "display ocxo status"},
   { "xadc-dump", xadc_dump, "dump xadc registers" },
   { "xadc-reg", xadc_reg, "read/write an xadc register" },
   { "mmcm-status", mmcm_status, "display fpga mmcm status" },
   { "pll-status", pll_status, "display ltc6950 pll status" },
   { "pll-dump", pll_dump, "dump ltc6950 pll registers"},
   { "pll-init", pll_init, "start ltc6950 pll, switch to pll clock"},
   { "dds-reset", dds_reset, "control ad9910 dds reset"},
   { "dds-init", dds_init, "init ad9910 dds"},
   { "dds-dump", dds_dump, "dump ad9910 dds registers"},
   { "dds-sync-cal", dds_sync_cal, "performs a timing calibration for the ad9910 sync_in from the fpga" },
   { "dds-sync-set", dds_sync_set, "writes the ad9910's sync_receiver_delay and sync_validation_delay values" },
   { "dds-sync-status", dds_sync_status, "Reads the dds sync error status" },
   { "dds-ioupdate", dds_ioupdate, "Resets the output phase of the RF (if already preprogrammed)" },
   { "dds-freq", dds_freq, "control the DDS output frequency - useful for standalone DDS rf signal output"},
   { "dac-xdds", dac_xdds, "control the xilinx DDS for testing the IQdac"},
   { "dac-iqsetpoint", dac_iqsetpoint, "configures the amplitude and phase for the IQdacs"},
   { "dac-iq-phase", dac_iq_phase, "change phase and amplitude of IQdacs"},
   { "dac-iqctrl", dac_iqctrl, "Selects the IQdata lines between RFNCO+IQMod or debug DDS"},
   { "dac-tman", dac_tman, "Forces the dac timing"},
   { "dac-power", dac_power, "Set dac power-down register"},
   { "dac-fsc", dac_fsc, "Changes the Full scale current for the I and/or Q DAC"},
   { "dac-reset", dac_reset, "reset ad9783 iq-dac"},
   { "dac-init", dac_init, "init ad9783 iq-dac"},
   { "dac-validate", dac_validate, "After initialising the ad9783 iq-dac, validate the timing parameters"},
   { "dac-param", dac_param, "set parameters of ad9783 iq-dac"},
   { "dac-timing", dac_timing, "adjust data timing of ad9783 iq-dac"},
   { "dac-timing-set", dac_timing_set, "override the data timing of ad9783 iq-dac"},
   { "dac-dump", dac_dump, "dump ad9783 iq-dac registers"},
   { "dac-ram", dac_ram, "load iq-dac ram pattern"},
   { "iodelay-init", iodelay_init, "Applies  reset sequence to the IDELAY_CONTROLLER for initialising iodelays e.g. trigger unit output signals"},
   { "set-rfout", set_rfout, "set muxers for rfout"},
   { "wrs-dump", wrs_dump, "Dumps the WR streamers configuration registers"},
   { "wrs-fixedlat", wrs_fixedlat, "Sets WR streamers fixed latency"},
   { "wrs-tolat", wrs_tolat, "Sets WR streamers timeout latency"},
   { "wrs-reset-rxstats", wrs_reset_rxstats, "Reset the RX statistics registers"},
   { "tmgio-term", tmgio_term, "set the lemo input termination for general io or timing"},
   { "tmgio-oe", tmgio_oe, "set the lemo output enable for general io or timing"},
   { "tmgclk-term", tmgclk_term, "set the lemo input termination for general io or timing"},
   { "tmgclk-oe", tmgclk_oe, "set the lemo output enable for general io or timing"},
   { "lemo-dbg-sel", lemo_dbg_sel, "configure debug to lemo output connector"},
   { "lemo-start-sel", lemo_start_sel, "configure lemo input to trigger unit start"},
   { "lemo-stop-sel", lemo_stop_sel, "configure lemo input to trigger unit stop"},
   { "lemo-gpio", lemo_gpio, "configure lemo gpio output signals"},
   { "lemo-ext-sel", lemo_ext_sel, "configure lemo extended pulse mode on output"},
   { "lemo-show", lemo_show, "disp lemo configuration" },
   { "ext-ref", ext_ref, "set or read ext_ref (pps+10Mhz) direction"},
   { "rf-diag", rf_diag, "rf diagnostics"},
   { "vtu", vtu, "trigger unit"},
   { "vtu-watch", vtu_watch, "watch (disp changes) of trigger unit"},
   { "vtu-out", vtu_out, "trigger unit outputs (delay, shapper, reset)"},
   { "vtu-odelay", vtu_odelay, "Can be used to set the fpga VTU odelay cells. Used for validation only of trig odelay, not runtime."},
   { "calib-dds-ioupdate", calib_dds_ioupdate, "generate local nco reset to calibate the dds ioupdate delay"},
   { "calib-vtu", calib_vtu, "generate local nco reset to calibate a vtu"},
   { NULL, NULL, NULL }
  };

static void
cmd_help (struct libwr2rf_dev *dev, int argc, char **argv)
{
  const struct cmds *cmd;

  for (cmd = cmds; cmd->name; cmd++)
    printf ("%-10s  %s\n", cmd->name, cmd->help);
}

int
main (int argc, char **argv)
{
  int c;
  unsigned slot = 0;
  int i;
  struct libwr2rf_dev *dev;
  int verbose;

  verbose = 0;
  while ((c = getopt (argc, argv, "s:hv")) != -1)
    switch (c)
      {
      case 's':
        slot = strtol (optarg, NULL, 0);
        break;
      case 'h':
        printf ("usage: %s [-h][-v] -s SLOT ACTION\n", argv[0]);
        return 1;
      case 'v':
        verbose = 1;
        break;
      case '?':
        fprintf (stderr, "incorrect option, try %s -h\n", argv[0]);
        return 2;
      }

  if (optind >= argc)
    {
      printf ("no action (try: %s help)\n", argv[0]);
      return 1;
    }

  if (argc - optind == 1 && strcmp (argv[optind], "help") == 0) {
          cmd_help (NULL, argc - optind, argv + optind);
          return 0;
  }

  dev = libwr2rf_host_init_by_slot_verbose (slot, verbose);

  if (dev == NULL)
    return 1;

  for (i = 0; cmds[i].name; i++)
          if (strcmp (argv[optind], cmds[i].name) == 0) {
                  cmds[i].cmd(dev, argc - optind, argv + optind);
                  return 0;
          }
  printf ("unknown action %s, try help\n", argv[optind]);
  return 1;
}
