diff options
Diffstat (limited to 'host')
107 files changed, 4965 insertions, 2287 deletions
diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index 3558d407f..c13b4517e 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -166,6 +166,22 @@ IF(MSVC) ADD_DEFINITIONS(/MP) #multi-threaded build ENDIF(MSVC) +IF(MINGW) + #Avoid depending on MinGW runtime DLLs + CHECK_CXX_COMPILER_FLAG(-static-libgcc HAVE_STATIC_LIBGCC_FLAG) + IF(HAVE_STATIC_LIBGCC_FLAG) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc") + SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -static-libgcc") + ENDIF() + CHECK_CXX_COMPILER_FLAG(-static-libstdc++ HAVE_STATIC_LIBSTDCXX_FLAG) + IF(HAVE_STATIC_LIBSTDCXX_FLAG) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++") + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libstdc++") + SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -static-libstdc++") + ENDIF() +ENDIF() + IF(CYGWIN) ADD_DEFINITIONS(-D__USE_W32_SOCKETS) #boost asio says we need this ENDIF(CYGWIN) @@ -268,8 +284,8 @@ UHD_INSTALL(FILES #{{{IMG_SECTION # This section is written automatically by /images/create_imgs_package.py # Any manual changes in here will be overwritten. -SET(UHD_IMAGES_MD5SUM "ffe3f95d9b4e4b8bdbd5483e32e4407c") -SET(UHD_IMAGES_DOWNLOAD_SRC "uhd-images_003.008.004-release.zip") +SET(UHD_IMAGES_MD5SUM "492f47ed0705b6b63238307f6e20e2a3") +SET(UHD_IMAGES_DOWNLOAD_SRC "uhd-images_003.009.git-184-gf2337d6f.zip") #}}} ######################################################################## @@ -420,5 +436,19 @@ ELSEIF(UHDHOST_PKG) SET(PRINT_APPEND " (Debian uhd-host package configuration)") ENDIF(LIBUHD_PKG) UHD_PRINT_COMPONENT_SUMMARY() +IF(UHD_VERSION_DEVEL) + MESSAGE(STATUS "******************************************************") + IF(UHD_VERSION_PATCH STREQUAL "git") + MESSAGE(STATUS "* You are building the UHD development master branch.") + MESSAGE(STATUS "* For production code, we recommend our stable,") + MESSAGE(STATUS "* releases or using the release branch (maint).") + ELSE() + MESSAGE(STATUS "* You are building a development branch of UHD.") + MESSAGE(STATUS "* These branches are designed to provide early access") + MESSAGE(STATUS "* to UHD and USRP features, but should be considered") + MESSAGE(STATUS "* unstable and/or experimental!") + ENDIF(UHD_VERSION_PATCH STREQUAL "git") + MESSAGE(STATUS "******************************************************") +ENDIF(UHD_VERSION_DEVEL) MESSAGE(STATUS "Building version: ${UHD_VERSION}${PRINT_APPEND}") MESSAGE(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") diff --git a/host/apps/omap_debug/.gitignore b/host/apps/omap_debug/.gitignore deleted file mode 100644 index 008a23138..000000000 --- a/host/apps/omap_debug/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -.gitignore -clkgen-config -fpga-downloader -usrp-e-button -usrp-e-crc-rw -usrp-e-ctl -usrp-e-debug-pins -usrp-e-fpga-rw -usrp-e-gpio -usrp-e-i2c -usrp-e-lb-test -usrp-e-led -usrp-e-loopback -usrp-e-random-loopback -usrp-e-rw -usrp-e-spi -usrp-e-timed -usrp-e-uart -usrp-e-uart-rx -usrp-e-mm-loopback diff --git a/host/apps/omap_debug/Makefile b/host/apps/omap_debug/Makefile deleted file mode 100644 index f8b9f2bd9..000000000 --- a/host/apps/omap_debug/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -CFLAGS=-Wall -I../../lib/usrp/usrp_e/ -march=armv7-a -mtune=cortex-a8 -mfpu=neon -O3 -CXXFLAGS=-Wall -I../../lib/usrp/usrp_e/ -march=armv7-a -mtune=cortex-a8 -mfpu=neon -O3 - -all : usrp-e-spi usrp-e-i2c usrp-e-uart usrp-e-led usrp-e-ctl usrp-e-button usrp-e-uart-rx usrp-e-gpio usrp-e-debug-pins - -usrp-e-spi : usrp-e-spi.c - -usrp-e-i2c : usrp-e-i2c.c - -usrp-e-uart : usrp-e-uart.c - -usrp-e-uart-rx : usrp-e-uart-rx.c - -usrp-e-led : usrp-e-led.c - -usrp-e-ctl : usrp-e-ctl.c - -usrp-e-button : usrp-e-button.c - -usrp-e-gpio : usrp-e-gpio.c - -usrp-e-debug-pins : usrp-e-debug-pins.c -clean : - rm -f usrp-e-spi - rm -f usrp-e-i2c - rm -f usrp-e-uart - rm -f usrp-e-uart-rx - rm -f usrp-e-led - rm -f usrp-e-ctl - rm -f usrp-e-button - rm -f usrp-e-gpio - rm -f usrp-e-debug-pins - rm -f usrp-e-lb-test diff --git a/host/apps/omap_debug/set_debug_pins.py b/host/apps/omap_debug/set_debug_pins.py deleted file mode 100755 index 0f9ecd7b9..000000000 --- a/host/apps/omap_debug/set_debug_pins.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/python - -import os - -# Memory Map -misc_base = 0 -uart_base = 1 -spi_base = 2 -i2c_base = 3 -gpio_base = 4 * 128 -settings_base = 5 - -# GPIO offset -gpio_pins = 0 -gpio_ddr = 4 -gpio_ctrl_lo = 8 -gpio_ctrl_hi = 12 - -def set_reg(reg, val): - os.system("./usrp1-e-ctl w %d 1 %d" % (reg,val)) - -def get_reg(reg): - fin,fout = os.popen4("./usrp1-e-ctl r %d 1" % (reg,)) - print fout.read() - -# Set DDRs to output -set_reg(gpio_base+gpio_ddr, 0xFFFF) -set_reg(gpio_base+gpio_ddr+2, 0xFFFF) - -# Set CTRL to Debug #0 ( A is for debug 0, F is for debug 1 ) -set_reg(gpio_base+gpio_ctrl_lo, 0xAAAA) -set_reg(gpio_base+gpio_ctrl_lo+2, 0xAAAA) -set_reg(gpio_base+gpio_ctrl_hi, 0xAAAA) -set_reg(gpio_base+gpio_ctrl_hi+2, 0xAAAA) - diff --git a/host/apps/omap_debug/test.c b/host/apps/omap_debug/test.c deleted file mode 100644 index 36f4d700a..000000000 --- a/host/apps/omap_debug/test.c +++ /dev/null @@ -1,34 +0,0 @@ -#include <stdio.h> - -void -main() -{ - int x; - char *y; - long long z; - - x = 0x01020304; - z = 0x0102030405060708LL; - - printf("%x\n",x); - y = (char *)&x; - printf("%x\n",y[0]); - printf("%x\n",y[1]); - printf("%x\n",y[2]); - printf("%x\n",y[3]); - - printf("Printing z ...\n"); - printf("%llx\n",z); - printf("Printing z done\n"); - - y = (char *)&z; - printf("%x\n",y[0]); - printf("%x\n",y[1]); - printf("%x\n",y[2]); - printf("%x\n",y[3]); - printf("%x\n",y[4]); - printf("%x\n",y[5]); - printf("%x\n",y[6]); - printf("%x\n",y[7]); -} - diff --git a/host/apps/omap_debug/u1e-read-stream.c b/host/apps/omap_debug/u1e-read-stream.c deleted file mode 100644 index 4e4c21d9e..000000000 --- a/host/apps/omap_debug/u1e-read-stream.c +++ /dev/null @@ -1,21 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <fcntl.h> - -int main(int rgc, char *argv[]) -{ - int fp, cnt, n; - short buf[1024]; - - n = 0; - - fp = open("/dev/usrp1_e0", O_RDONLY); - printf("fp = %d\n", fp); - - do { - cnt = read(fp, buf, 2048); - n++; -// printf("Bytes read - %d\n", cnt); - } while(n < 10*512); - printf("Data - %hX\n", buf[0]); -} diff --git a/host/apps/omap_debug/usrp-e-button.c b/host/apps/omap_debug/usrp-e-button.c deleted file mode 100644 index f13291491..000000000 --- a/host/apps/omap_debug/usrp-e-button.c +++ /dev/null @@ -1,56 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> -#include <unistd.h> - -#include "usrp_e.h" -#include "usrp_e_regs.hpp" - -// Usage: usrp_e_uart <string> - -#define PB1 (1<<8) -#define PB2 (1<<9) -#define PB3 (1<<10) -#define P1 (0) -#define P2 (0xFF) -#define P3 (0xAA) -#define P4 (0x55) - -int main(int argc, char *argv[]) -{ - int fp, ret; - struct usrp_e_ctl16 d; - int pb1=0, pb2=0, pb3=0, p1=0, p2=0, p3=0, p4=0; - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - d.offset = UE_REG_MISC_SW; - d.count = 1; - - do { - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - if (d.buf[0] & PB1) { - pb1 = 1; - printf("Pushbutton 1 hit\n"); - } - - if (d.buf[0] & PB2) { - pb2 = 1; - printf("Pushbutton 2 hit\n"); - } - - if (d.buf[0] & PB3) { - pb3 = 1; - printf("Pushbutton 3 hit\n"); - } - - sleep(1); - - } while (!(pb1 && pb2 && pb3)); - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-ctl.c b/host/apps/omap_debug/usrp-e-ctl.c deleted file mode 100644 index 69c48ee6f..000000000 --- a/host/apps/omap_debug/usrp-e-ctl.c +++ /dev/null @@ -1,48 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" - -// Usage: usrp_e_ctl w|r offset number_of_values val1 val2 .... - -int main(int argc, char *argv[]) -{ - int fp, i, cnt, ret; - struct usrp_e_ctl16 ctl_data; - - if (argc < 4) { - printf("Usage: usrp_e_ctl w|r offset number_of_values val1 val2 ....\n"); - exit(-1); - } - - cnt = atoi(argv[3]); - - ctl_data.offset = atoi(argv[2]); - ctl_data.count = cnt; - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - if (*argv[1] == 'w') { - for (i=0; i<cnt; i++) - ctl_data.buf[i] = atoi(argv[4+i]); - - ret = ioctl(fp, USRP_E_WRITE_CTL16, &ctl_data); - printf("Return value from write ioctl = %d\n", ret); - } - - if (*argv[1] == 'r') { - ret = ioctl(fp, USRP_E_READ_CTL16, &ctl_data); - printf("Return value from write ioctl = %d\n", ret); - - for (i=0; i<ctl_data.count; i++) { - if (!(i%8)) - printf("\nData at %4d :", i); - printf(" %5d", ctl_data.buf[i]); - } - printf("\n"); - } -} diff --git a/host/apps/omap_debug/usrp-e-debug-pins.c b/host/apps/omap_debug/usrp-e-debug-pins.c deleted file mode 100644 index d18bbf990..000000000 --- a/host/apps/omap_debug/usrp-e-debug-pins.c +++ /dev/null @@ -1,77 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" -#include "usrp_e_regs.hpp" - -// Usage: usrp_e_gpio <string> - -static int fp; - -static int read_reg(__u16 reg) -{ - int ret; - struct usrp_e_ctl16 d; - - d.offset = reg; - d.count = 1; - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - return d.buf[0]; -} - -static void write_reg(__u16 reg, __u16 val) -{ - int ret; - struct usrp_e_ctl16 d; - - d.offset = reg; - d.count = 1; - d.buf[0] = val; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); -} - -int main(int argc, char *argv[]) -{ - int test; - - test = 0; - if (argc < 2) { - printf("%s 0|1|off\n", argv[0]); - } - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - if (fp < 0) { - perror("Open failed"); - return -1; - } - - if (strcmp(argv[1], "0") == 0) { - printf("Selected 0 based on %s\n", argv[1]); - write_reg(UE_REG_GPIO_TX_DDR, 0xFFFF); - write_reg(UE_REG_GPIO_RX_DDR, 0xFFFF); - write_reg(UE_REG_GPIO_TX_SEL, 0x0); - write_reg(UE_REG_GPIO_RX_SEL, 0x0); - write_reg(UE_REG_GPIO_TX_DBG, 0xFFFF); - write_reg(UE_REG_GPIO_RX_DBG, 0xFFFF); - } else if (strcmp(argv[1], "1") == 0) { - printf("Selected 1 based on %s\n", argv[1]); - write_reg(UE_REG_GPIO_TX_DDR, 0xFFFF); - write_reg(UE_REG_GPIO_RX_DDR, 0xFFFF); - write_reg(UE_REG_GPIO_TX_SEL, 0xFFFF); - write_reg(UE_REG_GPIO_RX_SEL, 0xFFFF); - write_reg(UE_REG_GPIO_TX_DBG, 0xFFFF); - write_reg(UE_REG_GPIO_RX_DBG, 0xFFFF); - } else { - printf("Selected off based on %s\n", argv[1]); - write_reg(UE_REG_GPIO_TX_DDR, 0x0); - write_reg(UE_REG_GPIO_RX_DDR, 0x0); - } - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-i2c.c b/host/apps/omap_debug/usrp-e-i2c.c deleted file mode 100644 index da8709ae1..000000000 --- a/host/apps/omap_debug/usrp-e-i2c.c +++ /dev/null @@ -1,87 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/types.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" - -// Usage: usrp_e_i2c w address data0 data1 data 2 .... -// Usage: usrp_e_i2c r address count - -int main(int argc, char *argv[]) -{ - int fp, ret, i, tmp; - struct usrp_e_i2c *i2c_msg; - int direction, address, count; - - if (argc < 3) { - printf("Usage: usrp-e-i2c w address data0 data1 data2 ...\n"); - printf("Usage: usrp-e-i2c r address count\n"); - printf("All addresses and data in hex.\n"); - exit(-1); - } - - if (strcmp(argv[1], "r") == 0) { - direction = 0; - } else if (strcmp(argv[1], "w") == 0) { - direction = 1; - } else { - return -1; - } - - sscanf(argv[2], "%X", &address); - printf("Address = %X\n", address); - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - if (fp < 0) { - perror("Open failed"); - return -1; - } - -// sleep(1); - - if (direction) { - count = argc - 3; - } else { - sscanf(argv[3], "%X", &count); - } - printf("Count = %X\n", count); - - i2c_msg = malloc(sizeof(i2c_msg) + count * sizeof(char)); - - i2c_msg->addr = address; - i2c_msg->len = count; - - for (i = 0; i < count; i++) { - i2c_msg->data[i] = i; - } - - if (direction) { - // Write - - for (i=0; i<count; i++) { - sscanf(argv[3+i], "%X", &tmp); - i2c_msg->data[i] = tmp; - } - - ret = ioctl(fp, USRP_E_I2C_WRITE, i2c_msg); - printf("Return value from i2c_write ioctl: %d\n", ret); - } else { - // Read - - ret = ioctl(fp, USRP_E_I2C_READ, i2c_msg); - printf("Return value from i2c_read ioctl: %d\n", ret); - - printf("Ioctl: %d Data read :", ret); - for (i=0; i<count; i++) { - printf(" %X", i2c_msg->data[i]); - } - printf("\n"); - - } - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-lb-test.c b/host/apps/omap_debug/usrp-e-lb-test.c deleted file mode 100644 index 68848064e..000000000 --- a/host/apps/omap_debug/usrp-e-lb-test.c +++ /dev/null @@ -1,58 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <fcntl.h> -#include <pthread.h> -#include <stdlib.h> -#include <unistd.h> -#include <stddef.h> -#include "usrp_e.h" - -// max length #define PKT_DATA_LENGTH 1016 - -int main(int argc, char *argv[]) -{ - struct usrp_transfer_frame *tx_data, *rx_data; - int i, fp, packet_data_length, cnt; - struct usrp_e_ctl16 d; - - if (argc < 2) { - printf("%s data_size (in bytes < 2040)\n", argv[0]); - return -1; - } - - packet_data_length = atoi(argv[1]); - - fp = open("/dev/usrp_e0", O_RDWR); - - d.offset = 14; - d.count = 1; - d.buf[0] = (1 << 13); - ioctl(fp, USRP_E_WRITE_CTL16, &d); - - tx_data = malloc(2048); - rx_data = malloc(2048); - - tx_data->status = 0; - tx_data->len = sizeof(struct usrp_transfer_frame) + packet_data_length; - - while (1) { - - for (i = 0; i < packet_data_length; i++) { - tx_data->buf[i] = random() >> 24; - - } - - cnt = write(fp, tx_data, 2048); - cnt = read(fp, rx_data, 2048); - - if (tx_data->len != rx_data->len) - printf("Bad frame length sent %d, read %d\n", tx_data->len, rx_data->len); - - for (i = 0; i < packet_data_length; i++) { - if (tx_data->buf[i] != rx_data->buf[i]) - printf("Bad data at %d, sent %d, received %d\n", i, tx_data->buf[i], rx_data->buf[i]); - } - printf("---------------------------------------------------\n"); - sleep(1); - } -} diff --git a/host/apps/omap_debug/usrp-e-led.c b/host/apps/omap_debug/usrp-e-led.c deleted file mode 100644 index d1b6c8996..000000000 --- a/host/apps/omap_debug/usrp-e-led.c +++ /dev/null @@ -1,35 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> -#include <unistd.h> - -#include "usrp_e.h" -#include "usrp_e_regs.hpp" - -// Usage: usrp_e_uart <string> - - -int main(int argc, char *argv[]) -{ - int fp, i, ret; - struct usrp_e_ctl16 d; - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - d.offset = UE_REG_MISC_BASE; - d.count = 1; - - while (1) { - for (i=0; i<8; i++) { - d.buf[0] = i; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); - sleep(1); - } - } - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-ram.c b/host/apps/omap_debug/usrp-e-ram.c deleted file mode 100644 index d548f7ccd..000000000 --- a/host/apps/omap_debug/usrp-e-ram.c +++ /dev/null @@ -1,25 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <fcntl.h> - -int main(int rgc, char *argv[]) -{ - int fp, i, cnt; - unsigned short buf[1024]; - unsigned short buf_rb[1024]; - - fp = open("/dev/usrp1_e0", O_RDWR); - printf("fp = %d\n", fp); - - for (i=0; i<1024; i++) - buf[i] = i*256; - write(fp, buf, 2048); - read(fp, buf_rb, 2048); - - printf("Read back %hX %hX\n", buf_rb[0], buf_rb[1]); - - for (i=0; i<1024; i++) { - if (buf[i] != buf_rb[i]) - printf("Read - %hX, expected - %hX\n", buf_rb[i], buf[i]); - } -} diff --git a/host/apps/omap_debug/usrp-e-read.c b/host/apps/omap_debug/usrp-e-read.c deleted file mode 100644 index c28f018d5..000000000 --- a/host/apps/omap_debug/usrp-e-read.c +++ /dev/null @@ -1,18 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <fcntl.h> - -int main(int rgc, char *argv[]) -{ - int fp, cnt; - short buf[1024]; - - fp = open("/dev/usrp1_e0", O_RDONLY); - printf("fp = %d\n", fp); - - do { - cnt = read(fp, buf, 2048); -// printf("Bytes read - %d\n", cnt); - } while(1); - printf("Data - %hX\n", buf[0]); -} diff --git a/host/apps/omap_debug/usrp-e-spi.c b/host/apps/omap_debug/usrp-e-spi.c deleted file mode 100644 index c353c409b..000000000 --- a/host/apps/omap_debug/usrp-e-spi.c +++ /dev/null @@ -1,54 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" - -// Usage: usrp_e_spi w|rb slave data - -int main(int argc, char *argv[]) -{ - int fp, slave, length, ret; - unsigned int data; - struct usrp_e_spi spi_dat; - - if (argc < 5) { - printf("Usage: usrp_e_spi w|rb slave transfer_length data\n"); - exit(-1); - } - - slave = atoi(argv[2]); - length = atoi(argv[3]); - data = atoll(argv[4]); - - printf("Data = %X\n", data); - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - if (fp < 0) { - perror("Open failed"); - return -1; - } - -// sleep(1); - - - spi_dat.slave = slave; - spi_dat.data = data; - spi_dat.length = length; - spi_dat.flags = UE_SPI_PUSH_FALL | UE_SPI_LATCH_RISE; - - if (*argv[1] == 'r') { - spi_dat.readback = 1; - ret = ioctl(fp, USRP_E_SPI, &spi_dat); - printf("Ioctl returns: %d, Data returned = %d\n", ret, spi_dat.data); - } else { - spi_dat.readback = 0; - ioctl(fp, USRP_E_SPI, &spi_dat); - } - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-uart-rx.c b/host/apps/omap_debug/usrp-e-uart-rx.c deleted file mode 100644 index 24b417980..000000000 --- a/host/apps/omap_debug/usrp-e-uart-rx.c +++ /dev/null @@ -1,53 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" -#include "usrp_e_regs.hpp" - -// Usage: usrp_e_uart <string> - - -int main(int argc, char *argv[]) -{ - int fp, ret; - struct usrp_e_ctl16 d; - __u16 clkdiv; - - if (argc == 0) { - printf("Usage: usrp-e-uart-rx <opt clkdiv>\n"); - printf("clkdiv = 278 is 230.4k \n"); - printf("clkdiv = 556 is 115.2k \n"); - exit(-1); - } - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - if (argc == 2) { - clkdiv = atoi(argv[1]); - d.offset = UE_REG_UART_CLKDIV; - d.count = 1; - d.buf[0] = clkdiv; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); - } - - while(1) { - d.offset = UE_REG_UART_RXLEVEL; - d.count = 1; - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - - if (d.buf[0] > 0) { - d.offset = UE_REG_UART_RXCHAR; - d.count = 1; - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - printf("%c", d.buf[0]); - fflush(stdout); - } - } - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-uart.c b/host/apps/omap_debug/usrp-e-uart.c deleted file mode 100644 index 2956c407f..000000000 --- a/host/apps/omap_debug/usrp-e-uart.c +++ /dev/null @@ -1,48 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" -#include "usrp_e_regs.hpp" - -// Usage: usrp_e_uart <string> - - -int main(int argc, char *argv[]) -{ - int fp, i, ret; - struct usrp_e_ctl16 d; - char *str = argv[1]; - __u16 clkdiv; - - if (argc < 2) { - printf("Usage: usrp_e_uart <string> <opt clkdiv>\n"); - printf("clkdiv = 278 is 230.4k \n"); - printf("clkdiv = 556 is 115.2k \n"); - exit(-1); - } - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - if (argc == 3) { - clkdiv = atoi(argv[2]); - d.offset = UE_REG_UART_CLKDIV; - d.count = 1; - d.buf[0] = clkdiv; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); - } - - for (i=0; i<strlen(str); i++) { - d.offset = UE_REG_UART_TXCHAR; - d.count = 1; - d.buf[0] = str[i]; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); - printf("Wrote %X, to %X, ret = %d\n", d.buf[0], d.offset, ret); - } - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-write.c b/host/apps/omap_debug/usrp-e-write.c deleted file mode 100644 index 903c0071f..000000000 --- a/host/apps/omap_debug/usrp-e-write.c +++ /dev/null @@ -1,21 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <fcntl.h> - -int main(int rgc, char *argv[]) -{ - int fp, i, cnt; - short buf[1024]; - - fp = open("/dev/usrp1_e0", O_WRONLY); - printf("fp = %d\n", fp); - - for (i=0; i<1024; i++) { - buf[i] = i; - } - -// do { - cnt = write(fp, buf, 2048); - printf("Bytes written - %d\n", cnt); -// } while (1); -} diff --git a/host/apps/omap_debug/usrp_e.h b/host/apps/omap_debug/usrp_e.h deleted file mode 100644 index 2c4aa2ac1..000000000 --- a/host/apps/omap_debug/usrp_e.h +++ /dev/null @@ -1,60 +0,0 @@ - -/* - * Copyright (C) 2010 Ettus Research, LLC - * - * Written by Philip Balister <philip@opensdr.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __USRP_E_H -#define __USRP_E_H - -#include <linux/types.h> -#include <linux/ioctl.h> - -struct usrp_e_ctl16 { - __u32 offset; - __u32 count; - __u16 buf[20]; -}; - -struct usrp_e_ctl32 { - __u32 offset; - __u32 count; - __u32 buf[10]; -}; - -#define USRP_E_IOC_MAGIC 'u' -#define USRP_E_WRITE_CTL16 _IOW(USRP_E_IOC_MAGIC, 0x20, struct usrp_e_ctl16) -#define USRP_E_READ_CTL16 _IOWR(USRP_E_IOC_MAGIC, 0x21, struct usrp_e_ctl16) -#define USRP_E_WRITE_CTL32 _IOW(USRP_E_IOC_MAGIC, 0x22, struct usrp_e_ctl32) -#define USRP_E_READ_CTL32 _IOWR(USRP_E_IOC_MAGIC, 0x23, struct usrp_e_ctl32) -#define USRP_E_GET_RB_INFO _IOR(USRP_E_IOC_MAGIC, 0x27, struct usrp_e_ring_buffer_size_t) -#define USRP_E_GET_COMPAT_NUMBER _IO(USRP_E_IOC_MAGIC, 0x28) - -#define USRP_E_COMPAT_NUMBER 2 - -/* Flag defines */ -#define RB_USER (1<<0) -#define RB_KERNEL (1<<1) -#define RB_OVERRUN (1<<2) -#define RB_DMA_ACTIVE (1<<3) -#define RB_USER_PROCESS (1<<4) - -struct ring_buffer_info { - int flags; - int len; -}; - -struct usrp_e_ring_buffer_size_t { - int num_pages_rx_flags; - int num_rx_frames; - int num_pages_tx_flags; - int num_tx_frames; -}; - -#endif diff --git a/host/cmake/Modules/UHDVersion.cmake b/host/cmake/Modules/UHDVersion.cmake index 2a7d88e14..aefe516d6 100644 --- a/host/cmake/Modules/UHDVersion.cmake +++ b/host/cmake/Modules/UHDVersion.cmake @@ -27,9 +27,9 @@ FIND_PACKAGE(Git QUIET) # - set UHD_VERSION_DEVEL to true for master and development branches ######################################################################## SET(UHD_VERSION_MAJOR 003) -SET(UHD_VERSION_MINOR 008) -SET(UHD_VERSION_PATCH 004) -SET(UHD_VERSION_DEVEL FALSE) +SET(UHD_VERSION_MINOR 009) +SET(UHD_VERSION_PATCH git) +SET(UHD_VERSION_DEVEL TRUE) ######################################################################## # Set up trimmed version numbers for DLL resource files and packages diff --git a/host/cmake/Toolchains/mingw_cross.cmake b/host/cmake/Toolchains/mingw_cross.cmake new file mode 100644 index 000000000..7c5adb002 --- /dev/null +++ b/host/cmake/Toolchains/mingw_cross.cmake @@ -0,0 +1,69 @@ +# Use this command: +# +# cmake -DCMAKE_TOOLCHAIN_FILE=cmake/Toolchain-mingw.cmake . +# +# or for out of source: +# +# cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-mingw.cmake .. +# +# You will need at least CMake 2.6.0. +# +# Adjust the following paths to suit your environment. +# +# This file was based on http://www.cmake.org/Wiki/CmakeMingw + +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Windows) + +# Assume the target architecture. +# XXX for some reason the value set here gets cleared before we reach the +# main CMakeLists.txt; see that file for a workaround. +# set(CMAKE_SYSTEM_PROCESSOR i686) + +# Which compilers to use for C and C++, and location of target +# environment. +if(EXISTS /usr/i586-mingw32msvc) + # First look in standard location as used by Debian/Ubuntu/etc. + set(CMAKE_C_COMPILER i586-mingw32msvc-gcc) + set(CMAKE_CXX_COMPILER i586-mingw32msvc-g++) + set(CMAKE_RC_COMPILER i586-mingw32msvc-windres) + set(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc) +elseif(EXISTS /usr/i686-w64-mingw32) + # First look in standard location as used by Debian/Ubuntu/etc. + set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) + set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) + set(CMAKE_RC_COMPILER i686-w64-mingw32-windres) + set(CMAKE_AR:FILEPATH /usr/bin/i686-w64-mingw32-ar) +elseif(EXISTS /opt/mingw) + # You can get a MinGW environment using the script at <http://mxe.cc>. + # It downloads and builds MinGW and most of the dependencies for you. + # You can use the toolchain file generated by MXE called `mxe-conf.cmake' + # or you can use this file by adjusting the above and following paths. + set(CMAKE_C_COMPILER /opt/mingw/usr/bin/i686-pc-mingw32-gcc) + set(CMAKE_CXX_COMPILER /opt/mingw/usr/bin/i686-pc-mingw32-g++) + set(CMAKE_RC_COMPILER /opt/mingw/usr/bin/i686-pc-mingw32-windres) + set(CMAKE_FIND_ROOT_PATH /opt/mingw/usr/i686-pc-mingw32) +else() + # Else fill in local path which the user will likely adjust. + # This is the location assumed by <http://www.libsdl.org/extras/win32/cross/> + set(CMAKE_C_COMPILER /usr/local/cross-tools/bin/i386-mingw32-gcc) + set(CMAKE_CXX_COMPILER /usr/local/cross-tools/bin/i386-mingw32-g++) + set(CMAKE_RC_COMPILER /usr/local/cross-tools/bin/i386-mingw32-windres) + set(CMAKE_FIND_ROOT_PATH /usr/local/cross-tools) +endif() + +# Adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# Tell pkg-config not to look at the target environment's .pc files. +# Setting PKG_CONFIG_LIBDIR sets the default search directory, but we have to +# set PKG_CONFIG_PATH as well to prevent pkg-config falling back to the host's +# path. +set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_FIND_ROOT_PATH}/lib/pkgconfig) +set(ENV{PKG_CONFIG_PATH} ${CMAKE_FIND_ROOT_PATH}/lib/pkgconfig) + +set(ENV{MINGDIR} ${CMAKE_FIND_ROOT_PATH}) diff --git a/host/docs/dboards.dox b/host/docs/dboards.dox index 3d6866a42..812a3a09e 100644 --- a/host/docs/dboards.dox +++ b/host/docs/dboards.dox @@ -372,6 +372,10 @@ Sensors: - **rssi**: float for measured RSSI in dBm - **temperature**: float for measured temperature in degC +\subsection dboards_e300 E310 MIMO XCVR board + +Please refer to \ref e3x0_dboard_e310. + \subsection dboards_dbsrxmod DBSRX - Modifying for other boards that USRP1 Due to different clocking capabilities, the DBSRX will require diff --git a/host/docs/uhd.dox b/host/docs/uhd.dox index 949c710b1..fcd0a25b0 100644 --- a/host/docs/uhd.dox +++ b/host/docs/uhd.dox @@ -12,6 +12,7 @@ Some additional pages on developing UHD are also available here: \li \subpage page_coding \li \subpage page_converters \li \subpage page_stream +\li \subpage page_rtp */ // vim:ft=doxygen: diff --git a/host/docs/usrp_b200.dox b/host/docs/usrp_b200.dox index bd79c4470..1da7f2aee 100644 --- a/host/docs/usrp_b200.dox +++ b/host/docs/usrp_b200.dox @@ -38,15 +38,40 @@ images: The master clock rate feeds the RF frontends and the DSP chains. Users may select non-default clock rates to acheive integer decimations or -interpolations in the DSP chains. The default master clock rate defaults -to 32 MHz, but can be set to any rate between 5 MHz and 61.44 MHz. +interpolations in the DSP chains. The clock rate can be set to any value +between 5 MHz and 61.44 MHz (or 30.72 MHz for dual-channel mode). +Note that rates above 56 MHz are possible, but not recommended. The user can set the master clock rate through the usrp API call uhd::usrp::multi_usrp::set_master_clock_rate(), or the clock rate can be set through the -device arguments, which many applications take: : +device arguments, which many applications take: uhd_usrp_probe --args="master_clock_rate=52e6" +The property to control the master clock rate is a double value, called `tick_rate`. + +\subsection b200_auto_mcr Automatic Clock Rate Setting + +The default clock rate setting is to automatically set a clock rate +depending on the requested sampling rate. The automatic clock rate selection +is disabled when either `master_clock_rate` is given in the device initialization +arguments, or when uhd::usrp::multi_usrp::set_master_clock_rate() is called. + +Note that the master clock rate must be an integer multiple of the sampling +rate. If a master clock rate is chosen for which this condition does not +hold, a warning will be displayed and a different sampling rate is used internally. + +Nevertheless, there are multiple valid values for the master clock rate +for most sampling rates. The auto clock rate selection attempts to use +the largest possible clock rate as to enable as many half-band filters +as possible. Expert users might have cases where a more fine-grained +control over the resampling stages is required, in which case manually +selecting a master clock rate might be more suitable than the automatic +rate. + +The property to dis- or enable the auto tick rate is a boolean value, +`auto_tick_rate`. + \section b200_fe RF Frontend Notes The B200 features an integrated RF frontend. diff --git a/host/docs/usrp_e3x0.dox b/host/docs/usrp_e3x0.dox index a05914bba..ff03dedcd 100644 --- a/host/docs/usrp_e3x0.dox +++ b/host/docs/usrp_e3x0.dox @@ -188,9 +188,9 @@ builds) $ export MACHINE="ettus-e300" $ bitbake gnuradio-dev-image \endcode -When this completes, the files needed to create the sd card are in -`tmp-glibc/deploy/images/ettus-e300`. See \ref e3x0_upgrade_sd_card for instructions to write the image to your sd card. +When this completes, the files needed to create the SD card are in +`tmp-glibc/deploy/images/ettus-e300` -# Build the toolchain. \code{.sh} @@ -544,7 +544,7 @@ usrp->set_rx_subdev_spec("A:A A:B"); The following sensors are available for the USRP-E Series motherboards; they can be queried through the API. -- **fe_locked** - rx / tx frontend pll locked +- **fe_locked** - rx / tx frontend PLL locked - **temp** - processor temperature value - **gps_time** and **gps_locked** sensors are added when the GPS is found diff --git a/host/docs/vrt_chdr.dox b/host/docs/vrt_chdr.dox new file mode 100644 index 000000000..8ab177b21 --- /dev/null +++ b/host/docs/vrt_chdr.dox @@ -0,0 +1,83 @@ +/*! \page page_rtp Radio Transport Protocols + +\tableofcontents + +Radio transport protocols are used to exchange samples (or other items) between host and devices. +If one were to sniff Ethernet traffic between a USRP and a PC, the packets would conform to a +radio transport protocol. + +For USRP devices, two radio transport protocols are relevent: VRT (the VITA Radio Transport protocol) +and CVITA (compressed VITA), also known as CHDR. Generation-3 devices and the B200 use CHDR, the rest +use VRT. + +\section rtp_vrt VRT + +VRT is an open protocol defined by the VITA-49 standard. It was designed for interoperability, +and to allow different device types to work with different software stacks. + +VRT is a very verbose standard, and only a subset is implemented in UHD/USRPs. +The full standard is available from the VITA website: http://www.vita.com . + + +\section rtp_chdr CVITA (CHDR) + +For the third generation of Ettus devices, a new type transport protocol was designed. +It reduces the complexity of the original standard and uses a fixed-length 64-Bit header +for everything except the timestamp. Because this is a "compressed" form of VITA, it +was dubbed "Compressed VITA" (CVITA). The compressed header is called CHDR, which is why +the protocol is often called CHDR itself (pronounced like the cheese "cheddar"). + +By compressing all information into a 64-bit line, the header can efficiently be parsed +in newer FPGAs, where the common streaming protocol is 64-Bit AXI. The first line in a +packet already provides all necessary information to proceed. + +Some CHDR-specific functions can be found in: uhd::transport::vrt::chdr. + +The form of a CVITA packet is the following: + +Address (Bytes) | Length (Bytes) | Payload +----------------|----------------|---------------------------- +0 | 8 | Compressed Header (CHDR) +8 | 8 | Fractional Time (Optional!) +8/16 | - | Data + +If there is no timestamp present, the data starts at address 8, otherwise, it starts at 16. + +The 64 Bits in the compressed header have the following meaning: + +Bits | Meaning +-------|-------------------------------------------------- +63:62 | Packet Type +61 | Has fractional time stamp (1: Yes) +60 | End-of-burst or error flag +59:48 | 12-bit sequence number +47:32 | Total packet length in Bytes +31:0 | Stream ID (SID) + + +The packet type is determined mainly by the first two bits, although +the EOB or error flag are also taken into consideration: + +Bit 63 | Bit 62 | Bit 60 | Packet Type +-------|--------|--------|-------------- +0 | 0 | 0 | Data +0 | 0 | 1 | Data (End-of-burst) +0 | 1 | 0 | Flow Control +1 | 0 | 0 | Command Packet +1 | 1 | 0 | Command Response +1 | 1 | 1 | Command Response (Error) + +\section vrt_tools Tools + +For CHDR, we provide a Wireshark dissector under tools/chdr_dissector. It can be used +for Ethernet links as well as USB (e.g., for the B210). + +\section vrt_code Code + +Relevent code sections for the radio transport layer are: +* uhd::transport::vrt - Namespace for radio transport protocol related functions and definitions +* uhd::transport::vrt::chdr - Sub-namespace specifically for CVITA/CHDR +* uhd::sid_t - Datatype to represent SIDs + +*/ +// vim:ft=doxygen: diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index 1e6f2f013..92947d86c 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -29,14 +29,13 @@ SET(example_sources test_messages.cpp test_pps_input.cpp test_timed_commands.cpp - transport_hammer.cpp tx_bursts.cpp tx_samples_from_file.cpp tx_timed_samples.cpp tx_waveforms.cpp txrx_loopback_to_file.cpp latency_test.cpp - fpgpio.cpp + gpio.cpp ) IF(ENABLE_OCTOCLOCK) diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index 03d8f3477..cc3ef04a4 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -43,7 +43,12 @@ unsigned long long num_seq_errors = 0; /*********************************************************************** * Benchmark RX Rate **********************************************************************/ -void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd::rx_streamer::sptr rx_stream){ +void benchmark_rx_rate( + uhd::usrp::multi_usrp::sptr usrp, + const std::string &rx_cpu, + uhd::rx_streamer::sptr rx_stream, + bool random_nsamps +) { uhd::set_thread_priority_safe(); //print pre-test summary @@ -68,15 +73,19 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c rx_stream->issue_stream_cmd(cmd); while (not boost::this_thread::interruption_requested()){ + if (random_nsamps) { + cmd.num_samps = rand() % max_samps_per_packet; + rx_stream->issue_stream_cmd(cmd); + } try { - num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md)*rx_stream->get_num_channels(); + num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md)*rx_stream->get_num_channels(); } catch (...) { - /* apparently, the boost thread interruption can sometimes result in - throwing exceptions not of type boost::exception, this catch allows - this thread to still attempt to issue the STREAM_MODE_STOP_CONTINUOUS - */ - break; + /* apparently, the boost thread interruption can sometimes result in + throwing exceptions not of type boost::exception, this catch allows + this thread to still attempt to issue the STREAM_MODE_STOP_CONTINUOUS + */ + break; } //handle the error codes @@ -109,7 +118,12 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c /*********************************************************************** * Benchmark TX Rate **********************************************************************/ -void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, uhd::tx_streamer::sptr tx_stream){ +void benchmark_tx_rate( + uhd::usrp::multi_usrp::sptr usrp, + const std::string &tx_cpu, + uhd::tx_streamer::sptr tx_stream, + bool random_nsamps=false +) { uhd::set_thread_priority_safe(); //print pre-test summary @@ -127,9 +141,25 @@ void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_c buffs.push_back(&buff.front()); //same buffer for each channel md.has_time_spec = (buffs.size() != 1); - while (not boost::this_thread::interruption_requested()){ - num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md)*tx_stream->get_num_channels();; - md.has_time_spec = false; + if (random_nsamps) { + std::srand( (unsigned int)time(NULL) ); + while(not boost::this_thread::interruption_requested()){ + size_t total_num_samps = rand() % max_samps_per_packet; + size_t num_acc_samps = 0; + const float timeout = 1; + + usrp->set_time_now(uhd::time_spec_t(0.0)); + while(num_acc_samps < total_num_samps){ + //send a single packet + num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md, timeout)*tx_stream->get_num_channels(); + num_acc_samps += std::min(total_num_samps-num_acc_samps, tx_stream->get_max_num_samps()); + } + } + } else { + while (not boost::this_thread::interruption_requested()){ + num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md)*tx_stream->get_num_channels(); + md.has_time_spec = false; + } } //send a mini EOB packet @@ -182,6 +212,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::string rx_cpu, tx_cpu; std::string mode; std::string channel_list; + bool random_nsamps = false; //setup the program options po::options_description desc("Allowed options"); @@ -196,6 +227,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("rx_cpu", po::value<std::string>(&rx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for RX") ("tx_cpu", po::value<std::string>(&tx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for TX") ("mode", po::value<std::string>(&mode)->default_value("none"), "multi-channel sync mode option: none, mimo") + ("random", "Run with random values of samples in send() and recv() to stress-test the I/O.") ("channels", po::value<std::string>(&channel_list)->default_value("0"), "which channel(s) to use (specify \"0\", \"1\", \"0,1\", etc)") ; po::variables_map vm; @@ -213,6 +245,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ return ~0; } + // Random number of samples? + if (vm.count("random")) { + std::cout << "Using random number of samples in send() and recv() calls." << std::endl; + random_nsamps = true; + } + //create a usrp device std::cout << std::endl; uhd::device_addrs_t device_addrs = uhd::device::find(args, uhd::device::USRP); @@ -251,7 +289,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::stream_args_t stream_args(rx_cpu, rx_otw); stream_args.channels = channel_nums; uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); - thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_cpu, rx_stream)); + thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_cpu, rx_stream, random_nsamps)); } //spawn the transmit test thread @@ -261,7 +299,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::stream_args_t stream_args(tx_cpu, tx_otw); stream_args.channels = channel_nums; uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); - thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_cpu, tx_stream)); + thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_cpu, tx_stream, random_nsamps)); thread_group.create_thread(boost::bind(&benchmark_tx_rate_async_helper, tx_stream)); } @@ -287,6 +325,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return EXIT_SUCCESS; } diff --git a/host/examples/fpgpio.cpp b/host/examples/fpgpio.cpp deleted file mode 100644 index c57893669..000000000 --- a/host/examples/fpgpio.cpp +++ /dev/null @@ -1,418 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -// Example for front panel GPIO. -// Bits are set as follows: -// FPGPIO[0] = ATR output 1 at idle -// FPGPIO[1] = ATR output 1 during RX -// FPGPIO[2] = ATR output 1 during TX -// FPGPIO[3] = ATR output 1 during full duplex -// FPGPIO[4] = output -// FPGPIO[5] = input -// FPGPIO[6] = input (X series only) -// FPGPIO[7] = input (X series only) -// FPGPIO[8] = input (X series only) -// FPGPIO[9] = input (X series only) -// FPGPIO[10] = input (X series only) -// The example cycles through idle, TX, RX, and full duplex, spending 2 seconds for each. -// Outputs can be physically looped back to inputs for verification testing. - -#include <uhd/utils/thread_priority.hpp> -#include <uhd/utils/safe_main.hpp> -#include <uhd/usrp/multi_usrp.hpp> -#include <uhd/convert.hpp> -#include <boost/program_options.hpp> -#include <boost/format.hpp> -#include <boost/cstdint.hpp> -#include <boost/thread.hpp> -#include <csignal> -#include <iostream> - -static const std::string FPGPIO_DEFAULT_CPU_FORMAT = "fc32"; -static const std::string FPGPIO_DEFAULT_OTW_FORMAT = "sc16"; -static const double FPGPIO_DEFAULT_RX_RATE = 500e3; -static const double FPGPIO_DEFAULT_TX_RATE = 500e3; -static const double FPGPIO_DEFAULT_DWELL_TIME = 2.0; -static const std::string FPGPIO_DEFAULT_GPIO = "FP0"; -static const size_t FPGPIO_DEFAULT_NUM_BITS = 11; - -static UHD_INLINE boost::uint32_t FPGPIO_BIT(const size_t x) -{ - return (1 << x); -} - -namespace po = boost::program_options; - -static bool stop_signal_called = false; -void sig_int_handler(int){stop_signal_called = true;} - -std::string to_bit_string(boost::uint32_t val, const size_t num_bits) -{ - std::string out; - for (int i = num_bits - 1; i >= 0; i--) - { - std::string bit = ((val >> i) & 1) ? "1" : "0"; - out += " "; - out += bit; - } - return out; -} - -void output_reg_values( - const std::string bank, - const uhd::usrp::multi_usrp::sptr &usrp, - const size_t num_bits) -{ - std::cout << (boost::format("Bit ")); - for (int i = num_bits - 1; i >= 0; i--) - std::cout << (boost::format(" %s%d") % (i < 10 ? " " : "") % i); - std::cout << std::endl; - std::cout << "CTRL: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("CTRL"))), - num_bits) - << std::endl; - - std::cout << "DDR: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("DDR"))), - num_bits) - << std::endl; - - std::cout << "ATR_0X: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("ATR_0X"))), - num_bits) - << std::endl; - - std::cout << "ATR_RX: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("ATR_RX"))), - num_bits) - << std::endl; - - std::cout << "ATR_TX: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("ATR_TX"))), - num_bits) - << std::endl; - - std::cout << "ATR_XX: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("ATR_XX"))), - num_bits) - << std::endl; - - std::cout << "OUT: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("OUT"))), - num_bits) - << std::endl; - - std::cout << "READBACK: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("READBACK"))), - num_bits) - << std::endl; -} - -int UHD_SAFE_MAIN(int argc, char *argv[]) -{ - uhd::set_thread_priority_safe(); - - //variables to be set by po - std::string args; - std::string cpu, otw; - double rx_rate, tx_rate, dwell; - std::string fpgpio; - size_t num_bits; - - //setup the program options - po::options_description desc("Allowed options"); - desc.add_options() - ("help", "help message") - ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args") - ("repeat", "repeat loop until Ctrl-C is pressed") - ("cpu", po::value<std::string>(&cpu)->default_value(FPGPIO_DEFAULT_CPU_FORMAT), "cpu data format") - ("otw", po::value<std::string>(&otw)->default_value(FPGPIO_DEFAULT_OTW_FORMAT), "over the wire data format") - ("rx_rate", po::value<double>(&rx_rate)->default_value(FPGPIO_DEFAULT_RX_RATE), "rx sample rate") - ("tx_rate", po::value<double>(&tx_rate)->default_value(FPGPIO_DEFAULT_TX_RATE), "tx sample rate") - ("dwell", po::value<double>(&dwell)->default_value(FPGPIO_DEFAULT_DWELL_TIME), "dwell time in seconds for each test case") - ("gpio", po::value<std::string>(&fpgpio)->default_value(FPGPIO_DEFAULT_GPIO), "name of gpio bank") - ("bits", po::value<size_t>(&num_bits)->default_value(FPGPIO_DEFAULT_NUM_BITS), "number of bits in gpio bank") - ; - po::variables_map vm; - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); - - //print the help message - if (vm.count("help")){ - std::cout << boost::format("Front Panel GPIO %s") % desc << std::endl; - return ~0; - } - - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; - - //print out initial unconfigured state of FP GPIO - std::cout << "Unconfigured GPIO values:" << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - - //configure GPIO registers - boost::uint32_t ctrl = 0; // default all as manual - boost::uint32_t ddr = 0; // default all as input - boost::uint32_t atr_idle = 0; - boost::uint32_t atr_rx = 0; - boost::uint32_t atr_tx = 0; - boost::uint32_t atr_duplex = 0; - boost::uint32_t mask = 0x7ff; - - //set up FPGPIO outputs: - //FPGPIO[0] = ATR output 1 at idle - ctrl |= FPGPIO_BIT(0); - atr_idle |= FPGPIO_BIT(0); - ddr |= FPGPIO_BIT(0); - - //FPGPIO[1] = ATR output 1 during RX - ctrl |= FPGPIO_BIT(1); - ddr |= FPGPIO_BIT(1); - atr_rx |= FPGPIO_BIT(1); - - //FPGPIO[2] = ATR output 1 during TX - ctrl |= FPGPIO_BIT(2); - ddr |= FPGPIO_BIT(2); - atr_tx |= FPGPIO_BIT(2); - - //FPGPIO[3] = ATR output 1 during full duplex - ctrl |= FPGPIO_BIT(3); - ddr |= FPGPIO_BIT(3); - atr_duplex |= FPGPIO_BIT(3); - - //FPGPIO[4] = output - ddr |= FPGPIO_BIT(4); - - //set data direction register (DDR) - usrp->set_gpio_attr(fpgpio, std::string("DDR"), ddr, mask); - - //set ATR registers - usrp->set_gpio_attr(fpgpio, std::string("ATR_0X"), atr_idle, mask); - usrp->set_gpio_attr(fpgpio, std::string("ATR_RX"), atr_rx, mask); - usrp->set_gpio_attr(fpgpio, std::string("ATR_TX"), atr_tx, mask); - usrp->set_gpio_attr(fpgpio, std::string("ATR_XX"), atr_duplex, mask); - - //set control register - usrp->set_gpio_attr(fpgpio, std::string("CTRL"), ctrl, mask); - - //print out initial state of FP GPIO - std::cout << "\nConfigured GPIO values:" << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - std::cout << std::endl; - - //set up streams - uhd::stream_args_t rx_args(cpu, otw); - uhd::stream_args_t tx_args(cpu, otw); - uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(rx_args); - uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(tx_args); - uhd::stream_cmd_t rx_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - rx_cmd.stream_now = true; - usrp->set_rx_rate(rx_rate); - usrp->set_tx_rate(tx_rate); - - //set up buffers for tx and rx - const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); - const size_t nsamps_per_buff = max_samps_per_packet; - std::vector<char> rx_buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(cpu)); - std::vector<char> tx_buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(cpu)); - std::vector<void *> rx_buffs, tx_buffs; - for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) - rx_buffs.push_back(&rx_buff.front()); //same buffer for each channel - for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) - tx_buffs.push_back(&tx_buff.front()); //same buffer for each channel - - uhd::rx_metadata_t rx_md; - uhd::tx_metadata_t tx_md; - tx_md.has_time_spec = false; - tx_md.start_of_burst = true; - uhd::time_spec_t stop_time; - double timeout = 0.01; - uhd::time_spec_t dwell_time(dwell); - int loop = 0; - boost::uint32_t rb, expected; - - //register singal handler - std::signal(SIGINT, &sig_int_handler); - - //Test the mask - only need to test once with no dwell time - std::cout << "\nTesting mask..." << std::flush; - //send a value of all 1's to the DDR with a mask for only bit 10 - usrp->set_gpio_attr(fpgpio, std::string("DDR"), ~0, FPGPIO_BIT(10)); - //bit 10 should now be 1, but all the other bits should be unchanged - rb = usrp->get_gpio_attr(fpgpio, std::string("DDR")) & mask; - expected = ddr | FPGPIO_BIT(10); - if (rb == expected) - std::cout << "pass" << std::endl; - else - std::cout << "fail" << std::endl; - std::cout << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - usrp->set_gpio_attr(fpgpio, std::string("DDR"), ddr, mask); - - while (not stop_signal_called) - { - int failures = 0; - - if (vm.count("repeat")) - std::cout << "Press Ctrl + C to quit..." << std::endl; - - // test user controlled GPIO and ATR idle by setting bit 4 high for 1 second - std::cout << "\nTesting user controlled GPIO and ATR idle output..." << std::flush; - usrp->set_gpio_attr(fpgpio, "OUT", 1 << 4, 1 << 4); - stop_time = usrp->get_time_now() + dwell_time; - while (not stop_signal_called and usrp->get_time_now() < stop_time) - { - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - rb = usrp->get_gpio_attr(fpgpio, "READBACK"); - expected = FPGPIO_BIT(4) | FPGPIO_BIT(0); - if ((rb & expected) != expected) - { - ++failures; - std::cout << "fail" << std::endl; - if ((rb & FPGPIO_BIT(0)) == 0) - std::cout << "Bit 0 should be set, but is not" << std::endl; - if ((rb & FPGPIO_BIT(4)) == 0) - std::cout << "Bit 4 should be set, but is not" << std::endl; - } else { - std::cout << "pass" << std::endl; - } - std::cout << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - usrp->set_gpio_attr(fpgpio, "OUT", 0, FPGPIO_BIT(4)); - if (stop_signal_called) - break; - - // test ATR RX by receiving for 1 second - std::cout << "\nTesting ATR RX output..." << std::flush; - rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; - rx_stream->issue_stream_cmd(rx_cmd); - stop_time = usrp->get_time_now() + dwell_time; - while (not stop_signal_called and usrp->get_time_now() < stop_time) - { - try { - rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); - } catch(...){} - } - rb = usrp->get_gpio_attr(fpgpio, "READBACK"); - expected = FPGPIO_BIT(1); - if ((rb & expected) != expected) - { - ++failures; - std::cout << "fail" << std::endl; - std::cout << "Bit 1 should be set, but is not" << std::endl; - } else { - std::cout << "pass" << std::endl; - } - std::cout << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); - //clear out any data left in the rx stream - try { - rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); - } catch(...){} - if (stop_signal_called) - break; - - // test ATR TX by transmitting for 1 second - std::cout << "\nTesting ATR TX output..." << std::flush; - stop_time = usrp->get_time_now() + dwell_time; - tx_md.start_of_burst = true; - tx_md.end_of_burst = false; - while (not stop_signal_called and usrp->get_time_now() < stop_time) - { - try { - tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); - tx_md.start_of_burst = false; - } catch(...){} - } - rb = usrp->get_gpio_attr(fpgpio, "READBACK"); - expected = FPGPIO_BIT(2); - if ((rb & expected) != expected) - { - ++failures; - std::cout << "fail" << std::endl; - std::cout << "Bit 2 should be set, but is not" << std::endl; - } else { - std::cout << "pass" << std::endl; - } - std::cout << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - tx_md.end_of_burst = true; - try { - tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); - } catch(...){} - if (stop_signal_called) - break; - - // test ATR RX by transmitting and receiving for 1 second - std::cout << "\nTesting ATR full duplex output..." << std::flush; - rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; - rx_stream->issue_stream_cmd(rx_cmd); - tx_md.start_of_burst = true; - tx_md.end_of_burst = false; - stop_time = usrp->get_time_now() + dwell_time; - while (not stop_signal_called and usrp->get_time_now() < stop_time) - { - try { - tx_stream->send(rx_buffs, nsamps_per_buff, tx_md, timeout); - tx_md.start_of_burst = false; - rx_stream->recv(tx_buffs, nsamps_per_buff, rx_md, timeout); - } catch(...){} - } - rb = usrp->get_gpio_attr(fpgpio, "READBACK"); - expected = FPGPIO_BIT(3); - if ((rb & expected) != expected) - { - ++failures; - std::cout << "fail" << std::endl; - std::cout << "Bit 3 should be set, but is not" << std::endl; - } else { - std::cout << "pass" << std::endl; - } - std::cout << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); - tx_md.end_of_burst = true; - try { - tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); - } catch(...){} - //clear out any data left in the rx stream - try { - rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); - } catch(...){} - - std::cout << std::endl; - if (failures) - std::cout << failures << " tests failed" << std::endl; - else - std::cout << "All tests passed!" << std::endl; - - if (!vm.count("repeat")) - break; - - std::cout << (boost::format("\nLoop %d completed") % ++loop) << std::endl; - } - - //finished - std::cout << std::endl << "Done!" << std::endl << std::endl; - - return EXIT_SUCCESS; -} diff --git a/host/examples/gpio.cpp b/host/examples/gpio.cpp new file mode 100644 index 000000000..b0d15f35a --- /dev/null +++ b/host/examples/gpio.cpp @@ -0,0 +1,462 @@ +// +// Copyright 2014-15 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +// Example for GPIO testing and bit banging. +// +// This example was originally designed to test the 11 bit wide front panel +// GPIO on the X300 series and has since been adapted to work with any GPIO +// bank on any USRP and provide optional bit banging. Please excuse the +// clutter. Also, there is no current way to detect the width of the +// specified GPIO bank, so the user must specify the width with the --bits +// flag if more than 11 bits. +// +// GPIO Testing: +// For testing, GPIO bits are set as follows: +// GPIO[0] = ATR output 1 at idle +// GPIO[1] = ATR output 1 during RX +// GPIO[2] = ATR output 1 during TX +// GPIO[3] = ATR output 1 during full duplex +// GPIO[4] = output +// GPIO[n:5] = input (all other pins) +// The testing cycles through idle, TX, RX, and full duplex, dwelling on each +// test case (default 2 seconds), and then comparing the readback register with +// the expected values of the outputs for verification. The values of all GPIO +// registers are displayed at the end of each test case. Outputs can be +// physically looped back to inputs to manually verify the inputs. +// +// GPIO Bit Banging: +// GPIO banks have the standard registers of DDR for data direction and OUT +// for output values. Users can bit bang the GPIO bits by using this example +// with the --bitbang flag and specifying the --ddr and --out flags to set the +// values of the corresponding registers. The READBACK register is +// continuously read for the duration of the dwell time (default 2 seconds) so +// users can monitor changes on the inputs. +// +// Automatic Transmit/Receive (ATR): +// In addition to the standard DDR and OUT registers, the GPIO banks also +// have ATR (Automatic Transmit/Receive) control registers that allow the +// GPIO pins to be automatically set to specific values when the USRP is +// idle, transmitting, receiving, or operating in full duplex mode. The +// description of these registers is below: +// CTRL - Control (0=manual, 1=ATR) +// ATR_0X - Values to be set when idle +// ATR_RX - Output values to be set when receiving +// ATR_TX - Output values to be set when transmitting +// ATR_XX - Output values to be set when operating in full duplex +// This code below contains examples of setting all these registers. On +// devices with multiple radios, the ATR for the front panel GPIO is driven +// by the state of the first radio (0 or A). +// +// The UHD API +// The multi_usrp::set_gpio_attr() method is the UHD API for configuring and +// controlling the GPIO banks. The parameters to the method are: +// bank - the name of the GPIO bank (typically "FP0" for front panel GPIO, +// "TX<n>" for TX daughter card GPIO, or +// "RX<n>" for RX daughter card GPIO) +// attr - attribute (register) to change ("DDR", "OUT", "CTRL", "ATR_0X", +// "ATR_RX", "ATR_TX", "ATR_XX") +// value - the value to be set +// mask - a mask indicating which bits in the specified attribute register are +// to be changed (default is all bits). + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <uhd/convert.hpp> +#include <boost/assign.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/cstdint.hpp> +#include <boost/thread.hpp> +#include <csignal> +#include <iostream> +#include <stdlib.h> + +static const std::string GPIO_DEFAULT_CPU_FORMAT = "fc32"; +static const std::string GPIO_DEFAULT_OTW_FORMAT = "sc16"; +static const double GPIO_DEFAULT_RX_RATE = 500e3; +static const double GPIO_DEFAULT_TX_RATE = 500e3; +static const double GPIO_DEFAULT_DWELL_TIME = 2.0; +static const std::string GPIO_DEFAULT_GPIO = "FP0"; +static const size_t GPIO_DEFAULT_NUM_BITS = 11; +static const std::string GPIO_DEFAULT_CTRL = "0x0"; // all as user controlled +static const std::string GPIO_DEFAULT_DDR = "0x0"; // all as inputs +static const std::string GPIO_DEFAULT_OUT = "0x0"; + +static UHD_INLINE boost::uint32_t GPIO_BIT(const size_t x) +{ + return (1 << x); +} + +namespace po = boost::program_options; + +static bool stop_signal_called = false; +void sig_int_handler(int){stop_signal_called = true;} + +std::string to_bit_string(boost::uint32_t val, const size_t num_bits) +{ + std::string out; + for (int i = num_bits - 1; i >= 0; i--) + { + std::string bit = ((val >> i) & 1) ? "1" : "0"; + out += " "; + out += bit; + } + return out; +} + +void output_reg_values( + const std::string bank, + const uhd::usrp::multi_usrp::sptr &usrp, + const size_t num_bits) +{ + std::vector<std::string> attrs = boost::assign::list_of("CTRL")("DDR")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX")("OUT")("READBACK"); + std::cout << (boost::format("%10s ") % "Bit"); + for (int i = num_bits - 1; i >= 0; i--) + std::cout << (boost::format(" %2d") % i); + std::cout << std::endl; + BOOST_FOREACH(std::string &attr, attrs) + { + std::cout << (boost::format("%10s:%s") + % attr % to_bit_string(boost::uint32_t(usrp->get_gpio_attr(bank, attr)), num_bits)) + << std::endl; + } +} + +int UHD_SAFE_MAIN(int argc, char *argv[]) +{ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + std::string cpu, otw; + double rx_rate, tx_rate, dwell; + std::string gpio; + size_t num_bits; + std::string ctrl_str; + std::string ddr_str; + std::string out_str; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args") + ("repeat", "repeat loop until Ctrl-C is pressed") + ("cpu", po::value<std::string>(&cpu)->default_value(GPIO_DEFAULT_CPU_FORMAT), "cpu data format") + ("otw", po::value<std::string>(&otw)->default_value(GPIO_DEFAULT_OTW_FORMAT), "over the wire data format") + ("rx_rate", po::value<double>(&rx_rate)->default_value(GPIO_DEFAULT_RX_RATE), "rx sample rate") + ("tx_rate", po::value<double>(&tx_rate)->default_value(GPIO_DEFAULT_TX_RATE), "tx sample rate") + ("dwell", po::value<double>(&dwell)->default_value(GPIO_DEFAULT_DWELL_TIME), "dwell time in seconds for each test case") + ("bank", po::value<std::string>(&gpio)->default_value(GPIO_DEFAULT_GPIO), "name of gpio bank") + ("bits", po::value<size_t>(&num_bits)->default_value(GPIO_DEFAULT_NUM_BITS), "number of bits in gpio bank") + ("bitbang", "single test case where user sets values for CTRL, DDR, and OUT registers") + ("ddr", po::value<std::string>(&ddr_str)->default_value(GPIO_DEFAULT_DDR), "GPIO DDR reg value") + ("out", po::value<std::string>(&out_str)->default_value(GPIO_DEFAULT_OUT), "GPIO OUT reg value") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("gpio %s") % desc << std::endl; + return ~0; + } + + //create a usrp device + std::cout << std::endl; + std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + + //print out initial unconfigured state of FP GPIO + std::cout << "Initial GPIO values:" << std::endl; + output_reg_values(gpio, usrp, num_bits); + + //configure GPIO registers + boost::uint32_t ddr = strtoul(ddr_str.c_str(), NULL, 0); + boost::uint32_t out = strtoul(out_str.c_str(), NULL, 0); + boost::uint32_t ctrl = 0; + boost::uint32_t atr_idle = 0; + boost::uint32_t atr_rx = 0; + boost::uint32_t atr_tx = 0; + boost::uint32_t atr_duplex = 0; + boost::uint32_t mask = (1 << num_bits) - 1; + + if (!vm.count("bitbang")) + { + //set up GPIO outputs: + //GPIO[0] = ATR output 1 at idle + ctrl |= GPIO_BIT(0); + atr_idle |= GPIO_BIT(0); + ddr |= GPIO_BIT(0); + + //GPIO[1] = ATR output 1 during RX + ctrl |= GPIO_BIT(1); + ddr |= GPIO_BIT(1); + atr_rx |= GPIO_BIT(1); + + //GPIO[2] = ATR output 1 during TX + ctrl |= GPIO_BIT(2); + ddr |= GPIO_BIT(2); + atr_tx |= GPIO_BIT(2); + + //GPIO[3] = ATR output 1 during full duplex + ctrl |= GPIO_BIT(3); + ddr |= GPIO_BIT(3); + atr_duplex |= GPIO_BIT(3); + + //GPIO[4] = output + ddr |= GPIO_BIT(4); + } + + //set data direction register (DDR) + usrp->set_gpio_attr(gpio, "DDR", ddr, mask); + + //set output values (OUT) + usrp->set_gpio_attr(gpio, "OUT", out, mask); + + //set ATR registers + usrp->set_gpio_attr(gpio, "ATR_0X", atr_idle, mask); + usrp->set_gpio_attr(gpio, "ATR_RX", atr_rx, mask); + usrp->set_gpio_attr(gpio, "ATR_TX", atr_tx, mask); + usrp->set_gpio_attr(gpio, "ATR_XX", atr_duplex, mask); + + //set control register + usrp->set_gpio_attr(gpio, "CTRL", ctrl, mask); + + //print out initial state of FP GPIO + std::cout << "\nConfigured GPIO values:" << std::endl; + output_reg_values(gpio, usrp, num_bits); + std::cout << std::endl; + + //set up streams + uhd::stream_args_t rx_args(cpu, otw); + uhd::stream_args_t tx_args(cpu, otw); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(rx_args); + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(tx_args); + uhd::stream_cmd_t rx_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + rx_cmd.stream_now = true; + usrp->set_rx_rate(rx_rate); + usrp->set_tx_rate(tx_rate); + + //set up buffers for tx and rx + const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); + const size_t nsamps_per_buff = max_samps_per_packet; + std::vector<char> rx_buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(cpu)); + std::vector<char> tx_buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(cpu)); + std::vector<void *> rx_buffs, tx_buffs; + for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) + rx_buffs.push_back(&rx_buff.front()); //same buffer for each channel + for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) + tx_buffs.push_back(&tx_buff.front()); //same buffer for each channel + + uhd::rx_metadata_t rx_md; + uhd::tx_metadata_t tx_md; + tx_md.has_time_spec = false; + tx_md.start_of_burst = true; + uhd::time_spec_t stop_time; + double timeout = 0.01; + uhd::time_spec_t dwell_time(dwell); + int loop = 0; + boost::uint32_t rb, expected; + + //register signal handler + std::signal(SIGINT, &sig_int_handler); + + if (!vm.count("bitbang")) + { + // Test the mask parameter of the multi_usrp::set_gpio_attr API + // We only need to test once with no dwell time + std::cout << "\nTesting mask..." << std::flush; + //send a value of all 1's to the DDR with a mask for only upper most bit + usrp->set_gpio_attr(gpio, "DDR", ~0, GPIO_BIT(num_bits - 1)); + //upper most bit should now be 1, but all the other bits should be unchanged + rb = usrp->get_gpio_attr(gpio, "DDR") & mask; + expected = ddr | GPIO_BIT(num_bits - 1); + if (rb == expected) + std::cout << "pass:" << std::endl; + else + std::cout << "fail:" << std::endl; + output_reg_values(gpio, usrp, num_bits); + //restore DDR value + usrp->set_gpio_attr(gpio, "DDR", ddr, mask); + } + + while (not stop_signal_called) + { + int failures = 0; + + if (vm.count("repeat")) + std::cout << "Press Ctrl + C to quit..." << std::endl; + + if (vm.count("bitbang")) + { + // dwell and continuously read back GPIO values + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + rb = usrp->get_gpio_attr(gpio, "READBACK"); + std::cout << "\rREADBACK: " << to_bit_string(rb, num_bits); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + std::cout << std::endl; + } + else + { + // test user controlled GPIO and ATR idle by setting bit 4 high for 1 second + std::cout << "\nTesting user controlled GPIO and ATR idle output..." << std::flush; + usrp->set_gpio_attr(gpio, "OUT", 1 << 4, 1 << 4); + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + rb = usrp->get_gpio_attr(gpio, "READBACK"); + expected = GPIO_BIT(4) | GPIO_BIT(0); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail:" << std::endl; + if ((rb & GPIO_BIT(0)) == 0) + std::cout << "Bit 0 should be set, but is not" << std::endl; + if ((rb & GPIO_BIT(4)) == 0) + std::cout << "Bit 4 should be set, but is not" << std::endl; + } else { + std::cout << "pass:" << std::endl; + } + output_reg_values(gpio, usrp, num_bits); + usrp->set_gpio_attr(gpio, "OUT", 0, GPIO_BIT(4)); + if (stop_signal_called) + break; + + // test ATR RX by receiving for 1 second + std::cout << "\nTesting ATR RX output..." << std::flush; + rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + rx_stream->issue_stream_cmd(rx_cmd); + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + } + rb = usrp->get_gpio_attr(gpio, "READBACK"); + expected = GPIO_BIT(1); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail:" << std::endl; + std::cout << "Bit 1 should be set, but is not" << std::endl; + } else { + std::cout << "pass:" << std::endl; + } + output_reg_values(gpio, usrp, num_bits); + rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + //clear out any data left in the rx stream + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + if (stop_signal_called) + break; + + // test ATR TX by transmitting for 1 second + std::cout << "\nTesting ATR TX output..." << std::flush; + stop_time = usrp->get_time_now() + dwell_time; + tx_md.start_of_burst = true; + tx_md.end_of_burst = false; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + tx_md.start_of_burst = false; + } catch(...){} + } + rb = usrp->get_gpio_attr(gpio, "READBACK"); + expected = GPIO_BIT(2); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail:" << std::endl; + std::cout << "Bit 2 should be set, but is not" << std::endl; + } else { + std::cout << "pass:" << std::endl; + } + output_reg_values(gpio, usrp, num_bits); + tx_md.end_of_burst = true; + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + } catch(...){} + if (stop_signal_called) + break; + + // test ATR RX by transmitting and receiving for 1 second + std::cout << "\nTesting ATR full duplex output..." << std::flush; + rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + rx_stream->issue_stream_cmd(rx_cmd); + tx_md.start_of_burst = true; + tx_md.end_of_burst = false; + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + try { + tx_stream->send(rx_buffs, nsamps_per_buff, tx_md, timeout); + tx_md.start_of_burst = false; + rx_stream->recv(tx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + } + rb = usrp->get_gpio_attr(gpio, "READBACK"); + expected = GPIO_BIT(3); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail:" << std::endl; + std::cout << "Bit 3 should be set, but is not" << std::endl; + } else { + std::cout << "pass:" << std::endl; + } + output_reg_values(gpio, usrp, num_bits); + rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + tx_md.end_of_burst = true; + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + } catch(...){} + //clear out any data left in the rx stream + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + + std::cout << std::endl; + if (failures) + std::cout << failures << " tests failed" << std::endl; + else + std::cout << "All tests passed!" << std::endl; + } + + if (!vm.count("repeat")) + break; + + if (not stop_signal_called) + std::cout << (boost::format("\nLoop %d completed") % ++loop) << std::endl; + } + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return EXIT_SUCCESS; +} diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp index 80b72de9c..934dce586 100644 --- a/host/examples/rx_samples_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -56,7 +56,7 @@ template<typename samp_type> void recv_to_file( std::vector<samp_type> buff(samps_per_buff); std::ofstream outfile; if (not null) - outfile.open(file.c_str(), std::ofstream::binary); + outfile.open(file.c_str(), std::ofstream::binary); bool overflow_message = true; //setup streaming @@ -78,8 +78,8 @@ template<typename samp_type> void recv_to_file( typedef std::map<size_t,size_t> SizeMap; SizeMap mapSizes; - while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)){ - boost::system_time now = boost::get_system_time(); + while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)) { + boost::system_time now = boost::get_system_time(); size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 3.0, enable_size_map); @@ -88,7 +88,7 @@ template<typename samp_type> void recv_to_file( break; } if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW){ - if (overflow_message){ + if (overflow_message) { overflow_message = false; std::cerr << boost::format( "Got an overflow indication. Please consider the following:\n" @@ -110,99 +110,99 @@ template<typename samp_type> void recv_to_file( throw std::runtime_error(error); } - if (enable_size_map){ - SizeMap::iterator it = mapSizes.find(num_rx_samps); - if (it == mapSizes.end()) - mapSizes[num_rx_samps] = 0; - mapSizes[num_rx_samps] += 1; - } + if (enable_size_map) { + SizeMap::iterator it = mapSizes.find(num_rx_samps); + if (it == mapSizes.end()) + mapSizes[num_rx_samps] = 0; + mapSizes[num_rx_samps] += 1; + } num_total_samps += num_rx_samps; - if (outfile.is_open()) - outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(samp_type)); - - if (bw_summary){ - last_update_samps += num_rx_samps; - boost::posix_time::time_duration update_diff = now - last_update; - if (update_diff.ticks() > boost::posix_time::time_duration::ticks_per_second()) { - double t = (double)update_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); - double r = (double)last_update_samps / t; - std::cout << boost::format("\t%f Msps") % (r/1e6) << std::endl; - last_update_samps = 0; - last_update = now; - } - } + if (outfile.is_open()) + outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(samp_type)); + + if (bw_summary) { + last_update_samps += num_rx_samps; + boost::posix_time::time_duration update_diff = now - last_update; + if (update_diff.ticks() > boost::posix_time::time_duration::ticks_per_second()) { + double t = (double)update_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); + double r = (double)last_update_samps / t; + std::cout << boost::format("\t%f Msps") % (r/1e6) << std::endl; + last_update_samps = 0; + last_update = now; + } + } ticks_diff = now - start; - if (ticks_requested > 0){ - if ((unsigned long long)ticks_diff.ticks() > ticks_requested) - break; - } + if (ticks_requested > 0){ + if ((unsigned long long)ticks_diff.ticks() > ticks_requested) + break; + } } stream_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS; rx_stream->issue_stream_cmd(stream_cmd); if (outfile.is_open()) - outfile.close(); - - if (stats){ - std::cout << std::endl; - - double t = (double)ticks_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); - std::cout << boost::format("Received %d samples in %f seconds") % num_total_samps % t << std::endl; - double r = (double)num_total_samps / t; - std::cout << boost::format("%f Msps") % (r/1e6) << std::endl; - - if (enable_size_map) { - std::cout << std::endl; - std::cout << "Packet size map (bytes: count)" << std::endl; - for (SizeMap::iterator it = mapSizes.begin(); it != mapSizes.end(); it++) - std::cout << it->first << ":\t" << it->second << std::endl; - } - } + outfile.close(); + + if (stats) { + std::cout << std::endl; + + double t = (double)ticks_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); + std::cout << boost::format("Received %d samples in %f seconds") % num_total_samps % t << std::endl; + double r = (double)num_total_samps / t; + std::cout << boost::format("%f Msps") % (r/1e6) << std::endl; + + if (enable_size_map) { + std::cout << std::endl; + std::cout << "Packet size map (bytes: count)" << std::endl; + for (SizeMap::iterator it = mapSizes.begin(); it != mapSizes.end(); it++) + std::cout << it->first << ":\t" << it->second << std::endl; + } + } } typedef boost::function<uhd::sensor_value_t (const std::string&)> get_sensor_fn_t; bool check_locked_sensor(std::vector<std::string> sensor_names, const char* sensor_name, get_sensor_fn_t get_sensor_fn, double setup_time){ - if (std::find(sensor_names.begin(), sensor_names.end(), sensor_name) == sensor_names.end()) - return false; - - boost::system_time start = boost::get_system_time(); - boost::system_time first_lock_time; - - std::cout << boost::format("Waiting for \"%s\": ") % sensor_name; - std::cout.flush(); - - while (true){ - if ((not first_lock_time.is_not_a_date_time()) and - (boost::get_system_time() > (first_lock_time + boost::posix_time::seconds(setup_time)))) - { - std::cout << " locked." << std::endl; - break; - } - if (get_sensor_fn(sensor_name).to_bool()){ - if (first_lock_time.is_not_a_date_time()) - first_lock_time = boost::get_system_time(); - std::cout << "+"; - std::cout.flush(); - } - else{ - first_lock_time = boost::system_time(); //reset to 'not a date time' - - if (boost::get_system_time() > (start + boost::posix_time::seconds(setup_time))){ - std::cout << std::endl; - throw std::runtime_error(str(boost::format("timed out waiting for consecutive locks on sensor \"%s\"") % sensor_name)); - } - std::cout << "_"; - std::cout.flush(); - } - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - std::cout << std::endl; - return true; + if (std::find(sensor_names.begin(), sensor_names.end(), sensor_name) == sensor_names.end()) + return false; + + boost::system_time start = boost::get_system_time(); + boost::system_time first_lock_time; + + std::cout << boost::format("Waiting for \"%s\": ") % sensor_name; + std::cout.flush(); + + while (true) { + if ((not first_lock_time.is_not_a_date_time()) and + (boost::get_system_time() > (first_lock_time + boost::posix_time::seconds(setup_time)))) + { + std::cout << " locked." << std::endl; + break; + } + if (get_sensor_fn(sensor_name).to_bool()){ + if (first_lock_time.is_not_a_date_time()) + first_lock_time = boost::get_system_time(); + std::cout << "+"; + std::cout.flush(); + } + else { + first_lock_time = boost::system_time(); //reset to 'not a date time' + + if (boost::get_system_time() > (start + boost::posix_time::seconds(setup_time))){ + std::cout << std::endl; + throw std::runtime_error(str(boost::format("timed out waiting for consecutive locks on sensor \"%s\"") % sensor_name)); + } + std::cout << "_"; + std::cout.flush(); + } + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + std::cout << std::endl; + return true; } int UHD_SAFE_MAIN(int argc, char *argv[]){ @@ -246,8 +246,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ po::notify(vm); //print the help message - if (vm.count("help")){ + if (vm.count("help")) { std::cout << boost::format("UHD RX samples to file %s") % desc << std::endl; + std::cout + << std::endl + << "This application streams data from a single channel of a USRP device to a file.\n" + << std::endl; return ~0; } @@ -258,7 +262,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ bool continue_on_bad_packet = vm.count("continue") > 0; if (enable_size_map) - std::cout << "Packet size tracking enabled - will only recv one packet at a time!" << std::endl; + std::cout << "Packet size tracking enabled - will only recv one packet at a time!" << std::endl; //create a usrp device std::cout << std::endl; @@ -283,23 +287,23 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate()/1e6) << std::endl << std::endl; //set the center frequency - if (vm.count("freq")){ //with default of 0.0 this will always be true - std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; + if (vm.count("freq")) { //with default of 0.0 this will always be true + std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; uhd::tune_request_t tune_request(freq); if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=integer"); - usrp->set_rx_freq(tune_request); - std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; - } + usrp->set_rx_freq(tune_request); + std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; + } //set the rf gain - if (vm.count("gain")){ + if (vm.count("gain")) { std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; usrp->set_rx_gain(gain); std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl; } - //set the analog frontend filter bandwidth - if (vm.count("bw")){ + //set the IF filter bandwidth + if (vm.count("bw")) { std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % (bw/1e6) << std::endl; usrp->set_rx_bandwidth(bw); std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % (usrp->get_rx_bandwidth()/1e6) << std::endl << std::endl; @@ -312,12 +316,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //check Ref and LO Lock detect if (not vm.count("skip-lo")){ - check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked", boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), setup_time); - if (ref == "mimo") - check_locked_sensor(usrp->get_mboard_sensor_names(0), "mimo_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); - if (ref == "external") - check_locked_sensor(usrp->get_mboard_sensor_names(0), "ref_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); - } + check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked", boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), setup_time); + if (ref == "mimo") + check_locked_sensor(usrp->get_mboard_sensor_names(0), "mimo_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); + if (ref == "external") + check_locked_sensor(usrp->get_mboard_sensor_names(0), "ref_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); + } if (total_num_samps == 0){ std::signal(SIGINT, &sig_int_handler); @@ -325,7 +329,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } #define recv_to_file_args(format) \ - (usrp, format, wirefmt, file, spb, total_num_samps, total_time, bw_summary, stats, null, enable_size_map, continue_on_bad_packet) + (usrp, format, wirefmt, file, spb, total_num_samps, total_time, bw_summary, stats, null, enable_size_map, continue_on_bad_packet) //recv to file if (type == "double") recv_to_file<std::complex<double> >recv_to_file_args("fc64"); else if (type == "float") recv_to_file<std::complex<float> >recv_to_file_args("fc32"); diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index 30535907f..20abd92fe 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -48,7 +48,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples") ("dilv", "specify to disable inner-loop verbose") ("channels", po::value<std::string>(&channel_list)->default_value("0"), "which channel(s) to use (specify \"0\", \"1\", \"0,1\", etc)") - ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); diff --git a/host/examples/test_pps_input.cpp b/host/examples/test_pps_input.cpp index 889c98a45..3e6c4ba9d 100644 --- a/host/examples/test_pps_input.cpp +++ b/host/examples/test_pps_input.cpp @@ -47,6 +47,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //print the help message if (vm.count("help")){ std::cout << boost::format("UHD Test PPS Input %s") % desc << std::endl; + std::cout + << std::endl + << "Tests if the PPS input signal is working. Will throw an error if not." + << std::endl + << std::endl; return ~0; } diff --git a/host/examples/transport_hammer.cpp b/host/examples/transport_hammer.cpp deleted file mode 100644 index 32e344e3e..000000000 --- a/host/examples/transport_hammer.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// -// Copyright 2012 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include <uhd/utils/thread_priority.hpp> -#include <uhd/convert.hpp> -#include <uhd/utils/safe_main.hpp> -#include <uhd/usrp/multi_usrp.hpp> -#include <boost/program_options.hpp> -#include <boost/format.hpp> -#include <boost/thread/thread.hpp> -#include <boost/math/special_functions/round.hpp> -#include <iostream> -#include <complex> - -namespace po = boost::program_options; - -/*********************************************************************** - * Test result variables - **********************************************************************/ -unsigned long long num_overflows = 0; -unsigned long long num_underflows = 0; -unsigned long long num_rx_samps = 0; -unsigned long long num_tx_samps = 0; -unsigned long long num_dropped_samps = 0; -unsigned long long num_seq_errors = 0; - -/*********************************************************************** - * RX Hammer - **********************************************************************/ -void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd::rx_streamer::sptr rx_stream){ - uhd::set_thread_priority_safe(); - - //print pre-test summary - std::cout << boost::format( - "Testing receive rate %f Msps" - ) % (usrp->get_rx_rate()/1e6) << std::endl; - - //setup variables and allocate buffer - uhd::rx_metadata_t md; - const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); - std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(rx_cpu)); - std::vector<void *> buffs; - for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) - buffs.push_back(&buff.front()); //same buffer for each channel - bool had_an_overflow = false; - uhd::time_spec_t last_time; - const double rate = usrp->get_rx_rate(); - double timeout = 1; - - uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); - cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.05); - cmd.stream_now = (buffs.size() == 1); - srand( time(NULL) ); - - while (not boost::this_thread::interruption_requested()){ - cmd.num_samps = rand() % 100000; - rx_stream->issue_stream_cmd(cmd); - num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md, timeout, true); - - //handle the error codes - switch(md.error_code){ - case uhd::rx_metadata_t::ERROR_CODE_NONE: - if (had_an_overflow){ - had_an_overflow = false; - num_dropped_samps += boost::math::iround((md.time_spec - last_time).get_real_secs()*rate); - } - break; - - case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: - had_an_overflow = true; - last_time = md.time_spec; - if (!md.out_of_sequence) - num_overflows++; - break; - - default: - std::cerr << "Receiver error: " << md.strerror() << std::endl; - std::cerr << "Unexpected error on recv, continuing..." << std::endl; - break; - } - } -} - -/*********************************************************************** - * TX Hammer - **********************************************************************/ -void tx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, uhd::tx_streamer::sptr tx_stream){ - uhd::set_thread_priority_safe(); - - uhd::tx_metadata_t md; - const size_t max_samps_per_packet = tx_stream->get_max_num_samps(); - std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(tx_cpu)); - std::vector<void *> buffs; - for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) - buffs.push_back(&buff.front()); //same buffer for each channel - - //print pre-test summary - std::cout << boost::format( - "Testing transmit rate %f Msps" - ) % (usrp->get_tx_rate()/1e6) << std::endl; - - //setup variables and allocate buffer - std::srand( time(NULL) ); - while(not boost::this_thread::interruption_requested()){ - size_t total_num_samps = rand() % 100000; - size_t num_acc_samps = 0; - float timeout = 1; - - usrp->set_time_now(uhd::time_spec_t(0.0)); - while(num_acc_samps < total_num_samps){ - - //send a single packet - num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md, timeout); - - num_acc_samps += std::min(total_num_samps-num_acc_samps, tx_stream->get_max_num_samps()); - } - //send a mini EOB packet - md.end_of_burst = true; - tx_stream->send("", 0, md); - } -} - -void tx_hammer_async_helper(uhd::tx_streamer::sptr tx_stream){ - //setup variables and allocate buffer - uhd::async_metadata_t async_md; - - while (not boost::this_thread::interruption_requested()){ - - if (not tx_stream->recv_async_msg(async_md)) continue; - - //handle the error codes - switch(async_md.event_code){ - case uhd::async_metadata_t::EVENT_CODE_BURST_ACK: - return; - - case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW: - case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET: - num_underflows++; - break; - - case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR: - case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST: - num_seq_errors++; - break; - - default: - std::cerr << "Event code: " << async_md.event_code << std::endl; - std::cerr << "Unexpected event on async recv, continuing..." << std::endl; - break; - } - } -} - -/*********************************************************************** - * Main code + dispatcher - **********************************************************************/ -int UHD_SAFE_MAIN(int argc, char *argv[]){ - uhd::set_thread_priority_safe(); - - //variables to be set by po - std::string args; - double duration; - double rx_rate, tx_rate; - std::string rx_otw, tx_otw; - std::string rx_cpu, tx_cpu; - std::string mode; - - //setup the program options - po::options_description desc("Allowed options"); - desc.add_options() - ("help", "help message") - ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") - ("duration", po::value<double>(&duration)->default_value(10.0), "if random, specify duration for the test in seconds") - ("rx_rate", po::value<double>(&rx_rate), "specify to perform a RX rate test (sps)") - ("tx_rate", po::value<double>(&tx_rate), "specify to perform a TX rate test (sps)") - ("rx_otw", po::value<std::string>(&rx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for RX") - ("tx_otw", po::value<std::string>(&tx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for TX") - ("rx_cpu", po::value<std::string>(&rx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for RX") - ("tx_cpu", po::value<std::string>(&tx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for TX") - ("mode", po::value<std::string>(&mode)->default_value("none"), "multi-channel sync mode option: none, mimo") - ; - po::variables_map vm; - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); - - //print the help message - if (vm.count("help") or (vm.count("rx_rate") + vm.count("tx_rate")) == 0){ - //std::cout << boost::format("UHD Transport Hammer - %s") % desc << std::endl; - std::cout << - "UHD Transport Hammer: a transport layer stress test that continuously\n" - "calls for random amounts of TX and RX samples\n\n"; - std::cout << desc << std::endl << - " Specify --rx_rate for a receive-only test.\n" - " Specify --tx_rate for a transmit-only test.\n" - " Specify both options for a full-duplex test.\n" - << std::endl; - return ~0; - } - - //create a usrp device - std::cout << std::endl; - uhd::device_addrs_t device_addrs = uhd::device::find(args, uhd::device::USRP); - if (not device_addrs.empty() and device_addrs.at(0).get("type", "") == "usrp1"){ - std::cerr << "*** Warning! ***" << std::endl; - std::cerr << "Results will be inaccurate on USRP1 due to insufficient features.\n" << std::endl; - } - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; - - if (mode == "mimo"){ - usrp->set_clock_source("mimo", 0); - usrp->set_time_source("mimo", 0); - boost::this_thread::sleep(boost::posix_time::seconds(1)); - } - - boost::thread_group thread_group; - - //spawn the receive test thread - if (vm.count("rx_rate")){ - usrp->set_rx_rate(rx_rate); - //create a receive streamer - uhd::stream_args_t stream_args(rx_cpu, rx_otw); - for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping - stream_args.channels.push_back(ch); - uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); - thread_group.create_thread(boost::bind(&rx_hammer, usrp, rx_cpu, rx_stream)); - } - - //spawn the transmit test thread - if (vm.count("tx_rate")){ - usrp->set_tx_rate(tx_rate); - //create a transmit streamer - uhd::stream_args_t stream_args(tx_cpu, tx_otw); - for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping - stream_args.channels.push_back(ch); - uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); - thread_group.create_thread(boost::bind(&tx_hammer, usrp, tx_cpu, tx_stream)); - thread_group.create_thread(boost::bind(&tx_hammer_async_helper, tx_stream)); - } - - //sleep for the required duration - const long secs = long(duration); - const long usecs = long((duration - secs)*1e6); - boost::this_thread::sleep(boost::posix_time::seconds(secs) + boost::posix_time::microseconds(usecs)); - - //interrupt and join the threads - thread_group.interrupt_all(); - thread_group.join_all(); - - //print summary - std::cout << std::endl << boost::format( - "Transport Hammer summary:\n" - " Num received samples: %u\n" - " Num dropped samples: %u\n" - " Num overflows detected: %u\n" - " Num transmitted samples: %u\n" - " Num sequence errors: %u\n" - " Num underflows detected: %u\n" - ) % num_rx_samps % num_dropped_samps % num_overflows % num_tx_samps % num_seq_errors % num_underflows << std::endl; - - //finished - std::cout << std::endl << "Done!" << std::endl << std::endl; - - return EXIT_SUCCESS; -} diff --git a/host/examples/tx_bursts.cpp b/host/examples/tx_bursts.cpp index fec89a0e4..bb71d4581 100644 --- a/host/examples/tx_bursts.cpp +++ b/host/examples/tx_bursts.cpp @@ -148,7 +148,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ size_t num_tx_samps = tx_stream->send( buffs, samps_to_send, md, timeout ); - //do not use time spec for subsequent packets md.has_time_spec = false; md.start_of_burst = false; diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index 7e633262c..9254c3b24 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include "wavetable.hpp" #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/utils/static.hpp> @@ -28,9 +29,7 @@ #include <boost/lexical_cast.hpp> #include <boost/algorithm/string.hpp> #include <iostream> -#include <complex> #include <csignal> -#include <cmath> namespace po = boost::program_options; @@ -41,52 +40,6 @@ static bool stop_signal_called = false; void sig_int_handler(int){stop_signal_called = true;} /*********************************************************************** - * Waveform generators - **********************************************************************/ -static const size_t wave_table_len = 8192; - -class wave_table_class{ -public: - wave_table_class(const std::string &wave_type, const float ampl): - _wave_table(wave_table_len) - { - //compute real wave table with 1.0 amplitude - std::vector<double> real_wave_table(wave_table_len); - if (wave_type == "CONST"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = 1.0; - } - else if (wave_type == "SQUARE"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = (i < wave_table_len/2)? 0.0 : 1.0; - } - else if (wave_type == "RAMP"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = 2.0*i/(wave_table_len-1) - 1.0; - } - else if (wave_type == "SINE"){ - static const double tau = 2*std::acos(-1.0); - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = std::sin((tau*i)/wave_table_len); - } - else throw std::runtime_error("unknown waveform type: " + wave_type); - - //compute i and q pairs with 90% offset and scale to amplitude - for (size_t i = 0; i < wave_table_len; i++){ - const size_t q = (i+(3*wave_table_len)/4)%wave_table_len; - _wave_table[i] = std::complex<float>(ampl*real_wave_table[i], ampl*real_wave_table[q]); - } - } - - inline std::complex<float> operator()(const size_t index) const{ - return _wave_table[index % wave_table_len]; - } - -private: - std::vector<std::complex<float> > _wave_table; -}; - -/*********************************************************************** * Main function **********************************************************************/ int UHD_SAFE_MAIN(int argc, char *argv[]){ diff --git a/host/examples/txrx_loopback_to_file.cpp b/host/examples/txrx_loopback_to_file.cpp index efa23c410..7dc3bd9c2 100644 --- a/host/examples/txrx_loopback_to_file.cpp +++ b/host/examples/txrx_loopback_to_file.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include "wavetable.hpp" #include <uhd/types/tune_request.hpp> #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> @@ -31,7 +32,6 @@ #include <iostream> #include <fstream> #include <csignal> -#include <cmath> namespace po = boost::program_options; @@ -61,51 +61,6 @@ std::string generate_out_filename(const std::string &base_fn, size_t n_names, si return base_fn_fp.string(); } -/*********************************************************************** - * Waveform generators - **********************************************************************/ -static const size_t wave_table_len = 8192; - -class wave_table_class{ -public: - wave_table_class(const std::string &wave_type, const float ampl): - _wave_table(wave_table_len) - { - //compute real wave table with 1.0 amplitude - std::vector<double> real_wave_table(wave_table_len); - if (wave_type == "CONST"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = 1.0; - } - else if (wave_type == "SQUARE"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = (i < wave_table_len/2)? 0.0 : 1.0; - } - else if (wave_type == "RAMP"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = 2.0*i/(wave_table_len-1) - 1.0; - } - else if (wave_type == "SINE"){ - static const double tau = 2*std::acos(-1.0); - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = std::sin((tau*i)/wave_table_len); - } - else throw std::runtime_error("unknown waveform type: " + wave_type); - - //compute i and q pairs with 90% offset and scale to amplitude - for (size_t i = 0; i < wave_table_len; i++){ - const size_t q = (i+(3*wave_table_len)/4)%wave_table_len; - _wave_table[i] = std::complex<float>(ampl*real_wave_table[i], ampl*real_wave_table[q]); - } - } - - inline std::complex<float> operator()(const size_t index) const{ - return _wave_table[index % wave_table_len]; - } - -private: - std::vector<std::complex<float> > _wave_table; -}; /*********************************************************************** * transmit_worker function diff --git a/host/examples/wavetable.hpp b/host/examples/wavetable.hpp new file mode 100644 index 000000000..d7ffc8406 --- /dev/null +++ b/host/examples/wavetable.hpp @@ -0,0 +1,66 @@ +// +// Copyright 2010-2012,2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <string> +#include <cmath> +#include <complex> +#include <vector> +#include <stdexcept> + +static const size_t wave_table_len = 8192; + +class wave_table_class{ +public: + wave_table_class(const std::string &wave_type, const float ampl): + _wave_table(wave_table_len) + { + //compute real wave table with 1.0 amplitude + std::vector<double> real_wave_table(wave_table_len); + if (wave_type == "CONST"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = 1.0; + } + else if (wave_type == "SQUARE"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = (i < wave_table_len/2)? 0.0 : 1.0; + } + else if (wave_type == "RAMP"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = 2.0*i/(wave_table_len-1) - 1.0; + } + else if (wave_type == "SINE"){ + static const double tau = 2*std::acos(-1.0); + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = std::sin((tau*i)/wave_table_len); + } + else throw std::runtime_error("unknown waveform type: " + wave_type); + + //compute i and q pairs with 90% offset and scale to amplitude + for (size_t i = 0; i < wave_table_len; i++){ + const size_t q = (i+(3*wave_table_len)/4)%wave_table_len; + _wave_table[i] = std::complex<float>(ampl*real_wave_table[i], ampl*real_wave_table[q]); + } + } + + inline std::complex<float> operator()(const size_t index) const{ + return _wave_table[index % wave_table_len]; + } + +private: + std::vector<std::complex<float> > _wave_table; +}; + diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index 7ecc4924a..8939cd773 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2011,2014-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -56,6 +56,13 @@ typedef ptrdiff_t ssize_t; #define UHD_DEPRECATED __declspec(deprecated) #define UHD_ALIGNED(x) __declspec(align(x)) #define UHD_UNUSED(x) x +#elif defined(__MINGW32__) + #define UHD_EXPORT __declspec(dllexport) + #define UHD_IMPORT __declspec(dllimport) + #define UHD_INLINE inline + #define UHD_DEPRECATED __declspec(deprecated) + #define UHD_ALIGNED(x) __declspec(align(x)) + #define UHD_UNUSED(x) x __attribute__((unused)) #elif defined(__GNUG__) && __GNUG__ >= 4 #define UHD_EXPORT __attribute__((visibility("default"))) #define UHD_IMPORT __attribute__((visibility("default"))) diff --git a/host/include/uhd/convert.hpp b/host/include/uhd/convert.hpp index d740d80fb..e42123b20 100644 --- a/host/include/uhd/convert.hpp +++ b/host/include/uhd/convert.hpp @@ -63,12 +63,13 @@ namespace uhd{ namespace convert{ typedef int priority_type; //! Identify a conversion routine in the registry - struct id_type : boost::equality_comparable<id_type>{ + struct UHD_API id_type : boost::equality_comparable<id_type>{ std::string input_format; size_t num_inputs; std::string output_format; size_t num_outputs; std::string to_pp_string(void) const; + std::string to_string(void) const; }; //! Implement equality_comparable interface diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index 2118674c6..623c179e9 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2014 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,11 +15,11 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # - UHD_INSTALL(FILES bounded_buffer.hpp bounded_buffer.ipp buffer_pool.hpp + chdr.hpp if_addrs.hpp udp_constants.hpp udp_simple.hpp diff --git a/host/include/uhd/transport/chdr.hpp b/host/include/uhd/transport/chdr.hpp new file mode 100644 index 000000000..5e8cd58a9 --- /dev/null +++ b/host/include/uhd/transport/chdr.hpp @@ -0,0 +1,113 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_CHDR_HPP +#define INCLUDED_UHD_TRANSPORT_CHDR_HPP + +#include <uhd/transport/vrt_if_packet.hpp> + +namespace uhd{ namespace transport{ namespace vrt{ + +/*! \brief CVITA/CHDR related function + * + * See \ref rtp_chdr for details on the CVITA/CHDR protocol. + * + * All packers take the host format into account. Choose the _le functions + * if the transport uses little endian format (e.g. PCIe) and the _be + * functions if the transport uses big endian format (e.g. Ethernet). + * + * Note 1: All packers assume there to be enough space at the address + * provided by \p packet_buff. See also \ref vrt_pack_contract. + * + * Note 2: All these packers assume the following options without checking them: + * - `if_packet_info.link_type == LINK_TYPE_CHDR` + * - `if_packet_info.has_cid == false` + * - `if_packet_info.has_sid == true` + * - `if_packet_info.has_tsi == false` + * - `if_packet_info.has_tlr == false` + * This relaxes some of \ref vrt_pack_contract, but adds the additional + * constraint that the input data must be CHDR. + * + * In the unpacker, these values will be set accordingly. + */ +namespace chdr{ + + //! The maximum number of 64-bit words in a CVITA header + static const size_t max_if_hdr_words64 = 2; // CHDR + tsf (fractional timestamp) + + /*! + * Pack a CHDR header from metadata (big endian format). + * + * See \ref vrt_pack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to write the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ + UHD_API void if_hdr_pack_be( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info + ); + + /*! + * Unpack a CHDR header to metadata (big endian format). + * + * See \ref vrt_unpack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to read the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ + UHD_API void if_hdr_unpack_be( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info + ); + + /*! + * Pack a CHDR header from metadata (little endian format). + * + * See \ref vrt_pack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to write the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ + UHD_API void if_hdr_pack_le( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info + ); + + /*! + * Unpack a CHDR header to metadata (little endian format). + * + * See \ref vrt_unpack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to read the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ + UHD_API void if_hdr_unpack_le( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info + ); + +} //namespace chdr + +}}} //namespace uhd::transport::vrt + +#endif /* INCLUDED_UHD_TRANSPORT_CHDR_HPP */ + diff --git a/host/include/uhd/transport/nirio/nirio_driver_iface.h b/host/include/uhd/transport/nirio/nirio_driver_iface.h index 83afd816a..c562f0ca5 100644 --- a/host/include/uhd/transport/nirio/nirio_driver_iface.h +++ b/host/include/uhd/transport/nirio/nirio_driver_iface.h @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,10 +24,14 @@ #include <uhd/transport/nirio/status.h> #include <uhd/config.hpp> #if defined(UHD_PLATFORM_WIN32) - #include <Windows.h> - #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union - #include <WinIoCtl.h> - #pragma warning(default:4201) + #include <windows.h> + #ifdef _MSC_VER + #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union + #endif + #include <winioctl.h> + #ifdef _MSC_VER + #pragma warning(default:4201) + #endif #elif !defined(UHD_PLATFORM_LINUX) #include <IOKit/IOKitLib.h> #endif diff --git a/host/include/uhd/transport/nirio/nirio_fifo.h b/host/include/uhd/transport/nirio/nirio_fifo.h index c424275fc..5a2e29631 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.h +++ b/host/include/uhd/transport/nirio/nirio_fifo.h @@ -59,8 +59,8 @@ public: inline const std::string& get_name() const { return _name; } inline uint32_t get_channel() const { return _fifo_channel; } - inline uint32_t get_direction() const { return _fifo_direction; } - inline uint32_t get_scalar_type() const { return _datatype_info.scalar_type; } + inline fifo_direction_t get_direction() const { return _fifo_direction; } + inline nirio_scalar_type_t get_scalar_type() const { return _datatype_info.scalar_type; } nirio_status start(); diff --git a/host/include/uhd/transport/usb_device_handle.hpp b/host/include/uhd/transport/usb_device_handle.hpp index fdea9e2be..bf122f549 100644 --- a/host/include/uhd/transport/usb_device_handle.hpp +++ b/host/include/uhd/transport/usb_device_handle.hpp @@ -41,6 +41,7 @@ namespace uhd { namespace transport { class UHD_API usb_device_handle : boost::noncopyable { public: typedef boost::shared_ptr<usb_device_handle> sptr; + typedef std::pair<boost::uint16_t, boost::uint16_t> vid_pid_pair_t; /*! * Return the device's serial number @@ -83,6 +84,8 @@ public: * \return a vector of USB device handles that match vid and pid */ static std::vector<usb_device_handle::sptr> get_device_list(boost::uint16_t vid, boost::uint16_t pid); + static std::vector<usb_device_handle::sptr> get_device_list(const std::vector<usb_device_handle::vid_pid_pair_t>& vid_pid_pair_list); + }; //namespace usb diff --git a/host/include/uhd/transport/vrt_if_packet.hpp b/host/include/uhd/transport/vrt_if_packet.hpp index 362531567..1e54607c1 100644 --- a/host/include/uhd/transport/vrt_if_packet.hpp +++ b/host/include/uhd/transport/vrt_if_packet.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -52,9 +52,18 @@ namespace vrt{ //packet type enum packet_type_t { + // VRT language: PACKET_TYPE_DATA = 0x0, PACKET_TYPE_IF_EXT = 0x1, - PACKET_TYPE_CONTEXT = 0x2 //extension context: has_sid = true + PACKET_TYPE_CONTEXT = 0x2, //extension context: has_sid = true + + // CVITA language: + //PACKET_TYPE_DATA = 0x0, // Data + PACKET_TYPE_FC = 0x1, // Flow control + PACKET_TYPE_ACK = 0x1, // Flow control (ack) + PACKET_TYPE_CMD = 0x2, // Command + PACKET_TYPE_RESP = 0x3, // Command response + PACKET_TYPE_ERROR = 0x3 // Command response: Error (the EOB bit is raised in this case) } packet_type; //size fields @@ -65,18 +74,46 @@ namespace vrt{ //header fields size_t packet_count; + //! Asserted for start- or end-of-burst bool sob, eob; + //! This is asserted for command responses that are errors (CHDR only) + bool error; //optional fields + //! Stream ID (SID). See uhd::sid_t bool has_sid; boost::uint32_t sid; + //! Class ID. bool has_cid; boost::uint64_t cid; + //! Integer timestamp bool has_tsi; boost::uint32_t tsi; + //! Fractional timestamp bool has_tsf; boost::uint64_t tsf; + //! Trailer bool has_tlr; boost::uint32_t tlr; }; /*! * Pack a vrt header from metadata (big endian format). + * + * \section vrt_pack_contract Packing contract + * + * \subsection Requirements: + * - packet_buff points to a valid address space with enough space to write + * the entire buffer, regardless of its length. At the very least, it must + * be able to hold an entire header. + * - `if_packet_info` has the following members set to correct values: + * - `has_*` members all set accordingly + * - For every true `has_*` member, the corresponding variable holds a valid + * value (e.g. if `has_sid` is true, `sid` contains a valid SID) + * - `num_payload_bytes` and `num_payload_words32` are both set to the correct values + * + * \subsection Result: + * - `packet_buff` now points to a valid header that can be sent over the transport + * without further modification + * - The following members on `if_packet_info` are set: + * - `num_header_words32` + * - `num_packet_words32` + * * \param packet_buff memory to write the packed vrt header * \param if_packet_info the if packet info (read/write) */ @@ -87,6 +124,34 @@ namespace vrt{ /*! * Unpack a vrt header to metadata (big endian format). + * + * \section vrt_unpack_contract Unpacking contract + * + * \subsection Requirements + * - `packet_buff` points to a readable address space with a + * CHDR packet, starting at the header. `packet_buff[0]` *must* always + * point to a valid first word of the header. This implies that num_packet_words32 + * must be at least 1. + * - `if_packet_info` has the following members set to correct values: + * - `num_packet_words32`. This means all values `packet_buff[0]` + * through `packet_buff[if_packet_info.num_packet_words32-1]` are + * readable words from this packet. + * - `link_type` + * + * \subsection Result + * - `if_packet_info` now has the following values set to correct values: + * - `packet_type` + * - `num_payload_bytes` + * - `num_payload_words32` + * - `num_header_words32` + * - `has_*` + * - `sob`, `eob`, `error`, `cid`, `sid` (if applicable) + * - `tsf`, `tsi` (if applicable) + * + * \subsection Exceptions + * - If the header is invalid, but the requirements are still met, + * will throw a uhd::value_error. + * * \param packet_buff memory to read the packed vrt header * \param if_packet_info the if packet info (read/write) */ @@ -97,6 +162,9 @@ namespace vrt{ /*! * Pack a vrt header from metadata (little endian format). + * + * See \ref vrt_pack_contract. + * * \param packet_buff memory to write the packed vrt header * \param if_packet_info the if packet info (read/write) */ @@ -107,6 +175,9 @@ namespace vrt{ /*! * Unpack a vrt header to metadata (little endian format). + * + * See \ref vrt_unpack_contract. + * * \param packet_buff memory to read the packed vrt header * \param if_packet_info the if packet info (read/write) */ @@ -124,6 +195,7 @@ namespace vrt{ num_packet_words32(0), packet_count(0), sob(false), eob(false), + error(false), has_sid(false), sid(0), has_cid(false), cid(0), has_tsi(false), tsi(0), diff --git a/host/include/uhd/types/CMakeLists.txt b/host/include/uhd/types/CMakeLists.txt index 8bb1de381..2a25df35f 100644 --- a/host/include/uhd/types/CMakeLists.txt +++ b/host/include/uhd/types/CMakeLists.txt @@ -17,6 +17,7 @@ UHD_INSTALL(FILES + byte_vector.hpp clock_config.hpp device_addr.hpp dict.ipp @@ -31,11 +32,13 @@ UHD_INSTALL(FILES ref_vector.hpp sensors.hpp serial.hpp + sid.hpp stream_cmd.hpp time_spec.hpp tune_request.hpp tune_result.hpp wb_iface.hpp + filters.hpp DESTINATION ${INCLUDE_DIR}/uhd/types COMPONENT headers ) diff --git a/host/include/uhd/types/byte_vector.hpp b/host/include/uhd/types/byte_vector.hpp new file mode 100644 index 000000000..b7637fb5d --- /dev/null +++ b/host/include/uhd/types/byte_vector.hpp @@ -0,0 +1,48 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_BYTE_VECTOR_HPP +#define INCLUDED_UHD_TYPES_BYTE_VECTOR_HPP + +#include <algorithm> +#include <string> +#include <vector> + +#include <boost/assign.hpp> +#include <boost/cstdint.hpp> + +#include <uhd/config.hpp> + +namespace uhd{ + + //! Byte vector used for I2C data passing and EEPROM parsing. + typedef std::vector<boost::uint8_t> byte_vector_t; + + template<typename RangeSrc, typename RangeDst> UHD_INLINE + void byte_copy(const RangeSrc &src, RangeDst &dst){ + std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); + } + + //! Create a string from a byte vector, terminate when invalid ASCII encountered + UHD_API std::string bytes_to_string(const byte_vector_t &bytes); + + //! Create a byte vector from a string, end at null terminator or max length + UHD_API byte_vector_t string_to_bytes(const std::string &str, size_t max_length); + +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_BYTE_VECTOR_HPP */ diff --git a/host/include/uhd/types/dict.hpp b/host/include/uhd/types/dict.hpp index 97fa8f09c..51e3e1814 100644 --- a/host/include/uhd/types/dict.hpp +++ b/host/include/uhd/types/dict.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2011,2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -117,6 +117,23 @@ namespace uhd{ */ Val pop(const Key &key); + /*! Update this dictionary with values from another. + * + * Basically, this copies all the key/value pairs from \p new_dict + * into this dict. When the key is already present in the current + * dict, it either overwrites the current value (if \p fail_on_conflict + * is false) or it throws (if \p fail_on_conflict is true *and* the + * values differ). + * + * With the exception of \p fail_on_conflict, this behaves analogously + * to Python's dict.update() method. + * + * \param new_args The arguments to copy. + * \param fail_on_conflict If true, throws. + * \throws uhd::value_error + */ + void update(const dict<Key, Val> &new_dict, bool fail_on_conflict=true); + private: typedef std::pair<Key, Val> pair_t; std::list<pair_t> _map; //private container diff --git a/host/include/uhd/types/dict.ipp b/host/include/uhd/types/dict.ipp index 5e9cf97ad..5fd4b536e 100644 --- a/host/include/uhd/types/dict.ipp +++ b/host/include/uhd/types/dict.ipp @@ -135,6 +135,20 @@ namespace uhd{ throw key_not_found<Key, Val>(key); } + template <typename Key, typename Val> + void dict<Key, Val>::update(const dict<Key, Val> &new_dict, bool fail_on_conflict) + { + BOOST_FOREACH(const Key &key, new_dict.keys()) { + if (fail_on_conflict and has_key(key) and get(key) != new_dict[key]) { + throw uhd::value_error(str( + boost::format("Option merge conflict: %s:%s != %s:%s") + % key % get(key) % key % new_dict[key] + )); + } + set(key, new_dict[key]); + } + } + } //namespace uhd #endif /* INCLUDED_UHD_TYPES_DICT_IPP */ diff --git a/host/include/uhd/types/direction.hpp b/host/include/uhd/types/direction.hpp index 0f257de44..59ee9b55f 100644 --- a/host/include/uhd/types/direction.hpp +++ b/host/include/uhd/types/direction.hpp @@ -1,5 +1,5 @@ // -// Copyright 2015 Ettus Research LLC +// Copyright 2014-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by diff --git a/host/include/uhd/types/filters.hpp b/host/include/uhd/types/filters.hpp new file mode 100644 index 000000000..976ae233d --- /dev/null +++ b/host/include/uhd/types/filters.hpp @@ -0,0 +1,286 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_FILTERS_HPP +#define INCLUDED_UHD_TYPES_FILTERS_HPP + +#include <uhd/config.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/scoped_array.hpp> +#include <string> +#include <vector> +#include <iostream> +#include <ostream> +#include <sstream> + +namespace uhd{ + + class UHD_API filter_info_base + { + public: + typedef boost::shared_ptr<filter_info_base> sptr; + enum filter_type + { + ANALOG_LOW_PASS, + ANALOG_BAND_PASS, + DIGITAL_I16, + DIGITAL_FIR_I16 + }; + + filter_info_base( + filter_type type, + bool bypass, + size_t position_index + ): + _type(type), _bypass(bypass), + _position_index(position_index) + { + //NOP + } + + inline virtual bool is_bypassed() + { + return _bypass; + } + + inline filter_type get_type() + { + return _type; + } + + virtual ~filter_info_base() + { + //NOP + } + + virtual std::string to_pp_string(); + + protected: + filter_type _type; + bool _bypass; + size_t _position_index; + + }; + + UHD_API std::ostream& operator<<(std::ostream& os, filter_info_base& f); + + class UHD_API analog_filter_base : public filter_info_base + { + std::string _analog_type; + public: + typedef boost::shared_ptr<analog_filter_base> sptr; + analog_filter_base( + filter_type type, + bool bypass, + size_t position_index, + const std::string& analog_type + ): + filter_info_base(type, bypass, position_index), + _analog_type(analog_type) + { + //NOP + } + + inline const std::string& get_analog_type() + { + return _analog_type; + } + + virtual std::string to_pp_string(); + }; + + class UHD_API analog_filter_lp : public analog_filter_base + { + double _cutoff; + double _rolloff; + + public: + typedef boost::shared_ptr<analog_filter_lp> sptr; + analog_filter_lp( + filter_type type, + bool bypass, + size_t position_index, + const std::string& analog_type, + double cutoff, + double rolloff + ): + analog_filter_base(type, bypass, position_index, analog_type), + _cutoff(cutoff), + _rolloff(rolloff) + { + //NOP + } + + inline double get_cutoff() + { + return _cutoff; + } + + inline double get_rolloff() + { + return _cutoff; + } + + inline void set_cutoff(const double cutoff) + { + _cutoff = cutoff; + } + + virtual std::string to_pp_string(); + }; + + template<typename tap_t> + class UHD_API digital_filter_base : public filter_info_base + { + protected: + double _rate; + boost::uint32_t _interpolation; + boost::uint32_t _decimation; + tap_t _tap_full_scale; + boost::uint32_t _max_num_taps; + std::vector<tap_t> _taps; + + public: + typedef boost::shared_ptr<digital_filter_base> sptr; + digital_filter_base( + filter_type type, + bool bypass, + size_t position_index, + double rate, + size_t interpolation, + size_t decimation, + double tap_full_scale, + size_t max_num_taps, + const std::vector<tap_t>& taps + ): + filter_info_base(type, bypass, position_index), + _rate(rate), + _interpolation(interpolation), + _decimation(decimation), + _tap_full_scale(tap_full_scale), + _max_num_taps(max_num_taps), + _taps(taps) + { + //NOP + } + + inline double get_output_rate() + { + return (_bypass ? _rate : (_rate / _decimation * _interpolation)); + } + + inline double get_input_rate() + { + return _rate; + } + + inline double get_interpolation() + { + return _interpolation; + } + + inline double get_decimation() + { + return _decimation; + } + + inline double get_tap_full_scale() + { + return _tap_full_scale; + } + + inline std::vector<tap_t>& get_taps() + { + return _taps; + } + + virtual std::string to_pp_string() + { + std::ostringstream os; + os<<filter_info_base::to_pp_string()<< + "\t[digital_filter_base]"<<std::endl<< + "\tinput rate: "<<_rate<<std::endl<< + "\tinterpolation: "<<_interpolation<<std::endl<< + "\tdecimation: "<<_decimation<<std::endl<< + "\tfull-scale: "<<_tap_full_scale<<std::endl<< + "\tmax num taps: "<<_max_num_taps<<std::endl<< + "\ttaps: "<<std::endl; + + os<<"\t\t"; + for(size_t i = 0; i < _taps.size(); i++) + { + os<<"(tap "<<i<<": "<<_taps[i]<<")"; + if( ((i%10) == 0) && (i != 0)) + { + os<<std::endl<<"\t\t"; + } + } + os<<std::endl; + return std::string(os.str()); + } + + }; + + template<typename tap_t> + class UHD_API digital_filter_fir : public digital_filter_base<tap_t> + { + public: + typedef boost::shared_ptr<digital_filter_fir<tap_t> > sptr; + + digital_filter_fir( + filter_info_base::filter_type type, + bool bypass, size_t position_index, + double rate, + size_t interpolation, + size_t decimation, + size_t tap_bit_width, + size_t max_num_taps, + const std::vector<tap_t>& taps + ): + digital_filter_base<tap_t>(type, bypass, position_index, rate, interpolation, decimation, tap_bit_width, max_num_taps, taps) + { + //NOP + } + + void set_taps(const std::vector<tap_t>& taps) + { + std::size_t num_taps = taps.size(); + if(num_taps < this->_max_num_taps){ + UHD_MSG(warning) << "digital_filter_fir::set_taps not enough coefficients. Appending zeros"; + std::vector<tap_t> coeffs; + for (size_t i = 0; i < this->_max_num_taps; i++) + { + if(i < num_taps) + { + coeffs.push_back(taps[i]); + } else { + coeffs.push_back(0); + } + } + this->_taps = coeffs; + } else { + this->_taps = taps; + } + } + }; + +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_FILTERS_HPP */ diff --git a/host/include/uhd/types/sid.hpp b/host/include/uhd/types/sid.hpp new file mode 100644 index 000000000..95034c7a5 --- /dev/null +++ b/host/include/uhd/types/sid.hpp @@ -0,0 +1,238 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_SID_HPP +#define INCLUDED_UHD_TYPES_SID_HPP + +#include <uhd/config.hpp> +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> +#include <iostream> + +namespace uhd { + /*! + * \brief Represents a stream ID (SID). + * + * A stream ID (SID) is an identifier for data. + * It is a 32-Bit value which consistst of 16 Bits + * for the source address and 16 Bits for the destination + * address. + * Every address is split into two parts: The _address_, which + * identifies the device used, and the _endpoint_, which identifies + * a specific object inside the given device (e.g., a block). + * *Note:* In the case where there are several crossbars on a single + * device, each crossbar gets its own address. + * Both address and endpoint are 8 bits in length. If a 16-bit address + * is required, we use the combination of the 8-bit address and the 8-bit + * endpoint. + * + * \section sid_str_repr String Representation + * + * The string representation of a SID is of the form + * + * 2.3>0.6 + * + * The '>' symbol shows the direction, so in this case, + * data is flowing from address 2.3 to 0.6. + * + * As a convention, ':' is used instead of '.' when giving the + * SID in hexadecimal numbers, and two characters are used for each + * address part. As an example, the following two SIDs are identical: + * + * 2.3>0.16 (decimal) + * 02:03>00:10 (hexadecimal) + * + * The format is: + * SRC_ADDRESS.SRC_ENDPOINT>DST_ADDRESS.DST_ENDPOINT + * + * + * \section sid_block_ports Block Ports + * + * In the special case where a block on a crossbar is addressed, the + * endpoint is further split up into two parts of four bits each: The + * first four bits specify the port number on the crossbar, whereas the + * lower four bits represent the *block port*. As an example, consider + * the following SID, given in hexadecimal: + * + * 00:10>02:A1 + * + * In this example, assume data is flowing from the host computer to an + * X300. The crossbar address is 02. The endpoint is A1, which means we + * are accessing a block on crossbar port A (the tenth port), and are addressing + * block port 1. + * + */ + class UHD_API sid_t + { + public: + //! Create an unset SID + sid_t(); + //! Create a sid_t object from a 32-Bit SID value + sid_t(boost::uint32_t sid); + //! Create a sid_t object from its four components + sid_t(boost::uint8_t src_addr, boost::uint8_t src_ep, boost::uint8_t dst_addr, boost::uint8_t dst_ep); + //! Convert a string representation of a SID into its numerical representation + sid_t(const std::string &); + + //! Return a decimal string representation of the SID. + std::string to_pp_string() const; + //! Return a hexadecimal string representation of the SID. + std::string to_pp_string_hex() const; + + //! Returns true if this actually holds a valid SID + bool is_set() const { return _set; }; + + // Getters + // + //! Alias for get_sid() + UHD_INLINE boost::uint32_t get() const { return get_sid(); }; + //! Returns a 32-Bit representation of the SID if set, or zero otherwise. + UHD_INLINE boost::uint32_t get_sid() const { return _set ? _sid : 0; }; + //! Return the 16-bit source address of this SID + UHD_INLINE boost::uint32_t get_src() const { + return (_sid >> 16) & 0xFFFF; + } + //! Return the 16-bit destination address of this SID + UHD_INLINE boost::uint32_t get_dst() const { + return _sid & 0xFFFF; + } + //! Return 8-bit address of the source + UHD_INLINE boost::uint32_t get_src_addr() const { + return (get_src() >> 8) & 0xFF; + } + //! Return endpoint of the source + UHD_INLINE boost::uint32_t get_src_endpoint() const { + return get_src() & 0xFF; + } + //! Return crossbar port of the source + UHD_INLINE boost::uint32_t get_src_xbarport() const { + return (get_src_endpoint() >> 4) & 0xF; + } + //! Return block port of the source + UHD_INLINE boost::uint32_t get_src_blockport() const { + return (get_src_endpoint()) & 0xF; + } + //! Return 8-bit address of the destination + UHD_INLINE boost::uint32_t get_dst_addr() const { + return (get_dst() >> 8) & 0xFF; + } + //! Return endpoint of the destination + UHD_INLINE boost::uint32_t get_dst_endpoint() const { + return get_dst() & 0xFF; + } + //! Return crossbar port of the source + UHD_INLINE boost::uint32_t get_dst_xbarport() const { + return (get_dst_endpoint() >> 4) & 0xF; + } + //! Return block port of the source + UHD_INLINE boost::uint32_t get_dst_blockport() const { + return (get_dst_endpoint()) & 0xF; + } + + // Setters + + //! Alias for set_sid() + void set(boost::uint32_t new_sid) { set_sid(new_sid); }; + //! Convert a string representation of a SID into a numerical one + // Throws uhd::value_error if the string is not a valid SID + // representation. + void set_from_str(const std::string &); + void set_sid(boost::uint32_t new_sid); + //! Set the source address of this SID + // (the first 16 Bits) + void set_src(boost::uint32_t new_addr); + //! Set the destination address of this SID + // (the last 16 Bits) + void set_dst(boost::uint32_t new_addr); + void set_src_addr(boost::uint32_t new_addr); + void set_src_endpoint(boost::uint32_t new_addr); + void set_dst_addr(boost::uint32_t new_addr); + void set_dst_endpoint(boost::uint32_t new_addr); + void set_dst_xbarport(boost::uint32_t new_xbarport); + void set_dst_blockport(boost::uint32_t new_blockport); + + // Manipulators + + //! Swaps dst and src address and returns the new SID. + sid_t reversed(); + + //! Swaps dst and src in-place. + void reverse(); + + // Overloaded operators + + sid_t operator = (boost::uint32_t new_sid) { + set_sid(new_sid); + return *this; + } + + sid_t operator = (sid_t &sid) { + set_sid(sid.get_sid()); + return *this; + } + + sid_t operator = (const std::string &sid_str) { + set_from_str(sid_str); + return *this; + } + + bool operator == (const sid_t &sid) const { + return (not _set and not sid.is_set()) or (_sid == sid.get_sid()); + } + + bool operator == (boost::uint32_t sid) const { + return _set and _sid == sid; + } + + bool operator == (const std::string &sid_str) const { + sid_t rhs(sid_str); + return *this == rhs; + } + + // overloaded type casts are tricky, but for now we'll need them + // for backward compatibility. consider them deprecated. + + //! If the SID is not set, always returns zero. + // Use is_set() to check if the return value is valid. + operator boost::uint32_t() const { + return get(); + } + + operator bool() const { + return _set; + } + + private: + boost::uint32_t _sid; + bool _set; + }; + + //! Stream output operator. Honors std::ios::hex. + UHD_INLINE std::ostream& operator<< (std::ostream& out, const sid_t &sid) { + std::ios_base::fmtflags ff = out.flags(); + if (ff & std::ios::hex) { + out << sid.to_pp_string_hex(); + } else { + out << sid.to_pp_string(); + } + return out; + } + +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_SID_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 7ac38f84b..f9c2ee407 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -22,6 +22,7 @@ #define UHD_USRP_MULTI_USRP_REF_SOURCES_API #define UHD_USRP_MULTI_USRP_GET_RATES_API #define UHD_USRP_MULTI_USRP_FRONTEND_CAL_API +#define UHD_USRP_MULTI_USRP_FRONTEND_IQ_AUTO_API #define UHD_USRP_MULTI_USRP_COMMAND_TIME_API #define UHD_USRP_MULTI_USRP_BW_RANGE_API #define UHD_USRP_MULTI_USRP_USER_REGS_API @@ -37,6 +38,7 @@ #include <uhd/types/tune_request.hpp> #include <uhd/types/tune_result.hpp> #include <uhd/types/sensors.hpp> +#include <uhd/types/filters.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <uhd/usrp/dboard_iface.hpp> #include <boost/shared_ptr.hpp> @@ -157,6 +159,11 @@ public: * If the specified rate is not available, this method will throw. * On other devices, this method notifies the software of the rate, * but requires the the user has made the necessary hardware change. + * + * If the device has an 'auto clock rate' setting (e.g. B200, see also + * \ref b200_auto_mcr), this will get disabled and the clock rate will be + * fixed to \p rate. + * * \param rate the new master clock rate in Hz * \param mboard the motherboard index 0 to M-1 */ @@ -492,6 +499,34 @@ public: } /*! + * Set the normalized RX gain value. + * + * The normalized gain is a value in [0, 1], where 0 is the + * smallest gain value available, and 1 is the largest, independent + * of the device. In between, gains are linearly interpolated. + * + * Check the individual device manual for notes on the gain range. + * + * Note that it is not possible to specify a gain name for + * this function, it will always set the overall gain. + * + * \param gain the normalized gain value + * \param chan the channel index 0 to N-1 + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual void set_normalized_rx_gain(double gain, size_t chan = 0) = 0; + + /*! + * Enable or disable the RX AGC module. + * Once this module is enabled manual gain settings will be ignored. + * The AGC will start in a default configuration which should be good for most use cases. + * Device specific configuration parameters can be found in the property tree. + * \param on Enable or Disable the AGC + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_agc(bool enable, size_t chan = 0) = 0; + + /*! * Get the RX gain value for the specified gain element. * For an empty name, sum across all gain elements. * \param name the name of the gain element @@ -506,6 +541,19 @@ public: } /*! + * Return the normalized RX gain value. + * + * See set_normalized_rx_gain() for a discussion of normalized + * gains. + * + * \param gain the normalized gain value + * \param chan the channel index 0 to N-1 + * \returns The normalized gain (in [0, 1]) + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual double get_normalized_rx_gain(size_t chan = 0) = 0; + + /*! * Get the RX gain range for the specified gain element. * For an empty name, calculate the overall gain range. * \param name the name of the gain element @@ -617,6 +665,14 @@ public: virtual void set_rx_dc_offset(const std::complex<double> &offset, size_t chan = ALL_CHANS) = 0; /*! + * Enable/disable the automatic IQ imbalance correction. + * + * \param enb true to enable automatic IQ balance correction + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_iq_balance(const bool enb, size_t chan) = 0; + + /*! * Set the RX frontend IQ imbalance correction. * Use this to adjust the magnitude and phase of I and Q. * @@ -730,6 +786,18 @@ public: } /*! + * Set the normalized TX gain value. + * + * See set_normalized_rx_gain() for a discussion on normalized + * gains. + * + * \param gain the normalized gain value + * \param chan the channel index 0 to N-1 + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual void set_normalized_tx_gain(double gain, size_t chan = 0) = 0; + + /*! * Get the TX gain value for the specified gain element. * For an empty name, sum across all gain elements. * \param name the name of the gain element @@ -744,6 +812,19 @@ public: } /*! + * Return the normalized TX gain value. + * + * See set_normalized_rx_gain() for a discussion of normalized + * gains. + * + * \param gain the normalized gain value + * \param chan the channel index 0 to N-1 + * \returns The normalized gain (in [0, 1]) + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual double get_normalized_tx_gain(size_t chan = 0) = 0; + + /*! * Get the TX gain range for the specified gain element. * For an empty name, calculate the overall gain range. * \param name the name of the gain element @@ -895,6 +976,38 @@ public: */ virtual boost::uint32_t get_gpio_attr(const std::string &bank, const std::string &attr, const size_t mboard = 0) = 0; + /******************************************************************* + * Filter API methods + ******************************************************************/ + + /*! + * Enumerate the available filters in the signal path. + * \param search_mask + * \parblock + * Select only certain filter names by specifying this search mask. + * + * E.g. if search mask is set to "rx_frontends/A" only filter names including that string will be returned. + * \endparblock + * \return a vector of strings representing the selected filter names. + */ + virtual std::vector<std::string> get_filter_names(const std::string &search_mask = "") = 0; + + /*! + * Return the filter object for the given name. + * \param path the name of the filter as returned from get_filter_names(). + * \return a filter_info_base::sptr. + */ + virtual filter_info_base::sptr get_filter(const std::string &path) = 0; + + /*! + * Write back a filter obtained by get_filter() to the signal path. + * This filter can be a modified version of the originally returned one. + * The information about Rx or Tx is contained in the path parameter. + * \param path the name of the filter as returned from get_filter_names(). + * \param filter the filter_info_base::sptr of the filter object to be written + */ + virtual void set_filter(const std::string &path, filter_info_base::sptr filter) = 0; + }; }} diff --git a/host/include/uhd/utils/math.hpp b/host/include/uhd/utils/math.hpp index 4f88494d6..46a1cf7e4 100644 --- a/host/include/uhd/utils/math.hpp +++ b/host/include/uhd/utils/math.hpp @@ -18,11 +18,11 @@ #ifndef INCLUDED_UHD_UTILS_MATH_HPP #define INCLUDED_UHD_UTILS_MATH_HPP +#include <cmath> #include <uhd/config.hpp> #include <boost/cstdint.hpp> #include <boost/numeric/conversion/bounds.hpp> - namespace uhd { /*! @@ -237,6 +237,16 @@ namespace fp_compare { == fp_compare::fp_compare_delta<double>(rhs, FREQ_COMPARISON_DELTA_HZ)); } + //! Portable log2() + template <typename float_t> UHD_INLINE + float_t log2(float_t x) + { + // C++11 defines std::log2(), when that's universally supported + // we can switch over. + return std::log(x) / std::log(float_t(2)); + } + + } // namespace math } // namespace uhd diff --git a/host/include/uhd/utils/paths.hpp b/host/include/uhd/utils/paths.hpp index 8edb87546..aa31fd5a2 100644 --- a/host/include/uhd/utils/paths.hpp +++ b/host/include/uhd/utils/paths.hpp @@ -87,7 +87,7 @@ namespace uhd { * The error string will include the full path to the utility to run. * \return the message suggesting the use of the named utility. */ - UHD_API std::string print_utility_error(std::string name); + UHD_API std::string print_utility_error(const std::string &name, const std::string &args=""); } //namespace uhd #endif /* INCLUDED_UHD_UTILS_PATHS_HPP */ diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index 329e94a4d..fd6c8497e 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -43,10 +43,10 @@ bool convert::operator==(const convert::id_type &lhs, const convert::id_type &rh std::string convert::id_type::to_pp_string(void) const{ return str(boost::format( "conversion ID\n" - " Input format: %s\n" - " Num inputs: %d\n" + " Input format: %s\n" + " Num inputs: %d\n" " Output format: %s\n" - " Num outputs: %d\n" + " Num outputs: %d\n" ) % this->input_format % this->num_inputs @@ -55,6 +55,15 @@ std::string convert::id_type::to_pp_string(void) const{ ); } +std::string convert::id_type::to_string(void) const{ + return str(boost::format("%s (%d) -> %s (%d)") + % this->input_format + % this->num_inputs + % this->output_format + % this->num_outputs + ); +} + /*********************************************************************** * Setup the table registry **********************************************************************/ @@ -92,7 +101,15 @@ convert::function_type convert::get_converter( //find a matching priority priority_type best_prio = -1; BOOST_FOREACH(priority_type prio_i, get_table()[id].keys()){ - if (prio_i == prio) return get_table()[id][prio]; + if (prio_i == prio) { + //----------------------------------------------------------------// + UHD_LOGV(always) << "get_converter: For converter ID: " << id.to_pp_string() << std::endl + << "Using prio: " << prio << std::endl + << std::endl + ; + //----------------------------------------------------------------// + return get_table()[id][prio]; + } best_prio = std::max(best_prio, prio_i); } @@ -100,6 +117,13 @@ convert::function_type convert::get_converter( if (prio != -1) throw uhd::key_error( "Cannot find a conversion routine [with prio] for " + id.to_pp_string()); + //----------------------------------------------------------------// + UHD_LOGV(always) << "get_converter: For converter ID: " << id.to_pp_string() << std::endl + << "Using prio: " << best_prio << std::endl + << std::endl + ; + //----------------------------------------------------------------// + //otherwise, return best prio return get_table()[id][best_prio]; } diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 5920f3d78..9ec8a5c0b 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -129,6 +129,7 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp ${CMAKE_CURRENT_SOURCE_DIR}/nirio_zero_copy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/chdr.cpp ) # Verbose Debug output for send/recv diff --git a/host/lib/transport/chdr.cpp b/host/lib/transport/chdr.cpp new file mode 100644 index 000000000..632887e56 --- /dev/null +++ b/host/lib/transport/chdr.cpp @@ -0,0 +1,182 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/chdr.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/exception.hpp> + +//define the endian macros to convert integers +#ifdef BOOST_BIG_ENDIAN + #define BE_MACRO(x) (x) + #define LE_MACRO(x) uhd::byteswap(x) +#else + #define BE_MACRO(x) uhd::byteswap(x) + #define LE_MACRO(x) (x) +#endif + +using namespace uhd::transport::vrt; + +static const boost::uint32_t HDR_FLAG_TSF = (1 << 29); +static const boost::uint32_t HDR_FLAG_EOB = (1 << 28); +static const boost::uint32_t HDR_FLAG_ERROR = (1 << 28); + +/***************************************************************************/ +/* Packing */ +/***************************************************************************/ +/*! Translate the contents of \p if_packet_info into a 32-Bit word and return it. + */ +UHD_INLINE boost::uint32_t _hdr_pack_chdr( + if_packet_info_t &if_packet_info +) { + // Set fields in if_packet_info + if_packet_info.num_header_words32 = 2 + (if_packet_info.has_tsf ? 2 : 0); + if_packet_info.num_packet_words32 = + if_packet_info.num_header_words32 + + if_packet_info.num_payload_words32; + + boost::uint16_t pkt_length = + if_packet_info.num_payload_bytes + (4 * if_packet_info.num_header_words32); + boost::uint32_t chdr = 0 + // 2 Bits: Packet type + | (if_packet_info.packet_type << 30) + // 1 Bit: Has time + | (if_packet_info.has_tsf ? HDR_FLAG_TSF : 0) + // 1 Bit: EOB or Error + | ((if_packet_info.eob or if_packet_info.error) ? HDR_FLAG_EOB : 0) + // 12 Bits: Sequence number + | ((if_packet_info.packet_count & 0xFFF) << 16) + // 16 Bits: Total packet length + | pkt_length; + return chdr; +} + +void chdr::if_hdr_pack_be( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Write header and update if_packet_info + packet_buff[0] = BE_MACRO(_hdr_pack_chdr(if_packet_info)); + + // Write SID + packet_buff[1] = BE_MACRO(if_packet_info.sid); + + // Write time + if (if_packet_info.has_tsf) { + packet_buff[2] = BE_MACRO(boost::uint32_t(if_packet_info.tsf >> 32)); + packet_buff[3] = BE_MACRO(boost::uint32_t(if_packet_info.tsf >> 0)); + } +} + +void chdr::if_hdr_pack_le( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Write header and update if_packet_info + packet_buff[0] = LE_MACRO(_hdr_pack_chdr(if_packet_info)); + + // Write SID + packet_buff[1] = LE_MACRO(if_packet_info.sid); + + // Write time + if (if_packet_info.has_tsf) { + packet_buff[2] = LE_MACRO(boost::uint32_t(if_packet_info.tsf >> 32)); + packet_buff[3] = LE_MACRO(boost::uint32_t(if_packet_info.tsf >> 0)); + } +} + + +/***************************************************************************/ +/* Unpacking */ +/***************************************************************************/ +UHD_INLINE void _hdr_unpack_chdr( + const boost::uint32_t chdr, + if_packet_info_t &if_packet_info +) { + // Set constant members + if_packet_info.link_type = if_packet_info_t::LINK_TYPE_CHDR; + if_packet_info.has_cid = false; + if_packet_info.has_sid = true; + if_packet_info.has_tsi = false; + if_packet_info.has_tlr = false; + if_packet_info.sob = false; + + // Set configurable members + if_packet_info.has_tsf = (chdr & HDR_FLAG_TSF) > 0; + if_packet_info.packet_type = if_packet_info_t::packet_type_t((chdr >> 30) & 0x3); + if_packet_info.eob = (if_packet_info.packet_type == if_packet_info_t::PACKET_TYPE_DATA) + && ((chdr & HDR_FLAG_EOB) > 0); + if_packet_info.error = (if_packet_info.packet_type == if_packet_info_t::PACKET_TYPE_RESP) + && ((chdr & HDR_FLAG_ERROR) > 0); + if_packet_info.packet_count = (chdr >> 16) & 0xFFF; + + // Set packet length variables + if (if_packet_info.has_tsf) { + if_packet_info.num_header_words32 = 4; + } else { + if_packet_info.num_header_words32 = 2; + } + size_t pkt_size_bytes = (chdr & 0xFFFF); + size_t pkt_size_word32 = (pkt_size_bytes / 4) + ((pkt_size_bytes % 4) ? 1 : 0); + // Check lengths match: + if (pkt_size_word32 < if_packet_info.num_header_words32) { + throw uhd::value_error("Bad CHDR or invalid packet length"); + } + if (if_packet_info.num_packet_words32 < pkt_size_word32) { + throw uhd::value_error("Bad CHDR or packet fragment"); + } + if_packet_info.num_payload_bytes = pkt_size_bytes - (4 * if_packet_info.num_header_words32); + if_packet_info.num_payload_words32 = pkt_size_word32 - if_packet_info.num_header_words32; +} + +void chdr::if_hdr_unpack_be( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Read header and update if_packet_info + boost::uint32_t chdr = BE_MACRO(packet_buff[0]); + _hdr_unpack_chdr(chdr, if_packet_info); + + // Read SID + if_packet_info.sid = BE_MACRO(packet_buff[1]); + + // Read time (has_tsf was updated earlier) + if (if_packet_info.has_tsf) { + if_packet_info.tsf = 0 + | boost::uint64_t(BE_MACRO(packet_buff[2])) << 32 + | BE_MACRO(packet_buff[3]); + } +} + +void chdr::if_hdr_unpack_le( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Read header and update if_packet_info + boost::uint32_t chdr = LE_MACRO(packet_buff[0]); + _hdr_unpack_chdr(chdr, if_packet_info); + + // Read SID + if_packet_info.sid = LE_MACRO(packet_buff[1]); + + // Read time (has_tsf was updated earlier) + if (if_packet_info.has_tsf) { + if_packet_info.tsf = 0 + | boost::uint64_t(LE_MACRO(packet_buff[2])) << 32 + | LE_MACRO(packet_buff[3]); + } +} + diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index ae33cc036..0baf8dc76 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -345,15 +345,21 @@ libusb::special_handle::sptr libusb::special_handle::make(device::sptr dev){ std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list( boost::uint16_t vid, boost::uint16_t pid ){ - std::vector<usb_device_handle::sptr> handles; + return usb_device_handle::get_device_list(std::vector<usb_device_handle::vid_pid_pair_t>(1,usb_device_handle::vid_pid_pair_t(vid,pid))); +} +std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list(const std::vector<usb_device_handle::vid_pid_pair_t>& vid_pid_pair_list) +{ + std::vector<usb_device_handle::sptr> handles; libusb::device_list::sptr dev_list = libusb::device_list::make(); - for (size_t i = 0; i < dev_list->size(); i++){ - usb_device_handle::sptr handle = libusb::special_handle::make(dev_list->at(i)); - if (handle->get_vendor_id() == vid and handle->get_product_id() == pid){ - handles.push_back(handle); - } + for(size_t iter = 0; iter < vid_pid_pair_list.size(); ++iter) + { + for (size_t i = 0; i < dev_list->size(); i++){ + usb_device_handle::sptr handle = libusb::special_handle::make(dev_list->at(i)); + if (handle->get_vendor_id() == vid_pid_pair_list[iter].first and handle->get_product_id() == vid_pid_pair_list[iter].second){ + handles.push_back(handle); + } + } } - return handles; } diff --git a/host/lib/transport/nirio/rpc/rpc_client.cpp b/host/lib/transport/nirio/rpc/rpc_client.cpp index cf8e9c1a9..48f47cfae 100644 --- a/host/lib/transport/nirio/rpc/rpc_client.cpp +++ b/host/lib/transport/nirio/rpc/rpc_client.cpp @@ -52,7 +52,7 @@ rpc_client::rpc_client ( //- address_configured: Only return addresses if a non-loopback address is configured for the system. //- numeric_host: No name resolution should be attempted for host //- numeric_service: No name resolution should be attempted for service - tcp::resolver::query::flags query_flags; + tcp::resolver::query::flags query_flags = tcp::resolver::query::passive; tcp::resolver::query query(tcp::v4(), server, port, query_flags); tcp::resolver::iterator iterator = resolver.resolve(query); diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 5c84327a4..c3c2b8e97 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -147,7 +147,7 @@ public: */ void set_xport_chan_get_buff(const size_t xport_chan, const get_buff_type &get_buff, const bool flush = false){ if (flush){ - while (get_buff(0.0)); + while (get_buff(0.0)) {}; } _props.at(xport_chan).get_buff = get_buff; } diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index f19043c1e..5e97628f0 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2013 Ettus Research LLC +# Copyright 2011-2013,2015 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -86,8 +86,11 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/ranges.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sensors.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serial.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/time_spec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tune.cpp ${CMAKE_CURRENT_SOURCE_DIR}/types.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wb_iface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/byte_vector.cpp ) diff --git a/host/lib/types/byte_vector.cpp b/host/lib/types/byte_vector.cpp new file mode 100644 index 000000000..36b311faf --- /dev/null +++ b/host/lib/types/byte_vector.cpp @@ -0,0 +1,55 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> + +#include <uhd/types/byte_vector.hpp> + +namespace uhd{ + +std::string bytes_to_string(const byte_vector_t &bytes){ + std::string out; + BOOST_FOREACH(boost::uint8_t byte, bytes){ + if (byte < 32 or byte > 127) return out; + out += byte; + } + return out; +} + +std::string uint16_bytes_to_string(const byte_vector_t &bytes){ + const boost::uint16_t num = (boost::uint16_t(bytes.at(0)) << 0) | (boost::uint16_t(bytes.at(1)) << 8); + return (num == 0 or num == 0xffff)? "" : boost::lexical_cast<std::string>(num); +} + +byte_vector_t string_to_bytes(const std::string &str, size_t max_length){ + byte_vector_t bytes; + for (size_t i = 0; i < std::min(str.size(), max_length); i++){ + bytes.push_back(str[i]); + } + if (bytes.size() < max_length - 1) bytes.push_back('\0'); + return bytes; +} + +byte_vector_t string_to_uint16_bytes(const std::string &num_str){ + const boost::uint16_t num = boost::lexical_cast<boost::uint16_t>(num_str); + const byte_vector_t lsb_msb = boost::assign::list_of + (boost::uint8_t(num >> 0))(boost::uint8_t(num >> 8)); + return lsb_msb; +} + +} /* namespace uhd */ diff --git a/host/lib/types/filters.cpp b/host/lib/types/filters.cpp new file mode 100644 index 000000000..4ee06491f --- /dev/null +++ b/host/lib/types/filters.cpp @@ -0,0 +1,74 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types/filters.hpp> + +using namespace uhd; + +std::ostream& uhd::operator<<(std::ostream& os, filter_info_base& f) +{ + return os << f.to_pp_string(); +} + +std::string filter_info_base::to_pp_string() +{ + std::ostringstream os; + os << "[filter_info_base]" << std::endl; + switch(_type){ + case ANALOG_LOW_PASS: + os << "type: " << "Analog Low-pass" << std::endl; + break; + case ANALOG_BAND_PASS: + os << "type: " << "Analog Band-pass" << std::endl; + break; + case DIGITAL_I16: + os << "type: " << "Digital (i16)" << std::endl; + break; + case DIGITAL_FIR_I16: + os << "type: " << "Digital FIR (i16)" << std::endl; + break; + default: + os << "type: " << "Unknown type!" << std::endl; + break; + } + + os << "bypass enable: " << _bypass << std::endl + <<"position index: " << _position_index << std::endl; + + std::string str = os.str(); + return str; +} + +std::string analog_filter_base::to_pp_string() +{ + std::ostringstream os; + os << filter_info_base::to_pp_string() << + "\t[analog_filter_base]" << std::endl << + "\tdesc: " << _analog_type << std::endl; + return std::string(os.str()); + +} + +std::string analog_filter_lp::to_pp_string() +{ + std::ostringstream os; + os << analog_filter_base::to_pp_string() << + "\t\t[analog_filter_lp]" << std::endl << + "\t\tcutoff: " << _cutoff << std::endl << + "\t\trolloff: " << _rolloff << std::endl; + return std::string(os.str()); +} diff --git a/host/lib/types/sid.cpp b/host/lib/types/sid.cpp new file mode 100644 index 000000000..2fc3781cf --- /dev/null +++ b/host/lib/types/sid.cpp @@ -0,0 +1,153 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/format.hpp> +#include <boost/regex.hpp> +#include <boost/lexical_cast.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/sid.hpp> +#include <uhd/utils/cast.hpp> + +using namespace uhd; + +sid_t::sid_t() + : _sid(0x0000), _set(false) +{ +} + +sid_t::sid_t(boost::uint32_t sid) + : _sid(sid), _set(true) +{ +} + +sid_t::sid_t(boost::uint8_t src_addr, boost::uint8_t src_ep, boost::uint8_t dst_addr, boost::uint8_t dst_ep) + : _sid(0x0000), _set(true) +{ + set_src_addr(src_addr); + set_src_endpoint(src_ep); + set_dst_addr(dst_addr); + set_dst_endpoint(dst_ep); +} + +sid_t::sid_t(const std::string &sid_str) + : _sid(0x0000), _set(false) +{ + set_from_str(sid_str); +} + +std::string sid_t::to_pp_string() const +{ + if (not _set) { + return "x.x>x.x"; + } + return str(boost::format("%d.%d>%d.%d") + % get_src_addr() + % get_src_endpoint() + % get_dst_addr() + % get_dst_endpoint() + ); +} + +std::string sid_t::to_pp_string_hex() const +{ + if (not _set) { + return "xx:xx>xx:xx"; + } + return str(boost::format("%02x:%02x>%02x:%02x") + % get_src_addr() + % get_src_endpoint() + % get_dst_addr() + % get_dst_endpoint() + ); +} + + +void sid_t::set_sid(boost::uint32_t new_sid) +{ + _set = true; + _sid = new_sid; +} + +void sid_t::set_from_str(const std::string &sid_str) +{ + const std::string dec_regex = "(\\d{1,3})\\.(\\d{1,3})[.:/><](\\d{1,3})\\.(\\d{1,3})"; + const std::string hex_regex = "([[:xdigit:]]{2}):([[:xdigit:]]{2})[.:/><]([[:xdigit:]]{2}):([[:xdigit:]]{2})"; + + boost::cmatch matches; + if (boost::regex_match(sid_str.c_str(), matches, boost::regex(dec_regex))) { + set_src_addr(boost::lexical_cast<size_t>(matches[1])); + set_src_endpoint(boost::lexical_cast<size_t>(matches[2])); + set_dst_addr(boost::lexical_cast<size_t>(matches[3])); + set_dst_endpoint(boost::lexical_cast<size_t>(matches[4])); + return; + } + + if (boost::regex_match(sid_str.c_str(), matches, boost::regex(hex_regex))) { + set_src_addr(uhd::cast::hexstr_cast<size_t>(matches[1])); + set_src_endpoint(uhd::cast::hexstr_cast<size_t>(matches[2])); + set_dst_addr(uhd::cast::hexstr_cast<size_t>(matches[3])); + set_dst_endpoint(uhd::cast::hexstr_cast<size_t>(matches[4])); + return; + } + + throw uhd::value_error(str(boost::format("Invalid SID representation: %s") % sid_str)); +} + +void sid_t::set_src(boost::uint32_t new_addr) { + set_sid((_sid & 0x0000FFFF) | ((new_addr & 0xFFFF) << 16)); +} + +void sid_t::set_dst(boost::uint32_t new_addr) { + set_sid((_sid & 0xFFFF0000) | (new_addr & 0xFFFF)); +} + +void sid_t::set_src_addr(boost::uint32_t new_addr) { + set_sid((_sid & 0x00FFFFFF) | ((new_addr & 0xFF) << 24)); +} + +void sid_t::set_src_endpoint(boost::uint32_t new_addr) { + set_sid((_sid & 0xFF00FFFF) | ((new_addr & 0xFF) << 16)); +} + +void sid_t::set_dst_addr(boost::uint32_t new_addr) { + set_sid((_sid & 0xFFFF00FF) | ((new_addr & 0xFF) << 8)); +} + +void sid_t::set_dst_endpoint(boost::uint32_t new_addr) { + set_sid((_sid & 0xFFFFFF00) | ((new_addr & 0xFF) << 0)); +} + +void sid_t::set_dst_xbarport(boost::uint32_t new_xbarport) +{ + set_sid((_sid & 0xFFFFFF0F) | ((new_xbarport & 0xF) << 4)); +} + +void sid_t::set_dst_blockport(boost::uint32_t new_blockport) +{ + set_sid((_sid & 0xFFFFFFF0) | ((new_blockport & 0xF) << 0)); +} + +sid_t sid_t::reversed() +{ + return sid_t((get_dst() << 16) | get_src()); +} + +void sid_t::reverse() +{ + set_sid((get_dst() << 16) | get_src()); +} + diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index bac904a31..768fde313 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2014 Ettus Research LLC +// Copyright 2012-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -104,14 +104,16 @@ static device_addrs_t b200_find(const device_addr_t &hint) if (hint_i.has_key("addr") || hint_i.has_key("resource")) return b200_addrs; } - boost::uint16_t vid, pid; + size_t found = 0; + std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;//vid pid pair search list for devices. if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { - vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); - pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")), + uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")))); } else { - vid = B200_VENDOR_ID; - pid = B200_PRODUCT_ID; + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID)); } // Important note: @@ -121,8 +123,9 @@ static device_addrs_t b200_find(const device_addr_t &hint) // This requirement is a courtesy of libusb1.0 on windows. //find the usrps and load firmware - size_t found = 0; - BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { + std::vector<usb_device_handle::sptr> uhd_usb_device_vector = usb_device_handle::get_device_list(vid_pid_pair_list); + + BOOST_FOREACH(usb_device_handle::sptr handle, uhd_usb_device_vector) { //extract the firmware path for the b200 std::string b200_fw_image; try{ @@ -153,7 +156,7 @@ static device_addrs_t b200_find(const device_addr_t &hint) //search for the device until found or timeout while (boost::get_system_time() < timeout_time and b200_addrs.empty() and found != 0) { - BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) + BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid_pid_pair_list)) { usb_control::sptr control; try{control = usb_control::make(handle, 0);} @@ -213,13 +216,50 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //try to match the given device address with something on the USB bus boost::uint16_t vid = B200_VENDOR_ID; boost::uint16_t pid = B200_PRODUCT_ID; + bool specified_vid = false; + bool specified_pid = false; + if (device_addr.has_key("vid")) + { vid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("vid")); + specified_vid = true; + } + if (device_addr.has_key("pid")) + { pid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("pid")); + specified_pid = true; + } + + std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;//search list for devices. + + // Search only for specified VID and PID if both specified + if (specified_vid && specified_pid) + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,pid)); + } + // Search for all supported PIDs limited to specified VID if only VID specified + else if (specified_vid) + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,B200_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,B200_PRODUCT_NI_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,B210_PRODUCT_NI_ID)); + } + // Search for all supported VIDs limited to specified PID if only PID specified + else if (specified_pid) + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID,pid)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,pid)); + } + // Search for all supported devices if neither VID nor PID specified + else + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID,B200_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,B200_PRODUCT_NI_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,B210_PRODUCT_NI_ID)); + } - std::vector<usb_device_handle::sptr> device_list = - usb_device_handle::get_device_list(vid, pid); + std::vector<usb_device_handle::sptr> device_list = usb_device_handle::get_device_list(vid_pid_pair_list); //locate the matching handle in the device list usb_device_handle::sptr handle; @@ -447,6 +487,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : .publish(boost::bind(&b200_impl::get_tick_rate, this)) .subscribe(boost::bind(&b200_impl::update_tick_rate, this, _1)); _tree->create<time_spec_t>(mb_path / "time" / "cmd"); + _tree->create<bool>(mb_path / "auto_tick_rate").set(false); //////////////////////////////////////////////////////////////////// // and do the misc mboard sensors @@ -512,6 +553,19 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources); //////////////////////////////////////////////////////////////////// + // front panel gpio + //////////////////////////////////////////////////////////////////// + _radio_perifs[0].fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); + BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) + { + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second) + .set(0) + .subscribe(boost::bind(&b200_impl::set_fp_gpio, this, _radio_perifs[0].fp_gpio, attr.first, _1)); + } + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") + .publish(boost::bind(&b200_impl::get_fp_gpio, this, _radio_perifs[0].fp_gpio)); + + //////////////////////////////////////////////////////////////////// // dboard eeproms but not really //////////////////////////////////////////////////////////////////// dboard_eeprom_t db_eeprom; @@ -549,6 +603,11 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : _tree->access<double>(mb_path / "rx_dsps" / str(boost::format("%u") % i)/ "rate/value").set(B200_DEFAULT_RATE); _tree->access<double>(mb_path / "tx_dsps" / str(boost::format("%u") % i) / "rate/value").set(B200_DEFAULT_RATE); } + // We can automatically choose a master clock rate, but not if the user specifies one + _tree->access<bool>(mb_path / "auto_tick_rate").set(not device_addr.has_key("master_clock_rate")); + if (not device_addr.has_key("master_clock_rate")) { + UHD_MSG(status) << "Setting master clock rate selection to 'automatic'." << std::endl; + } //GPS installed: use external ref, time, and init time spec if (_gps and _gps->gps_detected()) @@ -569,7 +628,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : b200_impl::~b200_impl(void) { - UHD_SAFE_CALL + UHD_SAFE_CALL ( _async_task.reset(); ) @@ -612,7 +671,7 @@ void b200_impl::setup_radio(const size_t dspno) _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); _tree->create<double>(rx_dsp_path / "rate" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + .coerce(boost::bind(&b200_impl::coerce_rx_samp_rate, this, perif.ddc, dspno, _1)) .subscribe(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _1)) .set(0.0); // Can only set this after tick rate is initialized. _tree->create<double>(rx_dsp_path / "freq" / "value") @@ -636,7 +695,7 @@ void b200_impl::setup_radio(const size_t dspno) _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); _tree->create<double>(tx_dsp_path / "rate" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + .coerce(boost::bind(&b200_impl::coerce_tx_samp_rate, this, perif.duc, dspno, _1)) .subscribe(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _1)) .set(0.0); // Can only set this after tick rate is initialized. _tree->create<double>(tx_dsp_path / "freq" / "value") @@ -680,7 +739,7 @@ void b200_impl::setup_radio(const size_t dspno) _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false); _tree->create<double>(rf_fe_path / "bandwidth" / "value") .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) - .set(40e6); + .set(56e6); _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range") .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)); _tree->create<double>(rf_fe_path / "freq" / "value") @@ -690,8 +749,29 @@ void b200_impl::setup_radio(const size_t dspno) .set(B200_DEFAULT_FREQ); _tree->create<meta_range_t>(rf_fe_path / "freq" / "range") .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)); + _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "temp") + .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl)); //setup RX related stuff + if(direction) + { + _tree->create<bool>(rf_fe_path / "dc_offset" / "enable" ) + .subscribe(boost::bind(&ad9361_ctrl::set_dc_offset_auto, _codec_ctrl, key, _1)).set(true); + + _tree->create<bool>(rf_fe_path / "iq_balance" / "enable" ) + .subscribe(boost::bind(&ad9361_ctrl::set_iq_balance_auto, _codec_ctrl, key, _1)).set(true); + } + + //add all frontend filters + std::vector<std::string> filter_names = _codec_ctrl->get_filter_names(key); + for(size_t i = 0;i < filter_names.size(); i++) + { + _tree->create<filter_info_base::sptr>(rf_fe_path / "filters" / filter_names[i] / "value" ) + .publish(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_names[i])) + .subscribe(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_names[i], _1)); + } + + //setup antenna stuff if (key[0] == 'R') { static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2"); @@ -701,6 +781,16 @@ void b200_impl::setup_radio(const size_t dspno) .set("RX2"); _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "rssi") .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key)); + + //AGC setup + const std::list<std::string> mode_strings = boost::assign::list_of("slow")("fast"); + _tree->create<bool>(rf_fe_path / "gain" / "agc" / "enable") + .subscribe(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1)) + .set(false); + _tree->create<std::string>(rf_fe_path / "gain" / "agc" / "mode" / "value") + .subscribe(boost::bind((&ad9361_ctrl::set_agc_mode), _codec_ctrl, key, _1)).set(mode_strings.front()); + _tree->create<std::list<std::string> >(rf_fe_path / "gain" / "agc" / "mode" / "options") + .set(mode_strings); } if (key[0] == 'T') { @@ -757,7 +847,7 @@ void b200_impl::codec_loopback_self_test(wb_iface::sptr iface) /*********************************************************************** * Sample and tick rate comprehension below **********************************************************************/ -void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction /*= NULL*/) +void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const std::string &direction /*= ""*/) { const size_t max_chans = 2; if (chan_count > max_chans) @@ -765,7 +855,7 @@ void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, co throw uhd::value_error(boost::str( boost::format("cannot not setup %d %s channels (maximum is %d)") % chan_count - % (direction ? direction : "data") + % (direction.empty() ? "data" : direction) % max_chans )); } @@ -779,20 +869,26 @@ void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, co % (tick_rate/1e6) % (max_tick_rate/1e6) % chan_count - % (direction ? direction : "data") + % (direction.empty() ? "data" : direction) )); } } } -double b200_impl::set_tick_rate(const double rate) +double b200_impl::set_tick_rate(const double new_tick_rate) { - UHD_MSG(status) << (boost::format("Asking for clock rate %.6f MHz\n") % (rate/1e6)); - - check_tick_rate_with_current_streamers(rate); // Defined in b200_io_impl.cpp + UHD_MSG(status) << (boost::format("Asking for clock rate %.6f MHz... ") % (new_tick_rate/1e6)) << std::flush; + check_tick_rate_with_current_streamers(new_tick_rate); // Defined in b200_io_impl.cpp + + // Make sure the clock rate is actually changed before doing + // the full Monty of setting regs and loopback tests etc. + if (std::abs(new_tick_rate - _tick_rate) < 1.0) { + UHD_MSG(status) << "OK" << std::endl; + return _tick_rate; + } - _tick_rate = _codec_ctrl->set_clock_rate(rate); - UHD_MSG(status) << (boost::format("Actually got clock rate %.6f MHz\n") % (_tick_rate/1e6)); + _tick_rate = _codec_ctrl->set_clock_rate(new_tick_rate); + UHD_MSG(status) << std::endl << (boost::format("Actually got clock rate %.6f MHz.") % (_tick_rate/1e6)) << std::endl; //reset after clock rate change this->reset_codec_dcm(); @@ -853,6 +949,26 @@ void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom) } +boost::uint32_t b200_impl::get_fp_gpio(gpio_core_200::sptr gpio) +{ + return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); +} + +void b200_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) +{ + switch (attr) + { + case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); + case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); + case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); + case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); + case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); + case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); + case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + default: UHD_THROW_INVALID_CODE_PATH(); + } +} + /*********************************************************************** * Reference time and clock **********************************************************************/ diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index aea9b78be..2491b36ad 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -47,7 +47,7 @@ #include "recv_packet_demuxer_3000.hpp" static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 7; static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0; -static const boost::uint16_t B200_FPGA_COMPAT_NUM = 6; +static const boost::uint16_t B200_FPGA_COMPAT_NUM = 7; static const double B200_BUS_CLOCK_RATE = 100e6; static const double B200_DEFAULT_TICK_RATE = 32e6; static const double B200_DEFAULT_FREQ = 100e6; // Hz @@ -97,7 +97,13 @@ public: uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); - void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction = NULL); + + //! Check that the combination of stream args and tick rate are valid. + // + // Basically figures out the arguments for enforce_tick_rate_limits() + // and calls said method. If arguments are invalid, throws a + // uhd::value_error. + void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction = ""); private: b200_type_t _b200_type; @@ -153,6 +159,7 @@ private: { radio_ctrl_core_3000::sptr ctrl; gpio_core_200_32wo::sptr atr; + gpio_core_200::sptr fp_gpio; time_core_3000::sptr time64; rx_vita_core_3000::sptr framer; rx_dsp_core_3000::sptr ddc; @@ -199,14 +206,63 @@ private: void update_enables(void); void update_atrs(void); + boost::uint32_t get_fp_gpio(gpio_core_200::sptr); + void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); + double _tick_rate; double get_tick_rate(void){return _tick_rate;} double set_tick_rate(const double rate); + + /*! \brief Choose a tick rate (master clock rate) that works well for the given sampling rate. + * + * This function will try and choose a master clock rate automatically. + * See the function definition for details on the algorithm. + * + * The chosen tick rate is the largest multiple of two that is smaler + * than the max tick rate. + * The base rate is either given explicitly, or is the lcm() of the tx + * and rx sampling rates. In that case, it reads the rates directly + * from the property tree. It also tries to guess the number of channels + * (for the max possible tick rate) by checking the available streamers. + * This value, too, can explicitly be given. + * + * \param rate If this is given, it will be used as a minimum rate, or + * argument to lcm(). + * \param tree_dsp_path The sampling rate from this property tree path + * will be ignored. + * \param num_chans If given, specifies the number of channels. + */ + void set_auto_tick_rate( + const double rate=0, + const uhd::fs_path &tree_dsp_path="", + size_t num_chans=0 + ); + void update_tick_rate(const double); - void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction = NULL); + + /*! Check if \p tick_rate works with \p chan_count channels. + * + * Throws a uhd::value_error if not. + */ + void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const std::string &direction = ""); void check_tick_rate_with_current_streamers(double rate); + /*! Return the max number of channels on active rx_streamer or tx_streamer objects associated with this device. + * + * \param direction Set to "TX" to only check tx_streamers, "RX" to only check + * rx_streamers. Any other value will check if \e any active + * streamers are available. + * \return Return the number of tx streamers (direction=="TX"), the number of rx + * streamers (direction=="RX") or the total number of streamers. + */ + size_t max_chan_count(const std::string &direction=""); + + //! Coercer, attached to the "rate/value" property on the rx dsps. + double coerce_rx_samp_rate(rx_dsp_core_3000::sptr, size_t, const double); void update_rx_samp_rate(const size_t, const double); + + //! Coercer, attached to the "rate/value" property on the tx dsps. + double coerce_tx_samp_rate(tx_dsp_core_3000::sptr, size_t, const double); void update_tx_samp_rate(const size_t, const double); }; diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index 1e11e7ff6..3171e25c3 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,8 +21,10 @@ #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" #include "async_packet_handler.hpp" +#include <uhd/utils/math.hpp> #include <boost/bind.hpp> #include <boost/make_shared.hpp> +#include <boost/math/common_factor.hpp> #include <set> using namespace uhd; @@ -34,30 +36,32 @@ using namespace uhd::transport; **********************************************************************/ void b200_impl::check_tick_rate_with_current_streamers(double rate) { - size_t max_tx_chan_count = 0, max_rx_chan_count = 0; + // Defined in b200_impl.cpp + enforce_tick_rate_limits(max_chan_count("RX"), rate, "RX"); + enforce_tick_rate_limits(max_chan_count("TX"), rate, "TX"); +} + +// direction can either be "TX", "RX", or empty (default) +size_t b200_impl::max_chan_count(const std::string &direction /* = "" */) +{ + size_t max_count = 0; BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { - { + if ((direction == "RX" or direction.empty()) and not perif.rx_streamer.expired()) { boost::shared_ptr<sph::recv_packet_streamer> rx_streamer = boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock()); - if (rx_streamer) - max_rx_chan_count = std::max(max_rx_chan_count, rx_streamer->get_num_channels()); + max_count = std::max(max_count, rx_streamer->get_num_channels()); } - - { + if ((direction == "TX" or direction.empty()) and not perif.tx_streamer.expired()) { boost::shared_ptr<sph::send_packet_streamer> tx_streamer = boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock()); - if (tx_streamer) - max_tx_chan_count = std::max(max_tx_chan_count, tx_streamer->get_num_channels()); + max_count = std::max(max_count, tx_streamer->get_num_channels()); } } - - // Defined in b200_impl.cpp - enforce_tick_rate_limits(max_rx_chan_count, rate, "RX"); - enforce_tick_rate_limits(max_tx_chan_count, rate, "TX"); + return max_count; } -void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction /*= NULL*/) +void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction /*= ""*/) { std::set<size_t> chans_set; for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) @@ -69,26 +73,151 @@ void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_ enforce_tick_rate_limits(chans_set.size(), tick_rate, direction); // Defined in b200_impl.cpp } -void b200_impl::update_tick_rate(const double rate) +void b200_impl::set_auto_tick_rate( + const double rate, + const fs_path &tree_dsp_path, + size_t num_chans +) { + if (num_chans == 0) { // Divine them + num_chans = std::max(size_t(1), max_chan_count()); + } + const double max_tick_rate = ad9361_device_t::AD9361_MAX_CLOCK_RATE/num_chans; + if (rate != 0.0 and + (uhd::math::fp_compare::fp_compare_delta<double>(rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ))) { + throw uhd::value_error(str( + boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate of %.2f MHz.") + % (rate / 1e6) % (max_tick_rate / 1e6) + )); + } + + // See also the doxygen documentation for these steps in b200_impl.hpp + // Step 1: Obtain LCM and max rate from all relevant dsps + boost::uint32_t lcm_rate = (rate == 0) ? 1 : static_cast<boost::uint32_t>(floor(rate + 0.5)); + for (int i = 0; i < 2; i++) { // Loop through rx and tx + std::string dir = (i == 0) ? "tx" : "rx"; + // We have no way of knowing which DSPs are used, so we check them all. + BOOST_FOREACH(const std::string &dsp_no, _tree->list(str(boost::format("/mboards/0/%s_dsps") % dir))) { + fs_path dsp_path = str(boost::format("/mboards/0/%s_dsps/%s") % dir % dsp_no); + if (dsp_path == tree_dsp_path) { + continue; + } + double this_dsp_rate = _tree->access<double>(dsp_path / "rate/value").get(); + // Check if the user selected something completely unreasonable: + if (uhd::math::fp_compare::fp_compare_delta<double>(this_dsp_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { + throw uhd::value_error(str( + boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate of %.2f MHz.") + % (this_dsp_rate / 1e6) % (max_tick_rate / 1e6) + )); + } + // If this_dsp_rate == B200_DEFAULT_RATE, we assume the user did not actually set + // the sampling rate. If the user *did* set the rate to + // B200_DEFAULT_RATE on all DSPs, then this will still work (see below). + // If the user set one DSP to B200_DEFAULT_RATE and the other to + // a different rate, this also works if the rates are integer multiples + // of one another. Only for certain configurations of B200_DEFAULT_RATE + // and another rate that is not an integer multiple, this will be problematic. + // Since this case is less common than the case where a rate is left unset, + // we don't handle that but rather explain that corner case in the documentation. + if (this_dsp_rate == B200_DEFAULT_RATE) { + continue; + } + lcm_rate = boost::math::lcm<boost::uint32_t>( + lcm_rate, + static_cast<boost::uint32_t>(floor(this_dsp_rate + 0.5)) + ); + } + } + if (lcm_rate == 1) { + lcm_rate = static_cast<boost::uint32_t>(B200_DEFAULT_RATE); + } + + // Step 2: Determine whether if we can use lcm_rate (preferred), + // or have to give up because too large: + double base_rate = static_cast<double>(lcm_rate); + if (uhd::math::fp_compare::fp_compare_delta<double>(base_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { + UHD_MSG(warning) + << "Cannot automatically determine an appropriate tick rate for these sampling rates." << std::endl + << "Consider using different sampling rates, or manually specify a suitable master clock rate." << std::endl; + return; // Let the others handle this + } + + // Step 3: Choose the new rate + // Rules for choosing the tick rate: + // Choose a rate that is a power of 2 larger than the sampling rate, + // but at least 4. Cannot exceed the max tick rate, of course, but must + // be larger than the minimum tick rate. + // An equation that does all that is: + // + // f_auto = r * 2^floor(log2(f_max/r)) + // + // where r is the base rate and f_max is the maximum tick rate. The case + // where floor() yields 1 must be caught. + const double min_tick_rate = _codec_ctrl->get_clock_rate_range().start(); + // We use shifts here instead of 2^x because exp2() is not available in all compilers, + // also this guarantees no rounding issues. The type cast to int32_t serves as floor(): + boost::int32_t multiplier = (1 << boost::int32_t(uhd::math::log2(max_tick_rate / base_rate))); + if (multiplier == 2 and base_rate >= min_tick_rate) { + // Don't bother (see above) + multiplier = 1; + } + double new_rate = base_rate * multiplier; + UHD_ASSERT_THROW( + uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >= + uhd::math::fp_compare::fp_compare_delta<double>(min_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) + ); + UHD_ASSERT_THROW( + uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) <= + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) + ); + + if (_tree->access<double>("/mboards/0/tick_rate").get() != new_rate) { + _tree->access<double>("/mboards/0/tick_rate").set(new_rate); + } +} + +void b200_impl::update_tick_rate(const double new_tick_rate) { - check_tick_rate_with_current_streamers(rate); + check_tick_rate_with_current_streamers(new_tick_rate); BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock()); - if (my_streamer) my_streamer->set_tick_rate(rate); - perif.framer->set_tick_rate(_tick_rate); + if (my_streamer) my_streamer->set_tick_rate(new_tick_rate); + perif.framer->set_tick_rate(new_tick_rate); } BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock()); - if (my_streamer) my_streamer->set_tick_rate(rate); - perif.deframer->set_tick_rate(_tick_rate); + if (my_streamer) my_streamer->set_tick_rate(new_tick_rate); + perif.deframer->set_tick_rate(new_tick_rate); } } +#define CHECK_RATE_AND_THROW(rate) \ + if (uhd::math::fp_compare::fp_compare_delta<double>(rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > \ + uhd::math::fp_compare::fp_compare_delta<double>(ad9361_device_t::AD9361_MAX_CLOCK_RATE, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { \ + throw uhd::value_error(str( \ + boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate.") \ + % (rate / 1e6) \ + )); \ + } + +double b200_impl::coerce_rx_samp_rate(rx_dsp_core_3000::sptr ddc, size_t dspno, const double rx_rate) +{ + // Have to set tick rate first, or the ddc will change the requested rate based on default tick rate + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + CHECK_RATE_AND_THROW(rx_rate); + const std::string dsp_path = (boost::format("/mboards/0/rx_dsps/%s") % dspno).str(); + set_auto_tick_rate(rx_rate, dsp_path); + } + return ddc->set_host_rate(rx_rate); +} + #define CHECK_BANDWIDTH(dir) \ if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \ UHD_MSG(warning) \ @@ -108,6 +237,17 @@ void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate) CHECK_BANDWIDTH("Rx"); } +double b200_impl::coerce_tx_samp_rate(tx_dsp_core_3000::sptr duc, size_t dspno, const double tx_rate) +{ + // Have to set tick rate first, or the duc will change the requested rate based on default tick rate + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + CHECK_RATE_AND_THROW(tx_rate); + const std::string dsp_path = (boost::format("/mboards/0/tx_dsps/%s") % dspno).str(); + set_auto_tick_rate(tx_rate, dsp_path); + } + return duc->set_host_rate(tx_rate); +} + void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate) { boost::shared_ptr<sph::send_packet_streamer> my_streamer = @@ -276,6 +416,9 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_) if (args.otw_format.empty()) args.otw_format = "sc16"; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + set_auto_tick_rate(0, "", args.channels.size()); + } check_streamer_args(args, this->get_tick_rate(), "RX"); boost::shared_ptr<sph::recv_packet_streamer> my_streamer; @@ -383,7 +526,10 @@ tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t &args_) if (args.otw_format.empty()) args.otw_format = "sc16"; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; - check_streamer_args(args, this->get_tick_rate(), "TX"); + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + set_auto_tick_rate(0, "", args.channels.size()); + } + check_streamer_args(args, this->get_tick_rate(), "RX"); boost::shared_ptr<sph::send_packet_streamer> my_streamer; for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) diff --git a/host/lib/usrp/b200/b200_regs.hpp b/host/lib/usrp/b200/b200_regs.hpp index 900651f94..8f2dd03f3 100644 --- a/host/lib/usrp/b200/b200_regs.hpp +++ b/host/lib/usrp/b200/b200_regs.hpp @@ -46,11 +46,13 @@ localparam SR_TX_DSP = 184; localparam SR_TIME = 128; localparam SR_RX_FMT = 136; localparam SR_TX_FMT = 138; +localparam SR_FP_GPIO = 200; localparam RB32_TEST = 0; localparam RB64_TIME_NOW = 8; localparam RB64_TIME_PPS = 16; localparam RB64_CODEC_READBACK = 24; +localparam RB32_FP_GPIO = 32; //pll constants static const int AD9361_SLAVENO = (1 << 0); diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp index 65e8e2df9..2d9f297b3 100644 --- a/host/lib/usrp/common/ad9361_ctrl.cpp +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -16,7 +16,6 @@ // #include "ad9361_ctrl.hpp" -#include <uhd/exception.hpp> #include <uhd/types/ranges.hpp> #include <uhd/utils/msg.hpp> #include <uhd/types/serial.hpp> @@ -108,6 +107,27 @@ public: return _device.set_gain(direction, chain, value); } + void set_agc(const std::string &which, bool enable) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); + _device.set_agc(chain, enable); + } + + void set_agc_mode(const std::string &which, const std::string &mode) + { + boost::lock_guard<boost::mutex> lock(_mutex); + ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); + if(mode == "slow") { + _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_SLOW_AGC); + } else if (mode == "fast"){ + _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_FAST_AGC); + } else { + throw uhd::runtime_error("ad9361_ctrl got an invalid AGC option."); + } + } + //! set a new clock rate, return the exact value double set_clock_rate(const double rate) { @@ -175,6 +195,62 @@ public: return sensor_value_t("RSSI", _device.get_rssi(chain), "dB"); } + //! read the internal temp sensor. Average over 3 results + sensor_value_t get_temperature() + { + return sensor_value_t("temp", _device.get_average_temperature(), "C"); + } + + void set_dc_offset_auto(const std::string &which, const bool on) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + _device.set_dc_offset_auto(direction,on); + } + + void set_iq_balance_auto(const std::string &which, const bool on) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + _device.set_iq_balance_auto(direction,on); + } + + double set_bw_filter(const std::string &which, const double bw) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + return _device.set_bw_filter(direction, bw); + } + + std::vector<std::string> get_filter_names(const std::string &which) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + return _device.get_filter_names(direction); + } + + filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); + return _device.get_filter(direction, chain, filter_name); + } + + void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr filter) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + ad9361_device_t::chain_t chain = _get_chain_from_antenna(which); + _device.set_filter(direction, chain, filter_name, filter); + } + private: static ad9361_device_t::direction_t _get_direction_from_antenna(const std::string& antenna) { diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp index b7d7b8e26..ac0404b24 100644 --- a/host/lib/usrp/common/ad9361_ctrl.hpp +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -22,9 +22,13 @@ #include <uhd/types/ranges.hpp> #include <uhd/types/serial.hpp> #include <uhd/types/sensors.hpp> +#include <uhd/exception.hpp> #include <boost/shared_ptr.hpp> #include <ad9361_device.h> #include <string> +#include <complex> +#include <uhd/types/filters.hpp> +#include <vector> namespace uhd { namespace usrp { @@ -77,15 +81,18 @@ public: return uhd::meta_range_t(5e6, ad9361_device_t::AD9361_MAX_CLOCK_RATE); //5 MHz DCM low end } - //! set the filter bandwidth for the frontend - double set_bw_filter(const std::string &/*which*/, const double /*bw*/) - { - return 56e6; //TODO - } + //! set the filter bandwidth for the frontend's analog low pass + virtual double set_bw_filter(const std::string &/*which*/, const double /*bw*/) = 0; //! set the gain for a particular gain element virtual double set_gain(const std::string &which, const double value) = 0; + //! Enable or disable the AGC module + virtual void set_agc(const std::string &which, bool enable) = 0; + + //! configure the AGC module to slow or fast mode + virtual void set_agc_mode(const std::string &which, const std::string &mode) = 0; + //! set a new clock rate, return the exact value virtual double set_clock_rate(const double rate) = 0; @@ -95,14 +102,46 @@ public: //! tune the given frontend, return the exact value virtual double tune(const std::string &which, const double value) = 0; + //! set the DC offset for I and Q manually + void set_dc_offset(const std::string &, const std::complex<double>) + { + //This feature should not be used according to Analog Devices + throw uhd::runtime_error("ad9361_ctrl::set_dc_offset this feature is not supported on this device."); + } + + //! enable or disable the BB/RF DC tracking feature + virtual void set_dc_offset_auto(const std::string &which, const bool on) = 0; + + //! set the IQ correction value manually + void set_iq_balance(const std::string &, const std::complex<double>) + { + //This feature should not be used according to Analog Devices + throw uhd::runtime_error("ad9361_ctrl::set_iq_balance this feature is not supported on this device."); + } + + //! enable or disable the quadrature calibration + virtual void set_iq_balance_auto(const std::string &which, const bool on) = 0; + //! get the current frequency for the given frontend virtual double get_freq(const std::string &which) = 0; - //! turn on/off data port loopback + //! turn on/off Catalina's data port loopback virtual void data_port_loopback(const bool on) = 0; //! read internal RSSI sensor virtual sensor_value_t get_rssi(const std::string &which) = 0; + + //! read the internal temp sensor + virtual sensor_value_t get_temperature() = 0; + + //! List all available filters by name + virtual std::vector<std::string> get_filter_names(const std::string &which) = 0; + + //! Return a list of all filters + virtual filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) = 0; + + //! Write back a filter + virtual void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr) = 0; }; }} diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp index e63460730..85e81cf97 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp @@ -11,6 +11,7 @@ #include <cmath> #include <uhd/exception.hpp> #include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> #include <boost/cstdint.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread.hpp> @@ -78,6 +79,7 @@ int get_num_taps(int max_num_taps) { const double ad9361_device_t::AD9361_MAX_GAIN = 89.75; const double ad9361_device_t::AD9361_MAX_CLOCK_RATE = 61.44e6; +const double ad9361_device_t::AD9361_CAL_VALID_WINDOW = 100e6; // Max bandwdith is due to filter rolloff in analog filter stage const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6; @@ -87,7 +89,7 @@ const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6; * how many taps are in the filter, and given a vector of the taps * themselves. */ -void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs) +void ad9361_device_t::_program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs) { boost::uint16_t base; @@ -102,8 +104,20 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b /* Encode number of filter taps for programming register */ boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; + boost::uint8_t reg_chain = 0; + switch (chain) { + case CHAIN_1: + reg_chain = 0x01 << 3; + break; + case CHAIN_2: + reg_chain = 0x02 << 3; + break; + default: + reg_chain = 0x03 << 3; + } + /* Turn on the filter clock. */ - _io_iface->poke8(base + 5, reg_numtaps | 0x1a); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | 0x02); boost::this_thread::sleep(boost::posix_time::milliseconds(1)); /* Zero the unused taps just in case they have stale data */ @@ -112,7 +126,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b _io_iface->poke8(base + 0, addr); _io_iface->poke8(base + 1, 0x0); _io_iface->poke8(base + 2, 0x0); - _io_iface->poke8(base + 5, reg_numtaps | 0x1e); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2)); _io_iface->poke8(base + 4, 0x00); _io_iface->poke8(base + 4, 0x00); } @@ -122,7 +136,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b _io_iface->poke8(base + 0, addr); _io_iface->poke8(base + 1, (coeffs[addr]) & 0xff); _io_iface->poke8(base + 2, (coeffs[addr] >> 8) & 0xff); - _io_iface->poke8(base + 5, reg_numtaps | 0x1e); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2)); _io_iface->poke8(base + 4, 0x00); _io_iface->poke8(base + 4, 0x00); } @@ -133,9 +147,9 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b before the clock stops. Wait 4 sample clock periods after setting D2 high while that data writes into the table" */ - _io_iface->poke8(base + 5, reg_numtaps | 0x1A); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1)); if (direction == RX) { - _io_iface->poke8(base + 5, reg_numtaps | 0x18); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain ); /* Rx Gain, set to prevent digital overflow/saturation in filters 0:+6dB, 1:0dB, 2:-6dB, 3:-12dB page 35 of UG-671 */ @@ -144,7 +158,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b /* Tx Gain. bit[0]. set to prevent digital overflow/saturation in filters 0: 0dB, 1:-6dB page 25 of UG-671 */ - _io_iface->poke8(base + 5, reg_numtaps | 0x18); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain ); } } @@ -175,7 +189,7 @@ void ad9361_device_t::_setup_rx_fir(size_t num_taps, boost::int32_t decimation) } } - _program_fir_filter(RX, num_taps, coeffs.get()); + _program_fir_filter(RX, CHAIN_BOTH, num_taps, coeffs.get()); } /* Program the TX FIR Filter. */ @@ -207,7 +221,7 @@ void ad9361_device_t::_setup_tx_fir(size_t num_taps, boost::int32_t interpolatio } } - _program_fir_filter(TX, num_taps, coeffs.get()); + _program_fir_filter(TX, CHAIN_BOTH, num_taps, coeffs.get()); } /*********************************************************************** @@ -282,16 +296,24 @@ void ad9361_device_t::_calibrate_synth_charge_pumps() * * Note that the filter calibration depends heavily on the baseband * bandwidth, so this must be re-done after any change to the RX sample - * rate. */ -double ad9361_device_t::_calibrate_baseband_rx_analog_filter() + * rate. + * UG570 Page 33 states that this filter should be calibrated to 1.4 * bbbw*/ +double ad9361_device_t::_calibrate_baseband_rx_analog_filter(double req_rfbw) { - /* For filter tuning, baseband BW is half the complex BW, and must be - * between 28e6 and 0.2e6. */ - double bbbw = _baseband_bw / 2.0; + double bbbw = req_rfbw / 2.0; + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 28e6 and 0.143e6. + * Max filter BW is 39.2 MHz. 39.2 / 1.4 = 28 + * Min filter BW is 200kHz. 200 / 1.4 = 143 */ if (bbbw > 28e6) { bbbw = 28e6; - } else if (bbbw < 0.20e6) { - bbbw = 0.20e6; + } else if (bbbw < 0.143e6) { + bbbw = 0.143e6; } double rxtune_clk = ((1.4 * bbbw * 2 * M_PI) / M_LN2); @@ -340,16 +362,25 @@ double ad9361_device_t::_calibrate_baseband_rx_analog_filter() * * Note that the filter calibration depends heavily on the baseband * bandwidth, so this must be re-done after any change to the TX sample - * rate. */ -double ad9361_device_t::_calibrate_baseband_tx_analog_filter() + * rate. + * UG570 Page 32 states that this filter should be calibrated to 1.6 * bbbw*/ +double ad9361_device_t::_calibrate_baseband_tx_analog_filter(double req_rfbw) { - /* For filter tuning, baseband BW is half the complex BW, and must be - * between 28e6 and 0.2e6. */ - double bbbw = _baseband_bw / 2.0; + double bbbw = req_rfbw / 2.0; + + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 20e6 and 0.391e6. + * Max filter BW is 32 MHz. 32 / 1.6 = 20 + * Min filter BW is 625 kHz. 625 / 1.6 = 391 */ if (bbbw > 20e6) { bbbw = 20e6; - } else if (bbbw < 0.625e6) { - bbbw = 0.625e6; + } else if (bbbw < 0.391e6) { + bbbw = 0.391e6; } double txtune_clk = ((1.6 * bbbw * 2 * M_PI) / M_LN2); @@ -386,16 +417,25 @@ double ad9361_device_t::_calibrate_baseband_tx_analog_filter() /* Calibrate the secondary TX filter. * * This filter also depends on the TX sample rate, so if a rate change is - * made, the previous calibration will no longer be valid. */ -void ad9361_device_t::_calibrate_secondary_tx_filter() + * made, the previous calibration will no longer be valid. + * UG570 Page 32 states that this filter should be calibrated to 5 * bbbw*/ +double ad9361_device_t::_calibrate_secondary_tx_filter(double req_rfbw) { - /* For filter tuning, baseband BW is half the complex BW, and must be - * between 20e6 and 0.53e6. */ - double bbbw = _baseband_bw / 2.0; + double bbbw = req_rfbw / 2.0; + + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 20e6 and 0.54e6. + * Max filter BW is 100 MHz. 100 / 5 = 20 + * Min filter BW is 2.7 MHz. 2.7 / 5 = 0.54 */ if (bbbw > 20e6) { bbbw = 20e6; - } else if (bbbw < 0.53e6) { - bbbw = 0.53e6; + } else if (bbbw < 0.54e6) { + bbbw = 0.54e6; } double bbbw_mhz = bbbw / 1e6; @@ -456,13 +496,17 @@ void ad9361_device_t::_calibrate_secondary_tx_filter() _io_iface->poke8(0x0d2, reg0d2); _io_iface->poke8(0x0d1, reg0d1); _io_iface->poke8(0x0d0, reg0d0); + + return bbbw; } /* Calibrate the RX TIAs. * * Note that the values in the TIA register, after calibration, vary with - * the RX gain settings. */ -void ad9361_device_t::_calibrate_rx_TIAs() + * the RX gain settings. + * We do not really program the BW here. Most settings are taken form the BB LPF registers + * UG570 page 33 states that this filter should be calibrated to 2.5 * bbbw */ +double ad9361_device_t::_calibrate_rx_TIAs(double req_rfbw) { boost::uint8_t reg1eb = _io_iface->peek8(0x1eb) & 0x3F; boost::uint8_t reg1ec = _io_iface->peek8(0x1ec) & 0x7F; @@ -473,13 +517,21 @@ void ad9361_device_t::_calibrate_rx_TIAs() boost::uint8_t reg1de = 0x00; boost::uint8_t reg1df = 0x00; - /* For calibration, baseband BW is half the complex BW, and must be - * between 28e6 and 0.2e6. */ - double bbbw = _baseband_bw / 2.0; - if (bbbw > 20e6) { - bbbw = 20e6; - } else if (bbbw < 0.20e6) { - bbbw = 0.20e6; + double bbbw = req_rfbw / 2.0; + + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 28e6 and 0.4e6. + * Max filter BW is 70 MHz. 70 / 2.5 = 28 + * Min filter BW is 1 MHz. 1 / 2.5 = 0.4*/ + if (bbbw > 28e6) { + bbbw = 28e6; + } else if (bbbw < 0.40e6) { + bbbw = 0.40e6; } double ceil_bbbw_mhz = std::ceil(bbbw / 1e6); @@ -520,6 +572,8 @@ void ad9361_device_t::_calibrate_rx_TIAs() _io_iface->poke8(0x1df, reg1df); _io_iface->poke8(0x1dc, reg1dc); _io_iface->poke8(0x1de, reg1de); + + return bbbw; } /* Setup the AD9361 ADC. @@ -651,11 +705,12 @@ void ad9361_device_t::_setup_adc() } /* Calibrate the baseband DC offset. - * - * Note that this function is called from within the TX quadrature - * calibration function! */ + * Disables tracking + */ void ad9361_device_t::_calibrate_baseband_dc_offset() { + _io_iface->poke8(0x18b, 0x83); //Reset RF DC tracking flag + _io_iface->poke8(0x193, 0x3f); // Calibration settings _io_iface->poke8(0x190, 0x0f); // Set tracking coefficient //write_ad9361_reg(device, 0x190, /*0x0f*//*0xDF*/0x80*1 | 0x40*1 | (16+8/*+4*/)); // Set tracking coefficient: don't *4 counter, do decim /4, increased gain shift @@ -675,9 +730,8 @@ void ad9361_device_t::_calibrate_baseband_dc_offset() } /* Calibrate the RF DC offset. - * - * Note that this function is called from within the TX quadrature - * calibration function. */ + * Disables tracking + */ void ad9361_device_t::_calibrate_rf_dc_offset() { /* Some settings are frequency-dependent. */ @@ -692,7 +746,7 @@ void ad9361_device_t::_calibrate_rf_dc_offset() } _io_iface->poke8(0x185, 0x20); // RF DC Offset wait count - _io_iface->poke8(0x18b, 0x83); + _io_iface->poke8(0x18b, 0x83); // Disable tracking _io_iface->poke8(0x189, 0x30); /* Run the calibration! */ @@ -708,6 +762,16 @@ void ad9361_device_t::_calibrate_rf_dc_offset() } } +void ad9361_device_t::_configure_bb_rf_dc_tracking(const bool on) +{ + if(on) + { + _io_iface->poke8(0x18b, 0xad); // Enable BB and RF DC tracking + } else { + _io_iface->poke8(0x18b, 0x83); // Disable BB and RF DC tracking + } +} + /* Start the RX quadrature calibration. * * Note that we are using AD9361's 'tracking' feature for RX quadrature @@ -720,16 +784,20 @@ void ad9361_device_t::_calibrate_rx_quadrature() _io_iface->poke8(0x16e, 0x25); // RX Gain index to use for cal _io_iface->poke8(0x16a, 0x75); // Set Kexp phase _io_iface->poke8(0x16b, 0x15); // Set Kexp amplitude - _io_iface->poke8(0x169, 0xcf); // Continuous tracking mode - _io_iface->poke8(0x18b, 0xad); + + if(_use_iq_balance_correction) + { + _io_iface->poke8(0x169, 0xcf); // Continuous tracking mode. Gets disabled in _tx_quadrature_cal_routine! + } } -/* TX quadtrature calibration routine. +/* TX quadrature calibration routine. * * The TX quadrature needs to be done twice, once for each TX chain, with * only one register change in between. Thus, this function enacts the * calibrations, and it is called from calibrate_tx_quadrature. */ void ad9361_device_t::_tx_quadrature_cal_routine() { + /* This is a weird process, but here is how it works: * 1) Read the calibrated NCO frequency bits out of 0A3. * 2) Write the two bits to the RX NCO freq part of 0A0. @@ -774,12 +842,6 @@ void ad9361_device_t::_tx_quadrature_cal_routine() { _io_iface->poke8(0x0a4, 0xf0); // Cal setting conut _io_iface->poke8(0x0ae, 0x00); // Cal LPF gain index (split mode) - /* First, calibrate the baseband DC offset. */ - _calibrate_baseband_dc_offset(); - - /* Second, calibrate the RF DC offset. */ - _calibrate_rf_dc_offset(); - /* Now, calibrate the TX quadrature! */ size_t count = 0; _io_iface->poke8(0x016, 0x10); @@ -794,9 +856,7 @@ void ad9361_device_t::_tx_quadrature_cal_routine() { } /* Run the TX quadrature calibration. - * - * Note that from within this function we are also triggering the baseband - * and RF DC calibrations. */ + */ void ad9361_device_t::_calibrate_tx_quadrature() { /* Make sure we are, in fact, in the ALERT state. If not, something is @@ -880,7 +940,7 @@ void ad9361_device_t::_program_mixer_gm_subtable() void ad9361_device_t::_program_gain_table() { /* Figure out which gain table we should be using for our current * frequency band. */ - boost::uint8_t (*gain_table)[5] = NULL; + boost::uint8_t (*gain_table)[3] = NULL; boost::uint8_t new_gain_table; if (_rx_freq < 1300e6) { gain_table = gain_table_sub_1300mhz; @@ -911,9 +971,9 @@ void ad9361_device_t::_program_gain_table() { boost::uint8_t index = 0; for (; index < 77; index++) { _io_iface->poke8(0x130, index); - _io_iface->poke8(0x131, gain_table[index][1]); - _io_iface->poke8(0x132, gain_table[index][2]); - _io_iface->poke8(0x133, gain_table[index][3]); + _io_iface->poke8(0x131, gain_table[index][0]); + _io_iface->poke8(0x132, gain_table[index][1]); + _io_iface->poke8(0x133, gain_table[index][2]); _io_iface->poke8(0x137, 0x1E); _io_iface->poke8(0x134, 0x00); _io_iface->poke8(0x134, 0x00); @@ -939,28 +999,58 @@ void ad9361_device_t::_program_gain_table() { /* Setup gain control registers. * - * This really only needs to be done once, at initialization. */ -void ad9361_device_t::_setup_gain_control() + * This really only needs to be done once, at initialization. + * If AGC is used the mode select bits (Reg 0x0FA) must be written manually */ +void ad9361_device_t::_setup_gain_control(bool agc) { - _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select - _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl - _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size - _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index - _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time - _io_iface->poke8(0x100, 0x6F); // Max Digital Gain - _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold - _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold - _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold - _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold - _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index - _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index - _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index - _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index - _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index - _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index - _io_iface->poke8(0x114, 0x30); // Low Power Threshold - _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit - _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control + /* The AGC mode configuration should be good for all cases. + * However, non AGC configuration still used for backward compatibility. */ + if (agc) { + /*mode select bits must be written before hand!*/ + _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl + _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size + _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index + _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time + _io_iface->poke8(0x100, 0x6F); // Max Digital Gain + _io_iface->poke8(0x101, 0x0A); // Max Digital Gain + _io_iface->poke8(0x103, 0x08); // Max Digital Gain + _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold + _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold + _io_iface->poke8(0x106, 0x22); // Max Digital Gain + _io_iface->poke8(0x107, 0x2B); // Large LMT Overload Threshold + _io_iface->poke8(0x108, 0x31); + _io_iface->poke8(0x111, 0x0A); + _io_iface->poke8(0x11A, 0x1C); + _io_iface->poke8(0x120, 0x0C); + _io_iface->poke8(0x121, 0x44); + _io_iface->poke8(0x122, 0x44); + _io_iface->poke8(0x123, 0x11); + _io_iface->poke8(0x124, 0xF5); + _io_iface->poke8(0x125, 0x3B); + _io_iface->poke8(0x128, 0x03); + _io_iface->poke8(0x129, 0x56); + _io_iface->poke8(0x12A, 0x22); + } else { + _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select + _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl + _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size + _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index + _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time + _io_iface->poke8(0x100, 0x6F); // Max Digital Gain + _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold + _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold + _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold + _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold + _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index + _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index + _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index + _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index + _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index + _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index + _io_iface->poke8(0x114, 0x30); // Low Power Threshold + _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit + _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control + } } /* Setup the RX or TX synthesizers. @@ -1257,6 +1347,7 @@ double ad9361_device_t::_setup_rates(const double rate) int divfactor = 0; _tfir_factor = 0; _rfir_factor = 0; + if (rate < 0.33e6) { // RX1 + RX2 enabled, 3, 2, 2, 4 _regs.rxfilt = B8(11101111); @@ -1412,6 +1503,19 @@ void ad9361_device_t::initialize() _rx2_gain = 0; _tx1_gain = 0; _tx2_gain = 0; + _use_dc_offset_correction = true; + _use_iq_balance_correction = true; + _rx1_agc_mode = GAIN_MODE_SLOW_AGC; + _rx2_agc_mode = GAIN_MODE_SLOW_AGC; + _rx1_agc_enable = false; + _rx2_agc_enable = false; + _last_calibration_freq = -AD9361_CAL_VALID_WINDOW; + _rx_analog_bw = 0; + _tx_analog_bw = 0; + _rx_tia_lp_bw = 0; + _tx_sec_lp_bw = 0; + _rx_bb_lp_bw = 0; + _tx_bb_lp_bw = 0; /* Reset the device. */ _io_iface->poke8(0x000, 0x01); @@ -1501,7 +1605,7 @@ void ad9361_device_t::initialize() /* Setup AuxADC */ _io_iface->poke8(0x00B, 0x00); // Temp Sensor Setup (Offset) _io_iface->poke8(0x00C, 0x00); // Temp Sensor Setup (Temp Window) - _io_iface->poke8(0x00D, 0x03); // Temp Sensor Setup (Periodic Measure) + _io_iface->poke8(0x00D, 0x00); // Temp Sensor Setup (Manual Measure) _io_iface->poke8(0x00F, 0x04); // Temp Sensor Setup (Decimation) _io_iface->poke8(0x01C, 0x10); // AuxADC Setup (Clock Div) _io_iface->poke8(0x01D, 0x01); // AuxADC Setup (Decimation/Enable) @@ -1555,17 +1659,18 @@ void ad9361_device_t::initialize() _program_mixer_gm_subtable(); _program_gain_table(); - _setup_gain_control(); + _setup_gain_control(false); - _calibrate_baseband_rx_analog_filter(); - _calibrate_baseband_tx_analog_filter(); - _calibrate_rx_TIAs(); - _calibrate_secondary_tx_filter(); + set_bw_filter(RX, _baseband_bw); + set_bw_filter(TX, _baseband_bw); _setup_adc(); + _calibrate_baseband_dc_offset(); + _calibrate_rf_dc_offset(); _calibrate_tx_quadrature(); _calibrate_rx_quadrature(); + _configure_bb_rf_dc_tracking(_use_dc_offset_correction); // cals done, set PPORT config switch (_client_params->get_digital_interface_mode()) { @@ -1680,18 +1785,19 @@ double ad9361_device_t::set_clock_rate(const double req_rate) _program_mixer_gm_subtable(); _program_gain_table(); - _setup_gain_control(); + _setup_gain_control(false); _reprogram_gains(); - _calibrate_baseband_rx_analog_filter(); - _calibrate_baseband_tx_analog_filter(); - _calibrate_rx_TIAs(); - _calibrate_secondary_tx_filter(); + set_bw_filter(RX, _baseband_bw); + set_bw_filter(TX, _baseband_bw); _setup_adc(); + _calibrate_baseband_dc_offset(); + _calibrate_rf_dc_offset(); _calibrate_tx_quadrature(); _calibrate_rx_quadrature(); + _configure_bb_rf_dc_tracking(_use_dc_offset_correction); // cals done, set PPORT config switch (_client_params->get_digital_interface_mode()) { @@ -1843,9 +1949,16 @@ double ad9361_device_t::tune(direction_t direction, const double value) /* Update the gain settings. */ _reprogram_gains(); - /* Run the calibration algorithms. */ - _calibrate_tx_quadrature(); - _calibrate_rx_quadrature(); + /* Only run the following calibrations if we are more than 100MHz away + * from the previous calibration point. */ + if (std::abs(_last_calibration_freq - tune_freq) > AD9361_CAL_VALID_WINDOW) { + /* Run the calibration algorithms. */ + _calibrate_rf_dc_offset(); + _calibrate_tx_quadrature(); + _calibrate_rx_quadrature(); + _configure_bb_rf_dc_tracking(_use_dc_offset_correction); + _last_calibration_freq = tune_freq; + } /* If we were in the FDD state, return it now. */ if (not_in_alert) { @@ -1960,4 +2073,678 @@ double ad9361_device_t::get_rssi(chain_t chain) return rssi; } +/* + * Returns the reading of the internal temperature sensor. + * One point calibration of the sensor was done according to datasheet + * leading to the given default constant correction factor. + */ +double ad9361_device_t::_get_temperature(const double cal_offset, const double timeout) +{ + //set 0x01D[0] to 1 to disable AuxADC GPIO reading + boost::uint8_t tmp = 0; + tmp = _io_iface->peek8(0x01D); + _io_iface->poke8(0x01D, (tmp | 0x01)); + _io_iface->poke8(0x00B, 0); //set offset to 0 + + _io_iface->poke8(0x00C, 0x01); //start reading, clears bit 0x00C[1] + boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration elapsed; + //wait for valid data (toggle of bit 1 in 0x00C) + while(((_io_iface->peek8(0x00C) >> 1) & 0x01) == 0) { + boost::this_thread::sleep(boost::posix_time::microseconds(100)); + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + if(elapsed.total_milliseconds() > (timeout*1000)) + { + throw uhd::runtime_error("[ad9361_device_t] timeout while reading temperature"); + } + } + _io_iface->poke8(0x00C, 0x00); //clear read flag + + boost::uint8_t temp = _io_iface->peek8(0x00E); //read temperature. + double tmp_temp = temp/1.140f; //according to ADI driver + tmp_temp = tmp_temp + cal_offset; //Constant offset acquired by one point calibration. + + return tmp_temp; +} + +double ad9361_device_t::get_average_temperature(const double cal_offset, const size_t num_samples) +{ + double d_temp = 0; + for(size_t i = 0; i < num_samples; i++) { + double tmp_temp = _get_temperature(cal_offset); + d_temp += (tmp_temp/num_samples); + } + return d_temp; +} + +void ad9361_device_t::set_dc_offset_auto(direction_t direction, const bool on) +{ + if(direction == RX) + { + _use_dc_offset_correction = on; + _configure_bb_rf_dc_tracking(_use_dc_offset_correction); + if(on) + { + _io_iface->poke8(0x182, (_io_iface->peek8(0x182) & (~((1 << 7) | (1 << 6) | (1 << 3) | (1 << 2))))); //Clear force bits + //Do a single shot DC offset cal before enabling tracking (Not possible if not in ALERT state. Is it necessary?) + } else { + //clear current config values + _io_iface->poke8(0x182, (_io_iface->peek8(0x182) | ((1 << 7) | (1 << 6) | (1 << 3) | (1 << 2)))); //Set input A and input B&C force enable bits + _io_iface->poke8(0x174, 0x00); + _io_iface->poke8(0x175, 0x00); + _io_iface->poke8(0x176, 0x00); + _io_iface->poke8(0x177, 0x00); + _io_iface->poke8(0x178, 0x00); + _io_iface->poke8(0x17D, 0x00); + _io_iface->poke8(0x17E, 0x00); + _io_iface->poke8(0x17F, 0x00); + _io_iface->poke8(0x180, 0x00); + _io_iface->poke8(0x181, 0x00); + } + } else { + // DC offset is removed during TX quad cal + throw uhd::runtime_error("[ad9361_device_t] [set_iq_balance_auto] INVALID_CODE_PATH"); + } +} + +void ad9361_device_t::set_iq_balance_auto(direction_t direction, const bool on) +{ + if(direction == RX) + { + _use_iq_balance_correction = on; + if(on) + { + //disable force registers and enable tracking + _io_iface->poke8(0x182, (_io_iface->peek8(0x182) & (~ ( (1<<1) | (1<<0) | (1<<5) | (1<<4) )))); + _calibrate_rx_quadrature(); + } else { + //disable IQ tracking + _io_iface->poke8(0x169, 0xc0); + //clear current config values + _io_iface->poke8(0x182, (_io_iface->peek8(0x182) | ((1 << 1) | (1 << 0) | (1 << 5) | (1 << 4)))); //Set Rx2 input B&C force enable bit + _io_iface->poke8(0x17B, 0x00); + _io_iface->poke8(0x17C, 0x00); + _io_iface->poke8(0x179, 0x00); + _io_iface->poke8(0x17A, 0x00); + _io_iface->poke8(0x170, 0x00); + _io_iface->poke8(0x171, 0x00); + _io_iface->poke8(0x172, 0x00); + _io_iface->poke8(0x173, 0x00); + } + } else { + throw uhd::runtime_error("[ad9361_device_t] [set_iq_balance_auto] INVALID_CODE_PATH"); + } +} + +/* Sets the RX gain mode to be used. + * If a transition from an AGC to an non AGC mode occurs (or vice versa) + * the gain configuration will be reloaded. */ +void ad9361_device_t::_setup_agc(chain_t chain, gain_mode_t gain_mode) +{ + boost::uint8_t gain_mode_reg = 0; + boost::uint8_t gain_mode_prev = 0; + boost::uint8_t gain_mode_bits_pos = 0; + + gain_mode_reg = _io_iface->peek8(0x0FA); + gain_mode_prev = (gain_mode_reg & 0x0F); + + if (chain == CHAIN_1) { + gain_mode_bits_pos = 0; + } else if (chain == CHAIN_2) { + gain_mode_bits_pos = 2; + } else + { + throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); + } + + gain_mode_reg = (gain_mode_reg & (~(0x03<<gain_mode_bits_pos))); //clear mode bits + switch (gain_mode) { + case GAIN_MODE_MANUAL: + //leave bits cleared + break; + case GAIN_MODE_SLOW_AGC: + gain_mode_reg = (gain_mode_reg | (0x02<<gain_mode_bits_pos)); + break; + case GAIN_MODE_FAST_AGC: + gain_mode_reg = (gain_mode_reg | (0x01<<gain_mode_bits_pos)); + break; + default: + throw uhd::runtime_error("[ad9361_device_t] Gain mode does not exist"); + } + _io_iface->poke8(0x0FA, gain_mode_reg); + boost::uint8_t gain_mode_status = _io_iface->peek8(0x0FA); + gain_mode_status = (gain_mode_status & 0x0F); + /*Check if gain mode configuration needs to be reprogrammed*/ + if (((gain_mode_prev == 0) && (gain_mode_status != 0)) || ((gain_mode_prev != 0) && (gain_mode_status == 0))) { + if (gain_mode_status == 0) { + /*load manual mode config*/ + _setup_gain_control(false); + } else { + /*load agc mode config*/ + _setup_gain_control(true); + } + } +} + +void ad9361_device_t::set_agc(chain_t chain, bool enable) +{ + if(chain == CHAIN_1) { + _rx1_agc_enable = enable; + if(enable) { + _setup_agc(chain, _rx1_agc_mode); + } else { + _setup_agc(chain, GAIN_MODE_MANUAL); + } + } else if (chain == CHAIN_2){ + _rx2_agc_enable = enable; + if(enable) { + _setup_agc(chain, _rx2_agc_mode); + } else { + _setup_agc(chain, GAIN_MODE_MANUAL); + } + } else + { + throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); + } +} + +void ad9361_device_t::set_agc_mode(chain_t chain, gain_mode_t gain_mode) +{ + if(chain == CHAIN_1) { + _rx1_agc_mode = gain_mode; + if(_rx1_agc_enable) { + _setup_agc(chain, _rx1_agc_mode); + } + } else if(chain == CHAIN_2){ + _rx2_agc_mode = gain_mode; + if(_rx2_agc_enable) { + _setup_agc(chain, _rx2_agc_mode); + } + } else + { + throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); + } +} + +std::vector<std::string> ad9361_device_t::get_filter_names(direction_t direction) +{ + std::vector<std::string> ret; + if(direction == RX) { + for(std::map<std::string, filter_query_helper>::iterator it = _rx_filters.begin(); it != _rx_filters.end(); ++it) { + ret.push_back(it->first); + } + } else if (direction == TX) + { + for(std::map<std::string, filter_query_helper>::iterator it = _tx_filters.begin(); it != _tx_filters.end(); ++it) { + ret.push_back(it->first); + } + } + return ret; +} + +filter_info_base::sptr ad9361_device_t::get_filter(direction_t direction, chain_t chain, const std::string &name) +{ + if(direction == RX) { + if (not _rx_filters[name].get) + { + throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read."); + } + return _rx_filters[name].get(direction, chain); + } else if (direction == TX) { + if (not _tx_filters[name].get) + { + throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read."); + } + return _tx_filters[name].get(direction, chain); + } + + throw uhd::runtime_error("ad9361_device_t::get_filter wrong direction parameter."); +} + +void ad9361_device_t::set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter) +{ + + if(direction == RX) { + if(not _rx_filters[name].set) + { + throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written."); + } + _rx_filters[name].set(direction, chain, filter); + } else if (direction == TX) { + if(not _tx_filters[name].set) + { + throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written."); + } + _tx_filters[name].set(direction, chain, filter); + } + +} + +double ad9361_device_t::set_bw_filter(direction_t direction, const double rf_bw) +{ + //both low pass filters are programmed to the same bw. However, their cutoffs will differ. + //Together they should create the requested bb bw. + double set_analog_bb_bw = 0; + if(direction == RX) + { + _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(rf_bw); //returns bb bw + _rx_tia_lp_bw = _calibrate_rx_TIAs(rf_bw); + _rx_analog_bw = _rx_bb_lp_bw; + set_analog_bb_bw = _rx_analog_bw; + } else { + _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(rf_bw); //returns bb bw + _tx_sec_lp_bw = _calibrate_secondary_tx_filter(rf_bw); + _tx_analog_bw = _tx_bb_lp_bw; + set_analog_bb_bw = _tx_analog_bw; + } + return (2.0 * set_analog_bb_bw); +} + +void ad9361_device_t::_set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps) +{ + size_t num_taps = taps.size(); + size_t num_taps_avail = _get_num_fir_taps(direction); + if(num_taps == num_taps_avail) + { + boost::scoped_array<boost::uint16_t> coeffs(new boost::uint16_t[num_taps_avail]); + for (size_t i = 0; i < num_taps_avail; i++) + { + coeffs[i] = boost::uint16_t(taps[i]); + } + _program_fir_filter(direction, chain, num_taps_avail, coeffs.get()); + } else if(num_taps < num_taps_avail){ + throw uhd::runtime_error("ad9361_device_t::_set_fir_taps not enough coefficients."); + } else { + throw uhd::runtime_error("ad9361_device_t::_set_fir_taps too many coefficients."); + } +} + +size_t ad9361_device_t::_get_num_fir_taps(direction_t direction) +{ + boost::uint8_t num = 0; + if(direction == RX) + num = _io_iface->peek8(0x0F5); + else + num = _io_iface->peek8(0x065); + num = ((num >> 5) & 0x07); + return ((num + 1) * 16); +} + +size_t ad9361_device_t::_get_fir_dec_int(direction_t direction) +{ + boost::uint8_t dec_int = 0; + if(direction == RX) + dec_int = _io_iface->peek8(0x003); + else + dec_int = _io_iface->peek8(0x002); + /* + * 0 = dec/int by 1 and bypass filter + * 1 = dec/int by 1 + * 2 = dec/int by 2 + * 3 = dec/int by 4 */ + dec_int = (dec_int & 0x03); + if(dec_int == 3) + { + return 4; + } + return dec_int; +} + +std::vector<boost::int16_t> ad9361_device_t::_get_fir_taps(direction_t direction, chain_t chain) +{ + int base; + size_t num_taps = _get_num_fir_taps(direction); + boost::uint8_t config; + boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; + config = reg_numtaps | 0x02; //start the programming clock + + if(chain == CHAIN_1) + { + config = config | (1 << 3); + } else if (chain == CHAIN_2){ + config = config | (1 << 4); + } else { + throw uhd::runtime_error("[ad9361_device_t] Can not read both chains synchronously"); + } + + if(direction == RX) + { + base = 0xF0; + } else { + base = 0x60; + } + + _io_iface->poke8(base+5,config); + + std::vector<boost::int16_t> taps; + boost::uint8_t lower_val; + boost::uint8_t higher_val; + boost::uint16_t coeff; + for(size_t i = 0;i < num_taps;i++) + { + _io_iface->poke8(base,0x00+i); + lower_val = _io_iface->peek8(base+3); + higher_val = _io_iface->peek8(base+4); + coeff = ((higher_val << 8) | lower_val); + taps.push_back(boost::int16_t(coeff)); + } + + config = (config & (~(1 << 1))); //disable filter clock + _io_iface->poke8(base+5,config); + return taps; +} + +/* + * Returns either RX TIA LPF or TX Secondary LPF + * depending on the direction. + * See UG570 for details on used scaling factors. */ +filter_info_base::sptr ad9361_device_t::_get_filter_lp_tia_sec(direction_t direction) +{ + double cutoff = 0; + + if(direction == RX) + { + cutoff = 2.5 * _rx_tia_lp_bw; + } else { + cutoff = 5 * _tx_sec_lp_bw; + } + + filter_info_base::sptr lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 0, "single-pole", cutoff, 20)); + return lp; +} + +/* + * Returns RX/TX BB LPF. + * See UG570 for details on used scaling factors. */ +filter_info_base::sptr ad9361_device_t::_get_filter_lp_bb(direction_t direction) +{ + double cutoff = 0; + if(direction == RX) + { + cutoff = 1.4 * _rx_bb_lp_bw; + } else { + cutoff = 1.6 * _tx_bb_lp_bw; + } + + filter_info_base::sptr bb_lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 1, "third-order Butterworth", cutoff, 60)); + return bb_lp; +} + +/* + * For RX direction the DEC3 is returned. + * For TX direction the INT3 is returned. */ +filter_info_base::sptr ad9361_device_t::_get_filter_dec_int_3(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = _adcclock_freq; + double full_scale; + size_t dec = 0; + size_t interpol = 0; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + std::string name; + boost::int16_t taps_array_rx[] = {55, 83, 0, -393, -580, 0, 1914, 4041, 5120, 4041, 1914, 0, -580, -393, 0, 83, 55}; + boost::int16_t taps_array_tx[] = {36, -19, 0, -156, -12, 0, 479, 233, 0, -1215, -993, 0, 3569, 6277, 8192, 6277, 3569, 0, -993, -1215, 0, 223, 479, 0, -12, -156, 0, -19, 36}; + std::vector<boost::int16_t> taps; + + filter_info_base::sptr ret; + + if(direction == RX) + { + full_scale = 16384; + dec = 3; + interpol = 1; + + enable = _io_iface->peek8(0x003); + enable = ((enable >> 4) & 0x03); + taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) ); + + } else { + full_scale = 8192; + dec = 1; + interpol = 3; + + boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A); + use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01); + if(use_dac_clk_div == 1) + { + rate = rate / 2; + } + + enable = _io_iface->peek8(0x002); + enable = ((enable >> 4) & 0x03); + if(enable == 2) //0 => int. by 1, 1 => int. by 2 (HB3), 2 => int. by 3 + { + rate /= 3; + } + + taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) ); + } + + ret = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 2) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps)); + return ret; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_3(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = _adcclock_freq; + double full_scale = 0; + size_t dec = 1; + size_t interpol = 1; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + boost::int16_t taps_array_rx[] = {1, 4, 6, 4, 1}; + boost::int16_t taps_array_tx[] = {1, 2, 1}; + std::vector<boost::int16_t> taps; + + if(direction == RX) + { + full_scale = 16; + dec = 2; + + enable = _io_iface->peek8(0x003); + enable = ((enable >> 4) & 0x03); + taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) ); + } else { + full_scale = 2; + interpol = 2; + + boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A); + use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01); + if(use_dac_clk_div == 1) + { + rate = rate / 2; + } + + enable = _io_iface->peek8(0x002); + enable = ((enable >> 4) & 0x03); + if(enable == 1) + { + rate /= 2; + } + taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) ); + } + + filter_info_base::sptr hb = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 1) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps)); + return hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_2(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = _adcclock_freq; + double full_scale = 0; + size_t dec = 1; + size_t interpol = 1; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + boost::int16_t taps_array[] = {-9, 0, 73, 128, 73, 0, -9}; + std::vector<boost::int16_t> taps(taps_array, taps_array + sizeof(taps_array) / sizeof(boost::int16_t) ); + + digital_filter_base<boost::int16_t>::sptr hb_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_3(direction)); + digital_filter_base<boost::int16_t>::sptr dec_int_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_dec_int_3(direction)); + + if(direction == RX) + { + full_scale = 256; + dec = 2; + enable = _io_iface->peek8(0x003); + } else { + full_scale = 128; + interpol = 2; + enable = _io_iface->peek8(0x002); + } + + enable = ((enable >> 3) & 0x01); + + if(!(hb_3->is_bypassed())) + { + if(direction == RX) + { + rate = hb_3->get_output_rate(); + }else if (direction == TX) { + rate = hb_3->get_input_rate(); + if(enable) + { + rate /= 2; + } + } + } else { //else dec3/int3 or none of them is used. + if(direction == RX) + { + rate = dec_int_3->get_output_rate(); + }else if (direction == TX) { + rate = dec_int_3->get_input_rate(); + if(enable) + { + rate /= 2; + } + } + } + + filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 3, rate, interpol, dec, full_scale, taps.size(), taps)); + return hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_1(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = 0; + double full_scale = 0; + size_t dec = 1; + size_t interpol = 1; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + + std::vector<boost::int16_t> taps; + boost::int16_t taps_rx_array[] = {-8, 0, 42, 0, -147, 0, 619, 1013, 619, 0, -147, 0, 42, 0, -8}; + boost::int16_t taps_tx_array[] = {-53, 0, 313, 0, -1155, 0, 4989, 8192, 4989, 0, -1155, 0, 313, 0, -53}; + + digital_filter_base<boost::int16_t>::sptr hb_2 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_2(direction)); + + if(direction == RX) + { + full_scale = 2048; + dec = 2; + enable = _io_iface->peek8(0x003); + enable = ((enable >> 2) & 0x01); + rate = hb_2->get_output_rate(); + taps.assign(taps_rx_array, taps_rx_array + sizeof(taps_rx_array) / sizeof(boost::int16_t) ); + } else if (direction == TX) { + full_scale = 8192; + interpol = 2; + enable = _io_iface->peek8(0x002); + enable = ((enable >> 2) & 0x01); + rate = hb_2->get_input_rate(); + if(enable) + { + rate /= 2; + } + taps.assign(taps_tx_array, taps_tx_array + sizeof(taps_tx_array) / sizeof(boost::int16_t) ); + } + + filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 4, rate, interpol, dec, full_scale, taps.size(), taps)); + return hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_fir(direction_t direction, chain_t chain) +{ + double rate = 0; + size_t dec = 1; + size_t interpol = 1; + size_t max_num_taps = 128; + boost::uint8_t enable = 1; + + digital_filter_base<boost::int16_t>::sptr hb_1 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_1(direction)); + + if(direction == RX) + { + dec = _get_fir_dec_int(direction); + if(dec == 0) + { + enable = 0; + dec = 1; + } + interpol = 1; + rate = hb_1->get_output_rate(); + }else if (direction == TX) { + interpol = _get_fir_dec_int(direction); + if(interpol == 0) + { + enable = 0; + interpol = 1; + } + dec = 1; + rate = hb_1->get_input_rate(); + if(enable) + { + rate /= interpol; + } + } + max_num_taps = _get_num_fir_taps(direction); + + filter_info_base::sptr fir(new digital_filter_fir<boost::int16_t>(filter_info_base::DIGITAL_FIR_I16, (enable == 0) ? true : false, 5, rate, interpol, dec, 32767, max_num_taps, _get_fir_taps(direction, chain))); + + return fir; +} + +void ad9361_device_t::_set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter) +{ + digital_filter_fir<boost::int16_t>::sptr fir = boost::dynamic_pointer_cast<digital_filter_fir<boost::int16_t> >(filter); + //only write taps. Ignore everything else for now + _set_fir_taps(direction, channel, fir->get_taps()); +} + +/* + * If BW of one of the analog filters gets overwritten manually, + * _tx_analog_bw and _rx_analog_bw are not valid any more! + * For useful data in those variables set_bw_filter method should be used + */ +void ad9361_device_t::_set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter) +{ + analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter); + double bw = lpf->get_cutoff(); + if(direction == RX) + { + //remember: this function takes rf bw as its input and calibrated to 1.4 x the given value + _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(2 * bw / 1.4); //returns bb bw + + } else { + //remember: this function takes rf bw as its input and calibrates to 1.6 x the given value + _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(2 * bw / 1.6); + } +} + +void ad9361_device_t::_set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter) +{ + analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter); + double bw = lpf->get_cutoff(); + if(direction == RX) + { + //remember: this function takes rf bw as its input and calibrated to 2.5 x the given value + _rx_tia_lp_bw = _calibrate_rx_TIAs(2 * bw / 2.5); //returns bb bw + + } else { + //remember: this function takes rf bw as its input and calibrates to 5 x the given value + _tx_sec_lp_bw = _calibrate_secondary_tx_filter(2 * bw / 5); + } +} + }} diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h index 71ce78da7..1c5c97829 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h @@ -8,6 +8,14 @@ #include <ad9361_client.h> #include <boost/noncopyable.hpp> #include <boost/thread/recursive_mutex.hpp> +#include <uhd/types/filters.hpp> +#include <uhd/types/sensors.hpp> +#include <complex> +#include <vector> +#include <map> +#include "boost/assign.hpp" +#include "boost/bind.hpp" +#include "boost/function.hpp" namespace uhd { namespace usrp { @@ -15,10 +23,34 @@ class ad9361_device_t : public boost::noncopyable { public: enum direction_t { RX, TX }; - enum chain_t { CHAIN_1, CHAIN_2 }; + enum gain_mode_t {GAIN_MODE_MANUAL, GAIN_MODE_SLOW_AGC, GAIN_MODE_FAST_AGC}; + enum chain_t { CHAIN_1, CHAIN_2, CHAIN_BOTH }; ad9361_device_t(ad9361_params::sptr client, ad9361_io::sptr io_iface) : - _client_params(client), _io_iface(io_iface) {} + _client_params(client), _io_iface(io_iface) { + + _rx_filters = boost::assign::map_list_of("LPF_TIA", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3))) + ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3))) + ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0)) + ("DEC_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0)) + ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0)) + ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0)) + ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2), + boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3))); + + _tx_filters = boost::assign::map_list_of("LPF_SECONDARY", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3))) + ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3))) + ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0)) + ("INT_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0)) + ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0)) + ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0)) + ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2), + boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3))); + } /* Initialize the AD9361 codec. */ void initialize(); @@ -69,21 +101,56 @@ public: /* Read back the internal RSSI measurement data. */ double get_rssi(chain_t chain); + /*! Read the internal temperature sensor + *\param calibrate return raw sensor readings or apply calibration factor. + *\param num_samples number of measurements to average over + */ + double get_average_temperature(const double cal_offset = -30.0, const size_t num_samples = 3); + + /* Turn on/off AD9361's RX DC offset correction */ + void set_dc_offset_auto(direction_t direction, const bool on); + + /* Turn on/off AD9361's RX IQ imbalance correction */ + void set_iq_balance_auto(direction_t direction, const bool on); + + /* Configure AD9361's AGC module to use either fast or slow AGC mode. */ + void set_agc_mode(chain_t chain, gain_mode_t gain_mode); + + /* Enable AD9361's AGC gain mode. */ + void set_agc(chain_t chain, bool enable); + + /* Set bandwidth of AD9361's analog LP filters. + * Bandwidth should be RF bandwidth */ + double set_bw_filter(direction_t direction, const double rf_bw); + + /* + * Filter API implementation + * */ + filter_info_base::sptr get_filter(direction_t direction, chain_t chain, const std::string &name); + + void set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter); + + std::vector<std::string> get_filter_names(direction_t direction); + //Constants static const double AD9361_MAX_GAIN; static const double AD9361_MAX_CLOCK_RATE; + static const double AD9361_CAL_VALID_WINDOW; static const double AD9361_RECOMMENDED_MAX_BANDWIDTH; private: //Methods void _program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs); void _setup_tx_fir(size_t num_taps, boost::int32_t interpolation); void _setup_rx_fir(size_t num_taps, boost::int32_t decimation); + void _program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs); + void _setup_tx_fir(size_t num_taps); + void _setup_rx_fir(size_t num_taps); void _calibrate_lock_bbpll(); void _calibrate_synth_charge_pumps(); - double _calibrate_baseband_rx_analog_filter(); - double _calibrate_baseband_tx_analog_filter(); - void _calibrate_secondary_tx_filter(); - void _calibrate_rx_TIAs(); + double _calibrate_baseband_rx_analog_filter(double rfbw); + double _calibrate_baseband_tx_analog_filter(double rfbw); + double _calibrate_secondary_tx_filter(double rfbw); + double _calibrate_rx_TIAs(double rfbw); void _setup_adc(); void _calibrate_baseband_dc_offset(); void _calibrate_rf_dc_offset(); @@ -92,12 +159,29 @@ private: //Methods void _calibrate_tx_quadrature(); void _program_mixer_gm_subtable(); void _program_gain_table(); - void _setup_gain_control(); + void _setup_gain_control(bool use_agc); void _setup_synth(direction_t direction, double vcorate); double _tune_bbvco(const double rate); void _reprogram_gains(); double _tune_helper(direction_t direction, const double value); double _setup_rates(const double rate); + double _get_temperature(const double cal_offset, const double timeout = 0.1); + void _configure_bb_rf_dc_tracking(const bool on); + void _setup_agc(chain_t chain, gain_mode_t gain_mode); + void _set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps); + std::vector<boost::int16_t> _get_fir_taps(direction_t direction, chain_t chain); + size_t _get_num_fir_taps(direction_t direction); + size_t _get_fir_dec_int(direction_t direction); + filter_info_base::sptr _get_filter_lp_tia_sec(direction_t direction); + filter_info_base::sptr _get_filter_lp_bb(direction_t direction); + filter_info_base::sptr _get_filter_dec_int_3(direction_t direction); + filter_info_base::sptr _get_filter_hb_3(direction_t direction); + filter_info_base::sptr _get_filter_hb_2(direction_t direction); + filter_info_base::sptr _get_filter_hb_1(direction_t direction); + filter_info_base::sptr _get_filter_fir(direction_t direction, chain_t chain); + void _set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter); + void _set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter); + void _set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter); private: //Members typedef struct { @@ -110,11 +194,30 @@ private: //Members boost::uint8_t bbftune_mode; } chip_regs_t; + struct filter_query_helper + { + filter_query_helper( + boost::function<filter_info_base::sptr (direction_t, chain_t)> p_get, + boost::function<void (direction_t, chain_t, filter_info_base::sptr)> p_set + ) : get(p_get), set(p_set) { } + + filter_query_helper(){ } + + boost::function<filter_info_base::sptr (direction_t, chain_t)> get; + boost::function<void (direction_t, chain_t, filter_info_base::sptr)> set; + }; + + std::map<std::string, filter_query_helper> _rx_filters; + std::map<std::string, filter_query_helper> _tx_filters; + //Interfaces ad9361_params::sptr _client_params; ad9361_io::sptr _io_iface; //Intermediate state double _rx_freq, _tx_freq, _req_rx_freq, _req_tx_freq; + double _last_calibration_freq; + double _rx_analog_bw, _tx_analog_bw, _rx_bb_lp_bw, _tx_bb_lp_bw; + double _rx_tia_lp_bw, _tx_sec_lp_bw; //! Current baseband sampling rate (this is the actual rate the device is // is running at) double _baseband_bw; @@ -129,10 +232,14 @@ private: //Members double _rx1_gain, _rx2_gain, _tx1_gain, _tx2_gain; boost::int32_t _tfir_factor; boost::int32_t _rfir_factor; + gain_mode_t _rx1_agc_mode, _rx2_agc_mode; + bool _rx1_agc_enable, _rx2_agc_enable; //Register soft-copies chip_regs_t _regs; //Synchronization boost::recursive_mutex _mutex; + bool _use_dc_offset_correction; + bool _use_iq_balance_correction; }; }} //namespace diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h index 786029d6e..553655fa5 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h @@ -7,91 +7,91 @@ #include <boost/cstdint.hpp> -boost::uint8_t gain_table_sub_1300mhz[77][5] = { {0,0x00,0x00,0x20,1}, - {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, - {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, - {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, - {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, - {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, - {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, - {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, - {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, - {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, - {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x27,0x20,1}, - {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, - {34,0x04,0x2B,0x00,1}, {35,0x24,0x21,0x20,0}, {36,0x24,0x22,0x00,1}, - {37,0x44,0x20,0x20,0}, {38,0x44,0x21,0x00,0}, {39,0x44,0x22,0x00,0}, - {40,0x44,0x23,0x00,0}, {41,0x44,0x24,0x00,0}, {42,0x44,0x25,0x00,0}, - {43,0x44,0x26,0x00,0}, {44,0x44,0x27,0x00,0}, {45,0x44,0x28,0x00,0}, - {46,0x44,0x29,0x00,0}, {47,0x44,0x2A,0x00,0}, {48,0x44,0x2B,0x00,0}, - {49,0x44,0x2C,0x00,0}, {50,0x44,0x2D,0x00,0}, {51,0x44,0x2E,0x00,0}, - {52,0x44,0x2F,0x00,0}, {53,0x44,0x30,0x00,0}, {54,0x44,0x31,0x00,0}, - {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, - {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, - {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, - {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, - {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, - {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, - {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, - {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_sub_1300mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, +{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, +{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, +{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, +{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, +{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, +{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, +{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, +{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, +{ 0x04, 0x28, 0x20 }, { 0x04, 0x29, 0x00 }, { 0x04, 0x2A, 0x00 }, +{ 0x04, 0x2B, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, +{ 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, { 0x44, 0x22, 0x00 }, +{ 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, { 0x44, 0x25, 0x00 }, +{ 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, { 0x44, 0x28, 0x00 }, +{ 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, { 0x44, 0x2B, 0x00 }, +{ 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, { 0x44, 0x2E, 0x00 }, +{ 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, { 0x44, 0x31, 0x00 }, +{ 0x44, 0x32, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; -boost::uint8_t gain_table_1300mhz_to_4000mhz[77][5] = { {0,0x00,0x00,0x20,1}, - {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, - {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, - {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, - {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, - {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, - {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, - {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, - {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, - {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, - {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x28,0x20,1}, - {31,0x04,0x29,0x00,0}, {32,0x04,0x2A,0x00,0}, {33,0x04,0x2B,0x00,0}, - {34,0x24,0x20,0x20,0}, {35,0x24,0x21,0x00,1}, {36,0x44,0x20,0x20,0}, - {37,0x44,0x21,0x00,1}, {38,0x44,0x22,0x00,0}, {39,0x44,0x23,0x00,0}, - {40,0x44,0x24,0x00,0}, {41,0x44,0x25,0x00,0}, {42,0x44,0x26,0x00,0}, - {43,0x44,0x27,0x00,0}, {44,0x44,0x28,0x00,0}, {45,0x44,0x29,0x00,0}, - {46,0x44,0x2A,0x00,0}, {47,0x44,0x2B,0x00,0}, {48,0x44,0x2C,0x00,0}, - {49,0x44,0x2D,0x00,0}, {50,0x44,0x2E,0x00,0}, {51,0x44,0x2F,0x00,0}, - {52,0x44,0x30,0x00,0}, {53,0x44,0x31,0x00,0}, {54,0x44,0x32,0x00,0}, - {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, - {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, - {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, - {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, - {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, - {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, - {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, - {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_1300mhz_to_4000mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, +{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, +{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, +{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, +{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, +{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, +{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, +{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, +{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, +{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, +{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x24, 0x21, 0x20 }, +{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, +{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, +{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, +{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, +{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, +{ 0x44, 0x2E, 0x00 }, { 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, +{ 0x44, 0x31, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; -boost::uint8_t gain_table_4000mhz_to_6000mhz[77][5] = { {0,0x00,0x00,0x20,1}, - {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x00,0x00,0}, - {4,0x00,0x00,0x00,0}, {5,0x00,0x01,0x00,0}, {6,0x00,0x02,0x00,0}, - {7,0x00,0x03,0x00,0}, {8,0x01,0x01,0x20,1}, {9,0x01,0x02,0x00,0}, - {10,0x01,0x03,0x00,0}, {11,0x01,0x04,0x20,1}, {12,0x01,0x05,0x00,0}, - {13,0x01,0x06,0x00,0}, {14,0x01,0x07,0x00,0}, {15,0x01,0x08,0x00,0}, - {16,0x01,0x09,0x00,0}, {17,0x01,0x0A,0x00,0}, {18,0x01,0x0B,0x00,0}, - {19,0x01,0x0C,0x00,0}, {20,0x02,0x08,0x20,1}, {21,0x02,0x09,0x00,0}, - {22,0x02,0x0A,0x00,0}, {23,0x02,0x0B,0x20,1}, {24,0x02,0x0C,0x00,0}, - {25,0x02,0x0D,0x00,0}, {26,0x02,0x0E,0x00,0}, {27,0x02,0x0F,0x00,0}, - {28,0x02,0x2A,0x20,1}, {29,0x02,0x2B,0x00,0}, {30,0x04,0x27,0x20,1}, - {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, - {34,0x04,0x2B,0x00,0}, {35,0x04,0x2C,0x00,0}, {36,0x04,0x2D,0x00,0}, - {37,0x24,0x20,0x20,1}, {38,0x24,0x21,0x00,0}, {39,0x24,0x22,0x00,0}, - {40,0x44,0x20,0x20,1}, {41,0x44,0x21,0x00,0}, {42,0x44,0x22,0x00,0}, - {43,0x44,0x23,0x00,0}, {44,0x44,0x24,0x00,0}, {45,0x44,0x25,0x00,0}, - {46,0x44,0x26,0x00,0}, {47,0x44,0x27,0x00,0}, {48,0x44,0x28,0x00,0}, - {49,0x44,0x29,0x00,0}, {50,0x44,0x2A,0x00,0}, {51,0x44,0x2B,0x00,0}, - {52,0x44,0x2C,0x00,0}, {53,0x44,0x2D,0x00,0}, {54,0x44,0x2E,0x00,0}, - {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, - {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, - {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, - {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, - {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, - {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, - {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, - {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_4000mhz_to_6000mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x01, 0x00 }, +{ 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, { 0x01, 0x01, 0x20 }, +{ 0x01, 0x02, 0x00 }, { 0x01, 0x03, 0x00 }, { 0x01, 0x04, 0x20 }, +{ 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, { 0x01, 0x07, 0x00 }, +{ 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, { 0x01, 0x0A, 0x00 }, +{ 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, { 0x02, 0x08, 0x20 }, +{ 0x02, 0x09, 0x00 }, { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x20 }, +{ 0x02, 0x0C, 0x00 }, { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, +{ 0x02, 0x0F, 0x00 }, { 0x02, 0x2A, 0x20 }, { 0x02, 0x2B, 0x00 }, +{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, +{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x04, 0x2C, 0x00 }, +{ 0x04, 0x2D, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, +{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, +{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, +{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, +{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, +{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, +{ 0x44, 0x2E, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; #endif /* INCLUDED_AD9361_GAIN_TABLES_HPP */ diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h index cb320e1f4..0475a5eb1 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h @@ -6,20 +6,20 @@ #define INCLUDED_AD9361_SYNTH_LUT_HPP -double vco_index[53] = {12605000000, 12245000000, 11906000000, 11588000000, - 11288000000, 11007000000, 10742000000, 10492000000, - 10258000000, 10036000000, 9827800000, 9631100000, - 9445300000, 9269800000, 9103600000, 8946300000, - 8797000000, 8655300000, 8520600000, 8392300000, - 8269900000, 8153100000, 8041400000, 7934400000, - 7831800000, 7733200000, 7638400000, 7547100000, - 7459000000, 7374000000, 7291900000, 7212400000, - 7135500000, 7061000000, 6988700000, 6918600000, - 6850600000, 6784600000, 6720500000, 6658200000, - 6597800000, 6539200000, 6482300000, 6427000000, - 6373400000, 6321400000, 6270900000, 6222000000, - 6174500000, 6128400000, 6083600000, 6040100000, - 5997700000}; +double vco_index[53] = {12605000000.0, 12245000000.0, 11906000000.0, 11588000000.0, + 11288000000.0, 11007000000.0, 10742000000.0, 10492000000.0, + 10258000000.0, 10036000000.0, 9827800000.0, 9631100000.0, + 9445300000.0, 9269800000.0, 9103600000.0, 8946300000.0, + 8797000000.0, 8655300000.0, 8520600000.0, 8392300000.0, + 8269900000.0, 8153100000.0, 8041400000.0, 7934400000.0, + 7831800000.0, 7733200000.0, 7638400000.0, 7547100000.0, + 7459000000.0, 7374000000.0, 7291900000.0, 7212400000.0, + 7135500000.0, 7061000000.0, 6988700000.0, 6918600000.0, + 6850600000.0, 6784600000.0, 6720500000.0, 6658200000.0, + 6597800000.0, 6539200000.0, 6482300000.0, 6427000000.0, + 6373400000.0, 6321400000.0, 6270900000.0, 6222000000.0, + 6174500000.0, 6128400000.0, 6083600000.0, 6040100000.0, + 5997700000.0}; int synth_cal_lut[53][12] = { {10, 0, 4, 0, 15, 8, 8, 13, 4, 13, 15, 9}, {10, 0, 4, 0, 15, 8, 9, 13, 4, 13, 15, 9}, diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index 164437f40..e22834fd9 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011,2014 Ettus Research LLC +// Copyright 2011,2014,2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -20,10 +20,34 @@ #include <uhd/config.hpp> #include <uhd/usrp/dboard_iface.hpp> +#include <boost/assign.hpp> #include <boost/cstdint.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <uhd/types/wb_iface.hpp> +#include <map> + +typedef enum { + GPIO_CTRL, + GPIO_DDR, + GPIO_OUT, + GPIO_ATR_0X, + GPIO_ATR_RX, + GPIO_ATR_TX, + GPIO_ATR_XX +} gpio_attr_t; + +typedef std::map<gpio_attr_t,std::string> gpio_attr_map_t; +static const gpio_attr_map_t gpio_attr_map = + boost::assign::map_list_of + (GPIO_CTRL, "CTRL") + (GPIO_DDR, "DDR") + (GPIO_OUT, "OUT") + (GPIO_ATR_0X, "ATR_0X") + (GPIO_ATR_RX, "ATR_RX") + (GPIO_ATR_TX, "ATR_TX") + (GPIO_ATR_XX, "ATR_XX") +; class gpio_core_200 : boost::noncopyable{ public: diff --git a/host/lib/usrp/dboard_eeprom.cpp b/host/lib/usrp/dboard_eeprom.cpp index f2bee47a9..3b56ae19a 100644 --- a/host/lib/usrp/dboard_eeprom.cpp +++ b/host/lib/usrp/dboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2011,2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include <uhd/types/byte_vector.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/exception.hpp> #include <uhd/utils/log.hpp> @@ -27,30 +28,6 @@ using namespace uhd; using namespace uhd::usrp; -/*********************************************************************** - * Utility functions - **********************************************************************/ - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ - std::string out; - BOOST_FOREACH(boost::uint8_t byte, bytes){ - if (byte < 32 or byte > 127) return out; - out += byte; - } - return out; -} - -//! create a byte vector from a string, null terminate unless max length -static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){ - byte_vector_t bytes; - for (size_t i = 0; i < std::min(string.size(), max_length); i++){ - bytes.push_back(string[i]); - } - if (bytes.size() < max_length - 1) bytes.push_back('\0'); - return bytes; -} - //////////////////////////////////////////////////////////////////////// // format of daughterboard EEPROM // 00: 0xDB code for ``I'm a daughterboard'' diff --git a/host/lib/usrp/e300/e300_defaults.hpp b/host/lib/usrp/e300/e300_defaults.hpp index 8fe8c3a05..7a8d49719 100644 --- a/host/lib/usrp/e300/e300_defaults.hpp +++ b/host/lib/usrp/e300/e300_defaults.hpp @@ -24,7 +24,7 @@ namespace uhd { namespace usrp { namespace e300 { static const double DEFAULT_TICK_RATE = 32e6; static const double MAX_TICK_RATE = 50e6; -static const double MIN_TICK_RATE = 1e6; +static const double MIN_TICK_RATE = 10e6; static const double DEFAULT_TX_SAMP_RATE = 1.0e6; static const double DEFAULT_RX_SAMP_RATE = 1.0e6; @@ -70,7 +70,7 @@ public: digital_interface_delays_t get_digital_interface_timing() { digital_interface_delays_t delays; delays.rx_clk_delay = 0; - delays.rx_data_delay = 0xF; + delays.rx_data_delay = 0x8; delays.tx_clk_delay = 0; delays.tx_data_delay = 0xF; return delays; diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp index c038efbae..b8f88a791 100644 --- a/host/lib/usrp/e300/e300_fpga_defs.hpp +++ b/host/lib/usrp/e300/e300_fpga_defs.hpp @@ -21,7 +21,7 @@ namespace uhd { namespace usrp { namespace e300 { namespace fpga { static const size_t NUM_RADIOS = 2; -static const boost::uint32_t COMPAT_MAJOR = 6; +static const boost::uint32_t COMPAT_MAJOR = 7; static const boost::uint32_t COMPAT_MINOR = 0; }}}} // namespace diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp index 41e8eacf3..231816fe8 100644 --- a/host/lib/usrp/e300/e300_impl.cpp +++ b/host/lib/usrp/e300/e300_impl.cpp @@ -469,15 +469,14 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) // internal gpios //////////////////////////////////////////////////////////////////// gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); - const std::vector<std::string> gpio_attrs = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX"); - BOOST_FOREACH(const std::string &attr, gpio_attrs) + BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) { - _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr) - .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr, _1)) + _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr.second) + .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr.first, _1)) .set(0); } _tree->create<boost::uint8_t>(mb_path / "gpio" / "INT0" / "READBACK") - .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio, "READBACK")); + .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio)); //////////////////////////////////////////////////////////////////// @@ -583,32 +582,35 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) boost::this_thread::sleep(boost::posix_time::seconds(1)); } -boost::uint8_t e300_impl::_get_internal_gpio( - gpio_core_200::sptr gpio, - const std::string &) +boost::uint8_t e300_impl::_get_internal_gpio(gpio_core_200::sptr gpio) { return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); } void e300_impl::_set_internal_gpio( gpio_core_200::sptr gpio, - const std::string &attr, + const gpio_attr_t attr, const boost::uint32_t value) { - if (attr == "CTRL") + switch (attr) + { + case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); - else if (attr == "DDR") + case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); - else if (attr == "OUT") + case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); - else if (attr == "ATR_0X") + case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); - else if (attr == "ATR_RX") + case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); - else if (attr == "ATR_TX") + case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); - else if (attr == "ATR_XX") + case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + default: + UHD_THROW_INVALID_CODE_PATH(); + } } uhd::sensor_value_t e300_impl::_get_fe_pll_lock(const bool is_tx) @@ -649,6 +651,17 @@ void e300_impl::_enforce_tick_rate_limits( % direction )); } + // Minimum rate restriction due to MMCM used in capture interface to AD9361. + // Xilinx Artix-7 FPGA MMCM minimum input frequency is 10 MHz. + const double min_tick_rate = uhd::usrp::e300::MIN_TICK_RATE / ((chan_count <= 1) ? 1 : 2); + if (tick_rate - min_tick_rate < 0.0) + { + throw uhd::value_error(boost::str( + boost::format("current master clock rate (%.6f MHz) set below minimum possible master clock rate (%.6f MHz)") + % (tick_rate/1e6) + % (min_tick_rate/1e6) + )); + } } } @@ -1035,6 +1048,8 @@ void e300_impl::_setup_radio(const size_t dspno) _tree->create<int>(rf_fe_path / "sensors"); //empty TODO _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked") .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, direction == "tx")); + _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "temp") + .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl)); BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key)) { _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range") @@ -1060,6 +1075,18 @@ void e300_impl::_setup_radio(const size_t dspno) _tree->create<meta_range_t>(rf_fe_path / "freq" / "range") .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)); + //only in local mode + if(_xport_path == AXI) { + //add all frontend filters + std::vector<std::string> filter_names = _codec_ctrl->get_filter_names(key); + for(size_t i = 0;i < filter_names.size(); i++) + { + _tree->create<filter_info_base::sptr>(rf_fe_path / "filters" / filter_names[i] / "value" ) + .publish(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_names[i])) + .subscribe(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_names[i], _1)); + } + } + //setup RX related stuff if (key[0] == 'R') { static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2"); diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp index c7d683f58..21aef215d 100644 --- a/host/lib/usrp/e300/e300_impl.hpp +++ b/host/lib/usrp/e300/e300_impl.hpp @@ -264,13 +264,11 @@ private: // methods uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx); // internal gpios - boost::uint8_t _get_internal_gpio( - gpio_core_200::sptr, - const std::string &); + boost::uint8_t _get_internal_gpio(gpio_core_200::sptr); void _set_internal_gpio( gpio_core_200::sptr gpio, - const std::string &attr, + const gpio_attr_t attr, const boost::uint32_t value); private: // members diff --git a/host/lib/usrp/e300/e300_network.cpp b/host/lib/usrp/e300/e300_network.cpp index 408f9e62d..d68dc4541 100644 --- a/host/lib/usrp/e300/e300_network.cpp +++ b/host/lib/usrp/e300/e300_network.cpp @@ -230,6 +230,27 @@ static void e300_codec_ctrl_tunnel( case codec_xact_t::ACTION_GET_RSSI: out->rssi = _codec_ctrl->get_rssi(which_str).to_real(); break; + case codec_xact_t::ACTION_GET_TEMPERATURE: + out->temp = _codec_ctrl->get_temperature().to_real(); + break; + case codec_xact_t::ACTION_SET_DC_OFFSET_AUTO: + _codec_ctrl->set_dc_offset_auto(which_str, in->use_dc_correction == 1); + break; + case codec_xact_t::ACTION_SET_IQ_BALANCE_AUTO: + _codec_ctrl->set_iq_balance_auto(which_str, in->use_iq_correction == 1); + case codec_xact_t::ACTION_SET_AGC: + _codec_ctrl->set_agc(which_str, in->use_agc == 1); + break; + case codec_xact_t::ACTION_SET_AGC_MODE: + if(in->agc_mode == 0) { + _codec_ctrl->set_agc_mode(which_str, "slow"); + } else if (in->agc_mode == 1) { + _codec_ctrl->set_agc_mode(which_str, "fast"); + } + break; + case codec_xact_t::ACTION_SET_BW: + out->bw = _codec_ctrl->set_bw_filter(which_str, in->bw); + break; default: UHD_MSG(status) << "Got unknown request?!" << std::endl; //Zero out actions to fail this request on client diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp index 6742f5f86..9708634dd 100644 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp @@ -130,10 +130,118 @@ public: _args.bits = uhd::htonx<boost::uint32_t>(0); _transact(); - return sensor_value_t("RSSI", _retval.rssi, "dB"); } + sensor_value_t get_temperature() + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_GET_TEMPERATURE); + _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_NONE); /*Unused*/ + _args.bits = uhd::htonx<boost::uint32_t>(0); + + _transact(); + return sensor_value_t("temp", _retval.temp, "C"); + } + + void set_dc_offset_auto(const std::string &which, const bool on) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_DC_OFFSET_AUTO); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.use_dc_correction = on ? 1 : 0; + + _transact(); + } + + void set_iq_balance_auto(const std::string &which, const bool on) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_IQ_BALANCE_AUTO); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.use_iq_correction = on ? 1 : 0; + + _transact(); + } + + void set_agc(const std::string &which, bool enable) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_AGC); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.use_agc = enable ? 1 : 0; + + _transact(); + } + + void set_agc_mode(const std::string &which, const std::string &mode) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_AGC_MODE); + + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + + if(mode == "slow") { + _args.agc_mode = 0; + } else if (mode == "fast") { + _args.agc_mode = 1; + } else { + throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect agc mode."); + } + + _transact(); + } + + //! set the filter bandwidth for the frontend's analog low pass + double set_bw_filter(const std::string &which, const double bw) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_BW); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.bw = bw; + + _transact(); + return _retval.bw; + } + + //! List all available filters by name + std::vector<std::string> get_filter_names(const std::string &) + { + UHD_THROW_INVALID_CODE_PATH(); + } + + //! Return a list of all filters + filter_info_base::sptr get_filter(const std::string &, const std::string &) + { + UHD_THROW_INVALID_CODE_PATH(); + } + + //! Write back a filter + void set_filter(const std::string &, const std::string &, const filter_info_base::sptr) + { + UHD_THROW_INVALID_CODE_PATH(); + } + private: void _transact() { { diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp index e21f2ef95..43723e0d5 100644 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp @@ -34,6 +34,12 @@ public: double gain; double freq; double rssi; + double temp; + double bw; + boost::uint32_t use_dc_correction; + boost::uint32_t use_iq_correction; + boost::uint32_t use_agc; + boost::uint32_t agc_mode; boost::uint64_t bits; }; @@ -44,7 +50,13 @@ public: static const boost::uint32_t ACTION_TUNE = 13; static const boost::uint32_t ACTION_SET_LOOPBACK = 14; static const boost::uint32_t ACTION_GET_RSSI = 15; - static const boost::uint32_t ACTION_GET_FREQ = 16; + static const boost::uint32_t ACTION_GET_TEMPERATURE = 16; + static const boost::uint32_t ACTION_SET_DC_OFFSET_AUTO = 17; + static const boost::uint32_t ACTION_SET_IQ_BALANCE_AUTO = 18; + static const boost::uint32_t ACTION_SET_AGC = 19; + static const boost::uint32_t ACTION_SET_AGC_MODE = 20; + static const boost::uint32_t ACTION_SET_BW = 21; + static const boost::uint32_t ACTION_GET_FREQ = 22; //Values for "which" static const boost::uint32_t CHAIN_NONE = 0; diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index 68c084589..9c92fe252 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2013,2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,6 +16,7 @@ // #include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/byte_vector.hpp> #include <uhd/types/mac_addr.hpp> #include <uhd/utils/byteswap.hpp> #include <boost/asio/ip/address_v4.hpp> @@ -39,32 +40,6 @@ static const size_t NAME_MAX_LEN = 32 - SERIAL_LEN; * Utility functions **********************************************************************/ -//! A wrapper around std::copy that takes ranges instead of iterators. -template<typename RangeSrc, typename RangeDst> inline -void byte_copy(const RangeSrc &src, RangeDst &dst){ - std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); -} - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ - std::string out; - BOOST_FOREACH(boost::uint8_t byte, bytes){ - if (byte < 32 or byte > 127) return out; - out += byte; - } - return out; -} - -//! create a byte vector from a string, null terminate unless max length -static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){ - byte_vector_t bytes; - for (size_t i = 0; i < std::min(string.size(), max_length); i++){ - bytes.push_back(string[i]); - } - if (bytes.size() < max_length - 1) bytes.push_back('\0'); - return bytes; -} - //! convert a string to a byte vector to write to eeprom static byte_vector_t string_to_uint16_bytes(const std::string &num_str){ const boost::uint16_t num = boost::lexical_cast<boost::uint16_t>(num_str); diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 794438b90..1866255c9 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -30,6 +30,8 @@ #include <boost/thread.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> +#include <boost/algorithm/string.hpp> +#include <algorithm> #include <cmath> using namespace uhd; @@ -431,6 +433,9 @@ public: ******************************************************************/ void set_master_clock_rate(double rate, size_t mboard){ if (mboard != ALL_MBOARDS){ + if (_tree->exists(mb_root(mboard) / "auto_tick_rate")) { + _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").set(false); + } _tree->access<double>(mb_root(mboard) / "tick_rate").set(rate); return; } @@ -821,6 +826,26 @@ public: } void set_rx_gain(double gain, const std::string &name, size_t chan){ + /* Check if any AGC mode is enable and if so warn the user */ + if (chan != ALL_CHANS) { + if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc")) { + bool agc = _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").get(); + if(agc) { + UHD_MSG(warning) << "AGC enabled for this channel. Setting will be ignored." << std::endl; + } + } + } else { + for (size_t c = 0; c < get_rx_num_channels(); c++){ + if (_tree->exists(rx_rf_fe_root(c) / "gain" / "agc")) { + bool agc = _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").get(); + if(agc) { + UHD_MSG(warning) << "AGC enabled for this channel. Setting will be ignored." << std::endl; + } + } + } + } + /* Apply gain setting. + * If device is in AGC mode it will ignore the setting. */ try { return rx_gain_group(chan)->set_value(gain, name); } catch (uhd::key_error &) { @@ -828,6 +853,32 @@ public: } } + void set_normalized_rx_gain(double gain, size_t chan = 0) + { + if (gain > 1.0 || gain < 0.0) { + throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1]."); + } + gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan); + double abs_gain = (gain * (gain_range.stop() - gain_range.start())) + gain_range.start(); + set_rx_gain(abs_gain, ALL_GAINS, chan); + } + + void set_rx_agc(bool enable, size_t chan = 0) + { + if (chan != ALL_CHANS){ + if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc" / "enable")) { + _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").set(enable); + } else { + UHD_MSG(warning) << "AGC is not available on this device." << std::endl; + } + return; + } + for (size_t c = 0; c < get_rx_num_channels(); c++){ + this->set_rx_agc(enable, c); + } + + } + double get_rx_gain(const std::string &name, size_t chan){ try { return rx_gain_group(chan)->get_value(name); @@ -836,6 +887,21 @@ public: } } + double get_normalized_rx_gain(size_t chan) + { + gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan); + double gain_range_width = gain_range.stop() - gain_range.start(); + // In case we have a device without a range of gains: + if (gain_range_width == 0.0) { + return 0; + } + double norm_gain = (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width; + // Avoid rounding errors: + if (norm_gain > 1.0) return 1.0; + if (norm_gain < 0.0) return 0.0; + return norm_gain; + } + gain_range_t get_rx_gain_range(const std::string &name, size_t chan){ try { return rx_gain_group(chan)->get_range(name); @@ -888,6 +954,9 @@ public: if (chan != ALL_CHANS){ if (_tree->exists(rx_fe_root(chan) / "dc_offset" / "enable")) { _tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb); + } else if (_tree->exists(rx_rf_fe_root(chan) / "dc_offset" / "enable")) { + /*For B2xx devices the dc-offset correction is implemented in the rf front-end*/ + _tree->access<bool>(rx_rf_fe_root(chan) / "dc_offset" / "enable").set(enb); } else { UHD_MSG(warning) << "Setting DC offset compensation is not possible on this device." << std::endl; } @@ -912,6 +981,20 @@ public: } } + void set_rx_iq_balance(const bool enb, size_t chan){ + if (chan != ALL_CHANS){ + if (_tree->exists(rx_rf_fe_root(chan) / "iq_balance" / "enable")) { + _tree->access<bool>(rx_rf_fe_root(chan) / "iq_balance" / "enable").set(enb); + } else { + UHD_MSG(warning) << "Setting IQ imbalance compensation is not possible on this device." << std::endl; + } + return; + } + for (size_t c = 0; c < get_rx_num_channels(); c++){ + this->set_rx_iq_balance(enb, c); + } + } + void set_rx_iq_balance(const std::complex<double> &offset, size_t chan){ if (chan != ALL_CHANS){ if (_tree->exists(rx_fe_root(chan) / "iq_balance" / "value")) { @@ -926,6 +1009,87 @@ public: } } + std::vector<std::string> get_filter_names(const std::string &search_mask) + { + std::vector<std::string> ret; + + for (size_t chan = 0; chan < get_rx_num_channels(); chan++){ + + if (_tree->exists(rx_rf_fe_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(rx_rf_fe_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = rx_rf_fe_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or boost::contains(name, search_mask)) { + ret.push_back(name); + } + } + } + if (_tree->exists(rx_dsp_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(rx_dsp_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = rx_dsp_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or (boost::contains(name, search_mask))) { + ret.push_back(name); + } + } + } + + } + + for (size_t chan = 0; chan < get_tx_num_channels(); chan++){ + + if (_tree->exists(tx_rf_fe_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(tx_rf_fe_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = tx_rf_fe_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or (boost::contains(name, search_mask))) { + ret.push_back(name); + } + } + } + if (_tree->exists(rx_dsp_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(tx_dsp_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = tx_dsp_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or (boost::contains(name, search_mask))) { + ret.push_back(name); + } + } + } + + } + + return ret; + } + + filter_info_base::sptr get_filter(const std::string &path) + { + std::vector<std::string> possible_names = get_filter_names(""); + std::vector<std::string>::iterator it; + it = find(possible_names.begin(), possible_names.end(), path); + if (it == possible_names.end()) { + throw uhd::runtime_error("Attempting to get non-existing filter: "+path); + } + + return _tree->access<filter_info_base::sptr>(path / "value").get(); + } + + void set_filter(const std::string &path, filter_info_base::sptr filter) + { + std::vector<std::string> possible_names = get_filter_names(""); + std::vector<std::string>::iterator it; + it = find(possible_names.begin(), possible_names.end(), path); + if (it == possible_names.end()) { + throw uhd::runtime_error("Attempting to set non-existing filter: "+path); + } + + _tree->access<filter_info_base::sptr>(path / "value").set(filter); + } + /******************************************************************* * TX methods ******************************************************************/ @@ -1029,6 +1193,17 @@ public: } } + void set_normalized_tx_gain(double gain, size_t chan = 0) + { + if (gain > 1.0 || gain < 0.0) { + throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1]."); + } + gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan); + double abs_gain = (gain * (gain_range.stop() - gain_range.start())) + gain_range.start(); + set_tx_gain(abs_gain, ALL_GAINS, chan); + } + + double get_tx_gain(const std::string &name, size_t chan){ try { return tx_gain_group(chan)->get_value(name); @@ -1037,6 +1212,21 @@ public: } } + double get_normalized_tx_gain(size_t chan) + { + gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan); + double gain_range_width = gain_range.stop() - gain_range.start(); + // In case we have a device without a range of gains: + if (gain_range_width == 0.0) { + return 0.0; + } + double norm_gain = (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width; + // Avoid rounding errors: + if (norm_gain > 1.0) return 1.0; + if (norm_gain < 0.0) return 0.0; + return norm_gain; + } + gain_range_t get_tx_gain_range(const std::string &name, size_t chan){ try { return tx_gain_group(chan)->get_range(name); diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp index 6450686dd..e182f649b 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.cpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -410,6 +410,8 @@ private: this->write_regs(0); _lmk04816_regs.CLKout0_1_DIV = master_clock_div; _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_EV_X; + _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_BOTH; this->write_regs(0); // Register 1 @@ -433,8 +435,10 @@ private: _lmk04816_regs.CLKout1_TYPE = lmk04816_regs_t::CLKOUT1_TYPE_P_DOWN; //CPRI feedback clock, use LVDS _lmk04816_regs.CLKout2_TYPE = lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP; //DB_0_RX _lmk04816_regs.CLKout3_TYPE = lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP; //DB_1_RX - // Analog delay of 900ps to synchronize the radio clock with the source synchronous ADC clocks. - // This delay may need to vary due to temperature. Tested and verified at room temperature only. + // Delay the FPGA_CLK by 900ps to ensure a safe ADC_SSCLK -> RADIO_CLK crossing. + // If the FPGA_CLK is delayed, we also need to delay the reference clocks going to the DAC + // because the data interface clock is generated from FPGA_CLK. + // NOTE: This delay value was verified at room temperature only. _lmk04816_regs.CLKout0_1_ADLY = 0x10; // Register 7 @@ -443,6 +447,7 @@ private: _lmk04816_regs.CLKout6_TYPE = lmk04816_regs_t::CLKOUT6_TYPE_LVPECL_700MVPP; //DB0_DAC _lmk04816_regs.CLKout7_TYPE = lmk04816_regs_t::CLKOUT7_TYPE_LVPECL_700MVPP; //DB1_DAC _lmk04816_regs.CLKout8_TYPE = lmk04816_regs_t::CLKOUT8_TYPE_LVPECL_700MVPP; //DB0_ADC + _lmk04816_regs.CLKout6_7_ADLY = _lmk04816_regs.CLKout0_1_ADLY; // Register 8 _lmk04816_regs.CLKout9_TYPE = lmk04816_regs_t::CLKOUT9_TYPE_LVPECL_700MVPP; //DB1_ADC diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp index d3bcb8644..bb41146b6 100644 --- a/host/lib/usrp/x300/x300_dac_ctrl.cpp +++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp @@ -129,12 +129,16 @@ public: _check_pll(); // Configure digital interface settings - write_ad9146_reg(0x16, 0x02); // Skew DCI signal by 615ps to find stable data eye - write_ad9146_reg(0x03, 0x00); // 2's comp, I first, byte wide interface - //fpga wants I,Q in the sample word: - //first transaction goes into low bits - //second transaction goes into high bits - //therefore, we want Q to go first (bit 6 == 1) + // Bypass DCI delay. We center the clock edge in the data + // valid window in the FPGA by phase shifting the DCI going + // to the DAC. + write_ad9146_reg(0x16, 0x04); + // 2's comp, I first, byte wide interface + write_ad9146_reg(0x03, 0x00); + // FPGA wants I,Q in the sample word: + // - First transaction goes into low bits + // - Second transaction goes into high bits + // therefore, we want Q to go first (bit 6 == 1) write_ad9146_reg(0x03, (1 << 6)); //2s comp, i first, byte mode // Configure interpolation filters diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h index 42583f7f0..a526cabe5 100644 --- a/host/lib/usrp/x300/x300_fw_common.h +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -29,9 +29,9 @@ extern "C" { #endif -#define X300_FW_COMPAT_MAJOR 3 +#define X300_FW_COMPAT_MAJOR 4 #define X300_FW_COMPAT_MINOR 0 -#define X300_FPGA_COMPAT_MAJOR 9 +#define X300_FPGA_COMPAT_MAJOR 10 //shared memory sections - in between the stack and the program space #define X300_FW_SHMEM_BASE 0x6000 diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index 809a56765..b2b9e5c6a 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -41,7 +41,7 @@ #define NIUSRPRIO_DEFAULT_RPC_PORT "5444" -#define X300_REV(x) (x - "A" + 1) +#define X300_REV(x) ((x) - "A" + 1) using namespace uhd; using namespace uhd::usrp; @@ -508,9 +508,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) x300_load_fw(mb.zpu_ctrl, x300_fw_image); } - //check compat -- good place to do after conditional loading - this->check_fw_compat(mb_path, mb.zpu_ctrl); + //check compat numbers + //check fpga compat before fw compat because the fw is a subset of the fpga image this->check_fpga_compat(mb_path, mb.zpu_ctrl); + this->check_fw_compat(mb_path, mb.zpu_ctrl); //store which FPGA image is loaded mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl); @@ -695,15 +696,14 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) // front panel gpio //////////////////////////////////////////////////////////////////// mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); - const std::vector<std::string> GPIO_ATTRS = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX"); - BOOST_FOREACH(const std::string &attr, GPIO_ATTRS) + BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) { - _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr) + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second) .set(0) - .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr, _1)); + .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr.first, _1)); } _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") - .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio, "READBACK")); + .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio)); //////////////////////////////////////////////////////////////////// // register the time keepers - only one can be the highlander @@ -1433,8 +1433,8 @@ bool x300_impl::wait_for_ref_locked(wb_iface::sptr ctrl, double timeout) boost::this_thread::sleep(boost::posix_time::milliseconds(1)); } while (boost::get_system_time() < timeout_time); - //failed to lock on reference - return false; + //Check one last time + return get_ref_locked(ctrl).to_bool(); } sensor_value_t x300_impl::get_ref_locked(wb_iface::sptr ctrl) @@ -1535,20 +1535,24 @@ void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eep * front-panel GPIO **********************************************************************/ -boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio, const std::string &) +boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio) { return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); } -void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const std::string &attr, const boost::uint32_t value) +void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) { - if (attr == "CTRL") return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); - if (attr == "DDR") return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); - if (attr == "OUT") return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); - if (attr == "ATR_0X") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); - if (attr == "ATR_RX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); - if (attr == "ATR_TX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); - if (attr == "ATR_XX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + switch (attr) + { + case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); + case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); + case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); + case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); + case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); + case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); + case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + default: UHD_THROW_INVALID_CODE_PATH(); + } } /*********************************************************************** @@ -1721,14 +1725,20 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_pcie(const std::string& res case X300_USRP_PCIE_SSID: mb_type = USRP_X300_MB; break; case X310_USRP_PCIE_SSID: - case X310_2940R_PCIE_SSID: - case X310_2942R_PCIE_SSID: - case X310_2943R_PCIE_SSID: - case X310_2944R_PCIE_SSID: - case X310_2950R_PCIE_SSID: - case X310_2952R_PCIE_SSID: - case X310_2953R_PCIE_SSID: - case X310_2954R_PCIE_SSID: + case X310_2940R_40MHz_PCIE_SSID: + case X310_2940R_120MHz_PCIE_SSID: + case X310_2942R_40MHz_PCIE_SSID: + case X310_2942R_120MHz_PCIE_SSID: + case X310_2943R_40MHz_PCIE_SSID: + case X310_2943R_120MHz_PCIE_SSID: + case X310_2944R_40MHz_PCIE_SSID: + case X310_2950R_40MHz_PCIE_SSID: + case X310_2950R_120MHz_PCIE_SSID: + case X310_2952R_40MHz_PCIE_SSID: + case X310_2952R_120MHz_PCIE_SSID: + case X310_2953R_40MHz_PCIE_SSID: + case X310_2953R_120MHz_PCIE_SSID: + case X310_2954R_40MHz_PCIE_SSID: mb_type = USRP_X310_MB; break; default: mb_type = UNKNOWN; break; @@ -1756,14 +1766,20 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo case X300_USRP_PCIE_SSID: mb_type = USRP_X300_MB; break; case X310_USRP_PCIE_SSID: - case X310_2940R_PCIE_SSID: - case X310_2942R_PCIE_SSID: - case X310_2943R_PCIE_SSID: - case X310_2944R_PCIE_SSID: - case X310_2950R_PCIE_SSID: - case X310_2952R_PCIE_SSID: - case X310_2953R_PCIE_SSID: - case X310_2954R_PCIE_SSID: + case X310_2940R_40MHz_PCIE_SSID: + case X310_2940R_120MHz_PCIE_SSID: + case X310_2942R_40MHz_PCIE_SSID: + case X310_2942R_120MHz_PCIE_SSID: + case X310_2943R_40MHz_PCIE_SSID: + case X310_2943R_120MHz_PCIE_SSID: + case X310_2944R_40MHz_PCIE_SSID: + case X310_2950R_40MHz_PCIE_SSID: + case X310_2950R_120MHz_PCIE_SSID: + case X310_2952R_40MHz_PCIE_SSID: + case X310_2952R_120MHz_PCIE_SSID: + case X310_2953R_40MHz_PCIE_SSID: + case X310_2953R_120MHz_PCIE_SSID: + case X310_2954R_40MHz_PCIE_SSID: mb_type = USRP_X310_MB; break; default: UHD_MSG(warning) << "X300 unknown product code in EEPROM: " << product_num << std::endl; diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 890ef7bcb..c27133745 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -361,8 +361,8 @@ private: void check_fpga_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant); - boost::uint32_t get_fp_gpio(gpio_core_200::sptr, const std::string &); - void set_fp_gpio(gpio_core_200::sptr, const std::string &, const boost::uint32_t); + boost::uint32_t get_fp_gpio(gpio_core_200::sptr); + void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); //**PRECONDITION** //This function assumes that all the VITA times in "radios" are synchronized diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index 334ae8168..e3515af0c 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -23,6 +23,7 @@ #include <uhd/transport/nirio_zero_copy.hpp> #include "async_packet_handler.hpp" #include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/chdr.hpp> #include <boost/bind.hpp> #include <uhd/utils/tasks.hpp> #include <uhd/utils/log.hpp> @@ -124,41 +125,6 @@ void x300_impl::update_subdev_spec(const std::string &tx_rx, const size_t mb_i, /*********************************************************************** - * VITA stuff - **********************************************************************/ -static void x300_if_hdr_unpack_be( - const boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_unpack_be(packet_buff, if_packet_info); -} - -static void x300_if_hdr_pack_be( - boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_pack_be(packet_buff, if_packet_info); -} - -static void x300_if_hdr_unpack_le( - const boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_unpack_le(packet_buff, if_packet_info); -} - -static void x300_if_hdr_pack_le( - boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_pack_le(packet_buff, if_packet_info); -} - -/*********************************************************************** * RX flow control handler **********************************************************************/ static size_t get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size, const device_addr_t& rx_args) @@ -209,9 +175,9 @@ static void handle_rx_flowctrl(const boost::uint32_t sid, zero_copy_if::sptr xpo //load header if (big_endian) - x300_if_hdr_pack_be(pkt, packet_info); + vrt::chdr::if_hdr_pack_be(pkt, packet_info); else - x300_if_hdr_pack_le(pkt, packet_info); + vrt::chdr::if_hdr_pack_le(pkt, packet_info); //load payload pkt[packet_info.num_header_words32+0] = uhd::htonx<boost::uint32_t>(0); @@ -276,12 +242,12 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero { if (big_endian) { - x300_if_hdr_unpack_be(packet_buff, if_packet_info); + vrt::chdr::if_hdr_unpack_be(packet_buff, if_packet_info); endian_conv = uhd::ntohx; } else { - x300_if_hdr_unpack_le(packet_buff, if_packet_info); + vrt::chdr::if_hdr_unpack_le(packet_buff, if_packet_info); endian_conv = uhd::wtohx; } } @@ -430,10 +396,10 @@ rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_) //init some streamer stuff std::string conv_endianness; if (mb.if_pkt_is_big_endian) { - my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_be); + my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be); conv_endianness = "be"; } else { - my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_le); + my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_le); conv_endianness = "le"; } @@ -594,10 +560,10 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_) std::string conv_endianness; if (mb.if_pkt_is_big_endian) { - my_streamer->set_vrt_packer(&x300_if_hdr_pack_be); + my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_be); conv_endianness = "be"; } else { - my_streamer->set_vrt_packer(&x300_if_hdr_pack_le); + my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_le); conv_endianness = "le"; } diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp index f920b5ae2..4c5729efe 100644 --- a/host/lib/usrp/x300/x300_regs.hpp +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -25,40 +25,40 @@ #define localparam static const int -localparam SR_DACSYNC = 5; -localparam SR_LOOPBACK = 6; -localparam SR_TEST = 7; -localparam SR_SPI = 8; -localparam SR_GPIO = 16; -localparam SR_MISC_OUTS = 24; -localparam SR_READBACK = 32; -localparam SR_TX_CTRL = 64; -localparam SR_RX_CTRL = 96; -localparam SR_TIME = 128; -localparam SR_RX_DSP = 144; -localparam SR_TX_DSP = 184; -localparam SR_LEDS = 195; -localparam SR_FP_GPIO = 200; -localparam SR_RX_FRONT = 208; -localparam SR_TX_FRONT = 216; - -localparam RB32_GPIO = 0; -localparam RB32_SPI = 4; -localparam RB64_TIME_NOW = 8; -localparam RB64_TIME_PPS = 16; -localparam RB32_TEST = 24; -localparam RB32_RX = 28; -localparam RB32_FP_GPIO = 32; - -localparam BL_ADDRESS = 0; -localparam BL_DATA = 1; +localparam SR_DACSYNC = 5; +localparam SR_LOOPBACK = 6; +localparam SR_TEST = 7; +localparam SR_SPI = 8; +localparam SR_GPIO = 16; +localparam SR_MISC_OUTS = 24; +localparam SR_READBACK = 32; +localparam SR_TX_CTRL = 64; +localparam SR_RX_CTRL = 96; +localparam SR_TIME = 128; +localparam SR_RX_DSP = 144; +localparam SR_TX_DSP = 184; +localparam SR_LEDS = 195; +localparam SR_FP_GPIO = 200; +localparam SR_RX_FRONT = 208; +localparam SR_TX_FRONT = 216; + +localparam RB32_GPIO = 0; +localparam RB32_SPI = 4; +localparam RB64_TIME_NOW = 8; +localparam RB64_TIME_PPS = 16; +localparam RB32_TEST = 24; +localparam RB32_RX = 28; +localparam RB32_FP_GPIO = 32; + +localparam BL_ADDRESS = 0; +localparam BL_DATA = 1; //wishbone settings map - relevant to host code -#define SET0_BASE 0xa000 -#define SETXB_BASE 0xb000 -#define BOOT_LDR_BASE 0xFA00 -#define I2C0_BASE 0xfe00 -#define I2C1_BASE 0xff00 +#define SET0_BASE 0xa000 +#define SETXB_BASE 0xb000 +#define BOOT_LDR_BASE 0xfa00 +#define I2C0_BASE 0xfe00 +#define I2C1_BASE 0xff00 #define SR_ADDR(base, offset) ((base) + (offset)*4) localparam ZPU_SR_LEDS = 00; @@ -70,56 +70,62 @@ localparam ZPU_SR_ETHINT0 = 40; localparam ZPU_SR_ETHINT1 = 56; //clock controls -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL 0x00 -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL 0x02 -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO 0x03 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL 0x00 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL 0x02 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO 0x03 - -localparam ZPU_RB_SPI = 2; +#define ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL 0x00 +#define ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL 0x02 +#define ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO 0x03 +#define ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL 0x00 +#define ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL 0x02 +#define ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO 0x03 + +localparam ZPU_RB_SPI = 2; localparam ZPU_RB_CLK_STATUS = 3; localparam ZPU_RB_COMPAT_NUM = 6; localparam ZPU_RB_ETH_TYPE0 = 4; localparam ZPU_RB_ETH_TYPE1 = 5; //clock status -#define ZPU_RB_CLK_STATUS_LMK_STATUS (0x3 << 0) -#define ZPU_RB_CLK_STATUS_LMK_LOCK (0x1 << 2) -#define ZPU_RB_CLK_STATUS_LMK_HOLDOVER (0x1 << 3) -#define ZPU_RB_CLK_STATUS_PPS_DETECT (0x1 << 4) +#define ZPU_RB_CLK_STATUS_LMK_STATUS (0x3 << 0) +#define ZPU_RB_CLK_STATUS_LMK_LOCK (0x1 << 2) +#define ZPU_RB_CLK_STATUS_LMK_HOLDOVER (0x1 << 3) +#define ZPU_RB_CLK_STATUS_PPS_DETECT (0x1 << 4) //spi slaves on radio -#define DB_DAC_SEN (1 << 7) -#define DB_ADC_SEN (1 << 6) +#define DB_DAC_SEN (1 << 7) +#define DB_ADC_SEN (1 << 6) #define DB_RX_LSADC_SEN (1 << 5) #define DB_RX_LSDAC_SEN (1 << 4) #define DB_TX_LSADC_SEN (1 << 3) #define DB_TX_LSDAC_SEN (1 << 2) -#define DB_RX_SEN (1 << 1) -#define DB_TX_SEN (1 << 0) +#define DB_RX_SEN (1 << 1) +#define DB_TX_SEN (1 << 0) //------------------------------------------------------------------- // PCIe Registers //------------------------------------------------------------------- -static const uint32_t X300_PCIE_VID = 0x1093; -static const uint32_t X300_PCIE_PID = 0xC4C4; -static const uint32_t X300_USRP_PCIE_SSID = 0x7736; -static const uint32_t X310_USRP_PCIE_SSID = 0x76CA; -static const uint32_t X310_2940R_PCIE_SSID = 0x772B; -static const uint32_t X310_2942R_PCIE_SSID = 0x772C; -static const uint32_t X310_2943R_PCIE_SSID = 0x772D; -static const uint32_t X310_2944R_PCIE_SSID = 0x772E; -static const uint32_t X310_2950R_PCIE_SSID = 0x772F; -static const uint32_t X310_2952R_PCIE_SSID = 0x7730; -static const uint32_t X310_2953R_PCIE_SSID = 0x7731; -static const uint32_t X310_2954R_PCIE_SSID = 0x7732; +static const uint32_t X300_PCIE_VID = 0x1093; +static const uint32_t X300_PCIE_PID = 0xC4C4; +static const uint32_t X300_USRP_PCIE_SSID = 0x7736; +static const uint32_t X310_USRP_PCIE_SSID = 0x76CA; +static const uint32_t X310_2940R_40MHz_PCIE_SSID = 0x772B; +static const uint32_t X310_2940R_120MHz_PCIE_SSID = 0x77FB; +static const uint32_t X310_2942R_40MHz_PCIE_SSID = 0x772C; +static const uint32_t X310_2942R_120MHz_PCIE_SSID = 0x77FC; +static const uint32_t X310_2943R_40MHz_PCIE_SSID = 0x772D; +static const uint32_t X310_2943R_120MHz_PCIE_SSID = 0x77FD; +static const uint32_t X310_2944R_40MHz_PCIE_SSID = 0x772E; +static const uint32_t X310_2950R_40MHz_PCIE_SSID = 0x772F; +static const uint32_t X310_2950R_120MHz_PCIE_SSID = 0x77FE; +static const uint32_t X310_2952R_40MHz_PCIE_SSID = 0x7730; +static const uint32_t X310_2952R_120MHz_PCIE_SSID = 0x77FF; +static const uint32_t X310_2953R_40MHz_PCIE_SSID = 0x7731; +static const uint32_t X310_2953R_120MHz_PCIE_SSID = 0x7800; +static const uint32_t X310_2954R_40MHz_PCIE_SSID = 0x7732; static const uint32_t FPGA_X3xx_SIG_VALUE = 0x58333030; static const uint32_t PCIE_FPGA_ADDR_BASE = 0xC0000; -#define PCIE_FPGA_REG(X) (PCIE_FPGA_ADDR_BASE + X) +#define PCIE_FPGA_REG(X) (PCIE_FPGA_ADDR_BASE + (X)) static const uint32_t FPGA_PCIE_SIG_REG = PCIE_FPGA_REG(0x0000); static const uint32_t FPGA_CNTR_LO_REG = PCIE_FPGA_REG(0x0004); @@ -140,8 +146,8 @@ static const uint32_t DMA_FRAME_SIZE_REG = 0x4; static const uint32_t DMA_SAMPLE_COUNT_REG = 0x8; static const uint32_t DMA_PKT_COUNT_REG = 0xC; -#define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) -#define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) +#define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + ((CHAN)*DMA_REG_GRP_SIZE) + (REG)) +#define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + ((CHAN)*DMA_REG_GRP_SIZE) + (REG)) static const uint32_t DMA_CTRL_DISABLED = 0x00000000; static const uint32_t DMA_CTRL_ENABLED = 0x00000002; @@ -154,15 +160,15 @@ static const uint32_t DMA_STATUS_ERROR = 0x00000001; static const uint32_t DMA_STATUS_BUSY = 0x00000002; static const uint32_t PCIE_ROUTER_REG_BASE = PCIE_FPGA_REG(0x0500); -#define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + X) +#define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + (X)) static const uint32_t PCIE_ZPU_DATA_BASE = 0x30000; static const uint32_t PCIE_ZPU_READ_BASE = 0x20000; //Trig and Status share the same base static const uint32_t PCIE_ZPU_STATUS_BASE = 0x20000; -#define PCIE_ZPU_DATA_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + X) -#define PCIE_ZPU_READ_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + X) -#define PCIE_ZPU_STATUS_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + X) +#define PCIE_ZPU_DATA_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + (X)) +#define PCIE_ZPU_READ_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + (X)) +#define PCIE_ZPU_STATUS_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + (X)) static const uint32_t PCIE_ZPU_READ_START = 0x0; static const uint32_t PCIE_ZPU_READ_CLOBBER = 0x80000000; diff --git a/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp b/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp index 93c317191..49d1a0442 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp +++ b/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ #include <uhd/usrp_clock/octoclock_eeprom.hpp> #include <uhd/transport/udp_simple.hpp> #include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/byte_vector.hpp> #include <uhd/types/mac_addr.hpp> #include <uhd/utils/byteswap.hpp> #include <boost/assign/list_of.hpp> @@ -37,26 +38,6 @@ using namespace uhd::usrp_clock; using namespace uhd::transport; /*********************************************************************** - * Utility functions - **********************************************************************/ - -//! A wrapper around std::copy that takes ranges instead of iterators. -template<typename RangeSrc, typename RangeDst> inline -void byte_copy(const RangeSrc &src, RangeDst &dst){ - std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); -} - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ - std::string out; - BOOST_FOREACH(boost::uint8_t byte, bytes){ - if (byte < 32 or byte > 127) return out; - out += byte; - } - return out; -} - -/*********************************************************************** * Implementation **********************************************************************/ void octoclock_eeprom_t::_load(){ diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index f29318ddd..a9c9965c8 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -367,10 +367,10 @@ std::string uhd::find_utility(std::string name) { .string(); } -std::string uhd::print_utility_error(std::string name){ +std::string uhd::print_utility_error(const std::string &name, const std::string &args){ #ifdef UHD_PLATFORM_WIN32 - return "As an Administrator, please run:\n\n\"" + find_utility(name) + "\""; + return "As an Administrator, please run:\n\n\"" + find_utility(name) + args + "\""; #else - return "Please run:\n\n \"" + find_utility(name) + "\""; + return "Please run:\n\n \"" + find_utility(name) + (args.empty() ? "" : (" " + args)) + "\""; #endif } diff --git a/host/lib/utils/platform.cpp b/host/lib/utils/platform.cpp index e2f92039e..a9cef663b 100644 --- a/host/lib/utils/platform.cpp +++ b/host/lib/utils/platform.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ #include <uhd/config.hpp> #include <boost/functional/hash.hpp> #ifdef UHD_PLATFORM_WIN32 -#include <Windows.h> +#include <windows.h> #else #include <unistd.h> #endif diff --git a/host/lib/utils/thread_priority.cpp b/host/lib/utils/thread_priority.cpp index 7c3faa37a..af25d088a 100644 --- a/host/lib/utils/thread_priority.cpp +++ b/host/lib/utils/thread_priority.cpp @@ -74,7 +74,7 @@ static void check_priority_range(float priority){ #ifdef HAVE_WIN_SETTHREADPRIORITY #include <windows.h> - void uhd::set_thread_priority(float priority, bool realtime){ + void uhd::set_thread_priority(float priority, UHD_UNUSED(bool realtime)){ check_priority_range(priority); /* diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 62544b69b..596ab1017 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -27,16 +27,19 @@ SET(test_sources addr_test.cpp buffer_test.cpp byteswap_test.cpp - convert_test.cpp cast_test.cpp + chdr_test.cpp + convert_test.cpp dict_test.cpp error_test.cpp fp_compare_delta_test.cpp fp_compare_epsilon_test.cpp gain_group_test.cpp + math_test.cpp msg_test.cpp property_test.cpp ranges_test.cpp + sid_t_test.cpp sph_recv_test.cpp sph_send_test.cpp subdev_spec_test.cpp @@ -45,7 +48,11 @@ SET(test_sources ) #turn each test cpp file into an executable with an int main() function -ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) +IF(MINGW) + ADD_DEFINITIONS(-DBOOST_TEST_MAIN) +ELSE() + ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) +ENDIF() SET(UHD_TEST_TARGET_DEPS uhd) SET(UHD_TEST_LIBRARY_DIRS ${Boost_LIBRARY_DIRS}) @@ -54,7 +61,7 @@ SET(UHD_TEST_LIBRARY_DIRS ${Boost_LIBRARY_DIRS}) FOREACH(test_source ${test_sources}) GET_FILENAME_COMPONENT(test_name ${test_source} NAME_WE) ADD_EXECUTABLE(${test_name} ${test_source}) - TARGET_LINK_LIBRARIES(${test_name} uhd) + TARGET_LINK_LIBRARIES(${test_name} uhd ${Boost_LIBRARIES}) UHD_ADD_TEST(${test_name} ${test_name}) UHD_INSTALL(TARGETS ${test_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/tests COMPONENT tests) ENDFOREACH(test_source) diff --git a/host/tests/addr_test.cpp b/host/tests/addr_test.cpp index cea2f224c..61bb6d049 100644 --- a/host/tests/addr_test.cpp +++ b/host/tests/addr_test.cpp @@ -66,6 +66,13 @@ BOOST_AUTO_TEST_CASE(test_device_addr){ old_dev_addr_vals.begin(), old_dev_addr_vals.end(), new_dev_addr_vals.begin(), new_dev_addr_vals.end() ); + + uhd::device_addr_t dev_addr_lhs1("key1=val1,key2=val2"); + dev_addr_lhs1.update(uhd::device_addr_t("key2=val2x,key3=val3"), false); + BOOST_CHECK_EQUAL(dev_addr_lhs1["key1"], "val1"); + BOOST_CHECK_EQUAL(dev_addr_lhs1["key2"], "val2x"); + BOOST_CHECK_EQUAL(dev_addr_lhs1["key3"], "val3"); + std::cout << "Merged: " << dev_addr_lhs1.to_string() << std::endl; } BOOST_AUTO_TEST_CASE(test_dboard_id){ diff --git a/host/tests/chdr_test.cpp b/host/tests/chdr_test.cpp new file mode 100644 index 000000000..f48073a09 --- /dev/null +++ b/host/tests/chdr_test.cpp @@ -0,0 +1,144 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/chdr.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/format.hpp> +#include <cstdlib> +#include <iostream> + +using namespace uhd::transport::vrt; + +static void pack_and_unpack( + if_packet_info_t &if_packet_info_in +){ + // Temp buffer for packed packet + boost::uint32_t packet_buff[2048] = {0}; + + // Check input (must not be lazy) + BOOST_REQUIRE( + (if_packet_info_in.num_payload_words32 == 0 and if_packet_info_in.num_payload_bytes == 0) + or + (if_packet_info_in.num_payload_words32 != 0 and if_packet_info_in.num_payload_bytes != 0) + ); + if (if_packet_info_in.num_payload_words32) { + BOOST_REQUIRE(if_packet_info_in.num_payload_bytes <= 4 * if_packet_info_in.num_payload_words32); + BOOST_REQUIRE(if_packet_info_in.num_payload_bytes > 4*(if_packet_info_in.num_payload_words32-1)); + } + + //pack metadata into a vrt header + chdr::if_hdr_pack_be( + packet_buff, if_packet_info_in + ); + std::cout << std::endl; + boost::uint32_t header_bits = (uhd::ntohx(packet_buff[0]) >> 28); + std::cout << boost::format("header bits = 0b%d%d%d%d") % ((header_bits & 8) > 0) + % ((header_bits & 4) > 0) + % ((header_bits & 2) > 0) + % ((header_bits & 1) > 0) << std::endl; + for (size_t i = 0; i < 5; i++) + { + std::cout << boost::format("packet_buff[%u] = 0x%08x") % i % uhd::ntohx(packet_buff[i]) << std::endl; + } + + if_packet_info_t if_packet_info_out; + // Must be set a-priori as per contract + if_packet_info_out.num_packet_words32 = if_packet_info_in.num_packet_words32; + + //unpack the vrt header back into metadata + chdr::if_hdr_unpack_be( + packet_buff, if_packet_info_out + ); + + //check the the unpacked metadata is the same + BOOST_CHECK_EQUAL(if_packet_info_in.packet_count, if_packet_info_out.packet_count); + BOOST_CHECK_EQUAL(if_packet_info_in.num_header_words32, if_packet_info_out.num_header_words32); + BOOST_CHECK_EQUAL(if_packet_info_in.num_payload_words32, if_packet_info_out.num_payload_words32); + BOOST_CHECK(if_packet_info_out.has_sid); + BOOST_CHECK_EQUAL(if_packet_info_in.sid, if_packet_info_out.sid); + BOOST_CHECK(if_packet_info_out.has_sid); + BOOST_CHECK_EQUAL(if_packet_info_in.has_tsf, if_packet_info_out.has_tsf); + if (if_packet_info_in.has_tsf and if_packet_info_out.has_tsf){ + BOOST_CHECK_EQUAL(if_packet_info_in.tsf, if_packet_info_out.tsf); + } +} + +BOOST_AUTO_TEST_CASE(test_with_chdr){ + if_packet_info_t if_packet_info; + if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_DATA; + if_packet_info.eob = false; + if_packet_info.packet_count = 7; + if_packet_info.has_tsf = true; + if_packet_info.tsf = 0x1234567890ABCDEFull; + if_packet_info.sid = 0xAABBCCDD; + if_packet_info.num_payload_words32 = 24; + if_packet_info.num_payload_bytes = 95; + pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_fc){ + if_packet_info_t if_packet_info; + if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_FC; + if_packet_info.eob = false; + if_packet_info.packet_count = 19; + if_packet_info.has_tsf = false; + if_packet_info.tsf = 0x1234567890ABCDEFull; + if_packet_info.sid = 0xAABBCCDD; + if_packet_info.num_payload_words32 = 4; + if_packet_info.num_payload_bytes = 16; + pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_cmd){ + if_packet_info_t if_packet_info; + if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_CMD; + if_packet_info.packet_count = 19; + if_packet_info.has_tsf = true; + if_packet_info.tsf = 0x1234567890ABCDEFull; + if_packet_info.sid = 0xAABBCCDD; + if_packet_info.num_payload_words32 = 4; + if_packet_info.num_payload_bytes = 16; + pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_resp){ + if_packet_info_t if_packet_info; + if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_RESP; + if_packet_info.packet_count = 123; + if_packet_info.has_tsf = false; + if_packet_info.tsf = 0x1234567890ABCDEFull; + if_packet_info.sid = 0xAABBCCDD; + if_packet_info.num_payload_words32 = 4; + if_packet_info.num_payload_bytes = 16; + pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_err){ + if_packet_info_t if_packet_info; + if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_ERROR; + if_packet_info.packet_count = 1928; + if_packet_info.eob = false; + if_packet_info.error = false; // Needs to be set explicitly + if_packet_info.has_tsf = false; + if_packet_info.tsf = 0x1234567890ABCDEFull; + if_packet_info.sid = 0xAABBCCDD; + if_packet_info.num_payload_words32 = 4; + if_packet_info.num_payload_bytes = 16; + pack_and_unpack(if_packet_info); +} + diff --git a/host/tests/dict_test.cpp b/host/tests/dict_test.cpp index 7b388d090..333aadbba 100644 --- a/host/tests/dict_test.cpp +++ b/host/tests/dict_test.cpp @@ -70,3 +70,28 @@ BOOST_AUTO_TEST_CASE(test_dict_pop){ BOOST_CHECK(d.keys()[0] == -1); BOOST_CHECK(d.keys()[1] == 1); } + +BOOST_AUTO_TEST_CASE(test_dict_update) +{ + uhd::dict<std::string, std::string> d1 = boost::assign::map_list_of + ("key1", "val1") + ("key2", "val2") + ; + uhd::dict<std::string, std::string> d2 = boost::assign::map_list_of + ("key2", "val2x") + ("key3", "val3") + ; + + d1.update(d2, false /* don't throw cause of conflict */); + BOOST_CHECK_EQUAL(d1["key1"], "val1"); + BOOST_CHECK_EQUAL(d1["key2"], "val2x"); + BOOST_CHECK_EQUAL(d1["key3"], "val3"); + + uhd::dict<std::string, std::string> d3 = boost::assign::map_list_of + ("key1", "val1") + ("key2", "val2") + ; + BOOST_CHECK_THROW(d3.update(d2), uhd::value_error); +} + + diff --git a/host/tests/fp_compare_delta_test.cpp b/host/tests/fp_compare_delta_test.cpp index 0ac4e257d..36ff14756 100644 --- a/host/tests/fp_compare_delta_test.cpp +++ b/host/tests/fp_compare_delta_test.cpp @@ -239,12 +239,12 @@ BOOST_AUTO_TEST_CASE(double_greaterthanequals_operators) { BOOST_AUTO_TEST_CASE(frequency_compare_function) { - BOOST_CHECK(uhd::math::frequencies_are_equal(6817333232, 6817333232)); - BOOST_CHECK(!uhd::math::frequencies_are_equal(6817333233, 6817333232)); + BOOST_CHECK(uhd::math::frequencies_are_equal(6817333232.0, 6817333232.0)); + BOOST_CHECK(!uhd::math::frequencies_are_equal(6817333233.0, 6817333232.0)); BOOST_CHECK(uhd::math::frequencies_are_equal(6817333232.1, 6817333232.1)); BOOST_CHECK(!uhd::math::frequencies_are_equal(6817333232.5, 6817333232.6)); BOOST_CHECK(uhd::math::frequencies_are_equal(16.8173332321e9, 16.8173332321e9)); BOOST_CHECK(!uhd::math::frequencies_are_equal(16.8173332322e9, 16.8173332321e9)); BOOST_CHECK(!uhd::math::frequencies_are_equal(5.0, 4.0)); - BOOST_CHECK(uhd::math::frequencies_are_equal(48750000, 48749999.9946)); + BOOST_CHECK(uhd::math::frequencies_are_equal(48750000.0, 48749999.9946)); } diff --git a/host/tests/math_test.cpp b/host/tests/math_test.cpp new file mode 100644 index 000000000..6c890c484 --- /dev/null +++ b/host/tests/math_test.cpp @@ -0,0 +1,29 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/test/unit_test.hpp> +#include <boost/cstdint.hpp> +#include <uhd/utils/math.hpp> + +// NOTE: This is not the only math test case, see e.g. special tests +// for fp comparison. + +BOOST_AUTO_TEST_CASE(test_log2){ + double y = uhd::math::log2(16.0); + BOOST_CHECK_EQUAL(y, 4.0); +} + diff --git a/host/tests/sid_t_test.cpp b/host/tests/sid_t_test.cpp new file mode 100644 index 000000000..31eb4b458 --- /dev/null +++ b/host/tests/sid_t_test.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <iostream> +#include <sstream> +#include <boost/test/unit_test.hpp> +#include <uhd/types/sid.hpp> +#include <uhd/exception.hpp> + +using uhd::sid_t; + +BOOST_AUTO_TEST_CASE(test_sid_t) { + boost::uint32_t sid_value = 0x01020310; + sid_t sid(sid_value); + + BOOST_CHECK_EQUAL(sid.is_set(), true); + BOOST_CHECK_EQUAL(sid.to_pp_string(), "1.2>3.16"); + BOOST_CHECK_EQUAL(sid.to_pp_string_hex(), "01:02>03:10"); + BOOST_CHECK_EQUAL(sid.get_src(), (boost::uint32_t)0x0102); + BOOST_CHECK_EQUAL(sid.get_dst(), (boost::uint32_t)0x0310); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x01); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x02); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x03); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x10); + BOOST_CHECK_EQUAL(sid == sid, true); + BOOST_CHECK_EQUAL(sid == sid_value, true); + + boost::uint32_t check_sid_val = (boost::uint32_t) sid; + BOOST_CHECK_EQUAL(check_sid_val, sid_value); + + std::stringstream ss_dec; + ss_dec << sid; + BOOST_CHECK_EQUAL(ss_dec.str(), "1.2>3.16"); + + std::stringstream ss_hex; + ss_hex << std::hex << sid; + BOOST_CHECK_EQUAL(ss_hex.str(), "01:02>03:10"); + + sid_t empty_sid; + BOOST_CHECK_EQUAL(empty_sid.is_set(), false); + BOOST_CHECK_EQUAL(empty_sid.to_pp_string(), "x.x>x.x"); + BOOST_CHECK_EQUAL(empty_sid.to_pp_string_hex(), "xx:xx>xx:xx"); + BOOST_CHECK_EQUAL(empty_sid == sid, false); + BOOST_CHECK_EQUAL(empty_sid == sid_value, false); + BOOST_CHECK_EQUAL((bool) empty_sid, false); + + empty_sid = sid_value; // No longer empty + BOOST_CHECK_EQUAL(empty_sid.is_set(), true); + BOOST_CHECK_EQUAL(empty_sid == sid, true); +} + +BOOST_AUTO_TEST_CASE(test_sid_t_set) { + boost::uint32_t sid_value = 0x0; + sid_t sid(sid_value); + + sid.set(0x01020304); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x01020304); + BOOST_CHECK_EQUAL(sid.get_src_addr(),(boost::uint32_t)0x01); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x02); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x03); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x04); + BOOST_CHECK_EQUAL(sid.get_dst_xbarport(), (boost::uint32_t)0x0); + BOOST_CHECK_EQUAL(sid.get_dst_blockport(), (boost::uint32_t)0x4); + + sid.set_src_addr(0x0a); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a020304); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x02); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x03); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x04); + + sid.set_src_endpoint(0x0b); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a0b0304); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x0b); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x03); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x04); + + sid.set_dst_addr(0x0c); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a0b0c04); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x0b); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x0c); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x04); + + sid.set_dst_endpoint(0x0d); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a0b0c0d); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x0b); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x0c); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x0d); + + sid.set_dst_xbarport(0xb); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a0b0cbd); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x0b); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x0c); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0xbd); + + sid.set_dst_blockport(0xc); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a0b0cbc); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x0b); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x0c); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0xbc); + + sid_t flipped_sid = sid.reversed(); + BOOST_CHECK_EQUAL(flipped_sid.get(), (boost::uint32_t)0x0cbc0a0b); + + // In-place + sid.reverse(); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0cbc0a0b); +} + +BOOST_AUTO_TEST_CASE(test_sid_t_from_str) { + sid_t sid("1.2>3.4"); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)1); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)2); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)3); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)4); + + sid = "01:02>03:10"; + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)1); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)2); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)3); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)16); + + sid = "01:06/03:10"; + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)1); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)6); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)3); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)16); + + sid = "01:02:04:10"; + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)1); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)2); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)4); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)16); + + BOOST_REQUIRE_THROW(sid_t fail_sid("foobar"), uhd::value_error); + BOOST_REQUIRE_THROW(sid_t fail_sid("01:02:03:4"), uhd::value_error); + BOOST_REQUIRE_THROW(sid_t fail_sid("01:02:03:004"), uhd::value_error); + BOOST_REQUIRE_THROW(sid_t fail_sid("1.2.3.0004"), uhd::value_error); +} diff --git a/host/tests/time_spec_test.cpp b/host/tests/time_spec_test.cpp index c9b9652f9..b98bea7d9 100644 --- a/host/tests/time_spec_test.cpp +++ b/host/tests/time_spec_test.cpp @@ -112,8 +112,8 @@ BOOST_AUTO_TEST_CASE(test_time_large_ticks_to_time_spec) BOOST_AUTO_TEST_CASE(test_time_error_irrational_rate) { - static const double rate = 1625e3/6; - const long long tick_in = 23423436291667; + static const double rate = 1625e3/6.0; + const long long tick_in = 23423436291667ll; const uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(tick_in, rate); const long long tick_out = ts.to_ticks(rate); const long long err = tick_in - tick_out; diff --git a/host/utils/b2xx_fx3_utils.cpp b/host/utils/b2xx_fx3_utils.cpp index 572daef70..bc14932f1 100644 --- a/host/utils/b2xx_fx3_utils.cpp +++ b/host/utils/b2xx_fx3_utils.cpp @@ -51,20 +51,26 @@ struct vid_pid_t { const static vid_pid_t known_vid_pids[] = { {FX3_VID, FX3_DEFAULT_PID}, {FX3_VID, FX3_REENUM_PID}, - {B200_VENDOR_ID, B200_PRODUCT_ID} + {B200_VENDOR_ID, B200_PRODUCT_ID}, + {B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID}, + {B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID} }; const static std::vector<vid_pid_t> known_vid_pid_vector(known_vid_pids, known_vid_pids + (sizeof(known_vid_pids) / sizeof(known_vid_pids[0]))); -const static boost::uint8_t eeprom_init_values[] = { - 0x43, - 0x59, - 0x14, - 0xB2, - (B200_PRODUCT_ID & 0xff), - (B200_PRODUCT_ID >> 8), - (B200_VENDOR_ID & 0xff), - (B200_VENDOR_ID >> 8) - }; -const static uhd::byte_vector_t eeprom_init_value_vector(eeprom_init_values, eeprom_init_values + (sizeof(eeprom_init_values) / sizeof(eeprom_init_values[0]))); + +static const size_t EEPROM_INIT_VALUE_VECTOR_SIZE = 8; +static uhd::byte_vector_t construct_eeprom_init_value_vector(boost::uint16_t vid, boost::uint16_t pid) +{ + uhd::byte_vector_t init_values(EEPROM_INIT_VALUE_VECTOR_SIZE); + init_values.at(0) = 0x43; + init_values.at(1) = 0x59; + init_values.at(2) = 0x14; + init_values.at(3) = 0xB2; + init_values.at(4) = static_cast<boost::uint8_t>(pid & 0xff); + init_values.at(5) = static_cast<boost::uint8_t>(pid >> 8); + init_values.at(6) = static_cast<boost::uint8_t>(vid & 0xff); + init_values.at(7) = static_cast<boost::uint8_t>(vid >> 8); + return init_values; +} //!used with lexical cast to parse a hex string template <class T> struct to_hex{ @@ -153,15 +159,22 @@ uhd::transport::usb_device_handle::sptr open_device(const boost::uint16_t vid, c try { // try caller's VID/PID first - handles = uhd::transport::usb_device_handle::get_device_list(vp.vid,vp.pid); - if (user_supplied && handles.size() == 0) - std::cerr << (boost::format("Failed to open device with VID 0x%04x and PID 0x%04x - trying other known VID/PIDs") % vid % pid).str() << std::endl; - - // try known VID/PIDs next - for (size_t i = 0; handles.size() == 0 && i < known_vid_pid_vector.size(); i++) + std::vector<uhd::transport::usb_device_handle::vid_pid_pair_t> vid_pid_pair_list(1,uhd::transport::usb_device_handle::vid_pid_pair_t(vid,pid)); + handles = uhd::transport::usb_device_handle::get_device_list(vid_pid_pair_list); + if (handles.size() == 0) { - vp = known_vid_pid_vector[i]; - handles = uhd::transport::usb_device_handle::get_device_list(vp.vid,vp.pid); + if (user_supplied) + { + std::cerr << (boost::format("Failed to open device with VID 0x%04x and PID 0x%04x - trying other known VID/PIDs") % vid % pid).str() << std::endl; + } + + // try known VID/PIDs next + for (size_t i = 0; handles.size() == 0 && i < known_vid_pid_vector.size(); i++) + { + vp = known_vid_pid_vector[i]; + handles = uhd::transport::usb_device_handle::get_device_list(vp.vid, vp.pid); + } + } if (handles.size() > 0) @@ -221,7 +234,7 @@ int read_eeprom(b200_iface::sptr& b200, uhd::byte_vector_t& data) int write_eeprom(b200_iface::sptr& b200, const uhd::byte_vector_t& data) { try { - b200->write_eeprom(0x0, 0x0, data); + b200->write_eeprom(0x0, 0x0, data); } catch (std::exception &e) { std::cerr << "Exception while writing EEPROM: " << e.what() << std::endl; return -1; @@ -281,7 +294,7 @@ int erase_eeprom(b200_iface::sptr& b200) boost::int32_t main(boost::int32_t argc, char *argv[]) { boost::uint16_t vid, pid; - std::string pid_str, vid_str, fw_file, fpga_file; + std::string pid_str, vid_str, fw_file, fpga_file, writevid_str, writepid_str; bool user_supplied_vid_pid = false; po::options_description visible("Allowed options"); @@ -295,7 +308,6 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { ("reset-device,D", "Reset the B2xx Device.") ("reset-fpga,F", "Reset the FPGA (does not require re-programming.") ("reset-usb,U", "Reset the USB subsystem on your host computer.") - ("init-device,I", "Initialize a B2xx device.") ("load-fw,W", po::value<std::string>(&fw_file), "Load a firmware (hex) file into the FX3.") ("load-fpga,L", po::value<std::string>(&fpga_file), @@ -305,9 +317,14 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { // Hidden options provided for testing - use at your own risk! po::options_description hidden("Hidden options"); hidden.add_options() - ("uninit-device,U", "Uninitialize a B2xx device.") + ("init-device,I", "Initialize a B2xx device.") + ("uninit-device", "Uninitialize a B2xx device.") ("read-eeprom,R", "Read first 8 bytes of EEPROM") - ("erase-eeprom,E", "Erase first 8 bytes of EEPROM"); + ("erase-eeprom,E", "Erase first 8 bytes of EEPROM") + ("write-vid", po::value<std::string>(&writevid_str), + "Write VID field of EEPROM") + ("write-pid", po::value<std::string>(&writepid_str), + "Write PID field of EEPROM"); po::options_description desc; desc.add(visible); @@ -486,9 +503,24 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { * Cypress VID/PID for the initial FW load, but we can initialize from any state. */ if (vm.count("init-device")) { + uint16_t writevid = B200_VENDOR_ID; + uint16_t writepid = B200_PRODUCT_ID; + /* Now, initialize the device. */ - if (write_and_verify_eeprom(b200, eeprom_init_value_vector)) - return -1; + // Added for testing purposes - not exposed + if (vm.count("write-vid") && vm.count("write-pid")) + { + try { + writevid = atoh(writevid_str); + writepid = atoh(writepid_str); + } catch (std::exception &e) { + std::cerr << "Exception while parsing write VID and PID: " << e.what() << std:: endl; + return ~0; + } + } + + std::cout << "Writing VID and PID to EEPROM..." << std::endl << std::endl; + if (write_and_verify_eeprom(b200, construct_eeprom_init_value_vector(writevid, writepid))) return -1; std::cout << "EEPROM initialized, resetting device..." << std::endl << std::endl; |