diff options
37 files changed, 861 insertions, 1097 deletions
diff --git a/firmware/zpu/apps/txrx_uhd.c b/firmware/zpu/apps/txrx_uhd.c index 4ccb585e2..0c93d2352 100644 --- a/firmware/zpu/apps/txrx_uhd.c +++ b/firmware/zpu/apps/txrx_uhd.c @@ -53,20 +53,34 @@ static void setup_network(void); // the fast-path setup global variables // ---------------------------------------------------------------- static eth_mac_addr_t fp_mac_addr_src, fp_mac_addr_dst; -extern struct socket_address fp_socket_src, fp_socket_dst; +struct socket_address fp_socket_src, fp_socket_dst; +extern uint16_t dsp0_dst_port, err0_dst_port, dsp1_dst_port; static void handle_udp_err0_packet( struct socket_address src, struct socket_address dst, unsigned char *payload, int payload_len ){ sr_udp_sm->err0_port = (((uint32_t)dst.port) << 16) | src.port; + err0_dst_port = src.port; printf("Storing for async error path:\n"); printf(" source udp port: %d\n", dst.port); printf(" destination udp port: %d\n", src.port); newline(); } -static void handle_udp_data_packet( +static void handle_udp_dsp1_packet( + struct socket_address src, struct socket_address dst, + unsigned char *payload, int payload_len +){ + sr_udp_sm->dsp1_port = (((uint32_t)dst.port) << 16) | src.port; + dsp1_dst_port = src.port; + printf("Storing for dsp1 path:\n"); + printf(" source udp port: %d\n", dst.port); + printf(" destination udp port: %d\n", src.port); + newline(); +} + +static void handle_udp_dsp0_packet( struct socket_address src, struct socket_address dst, unsigned char *payload, int payload_len ){ @@ -75,7 +89,8 @@ static void handle_udp_data_packet( fp_socket_src = dst; fp_socket_dst = src; sr_udp_sm->dsp0_port = (((uint32_t)dst.port) << 16) | src.port; - printf("Storing for fast path:\n"); + dsp0_dst_port = src.port; + printf("Storing for dsp0 path:\n"); printf(" source mac addr: "); print_mac_addr(&fp_mac_addr_src); newline(); printf(" source ip addr: "); @@ -341,13 +356,14 @@ main(void) //1) register the addresses into the network stack register_addrs(ethernet_mac_addr(), get_ip_addr()); - pkt_ctrl_program_inspector(get_ip_addr(), USRP2_UDP_DATA_PORT); + pkt_ctrl_program_inspector(get_ip_addr(), USRP2_UDP_DSP0_PORT); //2) register callbacks for udp ports we service init_udp_listeners(); register_udp_listener(USRP2_UDP_CTRL_PORT, handle_udp_ctrl_packet); - register_udp_listener(USRP2_UDP_DATA_PORT, handle_udp_data_packet); + register_udp_listener(USRP2_UDP_DSP0_PORT, handle_udp_dsp0_packet); register_udp_listener(USRP2_UDP_ERR0_PORT, handle_udp_err0_packet); + register_udp_listener(USRP2_UDP_DSP1_PORT, handle_udp_dsp1_packet); #ifdef USRP2P register_udp_listener(USRP2_UDP_UPDATE_PORT, handle_udp_fw_update_packet); #endif diff --git a/firmware/zpu/lib/net_common.c b/firmware/zpu/lib/net_common.c index d1b06976d..6a0fd254b 100644 --- a/firmware/zpu/lib/net_common.c +++ b/firmware/zpu/lib/net_common.c @@ -1,6 +1,5 @@ -/* -*- c -*- */ /* - * Copyright 2009,2010 Ettus Research LLC + * Copyright 2009-2011 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 @@ -42,7 +41,7 @@ static const bool debug = false; static const eth_mac_addr_t BCAST_MAC_ADDR = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; //used in the top level application... -struct socket_address fp_socket_src, fp_socket_dst; +uint16_t dsp0_dst_port, err0_dst_port, dsp1_dst_port; // ------------------------------------------------------------------------ @@ -277,15 +276,24 @@ handle_icmp_packet(struct ip_addr src, struct ip_addr dst, //filter out non udp data response struct ip_hdr *ip = (struct ip_hdr *)(((uint8_t*)icmp) + sizeof(struct icmp_echo_hdr)); struct udp_hdr *udp = (struct udp_hdr *)(((char *)ip) + IP_HLEN); - if (IPH_PROTO(ip) != IP_PROTO_UDP || udp->dest != fp_socket_dst.port) return; - - //end async update packets per second - sr_tx_ctrl->cyc_per_up = 0; - - //the end continuous streaming command - sr_rx_ctrl->cmd = 1 << 31; //no samples now - sr_rx_ctrl->time_secs = 0; - sr_rx_ctrl->time_ticks = 0; //latch the command + if (IPH_PROTO(ip) != IP_PROTO_UDP) break; + + if (udp->dest == dsp0_dst_port){ + //the end continuous streaming command + sr_rx_ctrl0->cmd = 1 << 31; //no samples now + sr_rx_ctrl0->time_secs = 0; + sr_rx_ctrl0->time_ticks = 0; //latch the command + } + else if (udp->dest == dsp1_dst_port){ + //the end continuous streaming command + sr_rx_ctrl1->cmd = 1 << 31; //no samples now + sr_rx_ctrl1->time_secs = 0; + sr_rx_ctrl1->time_ticks = 0; //latch the command + } + else if (udp->dest == err0_dst_port){ + //end async update packets per second + sr_tx_ctrl->cyc_per_up = 0; + } //struct udp_hdr *udp = (struct udp_hdr *)((char *)icmp + 28); //printf("icmp port unr %d\n", udp->dest); diff --git a/firmware/zpu/usrp2/memory_map.h b/firmware/zpu/usrp2/memory_map.h index e728a1ddb..79b11759a 100644 --- a/firmware/zpu/usrp2/memory_map.h +++ b/firmware/zpu/usrp2/memory_map.h @@ -1,4 +1,4 @@ -/* -*- c -*- */ +// Copyright 2010-2011 Ettus Research LLC /* * Copyright 2007,2008,2009 Free Software Foundation, Inc. * @@ -227,8 +227,10 @@ hwconfig_wishbone_divisor(void) #define SR_UDP_SM 96 #define SR_TX_DSP 208 #define SR_TX_CTRL 224 -#define SR_RX_DSP 160 -#define SR_RX_CTRL 176 +#define SR_RX_DSP0 160 +#define SR_RX_DSP1 240 +#define SR_RX_CTRL0 176 +#define SR_RX_CTRL1 32 #define SR_TIME64 192 #define SR_SIMTIMER 198 #define SR_LAST 255 @@ -350,10 +352,7 @@ typedef struct { #define sr_udp_sm ((sr_udp_sm_t *) _SR_ADDR(SR_UDP_SM)) -// --- dsp tx regs --- - -#define MIN_CIC_INTERP 1 -#define MAX_CIC_INTERP 128 +// --- VITA TX CTRL regs --- typedef struct { volatile uint32_t num_chan; @@ -366,52 +365,6 @@ typedef struct { #define sr_tx_ctrl ((sr_tx_ctrl_t *) _SR_ADDR(SR_TX_CTRL)) -typedef struct { - volatile int32_t freq; - volatile uint32_t scale_iq; // {scale_i,scale_q} - volatile uint32_t interp_rate; - volatile uint32_t _padding0; // padding for the tx_mux - // NOT freq, scale, interp - /*! - * \brief output mux configuration. - * - * <pre> - * 3 2 1 - * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - * +-------------------------------+-------+-------+-------+-------+ - * | | DAC1 | DAC0 | - * +-------------------------------+-------+-------+-------+-------+ - * - * There are N DUCs (1 now) with complex inputs and outputs. - * There are two DACs. - * - * Each 4-bit DACx field specifies the source for the DAC - * Each subfield is coded like this: - * - * 3 2 1 0 - * +-------+ - * | N | - * +-------+ - * - * N specifies which DUC output is connected to this DAC. - * - * N which interp output - * --- ------------------- - * 0 DUC 0 I - * 1 DUC 0 Q - * 2 DUC 1 I - * 3 DUC 1 Q - * F All Zeros - * - * The default value is 0x10 - * </pre> - */ - volatile uint32_t tx_mux; - -} dsp_tx_regs_t; - -#define dsp_tx_regs ((dsp_tx_regs_t *) _SR_ADDR(SR_TX_DSP)) - // --- VITA RX CTRL regs --- typedef struct { // The following 3 are logically a single command register. @@ -419,81 +372,10 @@ typedef struct { volatile uint32_t cmd; // {now, chain, num_samples(30) volatile uint32_t time_secs; volatile uint32_t time_ticks; - - volatile uint32_t clear_overrun; // write anything to clear overrun - volatile uint32_t vrt_header; // word 0 of packet. FPGA fills in packet counter - volatile uint32_t vrt_stream_id; // word 1 of packet. - volatile uint32_t vrt_trailer; - volatile uint32_t nsamples_per_pkt; - volatile uint32_t nchannels; // 1 in basic case, up to 4 for vector sources - volatile uint32_t pad[7]; // Make each structure 16 elements long } sr_rx_ctrl_t; -#define sr_rx_ctrl ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL)) - -// --- dsp rx regs --- -#define MIN_CIC_DECIM 1 -#define MAX_CIC_DECIM 128 - -typedef struct { - volatile int32_t freq; - volatile uint32_t scale_iq; // {scale_i,scale_q} - volatile uint32_t decim_rate; - volatile uint32_t dcoffset_i; // Bit 31 high sets fixed offset mode, using lower 14 bits, - // otherwise it is automatic - volatile uint32_t dcoffset_q; // Bit 31 high sets fixed offset mode, using lower 14 bits - - /*! - * \brief input mux configuration. - * - * This determines which ADC (or constant zero) is connected to - * each DDC input. There are N DDCs (1 now). Each has two inputs. - * - * <pre> - * Mux value: - * - * 3 2 1 - * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | |Q0 |I0 | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * - * Each 2-bit I field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) - * Each 2-bit Q field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) - * - * The default value is 0x4 - * </pre> - */ - volatile uint32_t rx_mux; // called adc_mux in dsp_core_rx.v - - /*! - * \brief Streaming GPIO configuration - * - * This determines whether the LSBs of I and Q samples come from the DSP - * pipeline or from the io_rx GPIO pins. To stream GPIO, one must first - * set the GPIO data direction register to have io_rx[15] and/or io_rx[14] - * configured as inputs. The GPIO pins will be sampled at the time the - * remainder of the DSP sample is strobed into the RX sample FIFO. There - * will be a decimation-dependent fixed time offset between the GPIO - * sample stream and the associated RF samples. - * - * 3 2 1 - * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | MBZ |Q|I| - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * - * I 0=LSB comes from DSP pipeline (default) - * 1=LSB comes from io_rx[15] - * - * Q 0=LSB comes from DSP pipeline (default) - * 1=LSB comes from io_rx[14] - */ - volatile uint32_t gpio_stream_enable; - -} dsp_rx_regs_t; - -#define dsp_rx_regs ((dsp_rx_regs_t *) _SR_ADDR(SR_RX_DSP)) +#define sr_rx_ctrl0 ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL0)) +#define sr_rx_ctrl1 ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL1)) // ---------------------------------------------------------------- // VITA49 64 bit time (write only) diff --git a/firmware/zpu/usrp2p/memory_map.h b/firmware/zpu/usrp2p/memory_map.h index 36d8ac9f2..2567a4588 100644 --- a/firmware/zpu/usrp2p/memory_map.h +++ b/firmware/zpu/usrp2p/memory_map.h @@ -1,4 +1,4 @@ -/* -*- c -*- */ +// Copyright 2010-2011 Ettus Research LLC /* * Copyright 2007,2008,2009 Free Software Foundation, Inc. * @@ -218,8 +218,10 @@ hwconfig_wishbone_divisor(void) #define SR_UDP_SM 96 #define SR_TX_DSP 208 #define SR_TX_CTRL 224 -#define SR_RX_DSP 160 -#define SR_RX_CTRL 176 +#define SR_RX_DSP0 160 +#define SR_RX_DSP1 240 +#define SR_RX_CTRL0 176 +#define SR_RX_CTRL1 32 #define SR_TIME64 192 #define SR_SIMTIMER 198 #define SR_LAST 255 @@ -343,10 +345,7 @@ typedef struct { #define sr_udp_sm ((sr_udp_sm_t *) _SR_ADDR(SR_UDP_SM)) -// --- dsp tx regs --- - -#define MIN_CIC_INTERP 1 -#define MAX_CIC_INTERP 128 +// --- VITA TX CTRL regs --- typedef struct { volatile uint32_t num_chan; @@ -359,52 +358,6 @@ typedef struct { #define sr_tx_ctrl ((sr_tx_ctrl_t *) _SR_ADDR(SR_TX_CTRL)) -typedef struct { - volatile int32_t freq; - volatile uint32_t scale_iq; // {scale_i,scale_q} - volatile uint32_t interp_rate; - volatile uint32_t _padding0; // padding for the tx_mux - // NOT freq, scale, interp - /*! - * \brief output mux configuration. - * - * <pre> - * 3 2 1 - * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - * +-------------------------------+-------+-------+-------+-------+ - * | | DAC1 | DAC0 | - * +-------------------------------+-------+-------+-------+-------+ - * - * There are N DUCs (1 now) with complex inputs and outputs. - * There are two DACs. - * - * Each 4-bit DACx field specifies the source for the DAC - * Each subfield is coded like this: - * - * 3 2 1 0 - * +-------+ - * | N | - * +-------+ - * - * N specifies which DUC output is connected to this DAC. - * - * N which interp output - * --- ------------------- - * 0 DUC 0 I - * 1 DUC 0 Q - * 2 DUC 1 I - * 3 DUC 1 Q - * F All Zeros - * - * The default value is 0x10 - * </pre> - */ - volatile uint32_t tx_mux; - -} dsp_tx_regs_t; - -#define dsp_tx_regs ((dsp_tx_regs_t *) _SR_ADDR(SR_TX_DSP)) - // --- VITA RX CTRL regs --- typedef struct { // The following 3 are logically a single command register. @@ -412,81 +365,10 @@ typedef struct { volatile uint32_t cmd; // {now, chain, num_samples(30) volatile uint32_t time_secs; volatile uint32_t time_ticks; - - volatile uint32_t clear_overrun; // write anything to clear overrun - volatile uint32_t vrt_header; // word 0 of packet. FPGA fills in packet counter - volatile uint32_t vrt_stream_id; // word 1 of packet. - volatile uint32_t vrt_trailer; - volatile uint32_t nsamples_per_pkt; - volatile uint32_t nchannels; // 1 in basic case, up to 4 for vector sources - volatile uint32_t pad[7]; // Make each structure 16 elements long } sr_rx_ctrl_t; -#define sr_rx_ctrl ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL)) - -// --- dsp rx regs --- -#define MIN_CIC_DECIM 1 -#define MAX_CIC_DECIM 128 - -typedef struct { - volatile int32_t freq; - volatile uint32_t scale_iq; // {scale_i,scale_q} - volatile uint32_t decim_rate; - volatile uint32_t dcoffset_i; // Bit 31 high sets fixed offset mode, using lower 14 bits, - // otherwise it is automatic - volatile uint32_t dcoffset_q; // Bit 31 high sets fixed offset mode, using lower 14 bits - - /*! - * \brief input mux configuration. - * - * This determines which ADC (or constant zero) is connected to - * each DDC input. There are N DDCs (1 now). Each has two inputs. - * - * <pre> - * Mux value: - * - * 3 2 1 - * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | |Q0 |I0 | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * - * Each 2-bit I field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) - * Each 2-bit Q field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) - * - * The default value is 0x4 - * </pre> - */ - volatile uint32_t rx_mux; // called adc_mux in dsp_core_rx.v - - /*! - * \brief Streaming GPIO configuration - * - * This determines whether the LSBs of I and Q samples come from the DSP - * pipeline or from the io_rx GPIO pins. To stream GPIO, one must first - * set the GPIO data direction register to have io_rx[15] and/or io_rx[14] - * configured as inputs. The GPIO pins will be sampled at the time the - * remainder of the DSP sample is strobed into the RX sample FIFO. There - * will be a decimation-dependent fixed time offset between the GPIO - * sample stream and the associated RF samples. - * - * 3 2 1 - * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | MBZ |Q|I| - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * - * I 0=LSB comes from DSP pipeline (default) - * 1=LSB comes from io_rx[15] - * - * Q 0=LSB comes from DSP pipeline (default) - * 1=LSB comes from io_rx[14] - */ - volatile uint32_t gpio_stream_enable; - -} dsp_rx_regs_t; - -#define dsp_rx_regs ((dsp_rx_regs_t *) _SR_ADDR(SR_RX_DSP)) +#define sr_rx_ctrl0 ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL0)) +#define sr_rx_ctrl1 ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL1)) // ---------------------------------------------------------------- // VITA49 64 bit time (write only) diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index baa5ab662..e4f272a76 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -79,19 +79,18 @@ IF(MSVC) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/msvc) ADD_DEFINITIONS(-D_WIN32_WINNT=0x0501) #minimum version required is windows xp ADD_DEFINITIONS(-DNOMINMAX) #disables stupidity and enables std::min and std::max - ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS) #avoid warnings from boost::split - ADD_DEFINITIONS(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc + ADD_DEFINITIONS( #stop all kinds of compatibility warnings + -D_SCL_SECURE_NO_WARNINGS + -D_CRT_SECURE_NO_WARNINGS + -D_CRT_SECURE_NO_DEPRECATE + -D_CRT_NONSTDC_NO_DEPRECATE + ) ENDIF(MSVC) ######################################################################## # Setup Boost ######################################################################## -IF(UNIX AND EXISTS "/usr/lib64") - LIST(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix -ENDIF(UNIX AND EXISTS "/usr/lib64") - -SET(Boost_ADDITIONAL_VERSIONS "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44") -FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} COMPONENTS +SET(BOOST_REQUIRED_COMPONENTS date_time filesystem program_options @@ -101,6 +100,22 @@ FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} COMPONENTS unit_test_framework ) +IF(UNIX AND EXISTS "/usr/lib64") + LIST(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix +ENDIF(UNIX AND EXISTS "/usr/lib64") + +IF(MSVC) + SET(BOOST_ALL_DYN_LINK "${BOOST_ALL_DYN_LINK}" CACHE BOOL "boost enable dynamic linking") + IF(BOOST_ALL_DYN_LINK) + ADD_DEFINITIONS(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc + ELSE(BOOST_ALL_DYN_LINK) + UNSET(BOOST_REQUIRED_COMPONENTS) #empty components list for static link + ENDIF(BOOST_ALL_DYN_LINK) +ENDIF(MSVC) + +SET(Boost_ADDITIONAL_VERSIONS "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44") +FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) + INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) diff --git a/host/apps/omap_debug/usrp-e-crc-rw.c b/host/apps/omap_debug/usrp-e-crc-rw.c index c6f3427f0..f91c4fa85 100644 --- a/host/apps/omap_debug/usrp-e-crc-rw.c +++ b/host/apps/omap_debug/usrp-e-crc-rw.c @@ -32,7 +32,7 @@ static u_int32_t chksum_crc32_gentab(void) unsigned long crc, poly; unsigned long i, j; - poly = 0xEDB88320L; + poly = 0x04C11DB7L; for (i = 0; i < 256; i++) { crc = i; @@ -44,23 +44,43 @@ static u_int32_t chksum_crc32_gentab(void) } } crc_tab[i] = crc; +// printf("crc_tab[%d] = %X\n", i , crc); } return 0; } +struct timeval delta_time(struct timeval f, struct timeval s) +{ + struct timeval d; + + if (f.tv_usec > s.tv_usec) { + d.tv_usec = f.tv_usec - s.tv_usec; + d.tv_sec = f.tv_sec - s.tv_sec; + } else { + d.tv_usec = f.tv_usec - s.tv_usec + 1e6; + d.tv_sec = f.tv_sec - s.tv_sec - 1; + } + + return d; +} + + static void *read_thread(void *threadid) { int cnt; int rx_pkt_cnt, rb_read; int i; - unsigned long crc; - unsigned int rx_crc; - unsigned long bytes_transfered, elapsed_seconds; - struct timeval start_time, finish_time; + unsigned long crc, ck_sum; + unsigned int rx_crc, pkt_len, pkt_seq; + unsigned long bytes_transfered; + struct timeval start_time; + unsigned int prev_seq = 0; + int first = 1; __u8 *p; + printf("Greetings from the reading thread!\n"); // IMPORTANT: must assume max length packet from fpga @@ -87,17 +107,41 @@ static void *read_thread(void *threadid) rx_crc = *(int *) &p[cnt-4]; crc = 0xFFFFFFFF; - for (i = 0; i < cnt - 4; i+=2) { - crc = ((crc >> 8) & 0x00FFFFFF) ^ - crc_tab[(crc ^ p[i+1]) & 0xFF]; -//printf("idx = %d, data = %X, crc = %X\n", i, p[i+1],crc); + ck_sum = 0; + + pkt_len = *(unsigned int *) &p[0]; + pkt_seq = *(unsigned int *) &p[4]; + +// printf("Pkt len = %X, pkt seq = %X, driver len = %X\n", pkt_len, pkt_seq, cnt); + + if (pkt_len != (cnt - 4)) + printf("Packet length check fail, driver len = %ud, content = %ud\n", + cnt, pkt_len); + + if (!first && (pkt_seq != (prev_seq + 1))) + printf("Sequence number check fail, pkt_seq = %ud, prev_seq = %ud\n", + pkt_seq, prev_seq); + first = 0; + prev_seq = pkt_seq; + + for (i = 0; i < cnt-4; i++) { + ck_sum += p[i]; + crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ p[i]) & 0xFF]; -//printf("idx = %d, data = %X, crc = %X\n", i, p[i],crc); +//printf("idx = %d, data = %X, crc = %X, ck_sum = %X\n", i, p[i], crc, ck_sum); +// crc = ((crc >> 8) & 0x00FFFFFF) ^ +// crc_tab[(crc ^ p[i+1]) & 0xFF]; +//printf("idx = %d, data = %X, crc = %X\n", i, p[i+1],crc); } (*rxi)[rb_read].flags = RB_KERNEL; + + if (rx_crc != ck_sum) + printf("Ck_sum eror, calc ck_sum = %lX, rx ck_sum = %X\n", + ck_sum, rx_crc); + #if 0 if (rx_crc != (crc & 0xFFFFFFFF)) { printf("CRC Error, calc crc: %X, rx_crc: %X\n", @@ -112,10 +156,18 @@ static void *read_thread(void *threadid) bytes_transfered += cnt; if (bytes_transfered > (100 * 1000000)) { + struct timeval finish_time, d_time; + float elapsed_seconds; + gettimeofday(&finish_time, NULL); - elapsed_seconds = finish_time.tv_sec - start_time.tv_sec; - printf("Bytes transfered = %ld, elapsed seconds = %ld\n", bytes_transfered, elapsed_seconds); + printf("sec = %ld, usec = %ld\n", finish_time.tv_sec, finish_time.tv_usec); + + d_time = delta_time(finish_time, start_time); + + elapsed_seconds = (float)d_time.tv_sec + ((float)d_time.tv_usec * 1e-6f); + + printf("Bytes transfered = %ld, elapsed seconds = %f\n", bytes_transfered, elapsed_seconds); printf("RX data transfer rate = %f K Samples/second\n", (float) bytes_transfered / (float) elapsed_seconds / 4000); @@ -131,8 +183,8 @@ static void *write_thread(void *threadid) int i, tx_pkt_cnt, rb_write; int tx_len; unsigned long crc; - unsigned long bytes_transfered, elapsed_seconds; - struct timeval start_time, finish_time; + unsigned long bytes_transfered; + struct timeval start_time; __u8 *p; printf("Greetings from the write thread!\n"); @@ -183,12 +235,13 @@ static void *write_thread(void *threadid) crc = 0xFFFFFFFF; for (i = 0; i < tx_len-4; i++) { p[i] = i & 0xFF; - +// printf("%X ", p[i]); crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ p[i]) & 0xFF]; } *(int *) &p[tx_len-4] = crc; +// printf("\n crc = %lX\n", crc); (*txi)[rb_write].len = tx_len; (*txi)[rb_write].flags = RB_USER; @@ -200,10 +253,16 @@ static void *write_thread(void *threadid) bytes_transfered += tx_len; if (bytes_transfered > (100 * 1000000)) { + struct timeval finish_time, d_time; + float elapsed_seconds; + gettimeofday(&finish_time, NULL); - elapsed_seconds = finish_time.tv_sec - start_time.tv_sec; - printf("Bytes transfered = %d, elapsed seconds = %d\n", bytes_transfered, elapsed_seconds); + d_time = delta_time(finish_time, start_time); + + elapsed_seconds = (float)d_time.tv_sec - ((float)d_time.tv_usec * 1e-6f); + + printf("Bytes transfered = %ld, elapsed seconds = %f\n", bytes_transfered, elapsed_seconds); printf("TX data transfer rate = %f K Samples/second\n", (float) bytes_transfered / (float) elapsed_seconds / 4000); diff --git a/host/docs/general.rst b/host/docs/general.rst index 90a880c2e..50ef24d6c 100644 --- a/host/docs/general.rst +++ b/host/docs/general.rst @@ -5,55 +5,6 @@ UHD - General Application Notes .. contents:: Table of Contents ------------------------------------------------------------------------ -Finding devices ------------------------------------------------------------------------- - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Device addressing -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Deviced are addressed through key/value string pairs. -These string pairs can be used to narrow down the search for a specific device or group of devices. -Most UHD utility applications and examples have a --args parameter that takes a device address; -where the device address is expressed as a delimited string. - -* See the documentation in types/device_addr.hpp for reference. -* See device-specific application notes for usage. - -**Example:** -:: - - serial=0x1234, type=usrpx - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Device discovery -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Devices attached to your system can be discovered using the "uhd_find_devices" program. -The find devices program scans your system for supported devices and prints -out an enumerated list of discovered devices and their addresses. -The list of discovered devices can be narrowed down by specifying device address args. - -**Usage:** -:: - - uhd_find_devices - - -- OR -- - - uhd_find_devices --args <device-specific-address-args> - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Device properties -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Properties of devices attached to your system can be probed with the "uhd_usrp_probe" program. -The usrp probe program constructs an instance of the device and prints out its properties; -properties such as detected daughter-boards, frequency range, gain ranges, etc... - -**Usage:** -:: - - uhd_usrp_probe --args <device-specific-address-args> - ------------------------------------------------------------------------- Misc notes ------------------------------------------------------------------------ diff --git a/host/docs/identification.rst b/host/docs/identification.rst index 49d36ec1a..90484744c 100644 --- a/host/docs/identification.rst +++ b/host/docs/identification.rst @@ -7,20 +7,36 @@ UHD - Device Identification Notes ------------------------------------------------------------------------ Identifying USRPs ------------------------------------------------------------------------ -Every device has several ways of identifying it on the host system: +Devices are addressed through key/value string pairs. +These string pairs can be used to narrow down the search for a specific device or group of devices. +Most UHD utility applications and examples have a --args parameter that takes a device address; +where the device address is expressed as a delimited string. +See the documentation in types/device_addr.hpp for reference. -* **Serial:** A globally unique identifier. -* **Address:** A unique identifier on a network. -* **Name:** An optional user-set identifier. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Common device identifiers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Every device has several ways of identifying it on the host system: -The address is only applicable for network-based devices. -See the USRP2 application notes. ++------------+------------+--------------------------------------------+ +| Identifier | Key | Notes | ++============+============+============================================+ +| Serial | serial | globally unique identifier | ++------------+------------+--------------------------------------------+ +| Address | addr | unique identifier on a network | ++------------+------------+--------------------------------------------+ +| Name | name | optional user-set identifier | ++------------+------------+--------------------------------------------+ +| Type | type | hardware series identifier | ++------------+------------+--------------------------------------------+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device discovery via command line ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A "find devices" utility application comes bundled with the UHD. -The find devices application will search for all devices on the host system and print the results. +Devices attached to your system can be discovered using the "uhd_find_devices" program. +The find devices program scans your system for supported devices and prints +out an enumerated list of discovered devices and their addresses. +The list of discovered devices can be narrowed down by specifying device address args. :: @@ -60,6 +76,18 @@ The hint argument can be populated to narrow the scope of the search. hint["serial"] = "12345678"; uhd::device_addrs_t dev_addrs = uhd::device::find(hint); +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Device properties +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Properties of devices attached to your system can be probed with the "uhd_usrp_probe" program. +The usrp probe program constructs an instance of the device and prints out its properties; +properties such as detected daughter-boards, frequency range, gain ranges, etc... + +**Usage:** +:: + + uhd_usrp_probe --args <device-specific-address-args> + ------------------------------------------------------------------------ Naming a USRP ------------------------------------------------------------------------ diff --git a/host/docs/images.rst b/host/docs/images.rst index 612a00aa5..f5be88a65 100644 --- a/host/docs/images.rst +++ b/host/docs/images.rst @@ -12,6 +12,8 @@ The methods of loading images into the device varies among devices: * **USRP1:** The host code will automatically load the firmware and FPGA at runtime. * **USRP2:** The user must manually write the images onto the USRP2 SD card. +* **USRP-N Series:** The user must manually transfer the images over ethernet. +* **USRP-E Series:** The host code will automatically load the FPGA at runtime. ------------------------------------------------------------------------ Pre-built images diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index 912fbc204..1a04680e9 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -27,7 +27,7 @@ //# pragma warning(disable: 4512) // assignment operator can't not be generated //# pragma warning(disable: 4100) // unreferenced formal parameter //# pragma warning(disable: 4996) // <symbol> was declared deprecated -//# pragma warning(disable: 4355) // 'this' : used in base member initializer list +# pragma warning(disable: 4355) // 'this' : used in base member initializer list //# pragma warning(disable: 4706) // assignment within conditional expression # pragma warning(disable: 4251) // class 'A<T>' needs to have dll-interface to be used by clients of class 'B' //# pragma warning(disable: 4127) // conditional expression is constant diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index d5a536b27..092028d09 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -21,7 +21,6 @@ #include <uhd/config.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include <boost/function.hpp> namespace uhd{ namespace transport{ @@ -30,23 +29,9 @@ namespace uhd{ namespace transport{ * Contains a reference to transport-managed memory, * and a method to release the memory after reading. */ - class UHD_API managed_recv_buffer : boost::noncopyable{ + class UHD_API managed_recv_buffer{ public: typedef boost::shared_ptr<managed_recv_buffer> sptr; - typedef boost::function<void(void)> release_fcn_t; - - /*! - * Make a safe managed receive buffer: - * A safe managed buffer ensures that release is called once, - * either by the user or automatically upon deconstruction. - * \param buff a pointer into read-only memory - * \param size the length of the buffer in bytes - * \param release_fcn callback to release the memory - * \return a new managed receive buffer - */ - static sptr make_safe( - const void *buff, size_t size, const release_fcn_t &release_fcn - ); /*! * Signal to the transport that we are done with the buffer. @@ -81,24 +66,9 @@ namespace uhd{ namespace transport{ * Contains a reference to transport-managed memory, * and a method to commit the memory after writing. */ - class UHD_API managed_send_buffer : boost::noncopyable{ + class UHD_API managed_send_buffer{ public: typedef boost::shared_ptr<managed_send_buffer> sptr; - typedef boost::function<void(size_t)> commit_fcn_t; - - /*! - * Make a safe managed send buffer: - * A safe managed buffer ensures that commit is called once, - * either by the user or automatically upon deconstruction. - * In the later case, the deconstructor will call commit(0). - * \param buff a pointer into writable memory - * \param size the length of the buffer in bytes - * \param commit_fcn callback to commit the memory - * \return a new managed send buffer - */ - static sptr make_safe( - void *buff, size_t size, const commit_fcn_t &commit_fcn - ); /*! * Signal to the transport that we are done with the buffer. diff --git a/host/include/uhd/types/ref_vector.hpp b/host/include/uhd/types/ref_vector.hpp index 2928cb150..bbfb5434d 100644 --- a/host/include/uhd/types/ref_vector.hpp +++ b/host/include/uhd/types/ref_vector.hpp @@ -27,7 +27,7 @@ namespace uhd{ * - Provides a std::vector-like interface for an array. * - Statically sized, and does not manage the memory. */ -template <typename T> class ref_vector{ +template <typename T> class UHD_API ref_vector{ public: /*! * Create a reference vector of size 1 from a pointer. diff --git a/host/include/uhd/usrp/dsp_props.hpp b/host/include/uhd/usrp/dsp_props.hpp index 3e1690317..e68e11deb 100644 --- a/host/include/uhd/usrp/dsp_props.hpp +++ b/host/include/uhd/usrp/dsp_props.hpp @@ -39,8 +39,8 @@ namespace uhd{ namespace usrp{ enum dsp_prop_t{ DSP_PROP_NAME, //ro, std::string DSP_PROP_OTHERS, //ro, prop_names_t + DSP_PROP_STREAM_CMD, //wo, stream_cmd_t DSP_PROP_FREQ_SHIFT, //rw, double Hz - DSP_PROP_FREQ_SHIFT_NAMES, //ro, prop_names_t DSP_PROP_CODEC_RATE, //ro, double Sps DSP_PROP_HOST_RATE //rw, double Sps }; diff --git a/host/include/uhd/usrp/mboard_props.hpp b/host/include/uhd/usrp/mboard_props.hpp index 559c96ecb..2145ab446 100644 --- a/host/include/uhd/usrp/mboard_props.hpp +++ b/host/include/uhd/usrp/mboard_props.hpp @@ -47,7 +47,6 @@ namespace uhd{ namespace usrp{ MBOARD_PROP_CLOCK_CONFIG, //rw, clock_config_t MBOARD_PROP_TIME_NOW, //rw, time_spec_t MBOARD_PROP_TIME_PPS, //wo, time_spec_t - MBOARD_PROP_STREAM_CMD, //wo, stream_cmd_t MBOARD_PROP_EEPROM_MAP //wr, mboard_eeprom_t::sptr }; diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 3c8dd5fac..e2b26f5fb 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -87,6 +87,9 @@ public: //! A wildcard motherboard index static const size_t ALL_MBOARDS = size_t(~0); + //! A wildcard channel index + static const size_t ALL_CHANS = size_t(~0); + //! A wildcard gain element name static const std::string ALL_GAINS; @@ -215,8 +218,9 @@ public: * to ensure that the packets can be aligned by their time specs. * * \param stream_cmd the stream command to issue + * \param chan the channel index 0 to N-1 */ - virtual void issue_stream_cmd(const stream_cmd_t &stream_cmd) = 0; + virtual void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan = ALL_CHANS) = 0; /*! * Set the clock configuration for the usrp device. @@ -282,16 +286,18 @@ public: virtual std::string get_rx_subdev_name(size_t chan = 0) = 0; /*! - * Set the RX sample rate across all channels. + * Set the RX sample rate. * \param rate the rate in Sps + * \param chan the channel index 0 to N-1 */ - virtual void set_rx_rate(double rate) = 0; + virtual void set_rx_rate(double rate, size_t chan = ALL_CHANS) = 0; /*! - * Gets the RX sample rate for all channels. + * Gets the RX sample rate. + * \param chan the channel index 0 to N-1 * \return the rate in Sps */ - virtual double get_rx_rate(void) = 0; + virtual double get_rx_rate(size_t chan = 0) = 0; /*! * Set the RX center frequency. @@ -480,16 +486,18 @@ public: virtual std::string get_tx_subdev_name(size_t chan = 0) = 0; /*! - * Set the TX sample rate across all channels. + * Set the TX sample rate. * \param rate the rate in Sps + * \param chan the channel index 0 to N-1 */ - virtual void set_tx_rate(double rate) = 0; + virtual void set_tx_rate(double rate, size_t chan = ALL_CHANS) = 0; /*! - * Gets the TX sample rate for all channels. + * Gets the TX sample rate. + * \param chan the channel index 0 to N-1 * \return the rate in Sps */ - virtual double get_tx_rate(void) = 0; + virtual double get_tx_rate(size_t chan) = 0; /*! * Set the TX center frequency. diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index d2845ffda..c8a5dd51e 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -108,6 +108,9 @@ ADD_LIBRARY(uhd SHARED ${libuhd_sources}) TARGET_LINK_LIBRARIES(uhd ${Boost_LIBRARIES} ${libuhd_libs}) SET_TARGET_PROPERTIES(uhd PROPERTIES DEFINE_SYMBOL "UHD_DLL_EXPORTS") SET_TARGET_PROPERTIES(uhd PROPERTIES SOVERSION ${UHD_VERSION_MAJOR}) +IF(DEFINED LIBUHD_OUTPUT_NAME) + SET_TARGET_PROPERTIES(uhd PROPERTIES OUTPUT_NAME ${LIBUHD_OUTPUT_NAME}) +ENDIF(DEFINED LIBUHD_OUTPUT_NAME) INSTALL(TARGETS uhd LIBRARY DESTINATION ${LIBRARY_DIR} # .so file diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index a9f977cdc..abc9c2707 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -22,15 +22,31 @@ INCLUDE(CheckIncludeFileCXX) MESSAGE(STATUS "") ######################################################################## -# Check for SIMD headers +# Check for SSE2 SIMD headers ######################################################################## +IF(CMAKE_COMPILER_IS_GNUCXX) + SET(EMMINTRIN_FLAGS -msse2) +ELSEIF(MSVC) + SET(EMMINTRIN_FLAGS /arch:SSE2) +ENDIF() + +SET(CMAKE_REQUIRED_FLAGS ${EMMINTRIN_FLAGS}) CHECK_INCLUDE_FILE_CXX(emmintrin.h HAVE_EMMINTRIN_H) +UNSET(CMAKE_REQUIRED_FLAGS) + IF(HAVE_EMMINTRIN_H) + SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_sse2.cpp + PROPERTIES COMPILE_FLAGS ${EMMINTRIN_FLAGS} + ) LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_sse2.cpp ) ENDIF(HAVE_EMMINTRIN_H) +######################################################################## +# Check for NEON SIMD headers +######################################################################## CHECK_INCLUDE_FILE_CXX(arm_neon.h HAVE_ARM_NEON_H) IF(HAVE_ARM_NEON_H) LIBUHD_APPEND_SOURCES( diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 8765c6703..a98bcc14e 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -55,14 +55,19 @@ CHECK_INCLUDE_FILE_CXX(winsock2.h HAVE_WINSOCK2_H) IF(HAVE_IFADDRS_H) MESSAGE(STATUS " Interface address discovery supported through getifaddrs.") - ADD_DEFINITIONS(-DHAVE_IFADDRS_H) + SET(IF_ADDRS_DEFS HAVE_IFADDRS_H) ELSEIF(HAVE_WINSOCK2_H) MESSAGE(STATUS " Interface address discovery supported through SIO_GET_INTERFACE_LIST.") - ADD_DEFINITIONS(-DHAVE_WINSOCK2_H) + SET(IF_ADDRS_DEFS HAVE_WINSOCK2_H) ELSE(HAVE_IFADDRS_H) MESSAGE(STATUS " Interface address discovery not supported.") ENDIF(HAVE_IFADDRS_H) +SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp + PROPERTIES COMPILE_DEFINITIONS "${IF_ADDRS_DEFS}" +) + ######################################################################## # Append to the list of sources for lib uhd ######################################################################## @@ -77,5 +82,4 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp ${CMAKE_CURRENT_SOURCE_DIR}/udp_zero_copy_asio.cpp ${CMAKE_CURRENT_SOURCE_DIR}/vrt_packet_handler.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/zero_copy.cpp ) diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 6fab5ae6f..87adece45 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -21,258 +21,88 @@ #include <uhd/transport/buffer_pool.hpp> #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/assert.hpp> +#include <boost/function.hpp> #include <boost/foreach.hpp> -#include <boost/thread.hpp> -#include <vector> +#include <boost/thread/thread.hpp> +#include <list> #include <iostream> using namespace uhd; using namespace uhd::transport; -static const double CLEANUP_TIMEOUT = 0.2; //seconds static const size_t DEFAULT_NUM_XFERS = 16; //num xfers static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes /*********************************************************************** - * Helper functions - ***********************************************************************/ -/* - * Print the values of a libusb_transfer struct - * http://libusb.sourceforge.net/api-1.0/structlibusb__transfer.html - */ -void pp_transfer(libusb_transfer *lut) -{ - std::cout << "Libusb transfer" << std::endl; - std::cout << " flags: 0x" << std::hex << (unsigned int) lut->flags << std::endl; - std::cout << " endpoint: 0x" << std::hex << (unsigned int) lut->endpoint << std::endl; - std::cout << " type: 0x" << std::hex << (unsigned int) lut->type << std::endl; - std::cout << " timeout: " << std::dec << lut->timeout << std::endl; - std::cout << " status: 0x" << std::hex << lut->status << std::endl; - std::cout << " length: " << std::dec << lut->length << std::endl; - std::cout << " actual_length: " << std::dec << lut->actual_length << std::endl; -} - -/*********************************************************************** - * USB asynchronous zero_copy endpoint - * This endpoint implementation provides asynchronous I/O to libusb-1.0 - * devices. Each endpoint is directional and two can be combined to - * create a bidirectional interface. It is a zero copy implementation - * with respect to libusb, however, each send and recv requires a copy - * operation from kernel to userspace; this is due to the usbfs - * interface provided by the kernel. + * Reusable managed receiver buffer: + * - Associated with a particular libusb transfer struct. + * - Submits the transfer to libusb in the release method. **********************************************************************/ -class usb_endpoint { +class libusb_zero_copy_mrb : public managed_recv_buffer{ public: - typedef boost::shared_ptr<usb_endpoint> sptr; - - usb_endpoint( - libusb::device_handle::sptr handle, - int endpoint, - bool input, - size_t transfer_size, - size_t num_transfers - ); - - ~usb_endpoint(void); + libusb_zero_copy_mrb(libusb_transfer *lut): + _lut(lut), _expired(true) { /* NOP */ } - // Exposed interface for submitting / retrieving transfer buffers - - //! Submit a new transfer that was presumably just filled or emptied. - void submit(libusb_transfer *lut); - - /*! - * Get an available transfer: - * For inputs, this is a just filled transfer. - * For outputs, this is a just emptied transfer. - * \param timeout the timeout to wait for a lut - * \return the transfer pointer or NULL if timeout - */ - libusb_transfer *get_lut_with_wait(double timeout); + void release(void){ + if (_expired) return; + UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); + _expired = true; + } - //Callback use only - void callback_handle_transfer(libusb_transfer *lut); + sptr get_new(void){ + _expired = false; + return sptr(this, &libusb_zero_copy_mrb::fake_deleter); + } private: - libusb::device_handle::sptr _handle; - int _endpoint; - bool _input; - - //! hold a bounded buffer of completed transfers - bounded_buffer<libusb_transfer *> _completed_list; - - //! a list of all transfer structs we allocated - std::vector<libusb_transfer *> _all_luts; + static void fake_deleter(void *obj){ + static_cast<libusb_zero_copy_mrb *>(obj)->release(); + } - //! memory allocated for the transfer buffers - buffer_pool::sptr _buffer_pool; + const void *get_buff(void) const{return _lut->buffer;} + size_t get_size(void) const{return _lut->actual_length;} - // Calls for processing asynchronous I/O - libusb_transfer *allocate_transfer(void *mem, size_t len); - void print_transfer_status(libusb_transfer *lut); + libusb_transfer *_lut; + bool _expired; }; - -/* - * Callback function called when submitted transfers complete. - * The endpoint upon which the transfer is part of is recovered - * and the transfer moved from pending to completed state. - * Callbacks occur during the reaping calls where libusb_handle_events() - * is used. The callback only modifies the transfer state by moving - * it from the pending to completed status list. - * \param lut pointer to libusb_transfer - */ -static void callback(libusb_transfer *lut){ - usb_endpoint *endpoint = (usb_endpoint *) lut->user_data; - endpoint->callback_handle_transfer(lut); -} - - -/* - * Accessor call to allow list access from callback space - * \param pointer to libusb_transfer - */ -void usb_endpoint::callback_handle_transfer(libusb_transfer *lut){ - _completed_list.push_with_haste(lut); -} - - -/* - * Constructor - * Allocate libusb transfers and mark as free. For IN endpoints, - * submit the transfers so that they're ready to return when - * data is available. - */ -usb_endpoint::usb_endpoint( - libusb::device_handle::sptr handle, - int endpoint, - bool input, - size_t transfer_size, - size_t num_transfers -): - _handle(handle), - _endpoint(endpoint), - _input(input), - _completed_list(num_transfers) -{ - _buffer_pool = buffer_pool::make(num_transfers, transfer_size); - for (size_t i = 0; i < num_transfers; i++){ - _all_luts.push_back(allocate_transfer(_buffer_pool->at(i), transfer_size)); - - //input luts are immediately submitted to be filled - //output luts go into the completed list as free buffers - if (_input) this->submit(_all_luts.back()); - else _completed_list.push_with_haste(_all_luts.back()); +/*********************************************************************** + * Reusable managed send buffer: + * - Associated with a particular libusb transfer struct. + * - Submits the transfer to libusb in the commit method. + **********************************************************************/ +class libusb_zero_copy_msb : public managed_send_buffer{ +public: + libusb_zero_copy_msb(libusb_transfer *lut): + _lut(lut), _expired(true) { /* NOP */ } + + void commit(size_t len){ + if (_expired) return; + _lut->length = len; + UHD_ASSERT_THROW(libusb_submit_transfer(_lut) == 0); + _expired = true; } -} - -/* - * Destructor - * Make sure all the memory is freed. Cancel any pending transfers. - * When all completed transfers are moved to the free list, release - * the transfers. Libusb will deallocate the data buffer held by - * each transfer. - */ -usb_endpoint::~usb_endpoint(void){ - //cancel all transfers - BOOST_FOREACH(libusb_transfer *lut, _all_luts){ - libusb_cancel_transfer(lut); + sptr get_new(void){ + _expired = false; + return sptr(this, &libusb_zero_copy_msb::fake_deleter); } - //collect canceled transfers (drain the queue) - while (this->get_lut_with_wait(CLEANUP_TIMEOUT) != NULL){}; - - //free all transfers - BOOST_FOREACH(libusb_transfer *lut, _all_luts){ - libusb_free_transfer(lut); +private: + static void fake_deleter(void *obj){ + static_cast<libusb_zero_copy_msb *>(obj)->commit(0); } -} - - -/* - * Allocate a libusb transfer - * The allocated transfer - and buffer it contains - is repeatedly - * submitted, reaped, and reused and should not be freed until shutdown. - * \param mem a pointer to the buffer memory - * \param len size of the individual buffer - * \return pointer to an allocated libusb_transfer - */ -libusb_transfer *usb_endpoint::allocate_transfer(void *mem, size_t len){ - libusb_transfer *lut = libusb_alloc_transfer(0); - UHD_ASSERT_THROW(lut != NULL); - - unsigned int endpoint = ((_endpoint & 0x7f) | (_input ? 0x80 : 0)); - unsigned char *buff = reinterpret_cast<unsigned char *>(mem); - libusb_transfer_cb_fn lut_callback = libusb_transfer_cb_fn(&callback); - - libusb_fill_bulk_transfer(lut, // transfer - _handle->get(), // dev_handle - endpoint, // endpoint - buff, // buffer - len, // length - lut_callback, // callback - this, // user_data - 0); // timeout - return lut; -} + void *get_buff(void) const{return _lut->buffer;} + size_t get_size(void) const{return _lut->length;} -/* - * Asynchonous transfer submission - * Submit a libusb transfer to libusb add pending status - * \param lut pointer to libusb_transfer - * \return true on success or false on error - */ -void usb_endpoint::submit(libusb_transfer *lut){ - UHD_ASSERT_THROW(libusb_submit_transfer(lut) == 0); -} - -/* - * Print status errors of a completed transfer - * \param lut pointer to an libusb_transfer - */ -void usb_endpoint::print_transfer_status(libusb_transfer *lut){ - std::cout << "here " << lut->status << std::endl; - switch (lut->status) { - case LIBUSB_TRANSFER_COMPLETED: - if (lut->actual_length < lut->length) { - std::cerr << "USB: transfer completed with short write," - << " length = " << lut->length - << " actual = " << lut->actual_length << std::endl; - } - - if ((lut->actual_length < 0) || (lut->length < 0)) { - std::cerr << "USB: transfer completed with invalid response" - << std::endl; - } - break; - case LIBUSB_TRANSFER_CANCELLED: - break; - case LIBUSB_TRANSFER_NO_DEVICE: - std::cerr << "USB: device was disconnected" << std::endl; - break; - case LIBUSB_TRANSFER_OVERFLOW: - std::cerr << "USB: device sent more data than requested" << std::endl; - break; - case LIBUSB_TRANSFER_TIMED_OUT: - std::cerr << "USB: transfer timed out" << std::endl; - break; - case LIBUSB_TRANSFER_STALL: - std::cerr << "USB: halt condition detected (stalled)" << std::endl; - break; - case LIBUSB_TRANSFER_ERROR: - std::cerr << "USB: transfer failed" << std::endl; - break; - default: - std::cerr << "USB: received unknown transfer status" << std::endl; - } -} + libusb_transfer *_lut; + bool _expired; +}; -libusb_transfer *usb_endpoint::get_lut_with_wait(double timeout){ - boost::this_thread::disable_interruption di; //disable because the wait can throw - libusb_transfer *lut = NULL; - if (_completed_list.pop_with_timed_wait(lut, timeout)) return lut; - return NULL; +//! helper function: handles all async callbacks +static void libusb_async_cb(libusb_transfer *lut){ + (*static_cast<boost::function<void()> *>(lut->user_data))(); } /*********************************************************************** @@ -286,16 +116,107 @@ public: size_t recv_endpoint, size_t send_endpoint, const device_addr_t &hints - ); + ): + _handle(handle), + _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))), + _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS))), + _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))), + _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS))), + _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), + _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), + _pending_recv_buffs(_num_recv_frames), + _pending_send_buffs(_num_send_frames) + { + _handle->claim_interface(2 /*in interface*/); + _handle->claim_interface(1 /*out interface*/); + + //allocate libusb transfer structs and managed receive buffers + for (size_t i = 0; i < get_num_recv_frames(); i++){ + + libusb_transfer *lut = libusb_alloc_transfer(0); + UHD_ASSERT_THROW(lut != NULL); + + _mrb_pool.push_back(libusb_zero_copy_mrb(lut)); + _callbacks.push_back(boost::bind( + &libusb_zero_copy_impl::handle_recv, this, &_mrb_pool.back() + )); + + libusb_fill_bulk_transfer( + lut, // transfer + _handle->get(), // dev_handle + (recv_endpoint & 0x7f) | 0x80, // endpoint + static_cast<unsigned char *>(_recv_buffer_pool->at(i)), // buffer + this->get_recv_frame_size(), // length + libusb_transfer_cb_fn(&libusb_async_cb), // callback + static_cast<void *>(&_callbacks.back()), // user_data + 0 // timeout + ); + + _all_luts.push_back(lut); + _mrb_pool.back().get_new(); + } + + //allocate libusb transfer structs and managed send buffers + for (size_t i = 0; i < get_num_send_frames(); i++){ + + libusb_transfer *lut = libusb_alloc_transfer(0); + UHD_ASSERT_THROW(lut != NULL); + + _msb_pool.push_back(libusb_zero_copy_msb(lut)); + _callbacks.push_back(boost::bind( + &libusb_zero_copy_impl::handle_send, this, &_msb_pool.back() + )); + + libusb_fill_bulk_transfer( + lut, // transfer + _handle->get(), // dev_handle + (send_endpoint & 0x7f) | 0x00, // endpoint + static_cast<unsigned char *>(_send_buffer_pool->at(i)), // buffer + this->get_send_frame_size(), // length + libusb_transfer_cb_fn(&libusb_async_cb), // callback + static_cast<void *>(&_callbacks.back()), // user_data + 0 // timeout + ); + + _all_luts.push_back(lut); + libusb_async_cb(lut); + } + + //spawn the event handler threads + size_t concurrency = hints.cast<size_t>("concurrency_hint", 1); + for (size_t i = 0; i < concurrency; i++) _thread_group.create_thread( + boost::bind(&libusb_zero_copy_impl::run_event_loop, this) + ); + } ~libusb_zero_copy_impl(void){ + //shutdown the threads _threads_running = false; _thread_group.interrupt_all(); _thread_group.join_all(); + + //cancel and free all transfers + BOOST_FOREACH(libusb_transfer *lut, _all_luts){ + libusb_cancel_transfer(lut); + libusb_free_transfer(lut); + } } - managed_recv_buffer::sptr get_recv_buff(double); - managed_send_buffer::sptr get_send_buff(double); + managed_recv_buffer::sptr get_recv_buff(double timeout){ + libusb_zero_copy_mrb *mrb = NULL; + if (_pending_recv_buffs.pop_with_timed_wait(mrb, timeout)){ + return mrb->get_new(); + } + return managed_recv_buffer::sptr(); + } + + managed_send_buffer::sptr get_send_buff(double timeout){ + libusb_zero_copy_msb *msb = NULL; + if (_pending_send_buffs.pop_with_timed_wait(msb, timeout)){ + return msb->get_new(); + } + return managed_send_buffer::sptr(); + } size_t get_num_recv_frames(void) const { return _num_recv_frames; } size_t get_num_send_frames(void) const { return _num_send_frames; } @@ -304,125 +225,50 @@ public: size_t get_send_frame_size(void) const { return _send_frame_size; } private: - void release(libusb_transfer *lut){ - _recv_ep->submit(lut); + //! Handle a bound async callback for recv + void handle_recv(libusb_zero_copy_mrb *mrb){ + _pending_recv_buffs.push_with_haste(mrb); } - void commit(libusb_transfer *lut, size_t num_bytes){ - lut->length = num_bytes; - try{ - _send_ep->submit(lut); - } - catch(const std::exception &e){ - std::cerr << "Error in commit: " << e.what() << std::endl; - } + //! Handle a bound async callback for send + void handle_send(libusb_zero_copy_msb *msb){ + _pending_send_buffs.push_with_haste(msb); } libusb::device_handle::sptr _handle; const size_t _recv_frame_size, _num_recv_frames; const size_t _send_frame_size, _num_send_frames; - usb_endpoint::sptr _recv_ep, _send_ep; - //event handler threads + //! Storage for transfer related objects + buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; + bounded_buffer<libusb_zero_copy_mrb *> _pending_recv_buffs; + bounded_buffer<libusb_zero_copy_msb *> _pending_send_buffs; + std::list<libusb_zero_copy_mrb> _mrb_pool; + std::list<libusb_zero_copy_msb> _msb_pool; + std::list<boost::function<void()> > _callbacks; + + //! a list of all transfer structs we allocated + std::list<libusb_transfer *> _all_luts; + + //! event handler threads boost::thread_group _thread_group; bool _threads_running; void run_event_loop(void){ set_thread_priority_safe(); - libusb::session::sptr session = libusb::session::get_global_session(); + libusb_context *context = libusb::session::get_global_session()->get_context(); _threads_running = true; try{ while(_threads_running){ timeval tv; tv.tv_sec = 0; tv.tv_usec = 100000; //100ms - libusb_handle_events_timeout(session->get_context(), &tv); + libusb_handle_events_timeout(context, &tv); } } catch(const boost::thread_interrupted &){} } -}; - -/* - * Constructor - * Initializes libusb, opens devices, and sets up interfaces for I/O. - * Finally, creates endpoints for asynchronous I/O. - */ -libusb_zero_copy_impl::libusb_zero_copy_impl( - libusb::device_handle::sptr handle, - size_t recv_endpoint, - size_t send_endpoint, - const device_addr_t &hints -): - _handle(handle), - _recv_frame_size(size_t(hints.cast<double>("recv_frame_size", DEFAULT_XFER_SIZE))), - _num_recv_frames(size_t(hints.cast<double>("num_recv_frames", DEFAULT_NUM_XFERS))), - _send_frame_size(size_t(hints.cast<double>("send_frame_size", DEFAULT_XFER_SIZE))), - _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_XFERS))) -{ - _handle->claim_interface(2 /*in interface*/); - _handle->claim_interface(1 /*out interface*/); - - _recv_ep = usb_endpoint::sptr(new usb_endpoint( - _handle, // libusb device_handle - recv_endpoint, // USB endpoint number - true, // IN endpoint - this->get_recv_frame_size(), // buffer size per transfer - this->get_num_recv_frames() // number of libusb transfers - )); - - _send_ep = usb_endpoint::sptr(new usb_endpoint( - _handle, // libusb device_handle - send_endpoint, // USB endpoint number - false, // OUT endpoint - this->get_send_frame_size(), // buffer size per transfer - this->get_num_send_frames() // number of libusb transfers - )); - - //spawn the event handler threads - size_t concurrency = hints.cast<size_t>("concurrency_hint", 1); - for (size_t i = 0; i < concurrency; i++) _thread_group.create_thread( - boost::bind(&libusb_zero_copy_impl::run_event_loop, this) - ); -} - -/* - * Construct a managed receive buffer from a completed libusb transfer - * (happy with buffer full of data) obtained from the receive endpoint. - * Return empty pointer if no transfer is available (timeout or error). - * \return pointer to a managed receive buffer - */ -managed_recv_buffer::sptr libusb_zero_copy_impl::get_recv_buff(double timeout){ - libusb_transfer *lut = _recv_ep->get_lut_with_wait(timeout); - if (lut == NULL) { - return managed_recv_buffer::sptr(); - } - else { - return managed_recv_buffer::make_safe( - lut->buffer, lut->actual_length, - boost::bind(&libusb_zero_copy_impl::release, this, lut) - ); - } -} - -/* - * Construct a managed send buffer from a free libusb transfer (with - * empty buffer). Return empty pointer of no transfer is available - * (timeout or error). - * \return pointer to a managed send buffer - */ -managed_send_buffer::sptr libusb_zero_copy_impl::get_send_buff(double timeout){ - libusb_transfer *lut = _send_ep->get_lut_with_wait(timeout); - if (lut == NULL) { - return managed_send_buffer::sptr(); - } - else { - return managed_send_buffer::make_safe( - lut->buffer, this->get_send_frame_size(), - boost::bind(&libusb_zero_copy_impl::commit, this, lut, _1) - ); - } -} +}; /*********************************************************************** * USB zero_copy make functions diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index 2794d383c..05352ffce 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -24,7 +24,7 @@ #include <boost/asio.hpp> #include <boost/format.hpp> #include <iostream> -#include <vector> +#include <list> using namespace uhd; using namespace uhd::transport; @@ -40,20 +40,18 @@ static const size_t DEFAULT_NUM_FRAMES = 32; **********************************************************************/ class udp_zero_copy_asio_mrb : public managed_recv_buffer{ public: - typedef boost::shared_ptr<udp_zero_copy_asio_mrb> sptr; typedef boost::function<void(udp_zero_copy_asio_mrb *)> release_cb_type; udp_zero_copy_asio_mrb(void *mem, const release_cb_type &release_cb): - _mem(mem), _release_cb(release_cb){/* NOP */} + _mem(mem), _len(0), _release_cb(release_cb){/* NOP */} void release(void){ - if (_expired) return; + if (_len == 0) return; this->_release_cb(this); - _expired = true; + _len = 0; } sptr get_new(size_t len){ - _expired = false; _len = len; return sptr(this, &udp_zero_copy_asio_mrb::fake_deleter); } @@ -68,7 +66,6 @@ private: const void *get_buff(void) const{return _mem;} size_t get_size(void) const{return _len;} - bool _expired; void *_mem; size_t _len; release_cb_type _release_cb; @@ -81,20 +78,18 @@ private: **********************************************************************/ class udp_zero_copy_asio_msb : public managed_send_buffer{ public: - typedef boost::shared_ptr<udp_zero_copy_asio_msb> sptr; typedef boost::function<void(udp_zero_copy_asio_msb *, size_t)> commit_cb_type; udp_zero_copy_asio_msb(void *mem, const commit_cb_type &commit_cb): - _mem(mem), _commit_cb(commit_cb){/* NOP */} + _mem(mem), _len(0), _commit_cb(commit_cb){/* NOP */} void commit(size_t len){ - if (_expired) return; + if (_len == 0) return; this->_commit_cb(this, len); - _expired = true; + _len = 0; } sptr get_new(size_t len){ - _expired = false; _len = len; return sptr(this, &udp_zero_copy_asio_msb::fake_deleter); } @@ -107,7 +102,6 @@ private: void *get_buff(void) const{return _mem;} size_t get_size(void) const{return _len;} - bool _expired; void *_mem; size_t _len; commit_cb_type _commit_cb; @@ -135,7 +129,8 @@ public: _num_send_frames(size_t(hints.cast<double>("num_send_frames", DEFAULT_NUM_FRAMES))), _recv_buffer_pool(buffer_pool::make(_num_recv_frames, _recv_frame_size)), _send_buffer_pool(buffer_pool::make(_num_send_frames, _send_frame_size)), - _pending_recv_buffs(_num_recv_frames), _pending_send_buffs(_num_send_frames) + _pending_recv_buffs(_num_recv_frames), + _pending_send_buffs(_num_send_frames) { //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; @@ -152,20 +147,18 @@ public: //allocate re-usable managed receive buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ - _mrb_pool.push_back(udp_zero_copy_asio_mrb::sptr( - new udp_zero_copy_asio_mrb(_recv_buffer_pool->at(i), + _mrb_pool.push_back(udp_zero_copy_asio_mrb(_recv_buffer_pool->at(i), boost::bind(&udp_zero_copy_asio_impl::release, this, _1)) - )); - handle_recv(_mrb_pool.back().get()); + ); + handle_recv(&_mrb_pool.back()); } //allocate re-usable managed send buffers for (size_t i = 0; i < get_num_send_frames(); i++){ - _msb_pool.push_back(udp_zero_copy_asio_msb::sptr( - new udp_zero_copy_asio_msb(_send_buffer_pool->at(i), + _msb_pool.push_back(udp_zero_copy_asio_msb(_send_buffer_pool->at(i), boost::bind(&udp_zero_copy_asio_impl::commit, this, _1, _2)) - )); - handle_send(_msb_pool.back().get()); + ); + handle_send(&_msb_pool.back()); } } @@ -264,8 +257,8 @@ private: buffer_pool::sptr _recv_buffer_pool, _send_buffer_pool; bounded_buffer<udp_zero_copy_asio_mrb *> _pending_recv_buffs; bounded_buffer<udp_zero_copy_asio_msb *> _pending_send_buffs; - std::vector<udp_zero_copy_asio_msb::sptr> _msb_pool; - std::vector<udp_zero_copy_asio_mrb::sptr> _mrb_pool; + std::list<udp_zero_copy_asio_msb> _msb_pool; + std::list<udp_zero_copy_asio_mrb> _mrb_pool; //asio guts -> socket and service asio::io_service _io_service; diff --git a/host/lib/transport/zero_copy.cpp b/host/lib/transport/zero_copy.cpp deleted file mode 100644 index b91eaae1d..000000000 --- a/host/lib/transport/zero_copy.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// -// Copyright 2010-2011 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/zero_copy.hpp> - -using namespace uhd::transport; - -/*********************************************************************** - * Safe managed receive buffer - **********************************************************************/ -static void release_nop(void){ - /* NOP */ -} - -class safe_managed_receive_buffer : public managed_recv_buffer{ -public: - safe_managed_receive_buffer( - const void *buff, size_t size, const release_fcn_t &release_fcn - ): - _buff(buff), _size(size), _release_fcn(release_fcn) - { - /* NOP */ - } - - ~safe_managed_receive_buffer(void){ - _release_fcn(); - } - - void release(void){ - release_fcn_t release_fcn = _release_fcn; - _release_fcn = &release_nop; - return release_fcn(); - } - -private: - const void *get_buff(void) const{ - return _buff; - } - - size_t get_size(void) const{ - return _size; - } - - const void *_buff; - size_t _size; - release_fcn_t _release_fcn; -}; - -managed_recv_buffer::sptr managed_recv_buffer::make_safe( - const void *buff, size_t size, const release_fcn_t &release_fcn -){ - return sptr(new safe_managed_receive_buffer(buff, size, release_fcn)); -} - -/*********************************************************************** - * Safe managed send buffer - **********************************************************************/ -static void commit_nop(size_t){ - /* NOP */ -} - -class safe_managed_send_buffer : public managed_send_buffer{ -public: - safe_managed_send_buffer( - void *buff, size_t size, const commit_fcn_t &commit_fcn - ): - _buff(buff), _size(size), _commit_fcn(commit_fcn) - { - /* NOP */ - } - - ~safe_managed_send_buffer(void){ - _commit_fcn(0); - } - - void commit(size_t num_bytes){ - commit_fcn_t commit_fcn = _commit_fcn; - _commit_fcn = &commit_nop; - return commit_fcn(num_bytes); - } - -private: - void *get_buff(void) const{ - return _buff; - } - - size_t get_size(void) const{ - return _size; - } - - void *_buff; - size_t _size; - commit_fcn_t _commit_fcn; -}; - -safe_managed_send_buffer::sptr managed_send_buffer::make_safe( - void *buff, size_t size, const commit_fcn_t &commit_fcn -){ - return sptr(new safe_managed_send_buffer(buff, size, commit_fcn)); -} diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index ad625111e..957dfd345 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -58,19 +58,24 @@ CHECK_CXX_SOURCE_COMPILES(" IF(HAVE_CLOCK_GETTIME) MESSAGE(STATUS " High resolution timing supported through clock_gettime.") - ADD_DEFINITIONS(-DTIME_SPEC_USE_CLOCK_GETTIME) - SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lrt") + SET(TIME_SPEC_DEFS HAVE_CLOCK_GETTIME) + LIBUHD_APPEND_LIBS("-lrt") ELSEIF(HAVE_MACH_ABSOLUTE_TIME) MESSAGE(STATUS " High resolution timing supported through mach_absolute_time.") - ADD_DEFINITIONS(-DTIME_SPEC_USE_MACH_ABSOLUTE_TIME) + SET(TIME_SPEC_DEFS HAVE_MACH_ABSOLUTE_TIME) ELSEIF(HAVE_QUERY_PERFORMANCE_COUNTER) MESSAGE(STATUS " High resolution timing supported through QueryPerformanceCounter.") - ADD_DEFINITIONS(-DTIME_SPEC_USE_QUERY_PERFORMANCE_COUNTER) + SET(TIME_SPEC_DEFS HAVE_QUERY_PERFORMANCE_COUNTER) ELSE() MESSAGE(STATUS " High resolution timing supported though microsec_clock.") - ADD_DEFINITIONS(-DTIME_SPEC_USE_MICROSEC_CLOCK) + SET(TIME_SPEC_DEFS HAVE_MICROSEC_CLOCK) ENDIF() +SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_CURRENT_SOURCE_DIR}/time_spec.cpp + PROPERTIES COMPILE_DEFINITIONS "${TIME_SPEC_DEFS}" +) + ######################################################################## # This file included, use CMake directory variables ######################################################################## diff --git a/host/lib/types/time_spec.cpp b/host/lib/types/time_spec.cpp index 4a41f0fb9..a785332c2 100644 --- a/host/lib/types/time_spec.cpp +++ b/host/lib/types/time_spec.cpp @@ -36,26 +36,26 @@ static UHD_INLINE time_spec_t time_spec_t_from_counts(intmax_t counts, intmax_t return time_spec_t(time_t(divres.quot), double(divres.rem)/freq); } -#ifdef TIME_SPEC_USE_CLOCK_GETTIME +#ifdef HAVE_CLOCK_GETTIME #include <time.h> time_spec_t time_spec_t::get_system_time(void){ timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return time_spec_t(ts.tv_sec, ts.tv_nsec, 1e9); } -#endif /* TIME_SPEC_USE_CLOCK_GETTIME */ +#endif /* HAVE_CLOCK_GETTIME */ -#ifdef TIME_SPEC_USE_MACH_ABSOLUTE_TIME +#ifdef HAVE_MACH_ABSOLUTE_TIME #include <mach/mach_time.h> time_spec_t time_spec_t::get_system_time(void){ mach_timebase_info_data_t info; mach_timebase_info(&info); intmax_t nanosecs = mach_absolute_time()*info.numer/info.denom; return time_spec_t_from_counts(nanosecs, intmax_t(1e9)); } -#endif /* TIME_SPEC_USE_MACH_ABSOLUTE_TIME */ +#endif /* HAVE_MACH_ABSOLUTE_TIME */ -#ifdef TIME_SPEC_USE_QUERY_PERFORMANCE_COUNTER +#ifdef HAVE_QUERY_PERFORMANCE_COUNTER #include <Windows.h> time_spec_t time_spec_t::get_system_time(void){ LARGE_INTEGER counts, freq; @@ -63,10 +63,10 @@ time_spec_t time_spec_t::get_system_time(void){ QueryPerformanceFrequency(&freq); return time_spec_t_from_counts(counts.QuadPart, freq.QuadPart); } -#endif /* TIME_SPEC_USE_QUERY_PERFORMANCE_COUNTER */ +#endif /* HAVE_QUERY_PERFORMANCE_COUNTER */ -#ifdef TIME_SPEC_USE_MICROSEC_CLOCK +#ifdef HAVE_MICROSEC_CLOCK #include <boost/date_time/posix_time/posix_time.hpp> namespace pt = boost::posix_time; time_spec_t time_spec_t::get_system_time(void){ @@ -78,7 +78,7 @@ time_spec_t time_spec_t::get_system_time(void){ double(pt::time_duration::ticks_per_second()) ); } -#endif /* TIME_SPEC_USE_MICROSEC_CLOCK */ +#endif /* HAVE_MICROSEC_CLOCK */ /*********************************************************************** * Time spec constructors diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 3b0c562ee..3e3cf00f2 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -25,10 +25,6 @@ #define MIXER_ENB MIXER_IO #define MIXER_DIS 0 -// Power constants -#define POWER_UP 0 -#define POWER_DOWN POWER_IO - // Antenna constants #define ANT_TX 0 //the tx line is transmitting #define ANT_RX ANTSW_IO //the tx line is receiving @@ -100,6 +96,7 @@ private: double _rx_lo_freq, _tx_lo_freq; std::string _rx_ant; uhd::dict<std::string, double> _rx_gains; + boost::uint16_t _power_up; void set_rx_lo_freq(double freq); void set_tx_lo_freq(double freq); @@ -130,7 +127,7 @@ private: * Register the RFX dboards (min freq, max freq, rx div2, tx div2) **********************************************************************/ static dboard_base::sptr make_rfx_flex400(dboard_base::ctor_args_t args){ - return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(400e6, 500e6), false, true)); + return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(400e6, 500e6), true, true)); } static dboard_base::sptr make_rfx_flex900(dboard_base::ctor_args_t args){ @@ -178,7 +175,8 @@ rfx_xcvr::rfx_xcvr( _div2(map_list_of (dboard_iface::UNIT_RX, rx_div2) (dboard_iface::UNIT_TX, tx_div2) - ) + ), + _power_up((get_rx_id() == 0x0024 && get_tx_id() == 0x0028) ? POWER_IO : 0) { //enable the clocks that we need this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); @@ -192,15 +190,15 @@ rfx_xcvr::rfx_xcvr( this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, output_enables); //setup the tx atr (this does not change with antenna) - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, POWER_UP | ANT_XX | MIXER_DIS); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, POWER_UP | ANT_RX | MIXER_DIS); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, POWER_UP | ANT_TX | MIXER_ENB); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP | ANT_TX | MIXER_ENB); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, _power_up | ANT_XX | MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, _power_up | ANT_RX | MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_TX | MIXER_ENB); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB); //setup the rx atr (this does not change with antenna) - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, POWER_UP | ANT_XX | MIXER_DIS); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, POWER_UP | ANT_XX | MIXER_DIS); - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP | ANT_RX2| MIXER_ENB); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, _power_up | ANT_XX | MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_XX | MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB); //set some default values set_rx_lo_freq((_freq_range.start() + _freq_range.stop())/2.0); @@ -226,7 +224,7 @@ void rfx_xcvr::set_rx_ant(const std::string &ant){ //set the rx atr regs that change with antenna setting this->get_iface()->set_atr_reg( dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, - POWER_UP | MIXER_ENB | ((ant == "TX/RX")? ANT_TXRX : ANT_RX2) + _power_up | MIXER_ENB | ((ant == "TX/RX")? ANT_TXRX : ANT_RX2) ); //shadow the setting @@ -363,7 +361,7 @@ double rfx_xcvr::set_lo_freq( regs.a_counter = A; regs.b_counter = B; regs.cp_gain_1 = adf4360_regs_t::CP_GAIN_1_SET1; - regs.divide_by_2_output = (_div2[unit])? + regs.divide_by_2_output = (_div2[unit] && (get_rx_id() != 0x0024)) ? // Special case RFX400 RX Mixer divides by two adf4360_regs_t::DIVIDE_BY_2_OUTPUT_DIV2 : adf4360_regs_t::DIVIDE_BY_2_OUTPUT_FUND ; regs.divide_by_2_prescaler = adf4360_regs_t::DIVIDE_BY_2_PRESCALER_FUND; diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 73bac029d..5130d3ae8 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -85,17 +85,14 @@ public: //----------- rx side of life ---------------------------------- for (size_t m = 0, chan = 0; m < get_num_mboards(); m++){ - buff += str(boost::format( - " RX DSP %d: %s\n" - ) % m - % _rx_dsp(m)[DSP_PROP_NAME].as<std::string>() - ); for (; chan < (m + 1)*get_rx_subdev_spec(m).size(); chan++){ buff += str(boost::format( " RX Channel: %u\n" + " RX DSP: %s\n" " RX Dboard: %s\n" " RX Subdev: %s\n" ) % chan + % _rx_dsp(chan)[DSP_PROP_NAME].as<std::string>() % _rx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() % _rx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() ); @@ -104,17 +101,14 @@ public: //----------- tx side of life ---------------------------------- for (size_t m = 0, chan = 0; m < get_num_mboards(); m++){ - buff += str(boost::format( - " TX DSP %d: %s\n" - ) % m - % _tx_dsp(m)[DSP_PROP_NAME].as<std::string>() - ); for (; chan < (m + 1)*get_tx_subdev_spec(m).size(); chan++){ buff += str(boost::format( " TX Channel: %u\n" + " TX DSP: %s\n" " TX Dboard: %s\n" " TX Subdev: %s\n" ) % chan + % _tx_dsp(chan)[DSP_PROP_NAME].as<std::string>() % _tx_dboard(chan)[DBOARD_PROP_NAME].as<std::string>() % _tx_subdev(chan)[SUBDEV_PROP_NAME].as<std::string>() ); @@ -194,9 +188,13 @@ public: return true; } - void issue_stream_cmd(const stream_cmd_t &stream_cmd){ - for (size_t m = 0; m < get_num_mboards(); m++){ - _mboard(m)[MBOARD_PROP_STREAM_CMD] = stream_cmd; + void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan){ + if (chan != ALL_CHANS){ + _rx_dsp(chan)[DSP_PROP_STREAM_CMD] = stream_cmd; + return; + } + for (size_t c = 0; m < get_rx_num_channels(); m++){ + issue_stream_cmd(stream_cmd, c); } } @@ -248,28 +246,32 @@ public: } void set_rx_rate(double rate){ - for (size_t m = 0; m < get_num_mboards(); m++){ - _rx_dsp(m)[DSP_PROP_HOST_RATE] = rate; + if (chan != ALL_CHANS){ + _rx_dsp(chan)[DSP_PROP_HOST_RATE] = rate; + do_samp_rate_warning_message(rate, get_rx_rate(chan), "RX"); + return; + } + for (size_t c = 0; m < get_rx_num_channels(); m++){ + set_rx_rate(rate, c); } - do_samp_rate_warning_message(rate, get_rx_rate(), "RX"); } - double get_rx_rate(void){ - return _rx_dsp(0)[DSP_PROP_HOST_RATE].as<double>(); + double get_rx_rate(size_t chan){ + return _rx_dsp(chan)[DSP_PROP_HOST_RATE].as<double>(); } tune_result_t set_rx_freq(const tune_request_t &tune_request, size_t chan){ - tune_result_t r = tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan/rx_cpm()), chan%rx_cpm(), tune_request); + tune_result_t r = tune_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), chan%rx_cpm(), tune_request); do_tune_freq_warning_message(tune_request.target_freq, get_rx_freq(chan), "RX"); return r; } double get_rx_freq(size_t chan){ - return derive_freq_from_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan/rx_cpm()), chan%rx_cpm()); + return derive_freq_from_rx_subdev_and_dsp(_rx_subdev(chan), _rx_dsp(chan), chan%rx_cpm()); } freq_range_t get_rx_freq_range(size_t chan){ - return add_dsp_shift(_rx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp(chan/rx_cpm())); + return add_dsp_shift(_rx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _rx_dsp(chan)); } void set_rx_gain(double gain, const std::string &name, size_t chan){ @@ -346,28 +348,32 @@ public: } void set_tx_rate(double rate){ - for (size_t m = 0; m < get_num_mboards(); m++){ - _tx_dsp(m)[DSP_PROP_HOST_RATE] = rate; + if (chan != ALL_CHANS){ + _tx_dsp(chan)[DSP_PROP_HOST_RATE] = rate; + do_samp_rate_warning_message(rate, get_tx_rate(chan), "TX"); + return; + } + for (size_t c = 0; m < get_tx_num_channels(); m++){ + set_tx_rate(rate, c); } - do_samp_rate_warning_message(rate, get_tx_rate(), "TX"); } - double get_tx_rate(void){ - return _tx_dsp(0)[DSP_PROP_HOST_RATE].as<double>(); + double get_tx_rate(size_t chan){ + return _tx_dsp(chan)[DSP_PROP_HOST_RATE].as<double>(); } tune_result_t set_tx_freq(const tune_request_t &tune_request, size_t chan){ - tune_result_t r = tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan/tx_cpm()), chan%tx_cpm(), tune_request); + tune_result_t r = tune_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), chan%tx_cpm(), tune_request); do_tune_freq_warning_message(tune_request.target_freq, get_tx_freq(chan), "TX"); return r; } double get_tx_freq(size_t chan){ - return derive_freq_from_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan/tx_cpm()), chan%tx_cpm()); + return derive_freq_from_tx_subdev_and_dsp(_tx_subdev(chan), _tx_dsp(chan), chan%tx_cpm()); } freq_range_t get_tx_freq_range(size_t chan){ - return add_dsp_shift(_tx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp(chan/tx_cpm())); + return add_dsp_shift(_tx_subdev(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(), _tx_dsp(chan)); } void set_tx_gain(double gain, const std::string &name, size_t chan){ @@ -445,11 +451,13 @@ private: std::string mb_name = (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>().at(mboard); return (*_dev)[named_prop_t(DEVICE_PROP_MBOARD, mb_name)]; } - wax::obj _rx_dsp(size_t mboard){ - return _mboard(mboard)[MBOARD_PROP_RX_DSP]; + wax::obj _rx_dsp(size_t chan){ + prop_names_t dsp_names = _mboard(chan/rx_cpm())[MBOARD_PROP_RX_DSP_NAMES].as<prop_names_t>(); + return _mboard(chan/rx_cpm())[named_prop_t(MBOARD_PROP_RX_DSP, dsp_names.at(chan%rx_cpm())]; } - wax::obj _tx_dsp(size_t mboard){ - return _mboard(mboard)[MBOARD_PROP_TX_DSP]; + wax::obj _tx_dsp(size_t chan){ + prop_names_t dsp_names = _mboard(chan/tx_cpm())[MBOARD_PROP_TX_DSP_NAMES].as<prop_names_t>(); + return _mboard(chan/tx_cpm())[named_prop_t(MBOARD_PROP_TX_DSP, dsp_names.at(chan%tx_cpm())]; } wax::obj _rx_dboard(size_t chan){ std::string db_name = get_rx_subdev_spec(chan/rx_cpm()).at(chan%rx_cpm()).db_name; diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index 88cbab073..8beeccf8f 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -37,21 +37,64 @@ static const size_t alignment_padding = 512; /*********************************************************************** * Helper struct to associate an offset with a buffer **********************************************************************/ -class offset_send_buffer{ -public: - typedef boost::shared_ptr<offset_send_buffer> sptr; +struct offset_send_buffer{ + offset_send_buffer(void){ + /* NOP */ + } - static sptr make(managed_send_buffer::sptr buff, size_t offset = 0){ - return sptr(new offset_send_buffer(buff, offset)); + offset_send_buffer(managed_send_buffer::sptr buff, size_t offset = 0): + buff(buff), offset(offset) + { + /* NOP */ } //member variables managed_send_buffer::sptr buff; size_t offset; /* in bytes */ +}; + +/*********************************************************************** + * Reusable managed send buffer to handle aligned commits + **********************************************************************/ +class offset_managed_send_buffer : public managed_send_buffer{ +public: + typedef boost::function<void(offset_send_buffer&, offset_send_buffer&, size_t)> commit_cb_type; + offset_managed_send_buffer(const commit_cb_type &commit_cb): + _expired(true), _commit_cb(commit_cb) + { + /* NOP */ + } + + bool expired(void){return _expired;} + + void commit(size_t size){ + if (_expired) return; + this->_commit_cb(_curr_buff, _next_buff, size); + _expired = true; + } + + sptr get_new( + offset_send_buffer &curr_buff, + offset_send_buffer &next_buff + ){ + _expired = false; + _curr_buff = curr_buff; + _next_buff = next_buff; + return sptr(this, &offset_managed_send_buffer::fake_deleter); + } private: - offset_send_buffer(managed_send_buffer::sptr buff, size_t offset): - buff(buff), offset(offset){/* NOP */} + static void fake_deleter(void *){ + //dont do anything and assume the bastard committed it + //static_cast<offset_managed_send_buffer *>(obj)->commit(0); + } + + void *get_buff(void) const{return _curr_buff.buff->cast<char *>() + _curr_buff.offset;} + size_t get_size(void) const{return _curr_buff.buff->size() - _curr_buff.offset;} + + bool _expired; + offset_send_buffer _curr_buff, _next_buff; + commit_cb_type _commit_cb; }; /*********************************************************************** @@ -60,10 +103,12 @@ private: struct usrp1_impl::io_impl{ io_impl(zero_copy_if::sptr data_transport): data_transport(data_transport), + get_recv_buffs_fcn(boost::bind(&usrp1_impl::io_impl::get_recv_buffs, this, _1)), + get_send_buffs_fcn(boost::bind(&usrp1_impl::io_impl::get_send_buffs, this, _1)), underflow_poll_samp_count(0), overflow_poll_samp_count(0), - curr_buff_committed(true), - curr_buff(offset_send_buffer::make(data_transport->get_send_buff())) + curr_buff(offset_send_buffer(data_transport->get_send_buff())), + omsb(boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, _1, _2, _3)) { /* NOP */ } @@ -74,6 +119,13 @@ struct usrp1_impl::io_impl{ zero_copy_if::sptr data_transport; + //timeouts set on calls to recv/send (passed into get buffs methods) + double recv_timeout, send_timeout; + + //bound callbacks for get buffs (bound once here, not in fast-path) + vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn; + vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn; + //state management for the vrt packet handler code vrt_packet_handler::recv_state packet_handler_recv_state; vrt_packet_handler::send_state packet_handler_send_state; @@ -86,11 +138,16 @@ struct usrp1_impl::io_impl{ //all of this to ensure only aligned lengths are committed //NOTE: you must commit before getting a new buffer //since the vrt packet handler obeys this, we are ok - bool curr_buff_committed; - offset_send_buffer::sptr curr_buff; - void commit_send_buff(offset_send_buffer::sptr, offset_send_buffer::sptr, size_t); + offset_send_buffer curr_buff; + offset_managed_send_buffer omsb; + void commit_send_buff(offset_send_buffer&, offset_send_buffer&, size_t); void flush_send_buff(void); - bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &, double); + bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &); + bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs){ + UHD_ASSERT_THROW(buffs.size() == 1); + buffs[0] = data_transport->get_recv_buff(recv_timeout); + return buffs[0].get() != NULL; + } }; /*! @@ -99,12 +156,12 @@ struct usrp1_impl::io_impl{ * Commit the current buffer at multiples of alignment. */ void usrp1_impl::io_impl::commit_send_buff( - offset_send_buffer::sptr curr, - offset_send_buffer::sptr next, + offset_send_buffer &curr, + offset_send_buffer &next, size_t num_bytes ){ //total number of bytes now in the current buffer - size_t bytes_in_curr_buffer = curr->offset + num_bytes; + size_t bytes_in_curr_buffer = curr.offset + num_bytes; //calculate how many to commit and remainder size_t num_bytes_remaining = bytes_in_curr_buffer % alignment_padding; @@ -112,17 +169,16 @@ void usrp1_impl::io_impl::commit_send_buff( //copy the remainder into the next buffer std::memcpy( - next->buff->cast<char *>() + next->offset, - curr->buff->cast<char *>() + num_bytes_to_commit, + next.buff->cast<char *>() + next.offset, + curr.buff->cast<char *>() + num_bytes_to_commit, num_bytes_remaining ); //update the offset into the next buffer - next->offset += num_bytes_remaining; + next.offset += num_bytes_remaining; //commit the current buffer - curr->buff->commit(num_bytes_to_commit); - curr_buff_committed = true; + curr.buff->commit(num_bytes_to_commit); } /*! @@ -130,14 +186,14 @@ void usrp1_impl::io_impl::commit_send_buff( */ void usrp1_impl::io_impl::flush_send_buff(void){ //calculate the number of bytes to alignment - size_t bytes_to_pad = (-1*curr_buff->offset)%alignment_padding; + size_t bytes_to_pad = (-1*curr_buff.offset)%alignment_padding; //send at least alignment_padding to guarantee zeros are sent if (bytes_to_pad == 0) bytes_to_pad = alignment_padding; //get the buffer, clear, and commit (really current buffer) vrt_packet_handler::managed_send_buffs_t buffs(1); - if (this->get_send_buffs(buffs, 0.1)){ + if (this->get_send_buffs(buffs)){ std::memset(buffs[0]->cast<void *>(), 0, bytes_to_pad); buffs[0]->commit(bytes_to_pad); } @@ -148,25 +204,19 @@ void usrp1_impl::io_impl::flush_send_buff(void){ * Always grab the next send buffer so we can timeout here. */ bool usrp1_impl::io_impl::get_send_buffs( - vrt_packet_handler::managed_send_buffs_t &buffs, double timeout + vrt_packet_handler::managed_send_buffs_t &buffs ){ - UHD_ASSERT_THROW(curr_buff_committed and buffs.size() == 1); + UHD_ASSERT_THROW(omsb.expired() and buffs.size() == 1); //try to get a new managed buffer with timeout - offset_send_buffer::sptr next_buff(offset_send_buffer::make(data_transport->get_send_buff(timeout))); - if (not next_buff->buff.get()) return false; /* propagate timeout here */ - - //calculate the buffer pointer and size given the offset - //references to the buffers are held in the bound function - buffs[0] = managed_send_buffer::make_safe( - curr_buff->buff->cast<char *>() + curr_buff->offset, - curr_buff->buff->size() - curr_buff->offset, - boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, curr_buff, next_buff, _1) - ); + offset_send_buffer next_buff(data_transport->get_send_buff(send_timeout)); + if (not next_buff.buff.get()) return false; /* propagate timeout here */ + + //make a new managed buffer with the offset buffs + buffs[0] = omsb.get_new(curr_buff, next_buff); //store the next buffer for the next call curr_buff = next_buff; - curr_buff_committed = false; return true; } @@ -226,6 +276,7 @@ size_t usrp1_impl::send( ){ if (_soft_time_ctrl->send_pre(metadata, timeout)) return num_samps; + _io_impl->send_timeout = timeout; size_t num_samps_sent = vrt_packet_handler::send( _io_impl->packet_handler_send_state, //last state of the send handler buffs, num_samps, //buffer to fill @@ -233,7 +284,7 @@ size_t usrp1_impl::send( io_type, _tx_otw_type, //input and output types to convert _clock_ctrl->get_master_clock_freq(), //master clock tick rate &usrp1_bs_vrt_packer, - boost::bind(&usrp1_impl::io_impl::get_send_buffs, _io_impl.get(), _1, timeout), + _io_impl->get_send_buffs_fcn, get_max_send_samps_per_packet(), 0, //vrt header offset _tx_subdev_spec.size() //num channels @@ -281,15 +332,6 @@ static void usrp1_bs_vrt_unpacker( if_packet_info.has_tlr = false; } -static bool get_recv_buffs( - zero_copy_if::sptr zc_if, double timeout, - vrt_packet_handler::managed_recv_buffs_t &buffs -){ - UHD_ASSERT_THROW(buffs.size() == 1); - buffs[0] = zc_if->get_recv_buff(timeout); - return buffs[0].get() != NULL; -} - size_t usrp1_impl::get_max_recv_samps_per_packet(void) const { return _data_transport->get_recv_frame_size() / _rx_otw_type.get_sample_size() @@ -302,6 +344,7 @@ size_t usrp1_impl::recv( rx_metadata_t &metadata, const io_type_t &io_type, recv_mode_t recv_mode, double timeout ){ + _io_impl->recv_timeout = timeout; size_t num_samps_recvd = vrt_packet_handler::recv( _io_impl->packet_handler_recv_state, //last state of the recv handler buffs, num_samps, //buffer to fill @@ -309,7 +352,7 @@ size_t usrp1_impl::recv( io_type, _rx_otw_type, //input and output types to convert _clock_ctrl->get_master_clock_freq(), //master clock tick rate &usrp1_bs_vrt_unpacker, - boost::bind(&get_recv_buffs, _data_transport, timeout, _1), + _io_impl->get_recv_buffs_fcn, &vrt_packet_handler::handle_overflow_nop, 0, //vrt header offset _rx_subdev_spec.size() //num channels diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index 8340f7cdd..5cf48fe96 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -98,7 +98,7 @@ void usrp2_mboard_impl::ddc_set(const wax::obj &key_, const wax::obj &val){ case DSP_PROP_FREQ_SHIFT:{ double new_freq = val.as<double>(); - _iface->poke32(_iface->regs.dsp_rx_freq, + _iface->poke32(_iface->regs.dsp0_rx_freq, dsp_type1::calc_cordic_word_and_update(new_freq, get_master_clock_freq()) ); _ddc_freq = new_freq; //shadow @@ -110,11 +110,11 @@ void usrp2_mboard_impl::ddc_set(const wax::obj &key_, const wax::obj &val){ _ddc_decim = pick_closest_rate(extact_rate, _allowed_decim_and_interp_rates); //set the decimation - _iface->poke32(_iface->regs.dsp_rx_decim_rate, dsp_type1::calc_cic_filter_word(_ddc_decim)); + _iface->poke32(_iface->regs.dsp0_rx_decim_rate, dsp_type1::calc_cic_filter_word(_ddc_decim)); //set the scaling static const boost::int16_t default_rx_scale_iq = 1024; - _iface->poke32(_iface->regs.dsp_rx_scale_iq, + _iface->poke32(_iface->regs.dsp0_rx_scale_iq, dsp_type1::calc_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq) ); } diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index a22f805e1..33c0b728a 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -31,7 +31,7 @@ extern "C" { //fpga and firmware compatibility numbers #define USRP2_FPGA_COMPAT_NUM 4 -#define USRP2_FW_COMPAT_NUM 8 +#define USRP2_FW_COMPAT_NUM 9 //used to differentiate control packets over data port #define USRP2_INVALID_VRT_HEADER 0 @@ -40,8 +40,9 @@ extern "C" { // Dynamic and/or private ports: 49152-65535 #define USRP2_UDP_CTRL_PORT 49152 //#define USRP2_UDP_UPDATE_PORT 49154 -#define USRP2_UDP_DATA_PORT 49156 +#define USRP2_UDP_DSP0_PORT 49156 #define USRP2_UDP_ERR0_PORT 49157 +#define USRP2_UDP_DSP1_PORT 49158 //////////////////////////////////////////////////////////////////////// // I2C addresses diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 67b52db71..b20b6652e 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -121,13 +121,12 @@ struct usrp2_impl::io_impl{ io_impl(size_t send_frame_size, const std::vector<zero_copy_if::sptr> &xports): xports(xports), + get_recv_buffs_fcn(boost::bind(&usrp2_impl::io_impl::get_recv_buffs, this, _1)), + get_send_buffs_fcn(boost::bind(&usrp2_impl::io_impl::get_send_buffs, this, _1)), packet_handler_recv_state(xports.size()), packet_handler_send_state(xports.size()), async_msg_fifo(100/*messages deep*/) { - get_recv_buffs_fcn = boost::bind(&usrp2_impl::io_impl::get_recv_buffs, this, _1); - get_send_buffs_fcn = boost::bind(&usrp2_impl::io_impl::get_send_buffs, this, _1); - for (size_t i = 0; i < xports.size(); i++){ fc_mons.push_back(flow_control_monitor::sptr( new flow_control_monitor(usrp2_impl::sram_bytes/send_frame_size) diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index 397fae636..5fbbfc0ee 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -87,17 +87,17 @@ usrp2_mboard_impl::usrp2_mboard_impl( } //setup the vrt rx registers - _iface->poke32(_iface->regs.rx_ctrl_clear_overrun, 1); //reset - _iface->poke32(_iface->regs.rx_ctrl_nsamps_per_pkt, recv_samps_per_packet); - _iface->poke32(_iface->regs.rx_ctrl_nchannels, 1); - _iface->poke32(_iface->regs.rx_ctrl_vrt_header, 0 + _iface->poke32(_iface->regs.rx_ctrl0_clear_overrun, 1); //reset + _iface->poke32(_iface->regs.rx_ctrl0_nsamps_per_pkt, recv_samps_per_packet); + _iface->poke32(_iface->regs.rx_ctrl0_nchannels, 1); + _iface->poke32(_iface->regs.rx_ctrl0_vrt_header, 0 | (0x1 << 28) //if data with stream id | (0x1 << 26) //has trailer | (0x3 << 22) //integer time other | (0x1 << 20) //fractional time sample count ); - _iface->poke32(_iface->regs.rx_ctrl_vrt_stream_id, usrp2_impl::RECV_SID); - _iface->poke32(_iface->regs.rx_ctrl_vrt_trailer, 0); + _iface->poke32(_iface->regs.rx_ctrl0_vrt_stream_id, usrp2_impl::RECV_SID); + _iface->poke32(_iface->regs.rx_ctrl0_vrt_trailer, 0); _iface->poke32(_iface->regs.time64_tps, size_t(get_master_clock_freq())); //init the tx control registers @@ -164,7 +164,7 @@ usrp2_mboard_impl::usrp2_mboard_impl( this->issue_ddc_stream_cmd(stream_cmd); data_transport->get_recv_buff().get(); //recv with timeout for lingering data_transport->get_recv_buff().get(); //recv with timeout for expected - _iface->poke32(_iface->regs.rx_ctrl_clear_overrun, 1); //resets sequence + _iface->poke32(_iface->regs.rx_ctrl0_clear_overrun, 1); //resets sequence } usrp2_mboard_impl::~usrp2_mboard_impl(void){ @@ -273,9 +273,9 @@ void usrp2_mboard_impl::handle_overflow(void){ void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){ _continuous_streaming = stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS; - _iface->poke32(_iface->regs.rx_ctrl_stream_cmd, dsp_type1::calc_stream_cmd_word(stream_cmd)); - _iface->poke32(_iface->regs.rx_ctrl_time_secs, boost::uint32_t(stream_cmd.time_spec.get_full_secs())); - _iface->poke32(_iface->regs.rx_ctrl_time_ticks, stream_cmd.time_spec.get_tick_count(get_master_clock_freq())); + _iface->poke32(_iface->regs.rx_ctrl0_stream_cmd, dsp_type1::calc_stream_cmd_word(stream_cmd)); + _iface->poke32(_iface->regs.rx_ctrl0_time_secs, boost::uint32_t(stream_cmd.time_spec.get_full_secs())); + _iface->poke32(_iface->regs.rx_ctrl0_time_ticks, stream_cmd.time_spec.get_tick_count(get_master_clock_freq())); } /*********************************************************************** @@ -399,10 +399,13 @@ void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){ _rx_subdev_spec = val.as<subdev_spec_t>(); verify_rx_subdev_spec(_rx_subdev_spec, this->get_link()); //sanity check - UHD_ASSERT_THROW(_rx_subdev_spec.size() == 1); + UHD_ASSERT_THROW(_rx_subdev_spec.size() <= 2); //set the mux - _iface->poke32(_iface->regs.dsp_rx_mux, dsp_type1::calc_rx_mux_word( - _dboard_manager->get_rx_subdev(_rx_subdev_spec.front().sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>() + if (_rx_subdev_spec.size() >= 1) _iface->poke32(_iface->regs.dsp0_rx_mux, dsp_type1::calc_rx_mux_word( + _dboard_manager->get_rx_subdev(_rx_subdev_spec[0].sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>() + )); + if (_rx_subdev_spec.size() >= 2)_iface->poke32(_iface->regs.dsp1_rx_mux, dsp_type1::calc_rx_mux_word( + _dboard_manager->get_rx_subdev(_rx_subdev_spec[1].sd_name)[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>() )); return; diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 9ce0f7359..d40a96364 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -220,7 +220,7 @@ static device::sptr usrp2_make(const device_addr_t &device_addr){ dev_addr_i["addr"], num2str(USRP2_UDP_CTRL_PORT) )); data_transports.push_back(udp_zero_copy::make( - dev_addr_i["addr"], num2str(USRP2_UDP_DATA_PORT), dsp_xport_hints + dev_addr_i["addr"], num2str(USRP2_UDP_DSP0_PORT), dsp_xport_hints )); err0_transports.push_back(udp_zero_copy::make( dev_addr_i["addr"], num2str(USRP2_UDP_ERR0_PORT), device_addr_t() diff --git a/host/lib/usrp/usrp2/usrp2_regs.cpp b/host/lib/usrp/usrp2/usrp2_regs.cpp index 84907c32e..2159c4276 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.cpp +++ b/host/lib/usrp/usrp2/usrp2_regs.cpp @@ -38,8 +38,10 @@ usrp2_regs_t usrp2_get_regs(bool use_n2xx_map) { x.sr_udp_sm = 96; x.sr_tx_dsp = 208; x.sr_tx_ctrl = 224; - x.sr_rx_dsp = 160; - x.sr_rx_ctrl = 176; + x.sr_rx_dsp0 = 160; + x.sr_rx_ctrl0 = 176; + x.sr_rx_dsp1 = 240; + x.sr_rx_ctrl1 = 32; x.sr_time64 = 192; x.sr_simtimer = 198; x.sr_last = 255; @@ -68,12 +70,12 @@ usrp2_regs_t usrp2_get_regs(bool use_n2xx_map) { x.dsp_tx_scale_iq = sr_addr(misc_output_base, x.sr_tx_dsp + 1); x.dsp_tx_interp_rate = sr_addr(misc_output_base, x.sr_tx_dsp + 2); x.dsp_tx_mux = sr_addr(misc_output_base, x.sr_tx_dsp + 4); - x.dsp_rx_freq = sr_addr(misc_output_base, x.sr_rx_dsp + 0); - x.dsp_rx_scale_iq = sr_addr(misc_output_base, x.sr_rx_dsp + 1); - x.dsp_rx_decim_rate = sr_addr(misc_output_base, x.sr_rx_dsp + 2); - x.dsp_rx_dcoffset_i = sr_addr(misc_output_base, x.sr_rx_dsp + 3); - x.dsp_rx_dcoffset_q = sr_addr(misc_output_base, x.sr_rx_dsp + 4); - x.dsp_rx_mux = sr_addr(misc_output_base, x.sr_rx_dsp + 5); + x.dsp0_rx_freq = sr_addr(misc_output_base, x.sr_rx_dsp0 + 0); + x.dsp0_rx_scale_iq = sr_addr(misc_output_base, x.sr_rx_dsp0 + 1); + x.dsp0_rx_decim_rate = sr_addr(misc_output_base, x.sr_rx_dsp0 + 2); + x.dsp0_rx_dcoffset_i = sr_addr(misc_output_base, x.sr_rx_dsp0 + 3); + x.dsp0_rx_dcoffset_q = sr_addr(misc_output_base, x.sr_rx_dsp0 + 4); + x.dsp0_rx_mux = sr_addr(misc_output_base, x.sr_rx_dsp0 + 5); x.gpio_io = gpio_base + 0; x.gpio_ddr = gpio_base + 4; x.gpio_tx_sel = gpio_base + 8; @@ -86,15 +88,15 @@ usrp2_regs_t usrp2_get_regs(bool use_n2xx_map) { x.atr_inrx_rxside = atr_base + 10; x.atr_full_txside = atr_base + 12; x.atr_full_rxside = atr_base + 14; - x.rx_ctrl_stream_cmd = sr_addr(misc_output_base, x.sr_rx_ctrl + 0); - x.rx_ctrl_time_secs = sr_addr(misc_output_base, x.sr_rx_ctrl + 1); - x.rx_ctrl_time_ticks = sr_addr(misc_output_base, x.sr_rx_ctrl + 2); - x.rx_ctrl_clear_overrun = sr_addr(misc_output_base, x.sr_rx_ctrl + 3); - x.rx_ctrl_vrt_header = sr_addr(misc_output_base, x.sr_rx_ctrl + 4); - x.rx_ctrl_vrt_stream_id = sr_addr(misc_output_base, x.sr_rx_ctrl + 5); - x.rx_ctrl_vrt_trailer = sr_addr(misc_output_base, x.sr_rx_ctrl + 6); - x.rx_ctrl_nsamps_per_pkt = sr_addr(misc_output_base, x.sr_rx_ctrl + 7); - x.rx_ctrl_nchannels = sr_addr(misc_output_base, x.sr_rx_ctrl + 8); + x.rx_ctrl0_stream_cmd = sr_addr(misc_output_base, x.sr_rx_ctrl0 + 0); + x.rx_ctrl0_time_secs = sr_addr(misc_output_base, x.sr_rx_ctrl0 + 1); + x.rx_ctrl0_time_ticks = sr_addr(misc_output_base, x.sr_rx_ctrl0 + 2); + x.rx_ctrl0_clear_overrun = sr_addr(misc_output_base, x.sr_rx_ctrl0 + 3); + x.rx_ctrl0_vrt_header = sr_addr(misc_output_base, x.sr_rx_ctrl0 + 4); + x.rx_ctrl0_vrt_stream_id = sr_addr(misc_output_base, x.sr_rx_ctrl0 + 5); + x.rx_ctrl0_vrt_trailer = sr_addr(misc_output_base, x.sr_rx_ctrl0 + 6); + x.rx_ctrl0_nsamps_per_pkt = sr_addr(misc_output_base, x.sr_rx_ctrl0 + 7); + x.rx_ctrl0_nchannels = sr_addr(misc_output_base, x.sr_rx_ctrl0 + 8); x.tx_ctrl_num_chan = sr_addr(misc_output_base, x.sr_tx_ctrl + 0); x.tx_ctrl_clear_state = sr_addr(misc_output_base, x.sr_tx_ctrl + 1); x.tx_ctrl_report_sid = sr_addr(misc_output_base, x.sr_tx_ctrl + 2); diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index 977b342cb..e150528a7 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -38,8 +38,10 @@ typedef struct { int sr_udp_sm; int sr_tx_dsp; int sr_tx_ctrl; - int sr_rx_dsp; - int sr_rx_ctrl; + int sr_rx_dsp0; + int sr_rx_ctrl0; + int sr_rx_dsp1; + int sr_rx_ctrl1; int sr_time64; int sr_simtimer; int sr_last; @@ -68,12 +70,12 @@ typedef struct { int dsp_tx_scale_iq; int dsp_tx_interp_rate; int dsp_tx_mux; - int dsp_rx_freq; - int dsp_rx_scale_iq; - int dsp_rx_decim_rate; - int dsp_rx_dcoffset_i; - int dsp_rx_dcoffset_q; - int dsp_rx_mux; + int dsp0_rx_freq; + int dsp0_rx_scale_iq; + int dsp0_rx_decim_rate; + int dsp0_rx_dcoffset_i; + int dsp0_rx_dcoffset_q; + int dsp0_rx_mux; int gpio_base; int gpio_io; int gpio_ddr; @@ -88,15 +90,15 @@ typedef struct { int atr_inrx_rxside; int atr_full_txside; int atr_full_rxside; - int rx_ctrl_stream_cmd; - int rx_ctrl_time_secs; - int rx_ctrl_time_ticks; - int rx_ctrl_clear_overrun; - int rx_ctrl_vrt_header; - int rx_ctrl_vrt_stream_id; - int rx_ctrl_vrt_trailer; - int rx_ctrl_nsamps_per_pkt; - int rx_ctrl_nchannels; + int rx_ctrl0_stream_cmd; + int rx_ctrl0_time_secs; + int rx_ctrl0_time_ticks; + int rx_ctrl0_clear_overrun; + int rx_ctrl0_vrt_header; + int rx_ctrl0_vrt_stream_id; + int rx_ctrl0_vrt_trailer; + int rx_ctrl0_nsamps_per_pkt; + int rx_ctrl0_nchannels; int tx_ctrl_num_chan; int tx_ctrl_clear_state; int tx_ctrl_report_sid; diff --git a/host/lib/usrp/usrp_e100/io_impl.cpp b/host/lib/usrp/usrp_e100/io_impl.cpp index 5fb2da7b8..fc6aaeaee 100644 --- a/host/lib/usrp/usrp_e100/io_impl.cpp +++ b/host/lib/usrp/usrp_e100/io_impl.cpp @@ -48,13 +48,10 @@ static const bool recv_debug = false; * - vrt packet handler states **********************************************************************/ struct usrp_e100_impl::io_impl{ - //state management for the vrt packet handler code - vrt_packet_handler::recv_state packet_handler_recv_state; - vrt_packet_handler::send_state packet_handler_send_state; - zero_copy_if::sptr data_xport; - bool continuous_streaming; io_impl(usrp_e100_iface::sptr iface): data_xport(usrp_e100_make_mmap_zero_copy(iface)), + get_recv_buffs_fcn(boost::bind(&usrp_e100_impl::io_impl::get_recv_buffs, this, _1)), + get_send_buffs_fcn(boost::bind(&usrp_e100_impl::io_impl::get_send_buffs, this, _1)), recv_pirate_booty(data_xport->get_num_recv_frames()), async_msg_fifo(100/*messages deep*/) { @@ -67,12 +64,34 @@ struct usrp_e100_impl::io_impl{ recv_pirate_crew.join_all(); } - bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, double timeout){ + bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs){ UHD_ASSERT_THROW(buffs.size() == 1); boost::this_thread::disable_interruption di; //disable because the wait can throw - return recv_pirate_booty.pop_with_timed_wait(buffs.front(), timeout); + return recv_pirate_booty.pop_with_timed_wait(buffs.front(), recv_timeout); } + bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &buffs){ + UHD_ASSERT_THROW(buffs.size() == 1); + buffs[0] = data_xport->get_send_buff(send_timeout); + return buffs[0].get() != NULL; + } + + //The data transport is listed first so that it is deconstructed last, + //which is after the states and booty which may hold managed buffers. + zero_copy_if::sptr data_xport; + + //bound callbacks for get buffs (bound once here, not in fast-path) + vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn; + vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn; + + //timeouts set on calls to recv/send (passed into get buffs methods) + double recv_timeout, send_timeout; + + //state management for the vrt packet handler code + vrt_packet_handler::recv_state packet_handler_recv_state; + vrt_packet_handler::send_state packet_handler_send_state; + bool continuous_streaming; + //a pirate's life is the life for me! void recv_pirate_loop(usrp_e100_clock_ctrl::sptr); bounded_buffer<managed_recv_buffer::sptr> recv_pirate_booty; @@ -204,15 +223,6 @@ void usrp_e100_impl::handle_overrun(size_t){ /*********************************************************************** * Data Send **********************************************************************/ -bool get_send_buffs( - zero_copy_if::sptr trans, double timeout, - vrt_packet_handler::managed_send_buffs_t &buffs -){ - UHD_ASSERT_THROW(buffs.size() == 1); - buffs[0] = trans->get_send_buff(timeout); - return buffs[0].get() != NULL; -} - size_t usrp_e100_impl::get_max_send_samps_per_packet(void) const{ static const size_t hdr_size = 0 + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) @@ -227,6 +237,7 @@ size_t usrp_e100_impl::send( const tx_metadata_t &metadata, const io_type_t &io_type, send_mode_t send_mode, double timeout ){ + _io_impl->send_timeout = timeout; return vrt_packet_handler::send( _io_impl->packet_handler_send_state, //last state of the send handler buffs, num_samps, //buffer to fill @@ -234,7 +245,7 @@ size_t usrp_e100_impl::send( io_type, _send_otw_type, //input and output types to convert _clock_ctrl->get_fpga_clock_rate(), //master clock tick rate uhd::transport::vrt::if_hdr_pack_le, - boost::bind(&get_send_buffs, _io_impl->data_xport, timeout, _1), + _io_impl->get_send_buffs_fcn, get_max_send_samps_per_packet() ); } @@ -257,6 +268,7 @@ size_t usrp_e100_impl::recv( rx_metadata_t &metadata, const io_type_t &io_type, recv_mode_t recv_mode, double timeout ){ + _io_impl->recv_timeout = timeout; return vrt_packet_handler::recv( _io_impl->packet_handler_recv_state, //last state of the recv handler buffs, num_samps, //buffer to fill @@ -264,7 +276,7 @@ size_t usrp_e100_impl::recv( io_type, _recv_otw_type, //input and output types to convert _clock_ctrl->get_fpga_clock_rate(), //master clock tick rate uhd::transport::vrt::if_hdr_unpack_le, - boost::bind(&usrp_e100_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout), + _io_impl->get_recv_buffs_fcn, boost::bind(&usrp_e100_impl::handle_overrun, this, _1) ); } diff --git a/host/lib/usrp/usrp_e100/usrp_e100_mmap_zero_copy.cpp b/host/lib/usrp/usrp_e100/usrp_e100_mmap_zero_copy.cpp index 4e0137fdb..c155d426a 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_mmap_zero_copy.cpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_mmap_zero_copy.cpp @@ -22,7 +22,7 @@ #include <sys/mman.h> //mmap #include <unistd.h> //getpagesize #include <poll.h> //poll -#include <boost/bind.hpp> +#include <vector> #include <iostream> using namespace uhd; @@ -33,6 +33,82 @@ static const bool sp_verbose = false; //slow-path verbose static const size_t poll_breakout = 10; //how many poll timeouts constitute a full timeout /*********************************************************************** + * Reusable managed receiver buffer: + * - The buffer knows how to claim and release a frame. + **********************************************************************/ +class usrp_e100_mmap_zero_copy_mrb : public managed_recv_buffer{ +public: + usrp_e100_mmap_zero_copy_mrb(void *mem, ring_buffer_info *info): + _mem(mem), _info(info) { /* NOP */ } + + void release(void){ + if (_info->flags != RB_USER_PROCESS) return; + if (fp_verbose) std::cout << "recv buff: release" << std::endl; + _info->flags = RB_KERNEL; //release the frame + } + + bool ready(void){return _info->flags & RB_USER;} + + sptr get_new(void){ + if (fp_verbose) std::cout << " make_recv_buff: " << get_size() << std::endl; + _info->flags = RB_USER_PROCESS; //claim the frame + return sptr(this, &usrp_e100_mmap_zero_copy_mrb::fake_deleter); + } + +private: + static void fake_deleter(void *obj){ + static_cast<usrp_e100_mmap_zero_copy_mrb *>(obj)->release(); + } + + const void *get_buff(void) const{return _mem;} + size_t get_size(void) const{return _info->len;} + + void *_mem; + ring_buffer_info *_info; +}; + +/*********************************************************************** + * Reusable managed send buffer: + * - The buffer knows how to claim and release a frame. + **********************************************************************/ +class usrp_e100_mmap_zero_copy_msb : public managed_send_buffer{ +public: + usrp_e100_mmap_zero_copy_msb(void *mem, ring_buffer_info *info, size_t len, int fd): + _mem(mem), _info(info), _len(len), _fd(fd) { /* NOP */ } + + void commit(size_t len){ + if (_info->flags != RB_USER_PROCESS) return; + if (fp_verbose) std::cout << "send buff: commit " << len << std::endl; + _info->len = len; + _info->flags = RB_USER; //release the frame + if (::write(_fd, NULL, 0) < 0){ //notifies the kernel + std::cerr << UHD_THROW_SITE_INFO("write error") << std::endl; + } + } + + bool ready(void){return _info->flags & RB_KERNEL;} + + sptr get_new(void){ + if (fp_verbose) std::cout << " make_send_buff: " << get_size() << std::endl; + _info->flags = RB_USER_PROCESS; //claim the frame + return sptr(this, &usrp_e100_mmap_zero_copy_msb::fake_deleter); + } + +private: + static void fake_deleter(void *obj){ + static_cast<usrp_e100_mmap_zero_copy_msb *>(obj)->commit(0); + } + + void *get_buff(void) const{return _mem;} + size_t get_size(void) const{return _len;} + + void *_mem; + ring_buffer_info *_info; + size_t _len; + int _fd; +}; + +/*********************************************************************** * The zero copy interface implementation **********************************************************************/ class usrp_e100_mmap_zero_copy_impl : public zero_copy_if{ @@ -81,13 +157,32 @@ public: std::cout << "send_buff_off: " << send_buff_off << std::endl; } + //pointers to sections in the mapped memory + ring_buffer_info (*recv_info)[], (*send_info)[]; + char *recv_buff, *send_buff; + //set the internal pointers for info and buffers typedef ring_buffer_info (*rbi_pta)[]; char *rb_ptr = reinterpret_cast<char *>(_mapped_mem); - _recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off); - _recv_buff = rb_ptr + recv_buff_off; - _send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off); - _send_buff = rb_ptr + send_buff_off; + recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off); + recv_buff = rb_ptr + recv_buff_off; + send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off); + send_buff = rb_ptr + send_buff_off; + + //initialize the managed receive buffers + for (size_t i = 0; i < get_num_recv_frames(); i++){ + _mrb_pool.push_back(usrp_e100_mmap_zero_copy_mrb( + recv_buff + get_recv_frame_size()*i, (*recv_info) + i + )); + } + + //initialize the managed send buffers + for (size_t i = 0; i < get_num_recv_frames(); i++){ + _msb_pool.push_back(usrp_e100_mmap_zero_copy_msb( + send_buff + get_send_frame_size()*i, (*send_info) + i, + get_send_frame_size(), _fd + )); + } } ~usrp_e100_mmap_zero_copy_impl(void){ @@ -97,13 +192,10 @@ public: managed_recv_buffer::sptr get_recv_buff(double timeout){ if (fp_verbose) std::cout << "get_recv_buff: " << _recv_index << std::endl; - - //grab pointers to the info and buffer - ring_buffer_info *info = (*_recv_info) + _recv_index; - void *mem = _recv_buff + _frame_size*_recv_index; + usrp_e100_mmap_zero_copy_mrb &mrb = _mrb_pool[_recv_index]; //poll/wait for a ready frame - if (not (info->flags & RB_USER)){ + if (not mrb.ready()){ for (size_t i = 0; i < poll_breakout; i++){ pollfd pfd; pfd.fd = _fd; @@ -115,18 +207,11 @@ public: return managed_recv_buffer::sptr(); //timed-out for real } found_user_frame: - //the process has claimed the frame - info->flags = RB_USER_PROCESS; - //increment the index for the next call - if (++_recv_index == size_t(_rb_size.num_rx_frames)) _recv_index = 0; + if (++_recv_index == get_num_recv_frames()) _recv_index = 0; //return the managed buffer for this frame - if (fp_verbose) std::cout << " make_recv_buff: " << info->len << std::endl; - return managed_recv_buffer::make_safe( - mem, info->len, - boost::bind(&usrp_e100_mmap_zero_copy_impl::release, this, info) - ); + return mrb.get_new(); } size_t get_num_recv_frames(void) const{ @@ -139,13 +224,10 @@ public: managed_send_buffer::sptr get_send_buff(double timeout){ if (fp_verbose) std::cout << "get_send_buff: " << _send_index << std::endl; - - //grab pointers to the info and buffer - ring_buffer_info *info = (*_send_info) + _send_index; - void *mem = _send_buff + _frame_size*_send_index; + usrp_e100_mmap_zero_copy_msb &msb = _msb_pool[_send_index]; //poll/wait for a ready frame - if (not (info->flags & RB_KERNEL)){ + if (not msb.ready()){ pollfd pfd; pfd.fd = _fd; pfd.events = POLLOUT; @@ -155,14 +237,10 @@ public: } //increment the index for the next call - if (++_send_index == size_t(_rb_size.num_tx_frames)) _send_index = 0; + if (++_send_index == get_num_send_frames()) _send_index = 0; //return the managed buffer for this frame - if (fp_verbose) std::cout << " make_send_buff: " << _frame_size << std::endl; - return managed_send_buffer::make_safe( - mem, _frame_size, - boost::bind(&usrp_e100_mmap_zero_copy_impl::commit, this, info, _1) - ); + return msb.get_new(); } size_t get_num_send_frames(void) const{ @@ -174,21 +252,7 @@ public: } private: - - void release(ring_buffer_info *info){ - if (fp_verbose) std::cout << "recv buff: release" << std::endl; - info->flags = RB_KERNEL; - } - - void commit(ring_buffer_info *info, size_t len){ - if (fp_verbose) std::cout << "send buff: commit " << len << std::endl; - info->len = len; - info->flags = RB_USER; - if (::write(_fd, NULL, 0) < 0){ - std::cerr << UHD_THROW_SITE_INFO("write error") << std::endl; - } - } - + //file descriptor for mmap int _fd; //the mapped memory itself @@ -198,9 +262,9 @@ private: usrp_e_ring_buffer_size_t _rb_size; size_t _frame_size, _map_size; - //pointers to sections in the mapped memory - ring_buffer_info (*_recv_info)[], (*_send_info)[]; - char *_recv_buff, *_send_buff; + //re-usable managed buffers + std::vector<usrp_e100_mmap_zero_copy_mrb> _mrb_pool; + std::vector<usrp_e100_mmap_zero_copy_msb> _msb_pool; //indexes into sub-sections of mapped memory size_t _recv_index, _send_index; diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index 5fa5b4d6d..743528189 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -48,14 +48,19 @@ CHECK_CXX_SOURCE_COMPILES(" IF(HAVE_PTHREAD_SETSCHEDPARAM) MESSAGE(STATUS " Priority scheduling supported through pthread_setschedparam.") - ADD_DEFINITIONS(-DHAVE_PTHREAD_SETSCHEDPARAM) + SET(THREAD_PRIO_DEFS HAVE_PTHREAD_SETSCHEDPARAM) ELSEIF(HAVE_WIN_SETTHREADPRIORITY) MESSAGE(STATUS " Priority scheduling supported through windows SetThreadPriority.") - ADD_DEFINITIONS(-DHAVE_WIN_SETTHREADPRIORITY) + SET(THREAD_PRIO_DEFS HAVE_WIN_SETTHREADPRIORITY) ELSE(HAVE_PTHREAD_SETSCHEDPARAM) MESSAGE(STATUS " Priority scheduling not supported.") ENDIF(HAVE_PTHREAD_SETSCHEDPARAM) +SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp + PROPERTIES COMPILE_DEFINITIONS "${THREAD_PRIO_DEFS}" +) + ######################################################################## # Setup defines for module loading ######################################################################## @@ -68,15 +73,20 @@ CHECK_INCLUDE_FILE_CXX(windows.h HAVE_WINDOWS_H) IF(HAVE_DLFCN_H) MESSAGE(STATUS " Module loading supported through dlopen.") - ADD_DEFINITIONS(-DHAVE_DLFCN_H) + SET(LOAD_MODULES_DEFS HAVE_DLFCN_H) LIBUHD_APPEND_LIBS(${CMAKE_DL_LIBS}) ELSEIF(HAVE_WINDOWS_H) MESSAGE(STATUS " Module loading supported through LoadLibrary.") - ADD_DEFINITIONS(-DHAVE_WINDOWS_H) + SET(LOAD_MODULES_DEFS HAVE_WINDOWS_H) ELSE(HAVE_DLFCN_H) MESSAGE(STATUS " Module loading not supported.") ENDIF(HAVE_DLFCN_H) +SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_CURRENT_SOURCE_DIR}/load_modules.cpp + PROPERTIES COMPILE_DEFINITIONS "${LOAD_MODULES_DEFS}" +) + ######################################################################## # Append sources ######################################################################## diff --git a/host/utils/usrp_n2xx_net_burner.py b/host/utils/usrp_n2xx_net_burner.py index f52a2cbc1..db94d50a4 100755 --- a/host/utils/usrp_n2xx_net_burner.py +++ b/host/utils/usrp_n2xx_net_burner.py @@ -27,6 +27,7 @@ import re import struct import socket import sys +import os.path ######################################################################## # constants @@ -258,6 +259,32 @@ def verify_image(ip, image, addr): else: print "Success." +def read_flash(ip, image, size, addr): + print "Reading image" + readsize = size + readdata = str() + while readsize > 0: + if readsize < FLASH_DATA_PACKET_SIZE: thisreadsize = readsize + else: thisreadsize = FLASH_DATA_PACKET_SIZE + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL, seq(), addr, thisreadsize, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + + if pktid != update_id_t.USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + readdata += data[:thisreadsize] + readsize -= FLASH_DATA_PACKET_SIZE + addr += FLASH_DATA_PACKET_SIZE + + print "Read back %i bytes" % len(readdata) + + #write to disk + f = open(image, 'w') + f.write(readdata) + f.close() + def reset_usrp(ip): out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL, seq(), 0, 0, "") in_pkt = send_and_recv(out_pkt, ip) @@ -299,6 +326,7 @@ def get_options(): parser.add_option("--fw", type="string", help="firmware image path (optional)", default='') parser.add_option("--fpga", type="string", help="fpga image path (optional)", default='') parser.add_option("--reset", action="store_true", help="reset the device after writing", default=False) + parser.add_option("--read", action="store_true", help="read to file instead of write from file", default=False) parser.add_option("--overwrite-safe", action="store_true", help="never ever use this option", default=False) (options, args) = parser.parse_args() @@ -313,11 +341,32 @@ if __name__=='__main__': if not options.fpga and not options.fw and not options.reset: raise Exception, 'Must specify either a firmware image or FPGA image, and/or reset.' - if options.overwrite_safe: + if options.overwrite_safe and not options.read: print("Are you REALLY, REALLY sure you want to overwrite the safe image? This is ALMOST ALWAYS a terrible idea.") print("If your image is faulty, your USRP2+ will become a brick until reprogrammed via JTAG.") response = raw_input("""Type "yes" to continue, or anything else to quit: """) if response != "yes": sys.exit(0) - burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga, reset=options.reset, safe=options.overwrite_safe) + if options.read is True: + if options.fw: + file = options.fw + if os.path.isfile(file): + response = raw_input("File already exists -- overwrite? (y/n) ") + if response != "y": + sys.exit(0) + size = FW_IMAGE_SIZE_BYTES + addr = SAFE_FW_IMAGE_LOCATION_ADDR if options.overwrite_safe else PROD_FW_IMAGE_LOCATION_ADDR + read_flash(options.ip, file, size, addr) + if options.fpga: + file = options.fpga + if os.path.isfile(file): + response = raw_input("File already exists -- overwrite? (y/n) ") + if response != "y": + sys.exit(0) + size = FPGA_IMAGE_SIZE_BYTES + addr = SAFE_FPGA_IMAGE_LOCATION_ADDR if options.overwrite_safe else PROD_FPGA_IMAGE_LOCATION_ADDR + read_flash(options.ip, file, size, addr) + + else: + burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga, reset=options.reset, safe=options.overwrite_safe) |