diff options
61 files changed, 2619 insertions, 1176 deletions
| diff --git a/firmware/microblaze/apps/txrx_uhd.c b/firmware/microblaze/apps/txrx_uhd.c index 45e5ff5fe..99c149d45 100644 --- a/firmware/microblaze/apps/txrx_uhd.c +++ b/firmware/microblaze/apps/txrx_uhd.c @@ -177,7 +177,7 @@ void handle_udp_ctrl_packet(      unsigned char *payload, int payload_len  ){      //printf("Got ctrl packet #words: %d\n", (int)payload_len); -    usrp2_ctrl_data_t *ctrl_data_in = (usrp2_ctrl_data_t *)payload; +    const usrp2_ctrl_data_t *ctrl_data_in = (usrp2_ctrl_data_t *)payload;      uint32_t ctrl_data_in_id = ctrl_data_in->id;      //ensure that the protocol versions match @@ -269,6 +269,10 @@ void handle_udp_ctrl_packet(              printf("error! tried to poke into 0x%x\n", ctrl_data_in->data.poke_args.addr);          }          else switch(ctrl_data_in->data.poke_args.num_bytes){ +        case sizeof(uint64_t): +            *((uint32_t *) ctrl_data_in->data.poke_args.addrhi) = (uint32_t)ctrl_data_in->data.poke_args.datahi; +            //continue to uint32_t for low addr: +          case sizeof(uint32_t):              *((uint32_t *) ctrl_data_in->data.poke_args.addr) = (uint32_t)ctrl_data_in->data.poke_args.data;              break; @@ -287,16 +291,20 @@ void handle_udp_ctrl_packet(      case USRP2_CTRL_ID_PEEK_AT_THIS_REGISTER_FOR_ME_BRO:          switch(ctrl_data_in->data.poke_args.num_bytes){ +        case sizeof(uint64_t): +            ctrl_data_out.data.poke_args.datahi = *((uint32_t *) ctrl_data_in->data.poke_args.addrhi); +            //continue to uint32_t for low addr: +          case sizeof(uint32_t): -            ctrl_data_in->data.poke_args.data = *((uint32_t *) ctrl_data_in->data.poke_args.addr); +            ctrl_data_out.data.poke_args.data = *((uint32_t *) ctrl_data_in->data.poke_args.addr);              break;          case sizeof(uint16_t): -            ctrl_data_in->data.poke_args.data = *((uint16_t *) ctrl_data_in->data.poke_args.addr); +            ctrl_data_out.data.poke_args.data = *((uint16_t *) ctrl_data_in->data.poke_args.addr);              break;          case sizeof(uint8_t): -            ctrl_data_in->data.poke_args.data = *((uint8_t *) ctrl_data_in->data.poke_args.addr); +            ctrl_data_out.data.poke_args.data = *((uint8_t *) ctrl_data_in->data.poke_args.addr);              break;          } diff --git a/fpga/usrp2/serdes/serdes_rx.v b/fpga/usrp2/serdes/serdes_rx.v index afefccaa1..b6688e858 100644 --- a/fpga/usrp2/serdes/serdes_rx.v +++ b/fpga/usrp2/serdes/serdes_rx.v @@ -260,12 +260,13 @@ module serdes_rx     // Internal FIFO, size 9 is 2K, size 10 is 4K Bytes     assign write = eop_i | (error_i & have_space) | (write_d & (state != CRC_CHECK)); - -   fifo_2clock_cascade #(.WIDTH(35),.SIZE(FIFOSIZE)) serdes_rx_fifo +   wire dummy; // avoid warning on unconnected pin +    +   fifo_2clock_cascade #(.WIDTH(36),.SIZE(FIFOSIZE)) serdes_rx_fifo       (.arst(rst), -      .wclk(ser_rx_clk),.datain({error_i,sop_i,eop_i,line_i}),  +      .wclk(ser_rx_clk),.datain({1'b0,error_i,sop_i,eop_i,line_i}),         .src_rdy_i(write), .dst_rdy_o(have_space), .space(fifo_space),  -      .rclk(clk),.dataout({error_o,sop_o,eop_o,line_o}),  +      .rclk(clk),.dataout({dummy,error_o,sop_o,eop_o,line_o}),         .src_rdy_o(wr_ready_o), .dst_rdy_i(wr_ready_i), .occupied(fifo_occupied) );     assign 	       fifo_full = ~have_space;   // Note -- in the wrong clock domain diff --git a/fpga/usrp2/top/Makefile.common b/fpga/usrp2/top/Makefile.common index d1c89fdfb..d0435fa1e 100644 --- a/fpga/usrp2/top/Makefile.common +++ b/fpga/usrp2/top/Makefile.common @@ -42,7 +42,8 @@ clean:  ##################################################  # Dependency Targets  ################################################## -$(ISE_FILE): $(SOURCES) +.SECONDEXPANSION: +$(ISE_FILE): $$(SOURCES) $$(MAKEFILE_LIST)  	@echo $@  	$(ISE_HELPER) "" diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index a8b89d6c5..c60372fb9 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -55,22 +55,28 @@ MACRO(UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG flag have)      ENDIF(${have})  ENDMACRO(UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG) -IF(UNIX) +#select the release build type by default to get optimization flags +IF(NOT CMAKE_BUILD_TYPE) +   SET(CMAKE_BUILD_TYPE "Release") +   MESSAGE(STATUS "Build type not specified: defaulting to release.") +ENDIF(NOT CMAKE_BUILD_TYPE) + +IF(CMAKE_COMPILER_IS_GNUCXX)      UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-Wall      HAVE_WALL)      UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-Wextra    HAVE_WEXTRA)      UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-pedantic  HAVE_PEDANTIC)      UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-ansi      HAVE_ANSI)      #only export symbols that are declared to be part of the uhd api:      UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN) -    UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-O3 HAVE_O3) #have some optimizations -ENDIF(UNIX) +ENDIF(CMAKE_COMPILER_IS_GNUCXX) -IF(WIN32) +IF(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 -ENDIF(WIN32) +    ADD_DEFINITIONS(/arch:SSE2 /G7) #processor optimization flags +ENDIF(MSVC)  ########################################################################  # Setup Boost diff --git a/host/docs/build.rst b/host/docs/build.rst index f5a8dac8d..108d8dc8b 100644 --- a/host/docs/build.rst +++ b/host/docs/build.rst @@ -97,7 +97,10 @@ Generate Makefiles with cmake      cd build      cmake ../ -For a custom prefix, use: cmake -DCMAKE_INSTALL_PREFIX=<prefix> ../ +**Notes:** + +* For a custom prefix, use: cmake -DCMAKE_INSTALL_PREFIX=<prefix> ../ +* On some Fedora 64-bit systems, cmake has trouble finding boost, use: cmake -DBOOST_LIBRARYDIR=/usr/lib64 ../  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Build and install @@ -138,6 +141,7 @@ Generate the project with cmake  Build the project in MSVC  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  * Open the generated project file in MSVC. +* Change the build type from "Debug" to "Release".  * Select the build all target, right click, and choose build.  * Select the install target, right click, and choose build. diff --git a/host/docs/coding.rst b/host/docs/coding.rst index 689667f30..84f9abf3e 100644 --- a/host/docs/coding.rst +++ b/host/docs/coding.rst @@ -5,8 +5,11 @@ UHD - Coding to the API  .. contents:: Table of Contents  ------------------------------------------------------------------------ -Low-Level: The device API +Various API interfaces  ------------------------------------------------------------------------ +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Low-Level: The device API +^^^^^^^^^^^^^^^^^^^^^^^^^^^  A device is an abstraction for hardware that is connected to the host system.  For a USRP, this means that the motherboard and everything on it would be considered to be a "device".  The device API provides ways to: @@ -17,12 +20,12 @@ The device API provides ways to:  * Streaming samples with metadata into and out of the device.  * Set and get properties on the device object. -See the documentation in device.hpp for reference. +See the documentation in *device.hpp* for reference. ------------------------------------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^  High-Level: The simple usrp ------------------------------------------------------------------------- -The goal of the simple usrp is to wrap high level functions around the device properties. +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The goal of the simple usrp API is to wrap high level functions around the device properties.  The simple usrp provides a fat interface to access the most common properties.  The simple usrp provides ways to: @@ -35,14 +38,32 @@ The simple usrp provides ways to:  * Set the usrp time registers.  * Get the underlying device (as discussed above). -It is recommended that users code to the simple_usrp api when possible. -See the documentation in usrp/simple_usrp.hpp for reference. +See the documentation in *usrp/simple_usrp.hpp* for reference. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +High-Level: The mimo usrp +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The mimo usrp API provides a wrapper around a device that represents several motherboards. +This API provides convenience calls just like the simple usrp, +however the calls either work across all channels in the configuration, +or take a channel argument to specify which channel to configure. +The mimo usrp provides ways to: + +* Set and get the sample rate across all channels. +* Issue a stream command across all channels. +* Set the time registers across all channels. +* Set and get individual daughterboard gains. +* Set and get individual daughterboard antennas. +* Tune individual DSPs and daughterboards. +* Get the underlying device (as discussed above). + +See the documentation in *usrp/mimo_usrp.hpp* for reference.  ------------------------------------------------------------------------  Integrating custom hardware  ------------------------------------------------------------------------  Creators of custom hardware can create drivers that use the UHD API. -These drivers can be built as dynamically lodable modules that the UHD will load at runtime. +These drivers can be built as dynamically loadable modules that the UHD will load at runtime.  For a module to be loaded at runtime, it must be found in the UHD_MODULE_PATH environment variable.  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/host/docs/dboards.rst b/host/docs/dboards.rst index e14f49933..9c496ebee 100644 --- a/host/docs/dboards.rst +++ b/host/docs/dboards.rst @@ -23,7 +23,7 @@ The Basic RX and LFRX boards have 3 subdevices:  The boards have no tunable elements or programmable gains.  Though the magic of aliasing, you can down-convert signals -greater than the nyquist rate of the ADC. +greater than the Nyquist rate of the ADC.  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  Basic TX and and LFTX @@ -50,7 +50,7 @@ Recieve Gains: **PGA0**, Range: 0-45dB  ^^^^^^^^^^^^^^^^^^^^^^^^^^^  XCVR 2450  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The XCVR2450 has a non-contiguous tuning range consiting of a high band and a low band. +The XCVR2450 has a non-contiguous tuning range consisting of a high band and a low band.  The high band consists of frequencies between...TODO  Transmit Antennas: **J1** or **J2** @@ -65,11 +65,11 @@ The XCVR2450 uses a common LO for both receive and transmit.  Even though the API allows the RX and TX LOs to be individually set,  a change of one LO setting will be reflected in the other LO setting. -Transmit Gains:  +Transmit Gains:   * **VGA**, Range: 0-30dB   * **BB**, Range: 0-5dB -Recieve Gains:  +Receive Gains:   * **LNA**, Range: 0-30.5dB   * **VGA**, Range: 0-62dB diff --git a/host/docs/general.rst b/host/docs/general.rst index 6b309cba0..90a880c2e 100644 --- a/host/docs/general.rst +++ b/host/docs/general.rst @@ -45,7 +45,7 @@ The list of discovered devices can be narrowed down by specifying device address  Device properties  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Properties of devices attached to your system can be probed with the "uhd_usrp_probe" program. -The usrp probe program contructs an instance of the device and prints out its properties; +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:** diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index 09987b3fa..d3ae1dec7 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -11,8 +11,7 @@ Building firmware and FPGA images  ^^^^^^^^^^^^^^^^^^  FPGA Image  ^^^^^^^^^^^^^^^^^^ -Xilinx ISE 10.1 is required to build the FPGA image for the USRP2 -(newer version of ISE are known to build buggy images). +Xilinx ISE 10.1 and up is required to build the FPGA image for the USRP2.  The build requires that you have a unix-like environment with make.  Make sure that xtclsh from the Xilinx ISE bin directory is in your $PATH. @@ -150,6 +149,40 @@ MAC addresses, control packets, and fast-path settings.  Use wireshark to monitor packets sent to and received from the USRP2.  ------------------------------------------------------------------------ +Addressing the device +------------------------------------------------------------------------ + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Single device configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +A USRP2 can be identified though its IPv4 address or resolvable hostname. +The USRP2 device is referenced through the "addr" key in the device address. +Use this addressing scheme with the *simple_usrp* interface. + +The device address string representation for a USRP2 with IPv4 address 192.168.10.2 + +:: + +    addr=192.168.10.2 + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Soft-MIMO configuration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In a soft-mimo configuration, each USRP2 must have a unique IPv4 address (per computer) +and be attached to its own dedicated network port. +The value for the addr key is a white-space separated list +of IPv4 addresses or resolvable hostnames. +The first address in the list will represent channel 0, +the second channel 1, and so on... +Use this addressing scheme with the *mimo_usrp* interface. + +The device address string representation for 2 USRP2s with IPv4 addresses 192.168.10.2 and 192.168.20.2 +:: + +    addr=192.168.10.2 192.168.20.2 + + +------------------------------------------------------------------------  Resize the send and receive buffers  ------------------------------------------------------------------------  It may be useful increase the size of the socket buffers to @@ -158,10 +191,27 @@ buffer incoming samples faster than they can be processed.  However, if you application cannot process samples fast enough,  no amount of buffering can save you. +By default, the UHD will try to request a reasonably large buffer size for both send and receive. +A warning will be printed on instantiation if the actual buffer size is insufficient. +See the OS specific notes below: + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +OS specific notes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +On linux, the maximum buffer sizes are capped by the sysctl values +**net.core.rmem_max** and **net.core.wmem_max**. +To change the maximum values, run the following commands: +:: + +    sudo sysctl -w net.core.rmem_max=<new value> +    sudo sysctl -w net.core.wmem_max=<new value> + +Set the values permanently by editing */etc/sysctl.conf* +  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Device address params  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To set the size of the buffers, +To manually set the size of the buffers,  the usrp2 will accept two optional parameters in the device address.  Each parameter will accept a numeric value for the number of bytes. @@ -172,14 +222,3 @@ Example, set the args string to the following:  ::      addr=192.168.10.2, recv_buff_size=100e6 - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -OS specific notes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On linux, the maximum buffer sizes are capped by the sysctl values -**net.core.rmem_max** and **net.core.wmem_max**. -To change the maximum values, run the following commands: -:: - -    sudo sysctl -w net.core.rmem_max=<new value> -    sudo sysctl -w net.core.wmem_max=<new value> diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index a537c0901..5071b114f 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -15,10 +15,18 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  # +ADD_EXECUTABLE(benchmark_rx_rate benchmark_rx_rate.cpp) +TARGET_LINK_LIBRARIES(benchmark_rx_rate uhd) +  ADD_EXECUTABLE(rx_timed_samples rx_timed_samples.cpp)  TARGET_LINK_LIBRARIES(rx_timed_samples uhd) -INSTALL(TARGETS rx_timed_samples RUNTIME DESTINATION ${PKG_DATA_DIR}/examples)  ADD_EXECUTABLE(tx_timed_samples tx_timed_samples.cpp)  TARGET_LINK_LIBRARIES(tx_timed_samples uhd) -INSTALL(TARGETS tx_timed_samples RUNTIME DESTINATION ${PKG_DATA_DIR}/examples) + +INSTALL(TARGETS +    benchmark_rx_rate +    rx_timed_samples +    tx_timed_samples +    RUNTIME DESTINATION ${PKG_DATA_DIR}/examples +) diff --git a/host/examples/benchmark_rx_rate.cpp b/host/examples/benchmark_rx_rate.cpp new file mode 100644 index 000000000..a913261eb --- /dev/null +++ b/host/examples/benchmark_rx_rate.cpp @@ -0,0 +1,143 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/simple_usrp.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +static inline void test_device( +    uhd::usrp::simple_usrp::sptr sdev, +    double rx_rate_sps, +    double duration_secs +){ +    uhd::device::sptr dev = sdev->get_device(); +    std::cout << boost::format("Testing receive rate %f Msps (%f second run)") % (rx_rate_sps/1e6) % duration_secs << std::endl; + +    //allocate recv buffer and metatdata +    uhd::rx_metadata_t md; +    std::vector<std::complex<float> > buff(dev->get_max_recv_samps_per_packet()); + +    //declare status variables +    bool got_first_packet = false; +    size_t total_recv_packets = 0; +    size_t total_lost_samples = 0; +    size_t total_recv_samples = 0; +    uhd::time_spec_t initial_time_spec; +    uhd::time_spec_t next_expected_time_spec; +     +    sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); +    do { +        size_t num_rx_samps = dev->recv( +            &buff.front(), buff.size(), md, +            uhd::io_type_t::COMPLEX_FLOAT32, +            uhd::device::RECV_MODE_ONE_PACKET +        ); +        if (num_rx_samps == 0){ +            std::cerr << "Unexpected timeout on recv, exit test..." << std::endl; +            return; +        } +        if (not md.has_time_spec){ +            std::cerr << "Metadata missing time spec, exit test..." << std::endl; +            return; +        } + +        total_recv_samples += num_rx_samps; +        total_recv_packets++; + +        if (not got_first_packet){ +            initial_time_spec = md.time_spec; +            next_expected_time_spec = initial_time_spec; +            got_first_packet = true; +        } + +        total_lost_samples += boost::math::iround(rx_rate_sps*(md.time_spec - next_expected_time_spec).get_real_secs()); +        next_expected_time_spec = md.time_spec + uhd::time_spec_t(0, num_rx_samps, rx_rate_sps); + +    } while((next_expected_time_spec - initial_time_spec) < uhd::time_spec_t(duration_secs)); +    sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); +     +    //flush the buffers +    while(dev->recv( +        &buff.front(), buff.size(), md, +        uhd::io_type_t::COMPLEX_FLOAT32, +        uhd::device::RECV_MODE_ONE_PACKET +    )){ +        /* NOP */ +    }; + +    //print a summary +    std::cout << std::endl; //go to newline, recv may spew SXSYSZ... +    std::cout << boost::format("    Received packets: %d") % total_recv_packets << std::endl; +    std::cout << boost::format("    Received samples: %d") % total_recv_samples << std::endl; +    std::cout << boost::format("    Lost samples: %d") % total_lost_samples << std::endl; +    size_t packets_lost = boost::math::iround(double(total_lost_samples)/dev->get_max_recv_samps_per_packet()); +    std::cout << boost::format("    Lost packets: %d (approximate)") % packets_lost << std::endl; +    double actual_rx_rate_sps = (total_recv_samples*rx_rate_sps)/(total_recv_samples+total_lost_samples); +    std::cout << boost::format("    Sustained receive rate: %f Msps") % (actual_rx_rate_sps/1e6) << std::endl; +    std::cout << std::endl << std::endl; +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ +    uhd::set_thread_priority_safe(); + +    //variables to be set by po +    std::string args; +    double duration; + +    //setup the program options +    po::options_description desc("Allowed options"); +    desc.add_options() +        ("help", "help message") +        ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") +        ("duration", po::value<double>(&duration)->default_value(10.0), "duration for each test in seconds") +    ; +    po::variables_map vm; +    po::store(po::parse_command_line(argc, argv, desc), vm); +    po::notify(vm); + +    //print the help message +    if (vm.count("help")){ +        std::cout << boost::format("UHD Benchmark RX Rate %s") % desc << std::endl; +        return ~0; +    } + +    //create a usrp device +    std::cout << std::endl; +    std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; +    uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args); +    std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl; + +    sdev->set_rx_rate(500e3); //initial rate +    while(true){ +        double rate = sdev->get_rx_rate(); +        test_device(sdev, rate, duration); +        sdev->set_rx_rate(rate*2); //double the rate +        if (sdev->get_rx_rate() == rate) break; +    } + +    //finished +    std::cout << std::endl << "Done!" << std::endl << std::endl; + +    return 0; +} diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index b4aa7b9e8..9ff8772bc 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -21,7 +21,6 @@  #include <boost/program_options.hpp>  #include <boost/format.hpp>  #include <iostream> -#include <fstream>  #include <complex>  namespace po = boost::program_options; @@ -30,8 +29,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      uhd::set_thread_priority_safe();      //variables to be set by po -    std::string args, file_path; -    int seconds_in_future; +    std::string args; +    time_t seconds_in_future;      size_t total_num_samps;      double rx_rate, freq; @@ -40,11 +39,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      desc.add_options()          ("help", "help message")          ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") -        ("secs", po::value<int>(&seconds_in_future)->default_value(3), "number of seconds in the future to receive") +        ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to receive")          ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to receive")          ("rxrate", po::value<double>(&rx_rate)->default_value(100e6/16), "rate of incoming samples")          ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") -        ("file", po::value<std::string>(&file_path)->default_value(""), "write samps to output file")      ;      po::variables_map vm;      po::store(po::parse_command_line(argc, argv, desc), vm); @@ -61,7 +59,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl;      uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args);      uhd::device::sptr dev = sdev->get_device(); -    std::cout << boost::format("Using Device: %s") % sdev->get_name() << std::endl; +    std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl;      //set properties on the device      std::cout << boost::format("Setting RX Rate: %f Msps...") % (rx_rate/1e6) << std::endl; @@ -69,7 +67,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl;      std::cout << boost::format("Setting device timestamp to 0...") << std::endl;      sdev->set_rx_freq(freq); -    sdev->set_time_now(uhd::time_spec_t(0)); +    sdev->set_time_now(uhd::time_spec_t(0.0));      //setup streaming      std::cout << std::endl; @@ -81,20 +79,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      stream_cmd.time_spec = uhd::time_spec_t(seconds_in_future);      sdev->issue_stream_cmd(stream_cmd); -    std::ofstream outfile; -    if(file_path != "") outfile.open (file_path.c_str(), std::ios::out | std::ios::binary); - -    //setup recv buffer, io type, and metadata for recv -    uhd::rx_metadata_t md; -    uhd::io_type_t io_type(uhd::io_type_t::COMPLEX_FLOAT32); -    std::vector<std::complex<float> > recv_mem(dev->get_max_recv_samps_per_packet()); -    boost::asio::mutable_buffer buff(boost::asio::buffer(recv_mem)); -      //loop until total number of samples reached      size_t num_acc_samps = 0; //number of accumulated samples      while(num_acc_samps < total_num_samps){ +        uhd::rx_metadata_t md; +        std::vector<std::complex<float> > buff(dev->get_max_recv_samps_per_packet());          size_t num_rx_samps = dev->recv( -            buff, md, io_type, +            &buff.front(), buff.size(), md, +            uhd::io_type_t::COMPLEX_FLOAT32,              uhd::device::RECV_MODE_ONE_PACKET          );          if (num_rx_samps == 0 and num_acc_samps > 0){ @@ -103,17 +95,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){          }          if (num_rx_samps == 0) continue; //wait for packets with contents -        std::cout << boost::format("Got packet: %u samples, %u secs, %u nsecs") -            % num_rx_samps % md.time_spec.secs % md.time_spec.nsecs << std::endl; - -        if(file_path != "") outfile.write(boost::asio::buffer_cast<const char *>(buff), num_rx_samps*io_type.size); +        std::cout << boost::format("Got packet: %u samples, %u full secs, %f frac secs") +            % num_rx_samps % md.time_spec.get_full_secs() % md.time_spec.get_frac_secs() << std::endl;          num_acc_samps += num_rx_samps;      }      //finished      std::cout << std::endl << "Done!" << std::endl << std::endl; -    if(file_path != "") outfile.close();      return 0;  } diff --git a/host/examples/tx_timed_samples.cpp b/host/examples/tx_timed_samples.cpp index 28fd2ee67..4226aa4c8 100644 --- a/host/examples/tx_timed_samples.cpp +++ b/host/examples/tx_timed_samples.cpp @@ -30,7 +30,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //variables to be set by po      std::string args; -    int seconds_in_future; +    time_t seconds_in_future;      size_t total_num_samps;      double tx_rate, freq;      float ampl; @@ -40,7 +40,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      desc.add_options()          ("help", "help message")          ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") -        ("secs", po::value<int>(&seconds_in_future)->default_value(3), "number of seconds in the future to transmit") +        ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to transmit")          ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to transmit")          ("txrate", po::value<double>(&tx_rate)->default_value(100e6/16), "rate of outgoing samples")          ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") @@ -61,7 +61,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl;      uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args);      uhd::device::sptr dev = sdev->get_device(); -    std::cout << boost::format("Using Device: %s") % sdev->get_name() << std::endl; +    std::cout << boost::format("Using Device: %s") % sdev->get_pp_string() << std::endl;      //set properties on the device      std::cout << boost::format("Setting TX Rate: %f Msps...") % (tx_rate/1e6) << std::endl; @@ -69,7 +69,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      std::cout << boost::format("Actual TX Rate: %f Msps...") % (sdev->get_tx_rate()/1e6) << std::endl;      std::cout << boost::format("Setting device timestamp to 0...") << std::endl;      sdev->set_tx_freq(freq); -    sdev->set_time_now(uhd::time_spec_t(0)); +    sdev->set_time_now(uhd::time_spec_t(0.0));      //data to send      std::vector<std::complex<float> > buff(total_num_samps, std::complex<float>(ampl, ampl)); @@ -81,8 +81,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //send the entire buffer, let the driver handle fragmentation      size_t num_tx_samps = dev->send( -        boost::asio::buffer(buff), -        md, uhd::io_type_t::COMPLEX_FLOAT32, +        &buff.front(), buff.size(), md, +        uhd::io_type_t::COMPLEX_FLOAT32,          uhd::device::SEND_MODE_FULL_BUFF      );      std::cout << std::endl << boost::format("Sent %d samples") % num_tx_samps << std::endl; diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt index d63062032..c0339dbd3 100644 --- a/host/include/uhd/CMakeLists.txt +++ b/host/include/uhd/CMakeLists.txt @@ -24,6 +24,7 @@ ADD_SUBDIRECTORY(utils)  INSTALL(FILES      config.hpp      device.hpp +    device.ipp      wax.hpp      DESTINATION ${INCLUDE_DIR}/uhd  ) diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index b23a4dc00..013354d33 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -78,7 +78,7 @@  #endif // UHD_DLL  // Define force inline macro -#ifdef BOOST_MSVC +#if defined(BOOST_MSVC)      #define UHD_INLINE __forceinline  #elif defined(__GNUG__) && __GNUG__ >= 4      #define UHD_INLINE inline __attribute__((always_inline)) @@ -86,4 +86,13 @@      #define UHD_INLINE inline  #endif +// Define deprecated attribute macro +#if defined(BOOST_MSVC) +    #define UHD_DEPRECATED __declspec(deprecated) +#elif defined(__GNUG__) && __GNUG__ >= 4 +    #define UHD_DEPRECATED __attribute__ ((deprecated)) +#else +    #define UHD_DEPRECATED +#endif +  #endif /* INCLUDED_UHD_CONFIG_HPP */ diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp index d3e9015c4..a0c29f2e6 100644 --- a/host/include/uhd/device.hpp +++ b/host/include/uhd/device.hpp @@ -27,6 +27,7 @@  #include <boost/shared_ptr.hpp>  #include <boost/function.hpp>  #include <boost/asio/buffer.hpp> +#include <vector>  namespace uhd{ @@ -97,7 +98,7 @@ public:      };      /*! -     * Send a buffer containing IF data with its metadata. +     * Send buffers containing IF data described by the metadata.       *       * Send handles fragmentation as follows:       * If the buffer has more samples than the maximum per packet, @@ -108,23 +109,42 @@ public:       * Fragmentation only applies in the full buffer send mode.       *       * This is a blocking call and will not return until the number -     * of samples returned have been read out of the buffer. +     * of samples returned have been read out of each buffer.       * -     * \param buff a buffer pointing to some read-only memory +     * \param buffs a vector of read-only memory containing IF data +     * \param nsamps_per_buff the number of samples to send, per buffer       * \param metadata data describing the buffer's contents       * \param io_type the type of data loaded in the buffer       * \param send_mode tells send how to unload the buffer       * \return the number of samples sent       */      virtual size_t send( -        const boost::asio::const_buffer &buff, +        const std::vector<const void *> &buffs, +        size_t nsamps_per_buff,          const tx_metadata_t &metadata,          const io_type_t &io_type,          send_mode_t send_mode      ) = 0;      /*! -     * Receive a buffer containing IF data and its metadata. +     * Convenience wrapper for send that takes a single buffer. +     */ +    size_t send( +        const void *buff, +        size_t nsamps_per_buff, +        const tx_metadata_t &metadata, +        const io_type_t &io_type, +        send_mode_t send_mode +    ); + +    //! Deprecated +    size_t send( +        const boost::asio::const_buffer &, const tx_metadata_t &, +        const io_type_t &, send_mode_t send_mode +    ); + +    /*! +     * Receive buffers containing IF data described by the metadata.       *       * Receive handles fragmentation as follows:       * If the buffer has insufficient space to hold all samples @@ -138,7 +158,7 @@ public:       * See the rx metadata fragment flags and offset fields for details.       *       * This is a blocking call and will not return until the number -     * of samples returned have been written into the buffer. +     * of samples returned have been written into each buffer.       * However, a call to receive may timeout and return zero samples.       * The timeout duration is decided by the underlying transport layer.       * The caller should assume that the call to receive will not return @@ -146,23 +166,42 @@ public:       * and that the timeout duration is reasonably tuned for performance.       *       * When using the full buffer recv mode, the metadata only applies -     * to the first packet received and written into the recv buffer. +     * to the first packet received and written into the recv buffers.       * Use the one packet recv mode to get per packet metadata.       * -     * \param buff the buffer to fill with IF data +     * \param buffs a vector of writable memory to fill with IF data +     * \param nsamps_per_buff the size of each buffer in number of samples       * \param metadata data to fill describing the buffer       * \param io_type the type of data to fill into the buffer       * \param recv_mode tells recv how to load the buffer       * \return the number of samples received       */      virtual size_t recv( -        const boost::asio::mutable_buffer &buff, +        const std::vector<void *> &buffs, +        size_t nsamps_per_buff,          rx_metadata_t &metadata,          const io_type_t &io_type,          recv_mode_t recv_mode      ) = 0;      /*! +     * Convenience wrapper for recv that takes a single buffer. +     */ +    size_t recv( +        void *buff, +        size_t nsamps_per_buff, +        rx_metadata_t &metadata, +        const io_type_t &io_type, +        recv_mode_t recv_mode +    ); + +    //! Deprecated +    size_t recv( +        const boost::asio::mutable_buffer &, rx_metadata_t &, +        const io_type_t &, recv_mode_t +    ); + +    /*!       * Get the maximum number of samples per packet on send.       * \return the number of samples       */ @@ -178,4 +217,6 @@ public:  } //namespace uhd +#include <uhd/device.ipp> +  #endif /* INCLUDED_UHD_DEVICE_HPP */ diff --git a/host/include/uhd/device.ipp b/host/include/uhd/device.ipp new file mode 100644 index 000000000..c38a2e43e --- /dev/null +++ b/host/include/uhd/device.ipp @@ -0,0 +1,79 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_DEVICE_IPP +#define INCLUDED_UHD_DEVICE_IPP + +namespace uhd{ + +    UHD_INLINE size_t device::send( +        const void *buff, +        size_t nsamps_per_buff, +        const tx_metadata_t &metadata, +        const io_type_t &io_type, +        send_mode_t send_mode +    ){ +        return this->send( +            std::vector<const void *>(1, buff), +            nsamps_per_buff, metadata, +            io_type, send_mode +        ); +    } + +    UHD_DEPRECATED UHD_INLINE size_t device::send( +        const boost::asio::const_buffer &buff, +        const tx_metadata_t &metadata, +        const io_type_t &io_type, +        send_mode_t send_mode +    ){ +        return this->send( +            boost::asio::buffer_cast<const void *>(buff), +            boost::asio::buffer_size(buff)/io_type.size, +            metadata, io_type, send_mode +        ); +    } + +    UHD_INLINE size_t device::recv( +        void *buff, +        size_t nsamps_per_buff, +        rx_metadata_t &metadata, +        const io_type_t &io_type, +        recv_mode_t recv_mode +    ){ +        return this->recv( +            std::vector<void *>(1, buff), +            nsamps_per_buff, metadata, +            io_type, recv_mode +        ); +    } + +    UHD_DEPRECATED UHD_INLINE size_t device::recv( +        const boost::asio::mutable_buffer &buff, +        rx_metadata_t &metadata, +        const io_type_t &io_type, +        recv_mode_t recv_mode +    ){ +        return this->recv( +            boost::asio::buffer_cast<void *>(buff), +            boost::asio::buffer_size(buff)/io_type.size, +            metadata, io_type, recv_mode +        ); +    } + +} //namespace uhd + +#endif /* INCLUDED_UHD_DEVICE_IPP */ diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index 23a4aae94..93e9a6485 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -18,12 +18,14 @@  INSTALL(FILES      alignment_buffer.hpp +    alignment_buffer.ipp      bounded_buffer.hpp +    bounded_buffer.ipp      convert_types.hpp      if_addrs.hpp      udp_simple.hpp      udp_zero_copy.hpp -    vrt.hpp +    vrt_if_packet.hpp      zero_copy.hpp      DESTINATION ${INCLUDE_DIR}/uhd/transport  ) diff --git a/host/include/uhd/transport/alignment_buffer.hpp b/host/include/uhd/transport/alignment_buffer.hpp index dc6ccc3ed..29ba74efc 100644 --- a/host/include/uhd/transport/alignment_buffer.hpp +++ b/host/include/uhd/transport/alignment_buffer.hpp @@ -19,16 +19,14 @@  #define INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP  #include <uhd/config.hpp> -#include <uhd/transport/bounded_buffer.hpp> -#include <boost/thread/condition_variable.hpp> +#include <uhd/transport/bounded_buffer.hpp> //time_duration_t  #include <boost/shared_ptr.hpp> -#include <utility>  #include <vector>  namespace uhd{ namespace transport{      /*! -     * Imlement a templated alignment buffer: +     * Implement a templated alignment buffer:       * Used for aligning asynchronously pushed elements with matching ids.       */      template <typename elem_type, typename seq_type> class alignment_buffer{ @@ -40,9 +38,7 @@ namespace uhd{ namespace transport{           * \param capacity the maximum elements per index           * \param width the number of elements to align           */ -        static sptr make(size_t capacity, size_t width){ -            return sptr(new alignment_buffer(capacity, width)); -        } +        static sptr make(size_t capacity, size_t width);          /*!           * Push an element with sequence id into the buffer at index. @@ -51,13 +47,11 @@ namespace uhd{ namespace transport{           * \param index the buffer index           * \return true if the element fit without popping for space           */ -        UHD_INLINE bool push_with_pop_on_full( +        virtual bool push_with_pop_on_full(              const elem_type &elem,              const seq_type &seq,              size_t index -        ){ -            return _buffs[index]->push_with_pop_on_full(buff_contents_type(elem, seq)); -        } +        ) = 0;          /*!           * Pop an aligned set of elements from this alignment buffer. @@ -65,70 +59,14 @@ namespace uhd{ namespace transport{           * \param time the timeout time           * \return false when the operation times out           */ -        template <typename elems_type, typename time_type> -        bool pop_elems_with_timed_wait(elems_type &elems, const time_type &time){ -            buff_contents_type buff_contents_tmp; -            std::list<size_t> indexes_to_do(_all_indexes); - -            //do an initial pop to load an initial sequence id -            size_t index = indexes_to_do.front(); -            if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; -            elems[index] = buff_contents_tmp.first; -            seq_type expected_seq_id = buff_contents_tmp.second; -            indexes_to_do.pop_front(); - -            //get an aligned set of elements from the buffers: -            while(indexes_to_do.size() != 0){ -                //pop an element off for this index -                index = indexes_to_do.front(); -                if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; - -                //if the sequence id matches: -                //  store the popped element into the output, -                //  remove this index from the list and continue -                if (buff_contents_tmp.second == expected_seq_id){ -                    elems[index] = buff_contents_tmp.first; -                    indexes_to_do.pop_front(); -                    continue; -                } - -                //if the sequence id is older: -                //  continue with the same index to try again -                if (buff_contents_tmp.second < expected_seq_id){ -                    continue; -                } - -                //if the sequence id is newer: -                //  store the popped element into the output, -                //  add all other indexes back into the list -                if (buff_contents_tmp.second > expected_seq_id){ -                    elems[index] = buff_contents_tmp.first; -                    expected_seq_id = buff_contents_tmp.second; -                    indexes_to_do = _all_indexes; -                    indexes_to_do.remove(index); -                    continue; -                } -            } -            return true; -        } - -    private: -        //a vector of bounded buffers for each index -        typedef std::pair<elem_type, seq_type> buff_contents_type; -        typedef bounded_buffer<buff_contents_type> bounded_buffer_type; -        typedef boost::shared_ptr<bounded_buffer_type> bounded_buffer_sptr; -        std::vector<bounded_buffer_sptr> _buffs; -        std::list<size_t> _all_indexes; - -        //private constructor -        alignment_buffer(size_t capacity, size_t width){ -            for (size_t i = 0; i < width; i++){ -                _buffs.push_back(bounded_buffer_type::make(capacity)); -                _all_indexes.push_back(i); -            } -        } +        virtual bool pop_elems_with_timed_wait( +            std::vector<elem_type> &elems, +            const time_duration_t &time +        ) = 0;      };  }} //namespace +#include <uhd/transport/alignment_buffer.ipp> +  #endif /* INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP */ diff --git a/host/include/uhd/transport/alignment_buffer.ipp b/host/include/uhd/transport/alignment_buffer.ipp new file mode 100644 index 000000000..ed7cfd26c --- /dev/null +++ b/host/include/uhd/transport/alignment_buffer.ipp @@ -0,0 +1,140 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_IPP +#define INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_IPP + +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/thread/condition_variable.hpp> +#include <utility> + +namespace uhd{ namespace transport{ namespace{ /*anon*/ + +    /*! +     * Imlement a templated alignment buffer: +     * Used for aligning asynchronously pushed elements with matching ids. +     */ +    template <typename elem_type, typename seq_type> +    class alignment_buffer_impl : public alignment_buffer<elem_type, seq_type>{ +    public: + +        alignment_buffer_impl(size_t capacity, size_t width) : _last_seqs(width){ +            for (size_t i = 0; i < width; i++){ +                _buffs.push_back(bounded_buffer<buff_contents_type>::make(capacity)); +                _all_indexes.push_back(i); +            } +            _there_was_a_clear = false; +        } + +        UHD_INLINE bool push_with_pop_on_full( +            const elem_type &elem, +            const seq_type &seq, +            size_t index +        ){ +            //clear the buffer for this index if the seqs are mis-ordered +            if (seq < _last_seqs[index]){ +                _buffs[index]->clear(); +                _there_was_a_clear = true; +            } _last_seqs[index] = seq; +            return _buffs[index]->push_with_pop_on_full(buff_contents_type(elem, seq)); +        } + +        UHD_INLINE bool pop_elems_with_timed_wait( +            std::vector<elem_type> &elems, +            const time_duration_t &time +        ){ +            buff_contents_type buff_contents_tmp; +            std::list<size_t> indexes_to_do(_all_indexes); + +            //do an initial pop to load an initial sequence id +            size_t index = indexes_to_do.front(); +            if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; +            elems[index] = buff_contents_tmp.first; +            seq_type expected_seq_id = buff_contents_tmp.second; +            indexes_to_do.pop_front(); + +            //get an aligned set of elements from the buffers: +            while(indexes_to_do.size() != 0){ + +                //respond to a clear by starting from scratch +                if(_there_was_a_clear){ +                    _there_was_a_clear = false; +                    indexes_to_do = _all_indexes; +                    index = indexes_to_do.front(); +                    if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; +                    elems[index] = buff_contents_tmp.first; +                    expected_seq_id = buff_contents_tmp.second; +                    indexes_to_do.pop_front(); +                } + +                //pop an element off for this index +                index = indexes_to_do.front(); +                if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; + +                //if the sequence id matches: +                //  store the popped element into the output, +                //  remove this index from the list and continue +                if (buff_contents_tmp.second == expected_seq_id){ +                    elems[index] = buff_contents_tmp.first; +                    indexes_to_do.pop_front(); +                    continue; +                } + +                //if the sequence id is older: +                //  continue with the same index to try again +                if (buff_contents_tmp.second < expected_seq_id){ +                    continue; +                } + +                //if the sequence id is newer: +                //  store the popped element into the output, +                //  add all other indexes back into the list +                if (buff_contents_tmp.second > expected_seq_id){ +                    elems[index] = buff_contents_tmp.first; +                    expected_seq_id = buff_contents_tmp.second; +                    indexes_to_do = _all_indexes; +                    indexes_to_do.remove(index); +                    continue; +                } +            } +            return true; +        } + +    private: +        //a vector of bounded buffers for each index +        typedef std::pair<elem_type, seq_type> buff_contents_type; +        std::vector<typename bounded_buffer<buff_contents_type>::sptr> _buffs; +        std::vector<seq_type> _last_seqs; +        std::list<size_t> _all_indexes; +        bool _there_was_a_clear; +    }; + +}}} //namespace + +namespace uhd{ namespace transport{ + +    template <typename elem_type, typename seq_type> +    typename alignment_buffer<elem_type, seq_type>::sptr +    alignment_buffer<elem_type, seq_type>::make(size_t capacity, size_t width){ +        return typename alignment_buffer<elem_type, seq_type>::sptr( +            new alignment_buffer_impl<elem_type, seq_type>(capacity, width) +        ); +    } + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_IPP */ diff --git a/host/include/uhd/transport/bounded_buffer.hpp b/host/include/uhd/transport/bounded_buffer.hpp index baecd6382..d1deece96 100644 --- a/host/include/uhd/transport/bounded_buffer.hpp +++ b/host/include/uhd/transport/bounded_buffer.hpp @@ -19,15 +19,16 @@  #define INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_HPP  #include <uhd/config.hpp> -#include <boost/bind.hpp>  #include <boost/shared_ptr.hpp> -#include <boost/circular_buffer.hpp> -#include <boost/thread/condition.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp>  namespace uhd{ namespace transport{ +    //! typedef for the time duration type for wait operations +    typedef boost::posix_time::time_duration time_duration_t; +      /*! -     * Imlement a templated bounded buffer: +     * Implement a templated bounded buffer:       * Used for passing elements between threads in a producer-consumer model.       * The bounded buffer implemented waits and timed waits with condition variables.       * The pop operation blocks on the bounded_buffer to become non empty. @@ -41,9 +42,7 @@ namespace uhd{ namespace transport{           * Make a new bounded buffer object.           * \param capacity the bounded_buffer capacity           */ -        static sptr make(size_t capacity){ -            return sptr(new bounded_buffer(capacity)); -        } +        static sptr make(size_t capacity);          /*!           * Push a new element into the bounded buffer. @@ -52,35 +51,14 @@ namespace uhd{ namespace transport{           * \param elem the new element to push           * \return true if the element fit without popping for space           */ -        UHD_INLINE bool push_with_pop_on_full(const elem_type &elem){ -            boost::unique_lock<boost::mutex> lock(_mutex); -            if(_buffer.full()){ -                _buffer.pop_back(); -                _buffer.push_front(elem); -                lock.unlock(); -                _empty_cond.notify_one(); -                return false; -            } -            else{ -                _buffer.push_front(elem); -                lock.unlock(); -                _empty_cond.notify_one(); -                return true; -            } -        } +        virtual bool push_with_pop_on_full(const elem_type &elem) = 0;          /*!           * Push a new element into the bounded_buffer.           * Wait until the bounded_buffer becomes non-full.           * \param elem the new element to push           */ -        UHD_INLINE void push_with_wait(const elem_type &elem){ -            boost::unique_lock<boost::mutex> lock(_mutex); -            _full_cond.wait(lock, boost::bind(&bounded_buffer<elem_type>::not_full, this)); -            _buffer.push_front(elem); -            lock.unlock(); -            _empty_cond.notify_one(); -        } +        virtual void push_with_wait(const elem_type &elem) = 0;          /*!           * Push a new element into the bounded_buffer. @@ -89,28 +67,14 @@ namespace uhd{ namespace transport{           * \param time the timeout time           * \return false when the operation times out           */ -        template<typename time_type> UHD_INLINE -        bool push_with_timed_wait(const elem_type &elem, const time_type &time){ -            boost::unique_lock<boost::mutex> lock(_mutex); -            if (not _full_cond.timed_wait(lock, time, boost::bind(&bounded_buffer<elem_type>::not_full, this))) return false; -            _buffer.push_front(elem); -            lock.unlock(); -            _empty_cond.notify_one(); -            return true; -        } +        virtual bool push_with_timed_wait(const elem_type &elem, const time_duration_t &time) = 0;          /*!           * Pop an element from the bounded_buffer.           * Wait until the bounded_buffer becomes non-empty.           * \param elem the element reference pop to           */ -        UHD_INLINE void pop_with_wait(elem_type &elem){ -            boost::unique_lock<boost::mutex> lock(_mutex); -            _empty_cond.wait(lock, boost::bind(&bounded_buffer<elem_type>::not_empty, this)); -            elem = _buffer.back(); _buffer.pop_back(); -            lock.unlock(); -            _full_cond.notify_one(); -        } +        virtual void pop_with_wait(elem_type &elem) = 0;          /*!           * Pop an element from the bounded_buffer. @@ -119,28 +83,16 @@ namespace uhd{ namespace transport{           * \param time the timeout time           * \return false when the operation times out           */ -        template<typename time_type> UHD_INLINE -        bool pop_with_timed_wait(elem_type &elem, const time_type &time){ -            boost::unique_lock<boost::mutex> lock(_mutex); -            if (not _empty_cond.timed_wait(lock, time, boost::bind(&bounded_buffer<elem_type>::not_empty, this))) return false; -            elem = _buffer.back(); _buffer.pop_back(); -            lock.unlock(); -            _full_cond.notify_one(); -            return true; -        } - -    private: -        boost::mutex _mutex; -        boost::condition _empty_cond, _full_cond; -        boost::circular_buffer<elem_type> _buffer; - -        bool not_full(void) const{return not _buffer.full();} -        bool not_empty(void) const{return not _buffer.empty();} +        virtual bool pop_with_timed_wait(elem_type &elem, const time_duration_t &time) = 0; -        //private constructor -        bounded_buffer(size_t capacity) : _buffer(capacity){} +        /*! +         * Clear all elements from the bounded_buffer. +         */ +        virtual void clear(void) = 0;      };  }} //namespace +#include <uhd/transport/bounded_buffer.ipp> +  #endif /* INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_HPP */ diff --git a/host/include/uhd/transport/bounded_buffer.ipp b/host/include/uhd/transport/bounded_buffer.ipp new file mode 100644 index 000000000..e106e229e --- /dev/null +++ b/host/include/uhd/transport/bounded_buffer.ipp @@ -0,0 +1,112 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_IPP +#define INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_IPP + +#include <boost/bind.hpp> +#include <boost/circular_buffer.hpp> +#include <boost/thread/condition.hpp> + +namespace uhd{ namespace transport{ namespace{ /*anon*/ + +    template <typename elem_type> +    class bounded_buffer_impl : public bounded_buffer<elem_type>{ +    public: + +        bounded_buffer_impl(size_t capacity) : _buffer(capacity){ +            /* NOP */ +        } + +        UHD_INLINE bool push_with_pop_on_full(const elem_type &elem){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            if(_buffer.full()){ +                _buffer.pop_back(); +                _buffer.push_front(elem); +                lock.unlock(); +                _empty_cond.notify_one(); +                return false; +            } +            else{ +                _buffer.push_front(elem); +                lock.unlock(); +                _empty_cond.notify_one(); +                return true; +            } +        } + +        UHD_INLINE void push_with_wait(const elem_type &elem){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            _full_cond.wait(lock, boost::bind(&bounded_buffer_impl<elem_type>::not_full, this)); +            _buffer.push_front(elem); +            lock.unlock(); +            _empty_cond.notify_one(); +        } + +        bool push_with_timed_wait(const elem_type &elem, const time_duration_t &time){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            if (not _full_cond.timed_wait(lock, time, boost::bind(&bounded_buffer_impl<elem_type>::not_full, this))) return false; +            _buffer.push_front(elem); +            lock.unlock(); +            _empty_cond.notify_one(); +            return true; +        } + +        UHD_INLINE void pop_with_wait(elem_type &elem){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            _empty_cond.wait(lock, boost::bind(&bounded_buffer_impl<elem_type>::not_empty, this)); +            elem = _buffer.back(); _buffer.pop_back(); +            lock.unlock(); +            _full_cond.notify_one(); +        } + +        bool pop_with_timed_wait(elem_type &elem, const time_duration_t &time){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            if (not _empty_cond.timed_wait(lock, time, boost::bind(&bounded_buffer_impl<elem_type>::not_empty, this))) return false; +            elem = _buffer.back(); _buffer.pop_back(); +            lock.unlock(); +            _full_cond.notify_one(); +            return true; +        } + +        UHD_INLINE void clear(void){ +            boost::unique_lock<boost::mutex> lock(_mutex); +            while (not_empty()) _buffer.pop_back(); +            lock.unlock(); +            _full_cond.notify_one(); +        } + +    private: +        boost::mutex _mutex; +        boost::condition _empty_cond, _full_cond; +        boost::circular_buffer<elem_type> _buffer; + +        bool not_full(void) const{return not _buffer.full();} +        bool not_empty(void) const{return not _buffer.empty();} +    }; +}}} //namespace + +namespace uhd{ namespace transport{ + +    template <typename elem_type> typename bounded_buffer<elem_type>::sptr +    bounded_buffer<elem_type>::make(size_t capacity){ +        return typename bounded_buffer<elem_type>::sptr(new bounded_buffer_impl<elem_type>(capacity)); +    } + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_IPP */ diff --git a/host/include/uhd/transport/vrt.hpp b/host/include/uhd/transport/vrt.hpp deleted file mode 100644 index fb6efc99c..000000000 --- a/host/include/uhd/transport/vrt.hpp +++ /dev/null @@ -1,115 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#ifndef INCLUDED_UHD_TRANSPORT_VRT_HPP -#define INCLUDED_UHD_TRANSPORT_VRT_HPP - -#include <uhd/config.hpp> -#include <uhd/types/metadata.hpp> -#include <cstddef> - -namespace uhd{ namespace transport{ - -namespace vrt{ - -    static const size_t max_header_words32 = 5; //hdr+sid+tsi+tsf (no class id supported) - -    /*! -     * Pack a vrt header from metadata (big endian format). -     * \param metadata the tx metadata with flags and timestamps -     * \param header_buff memory to write the packed vrt header -     * \param num_header_words32 number of words in the vrt header -     * \param num_payload_words32 the length of the payload -     * \param num_packet_words32 the length of the packet -     * \param packet_count the packet count sequence number -     * \param tick_rate ticks per second used in time conversion -     */ -    UHD_API void pack_be( -        const tx_metadata_t &metadata, //input -        boost::uint32_t *header_buff,  //output -        size_t &num_header_words32,    //output -        size_t num_payload_words32,    //input -        size_t &num_packet_words32,    //output -        size_t packet_count,           //input -        double tick_rate               //input -    ); - -    /*! -     * Unpack a vrt header to metadata (big endian format). -     * \param metadata the rx metadata with flags and timestamps -     * \param header_buff memory to read the packed vrt header -     * \param num_header_words32 number of words in the vrt header -     * \param num_payload_words32 the length of the payload -     * \param num_packet_words32 the length of the packet -     * \param packet_count the packet count sequence number -     * \param tick_rate ticks per second used in time conversion -     */ -    UHD_API void unpack_be( -        rx_metadata_t &metadata,            //output -        const boost::uint32_t *header_buff, //input -        size_t &num_header_words32,         //output -        size_t &num_payload_words32,        //output -        size_t num_packet_words32,          //input -        size_t &packet_count,               //output -        double tick_rate                    //input -    ); - -    /*! -     * Pack a vrt header from metadata (little endian format). -     * \param metadata the tx metadata with flags and timestamps -     * \param header_buff memory to write the packed vrt header -     * \param num_header_words32 number of words in the vrt header -     * \param num_payload_words32 the length of the payload -     * \param num_packet_words32 the length of the packet -     * \param packet_count the packet count sequence number -     * \param tick_rate ticks per second used in time conversion -     */ -    UHD_API void pack_le( -        const tx_metadata_t &metadata, //input -        boost::uint32_t *header_buff,  //output -        size_t &num_header_words32,    //output -        size_t num_payload_words32,    //input -        size_t &num_packet_words32,    //output -        size_t packet_count,           //input -        double tick_rate               //input -    ); - -    /*! -     * Unpack a vrt header to metadata (little endian format). -     * \param metadata the rx metadata with flags and timestamps -     * \param header_buff memory to read the packed vrt header -     * \param num_header_words32 number of words in the vrt header -     * \param num_payload_words32 the length of the payload -     * \param num_packet_words32 the length of the packet -     * \param packet_count the packet count sequence number -     * \param tick_rate ticks per second used in time conversion -     */ -    UHD_API void unpack_le( -        rx_metadata_t &metadata,            //output -        const boost::uint32_t *header_buff, //input -        size_t &num_header_words32,         //output -        size_t &num_payload_words32,        //output -        size_t num_packet_words32,          //input -        size_t &packet_count,               //output -        double tick_rate                    //input -    ); - -} //namespace vrt - -}} //namespace - -#endif /* INCLUDED_UHD_TRANSPORT_VRT_HPP */ diff --git a/host/include/uhd/transport/vrt_if_packet.hpp b/host/include/uhd/transport/vrt_if_packet.hpp new file mode 100644 index 000000000..ccefe14ea --- /dev/null +++ b/host/include/uhd/transport/vrt_if_packet.hpp @@ -0,0 +1,99 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_VRT_IF_PACKET_HPP +#define INCLUDED_UHD_TRANSPORT_VRT_IF_PACKET_HPP + +#include <uhd/config.hpp> +#include <boost/cstdint.hpp> +#include <cstddef> //size_t + +namespace uhd{ namespace transport{ + +namespace vrt{ + +    //! The maximum number of 32-bit words in a vrt if packet header +    static const size_t max_if_hdr_words32 = 7; //hdr+sid+cid+tsi+tsf + +    /*! +     * Definition for fields that can be packed into a vrt if header. +     * The size fields are used for input and output depending upon +     * the operation used (ie the pack or unpack function call). +     */ +    struct UHD_API if_packet_info_t{ +        //size fields +        size_t num_payload_words32; //required in pack, derived in unpack +        size_t num_header_words32;  //derived in pack, derived in unpack +        size_t num_packet_words32;  //derived in pack, required in unpack + +        //header fields +        size_t packet_count; +        bool sob, eob; + +        //optional fields +        bool has_sid; boost::uint32_t sid; +        bool has_cid; boost::uint64_t cid; +        bool has_tsi; boost::uint32_t tsi; +        bool has_tsf; boost::uint64_t tsf; +        bool has_tlr; boost::uint32_t tlr; +    }; + +    /*! +     * Pack a vrt header from metadata (big endian format). +     * \param packet_buff memory to write the packed vrt header +     * \param if_packet_info the if packet info (read/write) +     */ +    UHD_API void if_hdr_pack_be( +        boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +    ); + +    /*! +     * Unpack a vrt header to metadata (big endian format). +     * \param packet_buff memory to read the packed vrt header +     * \param if_packet_info the if packet info (read/write) +     */ +    UHD_API void if_hdr_unpack_be( +        const boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +    ); + +    /*! +     * Pack a vrt header from metadata (little endian format). +     * \param packet_buff memory to write the packed vrt header +     * \param if_packet_info the if packet info (read/write) +     */ +    UHD_API void if_hdr_pack_le( +        boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +    ); + +    /*! +     * Unpack a vrt header to metadata (little endian format). +     * \param packet_buff memory to read the packed vrt header +     * \param if_packet_info the if packet info (read/write) +     */ +    UHD_API void if_hdr_unpack_le( +        const boost::uint32_t *packet_buff, +        if_packet_info_t &if_packet_info +    ); + +} //namespace vrt + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_VRT_IF_PACKET_HPP */ diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 2815e3189..da10bfbe2 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -47,7 +47,7 @@ namespace uhd{ namespace transport{           * Get the size of the underlying buffer.           * \return the number of bytes           */ -        size_t size(void) const{ +        inline size_t size(void) const{              return boost::asio::buffer_size(this->get());          } @@ -55,7 +55,7 @@ namespace uhd{ namespace transport{           * Get a pointer to the underlying buffer.           * \return a pointer into memory           */ -        template <class T> T cast(void) const{ +        template <class T> inline T cast(void) const{              return boost::asio::buffer_cast<T>(this->get());          } @@ -89,7 +89,7 @@ namespace uhd{ namespace transport{           * Get the size of the underlying buffer.           * \return the number of bytes           */ -        size_t size(void) const{ +        inline size_t size(void) const{              return boost::asio::buffer_size(this->get());          } @@ -97,7 +97,7 @@ namespace uhd{ namespace transport{           * Get a pointer to the underlying buffer.           * \return a pointer into memory           */ -        template <class T> T cast(void) const{ +        template <class T> inline T cast(void) const{              return boost::asio::buffer_cast<T>(this->get());          } diff --git a/host/include/uhd/types/metadata.hpp b/host/include/uhd/types/metadata.hpp index 55add71cc..f4c962ff7 100644 --- a/host/include/uhd/types/metadata.hpp +++ b/host/include/uhd/types/metadata.hpp @@ -31,13 +31,6 @@ namespace uhd{       */      struct UHD_API rx_metadata_t{          /*! -         * Stream IDs may be used to identify source DSP units. -         * --Not currently used in any known device implementation.-- -         */ -        bool has_stream_id; -        boost::uint32_t stream_id; - -        /*!           * Time specification:           * Set from timestamps on incoming data when provided.           */ @@ -84,13 +77,6 @@ namespace uhd{       */      struct UHD_API tx_metadata_t{          /*! -         * Stream IDs may be used to identify destination DSP units. -         * --Not currently used in any known device implementation.-- -         */ -        bool has_stream_id; -        boost::uint32_t stream_id; - -        /*!           * Time specification:           * Set has time spec to false to perform a send "now".           * Or, set to true, and fill in time spec for a send "at". diff --git a/host/include/uhd/types/time_spec.hpp b/host/include/uhd/types/time_spec.hpp index 25d9e41d0..59b85f4b7 100644 --- a/host/include/uhd/types/time_spec.hpp +++ b/host/include/uhd/types/time_spec.hpp @@ -19,58 +19,92 @@  #define INCLUDED_UHD_TYPES_TIME_SPEC_HPP  #include <uhd/config.hpp> -#include <boost/cstdint.hpp> +#include <boost/operators.hpp> +#include <ctime>  namespace uhd{      /*! -     * A time_spec_t holds a seconds and fractional seconds time value. -     * The time_spec_t can be used when setting the time on devices, -     * and for dealing with time stamped samples though the metadata. -     * and for controlling the start of streaming for applicable dsps. +     * A time_spec_t holds a seconds and a fractional seconds time value. +     * Depending upon usage, the time_spec_t can represent absolute times, +     * relative times, or time differences (between absolute times).       * -     * The fractional seconds are represented in units of nanoseconds, -     * which provide a clock-domain independent unit of time storage. -     * The methods "get_ticks" and "set_ticks" can be used to convert -     * the fractional seconds to and from clock-domain specific units. +     * The time_spec_t provides clock-domain independent time storage, +     * but can convert fractional seconds to/from clock-domain specific units.       * -     * The nanoseconds count is stored as double precision floating point. +     * The fractional seconds are stored as double precision floating point.       * This gives the fractional seconds enough precision to unambiguously       * specify a clock-tick/sample-count up to rates of several petahertz.       */ -    struct UHD_API time_spec_t{ +    class UHD_API time_spec_t : boost::additive<time_spec_t>, boost::totally_ordered<time_spec_t>{ +    public: -        //! whole/integer seconds count in seconds -        boost::uint32_t secs; +        /*! +         * Create a time_spec_t from a real-valued seconds count. +         * \param secs the real-valued seconds count (default = 0) +         */ +        time_spec_t(double secs = 0); + +        /*! +         * Create a time_spec_t from whole and fractional seconds. +         * \param full_secs the whole/integer seconds count +         * \param frac_secs the fractional seconds count (default = 0) +         */ +        time_spec_t(time_t full_secs, double frac_secs = 0); -        //! fractional seconds count in nano-seconds -        double nsecs; +        /*! +         * Create a time_spec_t from whole and fractional seconds. +         * Translation from clock-domain specific units. +         * \param full_secs the whole/integer seconds count +         * \param tick_count the fractional seconds tick count +         * \param tick_rate the number of ticks per second +         */ +        time_spec_t(time_t full_secs, size_t tick_count, double tick_rate);          /*! -         * Convert the fractional nsecs to clock ticks. +         * Convert the fractional seconds to clock ticks.           * Translation into clock-domain specific units.           * \param tick_rate the number of ticks per second           * \return the fractional seconds tick count           */ -        boost::uint32_t get_ticks(double tick_rate) const; +        size_t get_tick_count(double tick_rate) const;          /*! -         * Set the fractional nsecs from clock ticks. -         * Translation from clock-domain specific units. -         * \param ticks the fractional seconds tick count -         * \param tick_rate the number of ticks per second +         * Get the time as a real-valued seconds count. +         * Note: If this time_spec_t represents an absolute time, +         * the precision of the fractional seconds may be lost. +         * \return the real-valued seconds           */ -        void set_ticks(boost::uint32_t ticks, double tick_rate); +        double get_real_secs(void) const;          /*! -         * Create a time_spec_t from whole and fractional seconds. -         * \param secs the whole/integer seconds count in seconds (default = 0) -         * \param nsecs the fractional seconds count in nanoseconds (default = 0) +         * Get the whole/integer part of the time in seconds. +         * \return the whole/integer seconds +         */ +        time_t get_full_secs(void) const; + +        /*! +         * Get the fractional part of the time in seconds. +         * \return the fractional seconds           */ -        time_spec_t(boost::uint32_t secs = 0, double nsecs = 0); +        double get_frac_secs(void) const; + +        //! Implement addable interface +        time_spec_t &operator+=(const time_spec_t &); + +        //! Implement subtractable interface +        time_spec_t &operator-=(const time_spec_t &); +    //private time storage details +    private: time_t _full_secs; double _frac_secs;      }; +    //! Implement equality_comparable interface +    UHD_API bool operator==(const time_spec_t &, const time_spec_t &); + +    //! Implement less_than_comparable interface +    UHD_API bool operator<(const time_spec_t &, const time_spec_t &); +  } //namespace uhd  #endif /* INCLUDED_UHD_TYPES_TIME_SPEC_HPP */ diff --git a/host/include/uhd/usrp/CMakeLists.txt b/host/include/uhd/usrp/CMakeLists.txt index 58aa8588a..6f8c1a2d8 100644 --- a/host/include/uhd/usrp/CMakeLists.txt +++ b/host/include/uhd/usrp/CMakeLists.txt @@ -33,7 +33,10 @@ INSTALL(FILES      ### utilities ###      tune_helper.hpp + +    ### interfaces ###      simple_usrp.hpp +    mimo_usrp.hpp      DESTINATION ${INCLUDE_DIR}/uhd/usrp  ) diff --git a/host/include/uhd/usrp/dboard_id.hpp b/host/include/uhd/usrp/dboard_id.hpp index 8b6eaf6bd..4c45e4334 100644 --- a/host/include/uhd/usrp/dboard_id.hpp +++ b/host/include/uhd/usrp/dboard_id.hpp @@ -25,7 +25,7 @@  namespace uhd{ namespace usrp{ -    class UHD_API dboard_id_t : boost::equality_comparable1<dboard_id_t>{ +    class UHD_API dboard_id_t : boost::equality_comparable<dboard_id_t>{      public:          /*!           * Create a dboard id from an integer. diff --git a/host/include/uhd/usrp/mboard_props.hpp b/host/include/uhd/usrp/mboard_props.hpp index 7ff454472..a432ce50c 100644 --- a/host/include/uhd/usrp/mboard_props.hpp +++ b/host/include/uhd/usrp/mboard_props.hpp @@ -40,7 +40,7 @@ namespace uhd{ namespace usrp{          MBOARD_PROP_TX_DBOARD       = 'v', //ro, wax::obj          MBOARD_PROP_TX_DBOARD_NAMES = 'V', //ro, prop_names_t          MBOARD_PROP_CLOCK_CONFIG    = 'C', //rw, clock_config_t -        MBOARD_PROP_TIME_NOW        = 't', //wo, time_spec_t +        MBOARD_PROP_TIME_NOW        = 't', //rw, time_spec_t          MBOARD_PROP_TIME_NEXT_PPS   = 'T', //wo, time_spec_t          MBOARD_PROP_STREAM_CMD      = 's'  //wo, stream_cmd_t      }; diff --git a/host/include/uhd/usrp/mimo_usrp.hpp b/host/include/uhd/usrp/mimo_usrp.hpp new file mode 100644 index 000000000..68a42cad8 --- /dev/null +++ b/host/include/uhd/usrp/mimo_usrp.hpp @@ -0,0 +1,171 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_MIMO_USRP_HPP +#define INCLUDED_UHD_USRP_MIMO_USRP_HPP + +#include <uhd/config.hpp> +#include <uhd/device.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/types/clock_config.hpp> +#include <uhd/types/tune_result.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> + +namespace uhd{ namespace usrp{ + +/*! + * The MIMO USRP device class: + * A mimo usrp facilitates ease-of-use for multi-usrp scenarios. + * The wrapper provides convenience functions to control the group + * of underlying devices as if they consisted of a single device. + */ +class UHD_API mimo_usrp : boost::noncopyable{ +public: +    typedef boost::shared_ptr<mimo_usrp> sptr; + +    /*! +     * Make a new mimo usrp from the device address. +     * \param dev_addr the device address +     * \return a new mimo usrp object +     */ +    static sptr make(const device_addr_t &dev_addr); + +    /*! +     * Get the underlying device object. +     * This is needed to get access to the streaming API and properties. +     * \return the device object within this simple usrp +     */ +    virtual device::sptr get_device(void) = 0; + +    /*! +     * Get a printable name for this mimo usrp. +     * \return a printable string +     */ +    virtual std::string get_pp_string(void) = 0; + +    /*! +     * Get the number of channels in this mimo configuration. +     * The number of rx channels == the number of tx channels. +     * \return the number of channels +     */ +    virtual size_t get_num_channels(void) = 0; + +    /******************************************************************* +     * Misc +     ******************************************************************/ +    /*! +     * Gets the current time in the usrp time registers. +     * \return a timespec representing current usrp time +     */ +    virtual time_spec_t get_time_now(void) = 0; + +    /*! +     * Set the time registers on the usrp at the next pps tick. +     * The values will not be latched in until the pulse occurs. +     * It is recommended that the user sleep(1) after calling to ensure +     * that the time registers will be in a known state prior to use. +     * This call works across all mboards in the mimo configuration. +     * +     * Note: Because this call sets the time on the "next" pps, +     * the seconds in the time spec should be current seconds + 1. +     * +     * \param time_spec the time to latch into the usrp device +     */ +    virtual void set_time_next_pps(const time_spec_t &time_spec) = 0; + +    /*! +     * Synchronize the times across all motherboards in this configuration. +     * Use this method to sync the times when the edge of the PPS is unknown. +     * +     * Ex: Host machine is not attached to serial port of GPSDO +     * and can therefore not query the GPSDO for the PPS edge. +     * +     * This is a 3-step process, and will take at most 3 seconds to complete. +     * Upon completion, the times will be synchronized to the time provided. +     * +     * - Step1: set the time at the next pps (potential race condition) +     * - Step2: wait for the seconds to rollover to catch the pps edge +     * - Step3: set the time at the next pps (synchronous for all boards) +     * +     * \param time_spec the time to latch into the usrp device +     */ +    virtual void set_time_unknown_pps(const time_spec_t &time_spec) = 0; + +    /*! +     * Issue a stream command to the usrp device. +     * This tells the usrp to send samples into the host. +     * See the documentation for stream_cmd_t for more info. +     * \param stream_cmd the stream command to issue +     */ +    virtual void issue_stream_cmd(const stream_cmd_t &stream_cmd) = 0; + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    virtual void set_rx_rate_all(double rate) = 0; +    virtual double get_rx_rate_all(void) = 0; + +    virtual tune_result_t set_rx_freq(size_t chan, double freq) = 0; +    virtual tune_result_t set_rx_freq(size_t chan, double freq, double lo_off) = 0; +    virtual freq_range_t get_rx_freq_range(size_t chan) = 0; + +    virtual void set_rx_gain(size_t chan, float gain) = 0; +    virtual float get_rx_gain(size_t chan) = 0; +    virtual gain_range_t get_rx_gain_range(size_t chan) = 0; + +    virtual void set_rx_antenna(size_t chan, const std::string &ant) = 0; +    virtual std::string get_rx_antenna(size_t chan) = 0; +    virtual std::vector<std::string> get_rx_antennas(size_t chan) = 0; + +    virtual bool get_rx_lo_locked(size_t chan) = 0; + +    /*! +     * Read the RSSI value from a usrp device. +     * Or throw if the dboard does not support an RSSI readback. +     * \param chan which mimo channel 0 to N-1 +     * \return the rssi in dB +     */ +    virtual float read_rssi(size_t chan) = 0; + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    virtual void set_tx_rate_all(double rate) = 0; +    virtual double get_tx_rate_all(void) = 0; + +    virtual tune_result_t set_tx_freq(size_t chan, double freq) = 0; +    virtual tune_result_t set_tx_freq(size_t chan, double freq, double lo_off) = 0; +    virtual freq_range_t get_tx_freq_range(size_t chan) = 0; + +    virtual void set_tx_gain(size_t chan, float gain) = 0; +    virtual float get_tx_gain(size_t chan) = 0; +    virtual gain_range_t get_tx_gain_range(size_t chan) = 0; + +    virtual void set_tx_antenna(size_t chan, const std::string &ant) = 0; +    virtual std::string get_tx_antenna(size_t chan) = 0; +    virtual std::vector<std::string> get_tx_antennas(size_t chan) = 0; + +    virtual bool get_tx_lo_locked(size_t chan) = 0; + +}; + +}} + +#endif /* INCLUDED_UHD_USRP_MIMO_USRP_HPP */ diff --git a/host/include/uhd/usrp/simple_usrp.hpp b/host/include/uhd/usrp/simple_usrp.hpp index 6ba1b90dd..1d9136f08 100644 --- a/host/include/uhd/usrp/simple_usrp.hpp +++ b/host/include/uhd/usrp/simple_usrp.hpp @@ -58,12 +58,18 @@ public:       * Get a printable name for this simple usrp.       * \return a printable string       */ -    virtual std::string get_name(void) = 0; +    virtual std::string get_pp_string(void) = 0;      /*******************************************************************       * Misc       ******************************************************************/      /*! +     * Gets the current time in the usrp time registers. +     * \return a timespec representing current usrp time +     */ +    virtual time_spec_t get_time_now(void) = 0; + +    /*!       * Sets the time registers on the usrp immediately.       * \param time_spec the time to latch into the usrp device       */ @@ -98,13 +104,6 @@ public:       */      virtual void set_clock_config(const clock_config_t &clock_config) = 0; -    /*! -     * Read the RSSI value from a usrp device. -     * Or throw if the dboard does not support an RSSI readback. -     * \return the rssi in dB -     */ -    virtual float read_rssi(void) = 0; -      /*******************************************************************       * RX methods       ******************************************************************/ @@ -125,6 +124,13 @@ public:      virtual bool get_rx_lo_locked(void) = 0; +    /*! +     * Read the RSSI value from a usrp device. +     * Or throw if the dboard does not support an RSSI readback. +     * \return the rssi in dB +     */ +    virtual float read_rssi(void) = 0; +      /*******************************************************************       * TX methods       ******************************************************************/ diff --git a/host/include/uhd/usrp/usrp2.hpp b/host/include/uhd/usrp/usrp2.hpp deleted file mode 100644 index 7387e5dd4..000000000 --- a/host/include/uhd/usrp/usrp2.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#ifndef INCLUDED_UHD_USRP_USRP2_HPP -#define INCLUDED_UHD_USRP_USRP2_HPP - -#include <uhd/config.hpp> -#include <uhd/device.hpp> - -namespace uhd{ namespace usrp{ - -/*! - * The usrp2 device class. - */ -class UHD_API usrp2 : public device{ -public: -    /*! -     * Find usrp2 devices over the ethernet. -     * -     * Recommended key/value pairs for the device hint address: -     * hint["addr"] = address, where address is a resolvable address -     * or ip address, which may or may not be a broadcast address. -     * -     * Other optional device address keys: -     *   recv_buff_size: resizes the recv buffer on the data socket -     *   send_buff_size: resizes the send buffer on the data socket -     * -     * \param hint a device addr with the usrp2 address filled in -     * \return a vector of device addresses for all usrp2s found -     */ -    static device_addrs_t find(const device_addr_t &hint); - -    /*! -     * Make a usrp2 from a device address. -     * -     * Required key/value pairs for the device address: -     * hint["addr"] = address, where address is a resolvable address -     * or ip address, which must be the specific address of a usrp2. -     * -     * \param addr the device address -     * \return a device sptr to a new usrp2 -     */ -    static device::sptr make(const device_addr_t &addr); -}; - -}} //namespace - -#endif /* INCLUDED_UHD_USRP_USRP2_HPP */ diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index c98eec639..36f86054a 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -19,6 +19,7 @@ INSTALL(FILES      algorithm.hpp      assert.hpp      byteswap.hpp +    byteswap.ipp      exception.hpp      gain_handler.hpp      pimpl.hpp diff --git a/host/include/uhd/utils/algorithm.hpp b/host/include/uhd/utils/algorithm.hpp index 08977a69f..b52edc6b5 100644 --- a/host/include/uhd/utils/algorithm.hpp +++ b/host/include/uhd/utils/algorithm.hpp @@ -83,6 +83,35 @@ namespace std{      }      /*! +     * Count the number of appearances of a value in a range. +     * +     * Uses std::count to count the appearances in the range. +     * +     * \param range the elements to iterate through +     * \param value the value to count in the range +     * \return the number of appearances of the value +     */ +    template<typename Range, typename T> inline +    size_t count(const Range &range, const T &value){ +        return std::count(boost::begin(range), boost::end(range), value); +    } + +    /*! +     * Are the ranges equal (are their elements equivalent)? +     * +     * Uses std::equal to search the iterable for an element. +     * +     * \param range1 the first range of elements +     * \param range2 the second range of elements +     * \return true when the elements are equivalent +     */ +    template<typename Range> inline +    bool equal(const Range &range1, const Range &range2){ +        return (boost::size(range1) == boost::size(range2)) and +        std::equal(boost::begin(range1), boost::end(range1), boost::begin(range2)); +    } + +    /*!       * A templated signum implementation.       * \param n the comparable to process       * \return -1 when n negative, +1 when n positive, otherwise 0 diff --git a/host/include/uhd/utils/byteswap.hpp b/host/include/uhd/utils/byteswap.hpp index dd5dcbc09..9a1871210 100644 --- a/host/include/uhd/utils/byteswap.hpp +++ b/host/include/uhd/utils/byteswap.hpp @@ -37,84 +37,14 @@ namespace uhd{      //! perform a byteswap on a 64 bit integer      boost::uint64_t byteswap(boost::uint64_t); -} //namespace uhd - -/*********************************************************************** - * Platform-specific implementation details for byteswap below: - **********************************************************************/ -#ifdef BOOST_MSVC //http://msdn.microsoft.com/en-us/library/a3140177%28VS.80%29.aspx -    #include <stdlib.h> - -    UHD_INLINE boost::uint16_t uhd::byteswap(boost::uint16_t x){ -        return _byteswap_ushort(x); -    } - -    UHD_INLINE boost::uint32_t uhd::byteswap(boost::uint32_t x){ -        return _byteswap_ulong(x); -    } - -    UHD_INLINE boost::uint64_t uhd::byteswap(boost::uint64_t x){ -        return _byteswap_uint64(x); -    } - -#elif defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 2 - -    UHD_INLINE boost::uint16_t uhd::byteswap(boost::uint16_t x){ -        return (x>>8) | (x<<8); //DNE return __builtin_bswap16(x); -    } - -    UHD_INLINE boost::uint32_t uhd::byteswap(boost::uint32_t x){ -        return __builtin_bswap32(x); -    } - -    UHD_INLINE boost::uint64_t uhd::byteswap(boost::uint64_t x){ -        return __builtin_bswap64(x); -    } - -#elif defined(__FreeBSD__) || defined(__MACOSX__) || defined(__APPLE__) -    #include <libkern/OSByteOrder.h> +    //! network to host: short, long, or long-long +    template<typename T> T ntohx(T); -    UHD_INLINE boost::uint16_t uhd::byteswap(boost::uint16_t x){ -        return OSSwapInt16(x); -    } +    //! host to network: short, long, or long-long +    template<typename T> T htonx(T); -    UHD_INLINE boost::uint32_t uhd::byteswap(boost::uint32_t x){ -        return OSSwapInt32(x); -    } - -    UHD_INLINE boost::uint64_t uhd::byteswap(boost::uint64_t x){ -        return OSSwapInt64(x); -    } - -#elif defined(linux) || defined(__linux) -    #include <byteswap.h> - -    UHD_INLINE boost::uint16_t uhd::byteswap(boost::uint16_t x){ -        return bswap_16(x); -    } - -    UHD_INLINE boost::uint32_t uhd::byteswap(boost::uint32_t x){ -        return bswap_32(x); -    } - -    UHD_INLINE boost::uint64_t uhd::byteswap(boost::uint64_t x){ -        return bswap_64(x); -    } - -#else //http://www.koders.com/c/fidB93B34CD44F0ECF724F1A4EAE3854BA2FE692F59.aspx - -    UHD_INLINE boost::uint16_t uhd::byteswap(boost::uint16_t x){ -        return (x>>8) | (x<<8); -    } - -    UHD_INLINE boost::uint32_t uhd::byteswap(boost::uint32_t x){ -        return (boost::uint32_t(uhd::byteswap(boost::uint16_t(x&0xfffful)))<<16) | (uhd::byteswap(boost::uint16_t(x>>16))); -    } - -    UHD_INLINE boost::uint64_t uhd::byteswap(boost::uint64_t x){ -        return (boost::uint64_t(uhd::byteswap(boost::uint32_t(x&0xffffffffull)))<<32) | (uhd::byteswap(boost::uint32_t(x>>32))); -    } +} //namespace uhd -#endif +#include <uhd/utils/byteswap.ipp>  #endif /* INCLUDED_UHD_UTILS_BYTESWAP_HPP */ diff --git a/host/include/uhd/utils/byteswap.ipp b/host/include/uhd/utils/byteswap.ipp new file mode 100644 index 000000000..11c82a4ec --- /dev/null +++ b/host/include/uhd/utils/byteswap.ipp @@ -0,0 +1,120 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_UTILS_BYTESWAP_IPP +#define INCLUDED_UHD_UTILS_BYTESWAP_IPP + +/*********************************************************************** + * Platform-specific implementation details for byteswap below: + **********************************************************************/ +#ifdef BOOST_MSVC //http://msdn.microsoft.com/en-us/library/a3140177%28VS.80%29.aspx +    #include <stdlib.h> + +    UHD_INLINE boost::uint16_t uhd::byteswap(boost::uint16_t x){ +        return _byteswap_ushort(x); +    } + +    UHD_INLINE boost::uint32_t uhd::byteswap(boost::uint32_t x){ +        return _byteswap_ulong(x); +    } + +    UHD_INLINE boost::uint64_t uhd::byteswap(boost::uint64_t x){ +        return _byteswap_uint64(x); +    } + +#elif defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 2 + +    UHD_INLINE boost::uint16_t uhd::byteswap(boost::uint16_t x){ +        return (x>>8) | (x<<8); //DNE return __builtin_bswap16(x); +    } + +    UHD_INLINE boost::uint32_t uhd::byteswap(boost::uint32_t x){ +        return __builtin_bswap32(x); +    } + +    UHD_INLINE boost::uint64_t uhd::byteswap(boost::uint64_t x){ +        return __builtin_bswap64(x); +    } + +#elif defined(__FreeBSD__) || defined(__MACOSX__) || defined(__APPLE__) +    #include <libkern/OSByteOrder.h> + +    UHD_INLINE boost::uint16_t uhd::byteswap(boost::uint16_t x){ +        return OSSwapInt16(x); +    } + +    UHD_INLINE boost::uint32_t uhd::byteswap(boost::uint32_t x){ +        return OSSwapInt32(x); +    } + +    UHD_INLINE boost::uint64_t uhd::byteswap(boost::uint64_t x){ +        return OSSwapInt64(x); +    } + +#elif defined(linux) || defined(__linux) +    #include <byteswap.h> + +    UHD_INLINE boost::uint16_t uhd::byteswap(boost::uint16_t x){ +        return bswap_16(x); +    } + +    UHD_INLINE boost::uint32_t uhd::byteswap(boost::uint32_t x){ +        return bswap_32(x); +    } + +    UHD_INLINE boost::uint64_t uhd::byteswap(boost::uint64_t x){ +        return bswap_64(x); +    } + +#else //http://www.koders.com/c/fidB93B34CD44F0ECF724F1A4EAE3854BA2FE692F59.aspx + +    UHD_INLINE boost::uint16_t uhd::byteswap(boost::uint16_t x){ +        return (x>>8) | (x<<8); +    } + +    UHD_INLINE boost::uint32_t uhd::byteswap(boost::uint32_t x){ +        return (boost::uint32_t(uhd::byteswap(boost::uint16_t(x&0xfffful)))<<16) | (uhd::byteswap(boost::uint16_t(x>>16))); +    } + +    UHD_INLINE boost::uint64_t uhd::byteswap(boost::uint64_t x){ +        return (boost::uint64_t(uhd::byteswap(boost::uint32_t(x&0xffffffffull)))<<32) | (uhd::byteswap(boost::uint32_t(x>>32))); +    } + +#endif + +/*********************************************************************** + * Define the templated network to/from host conversions + **********************************************************************/ +#include <boost/detail/endian.hpp> + +template<typename T> UHD_INLINE T uhd::ntohx(T num){ +    #ifdef BOOST_BIG_ENDIAN +        return num; +    #else +        return uhd::byteswap(num); +    #endif +} + +template<typename T> UHD_INLINE T uhd::htonx(T num){ +    #ifdef BOOST_BIG_ENDIAN +        return num; +    #else +        return uhd::byteswap(num); +    #endif +} + +#endif /* INCLUDED_UHD_UTILS_BYTESWAP_IPP */ diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 872865d6c..bde2b72b9 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -18,6 +18,16 @@  #This file will be included by cmake, use absolute paths!  ######################################################################## +# Check for SIMD headers +######################################################################## +INCLUDE(CheckIncludeFileCXX) +CHECK_INCLUDE_FILE_CXX(emmintrin.h HAVE_EMMINTRIN_H) + +IF(HAVE_EMMINTRIN_H) +    ADD_DEFINITIONS(-DHAVE_EMMINTRIN_H) +ENDIF(HAVE_EMMINTRIN_H) + +########################################################################  # Setup defines for interface address discovery  ########################################################################  MESSAGE(STATUS "Configuring interface address discovery...") @@ -40,8 +50,8 @@ ENDIF(HAVE_IFADDRS_H)  # Append to the list of sources for lib uhd  ########################################################################  LIBUHD_PYTHON_GEN_SOURCE( -    ${CMAKE_SOURCE_DIR}/lib/transport/gen_vrt.py -    ${CMAKE_BINARY_DIR}/lib/transport/vrt.cpp +    ${CMAKE_SOURCE_DIR}/lib/transport/gen_vrt_if_packet.py +    ${CMAKE_BINARY_DIR}/lib/transport/vrt_if_packet.cpp  )  LIBUHD_PYTHON_GEN_SOURCE( @@ -49,6 +59,16 @@ LIBUHD_PYTHON_GEN_SOURCE(      ${CMAKE_BINARY_DIR}/lib/transport/convert_types.cpp  ) +# append this directory to the include path so the generated convert types +# can include the implementation convert types file in the source directory +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/transport) + +# make the generated convert types depend on the implementation header +SET_SOURCE_FILES_PROPERTIES( +    ${CMAKE_BINARY_DIR}/lib/transport/convert_types.cpp PROPERTIES +    OBJECT_DEPENDS ${CMAKE_SOURCE_DIR}/lib/transport/convert_types_impl.hpp +) +  LIBUHD_APPEND_SOURCES(      ${CMAKE_SOURCE_DIR}/lib/transport/if_addrs.cpp      ${CMAKE_SOURCE_DIR}/lib/transport/udp_simple.cpp diff --git a/host/lib/transport/convert_types_impl.hpp b/host/lib/transport/convert_types_impl.hpp new file mode 100644 index 000000000..5958b08cb --- /dev/null +++ b/host/lib/transport/convert_types_impl.hpp @@ -0,0 +1,201 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_TRANSPORT_CONVERT_TYPES_IMPL_HPP +#define INCLUDED_LIBUHD_TRANSPORT_CONVERT_TYPES_IMPL_HPP + +#include <uhd/config.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/cstdint.hpp> +#include <cstring> +#include <complex> + +#ifdef HAVE_EMMINTRIN_H +    #define USE_EMMINTRIN_H //use sse2 intrinsics +#endif + +/*********************************************************************** + * Typedefs + **********************************************************************/ +typedef std::complex<float>          fc32_t; +typedef std::complex<boost::int16_t> sc16_t; +typedef boost::uint32_t              item32_t; + +/*********************************************************************** + * Convert complex short buffer to items32 + **********************************************************************/ +static UHD_INLINE void sc16_to_item32_nswap( +    const sc16_t *input, item32_t *output, size_t nsamps +){ +    std::memcpy(output, input, nsamps*sizeof(item32_t)); +} + +static UHD_INLINE void sc16_to_item32_bswap( +    const sc16_t *input, item32_t *output, size_t nsamps +){ +    const item32_t *item32_input = (const item32_t *)input; +    for (size_t i = 0; i < nsamps; i++){ +        output[i] = uhd::byteswap(item32_input[i]); +    } +} + +/*********************************************************************** + * Convert items32 buffer to complex short + **********************************************************************/ +static UHD_INLINE void item32_to_sc16_nswap( +    const item32_t *input, sc16_t *output, size_t nsamps +){ +    std::memcpy(output, input, nsamps*sizeof(item32_t)); +} + +static UHD_INLINE void item32_to_sc16_bswap( +    const item32_t *input, sc16_t *output, size_t nsamps +){ +    item32_t *item32_output = (item32_t *)output; +    for (size_t i = 0; i < nsamps; i++){ +        item32_output[i] = uhd::byteswap(input[i]); +    } +} + +/*********************************************************************** + * Convert complex float buffer to items32 + **********************************************************************/ +static const float shorts_per_float = float(32767); + +static UHD_INLINE item32_t fc32_to_item32(fc32_t num){ +    boost::uint16_t real = boost::int16_t(num.real()*shorts_per_float); +    boost::uint16_t imag = boost::int16_t(num.imag()*shorts_per_float); +    return (item32_t(real) << 16) | (item32_t(imag) << 0); +} + +static UHD_INLINE void fc32_to_item32_nswap( +    const fc32_t *input, item32_t *output, size_t nsamps +){ +    for (size_t i = 0; i < nsamps; i++){ +        output[i] = fc32_to_item32(input[i]); +    } +} + +#if defined(USE_EMMINTRIN_H) +#include <emmintrin.h> + +static UHD_INLINE void fc32_to_item32_bswap( +    const fc32_t *input, item32_t *output, size_t nsamps +){ +    __m128 scalar = _mm_set_ps1(shorts_per_float); + +    //convert blocks of samples with intrinsics +    size_t i = 0; for (; i < (nsamps & ~0x3); i+=4){ +        //load from input +        __m128 tmplo = _mm_loadu_ps(reinterpret_cast<const float *>(input+i+0)); +        __m128 tmphi = _mm_loadu_ps(reinterpret_cast<const float *>(input+i+2)); + +        //convert and scale +        __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); +        __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); + +        //pack + byteswap -> byteswap 32 bit words +        __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); +        tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); + +        //store to output +        _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); +    } + +    //convert remainder +    for (; i < nsamps; i++){ +        output[i] = uhd::byteswap(fc32_to_item32(input[i])); +    } +} + +#else +static UHD_INLINE void fc32_to_item32_bswap( +    const fc32_t *input, item32_t *output, size_t nsamps +){ +    for (size_t i = 0; i < nsamps; i++){ +        output[i] = uhd::byteswap(fc32_to_item32(input[i])); +    } +} + +#endif + +/*********************************************************************** + * Convert items32 buffer to complex float + **********************************************************************/ +static const float floats_per_short = float(1.0/shorts_per_float); + +static UHD_INLINE fc32_t item32_to_fc32(item32_t item){ +    return fc32_t( +        float(boost::int16_t(item >> 16)*floats_per_short), +        float(boost::int16_t(item >> 0)*floats_per_short) +    ); +} + +static UHD_INLINE void item32_to_fc32_nswap( +    const item32_t *input, fc32_t *output, size_t nsamps +){ +    for (size_t i = 0; i < nsamps; i++){ +        output[i] = item32_to_fc32(input[i]); +    } +} + +#if defined(USE_EMMINTRIN_H) +#include <emmintrin.h> + +static UHD_INLINE void item32_to_fc32_bswap( +    const item32_t *input, fc32_t *output, size_t nsamps +){ +    __m128 scalar = _mm_set_ps1(floats_per_short/(1 << 16)); +    __m128i zeroi = _mm_setzero_si128(); + +    //convert blocks of samples with intrinsics +    size_t i = 0; for (; i < (nsamps & ~0x3); i+=4){ +        //load from input +        __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); + +        //byteswap + unpack -> byteswap 32 bit words +        tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); +        __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); //value in upper 16 bits +        __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); + +        //convert and scale +        __m128 tmplo = _mm_mul_ps(_mm_cvtepi32_ps(tmpilo), scalar); +        __m128 tmphi = _mm_mul_ps(_mm_cvtepi32_ps(tmpihi), scalar); + +        //store to output +        _mm_storeu_ps(reinterpret_cast<float *>(output+i+0), tmplo); +        _mm_storeu_ps(reinterpret_cast<float *>(output+i+2), tmphi); +    } + +    //convert remainder +    for (; i < nsamps; i++){ +        output[i] = item32_to_fc32(uhd::byteswap(input[i])); +    } +} + +#else +static UHD_INLINE void item32_to_fc32_bswap( +    const item32_t *input, fc32_t *output, size_t nsamps +){ +    for (size_t i = 0; i < nsamps; i++){ +        output[i] = item32_to_fc32(uhd::byteswap(input[i])); +    } +} + +#endif + +#endif /* INCLUDED_LIBUHD_TRANSPORT_CONVERT_TYPES_IMPL_HPP */ diff --git a/host/lib/transport/gen_convert_types.py b/host/lib/transport/gen_convert_types.py index af2bcc7cb..951b634d9 100755 --- a/host/lib/transport/gen_convert_types.py +++ b/host/lib/transport/gen_convert_types.py @@ -24,66 +24,15 @@ TMPL_TEXT = """  \#include <uhd/config.hpp>  \#include <uhd/transport/convert_types.hpp> -\#include <uhd/utils/byteswap.hpp>  \#include <boost/cstdint.hpp>  \#include <boost/detail/endian.hpp>  \#include <stdexcept> -\#include <complex> - -//define the endian macros to convert integers -\#ifdef BOOST_BIG_ENDIAN -    \#define BE_MACRO(x) x -    \#define LE_MACRO(x) uhd::byteswap(x) -    static const bool is_big_endian = true; -\#else -    \#define BE_MACRO(x) uhd::byteswap(x) -    \#define LE_MACRO(x) x -    static const bool is_big_endian = false; -\#endif +\#include "convert_types_impl.hpp"  using namespace uhd;  /*********************************************************************** - * Constants - **********************************************************************/ -typedef std::complex<float>          fc32_t; -typedef std::complex<boost::int16_t> sc16_t; -typedef boost::uint32_t              item32_t; - -static const float shorts_per_float = float(32767); -static const float floats_per_short = float(1.0/shorts_per_float); - -/*********************************************************************** - * Single-sample converters - **********************************************************************/ -static UHD_INLINE item32_t sc16_to_item32(sc16_t num){ -    boost::uint16_t real = boost::int16_t(num.real()); -    boost::uint16_t imag = boost::int16_t(num.imag()); -    return (item32_t(real) << 16) | (item32_t(imag) << 0); -} - -static UHD_INLINE sc16_t item32_to_sc16(item32_t item){ -    return sc16_t( -        boost::uint16_t(item >> 16), -        boost::uint16_t(item >> 0) -    ); -} - -static UHD_INLINE item32_t fc32_to_item32(fc32_t num){ -    boost::uint16_t real = boost::int16_t(num.real()*shorts_per_float); -    boost::uint16_t imag = boost::int16_t(num.imag()*shorts_per_float); -    return (item32_t(real) << 16) | (item32_t(imag) << 0); -} - -static UHD_INLINE fc32_t item32_to_fc32(item32_t item){ -    return fc32_t( -        float(boost::int16_t(item >> 16)*floats_per_short), -        float(boost::int16_t(item >> 0)*floats_per_short) -    ); -} - -/*********************************************************************** - * Sample-buffer converters + * Generate predicate for jump table   **********************************************************************/  UHD_INLINE boost::uint8_t get_pred(      const io_type_t &io_type, @@ -92,27 +41,34 @@ UHD_INLINE boost::uint8_t get_pred(      boost::uint8_t pred = 0;      switch(otw_type.byteorder){ -    case otw_type_t::BO_BIG_ENDIAN:    pred |= $ph.be_p; break; -    case otw_type_t::BO_LITTLE_ENDIAN: pred |= $ph.le_p; break; -    ##let the compiler determine the native byte order (we could use python sys.byteorder) -    case otw_type_t::BO_NATIVE:        pred |= (is_big_endian)? $ph.be_p : $ph.le_p; break; -    default: throw std::runtime_error("unhandled byteorder type"); +    \#ifdef BOOST_BIG_ENDIAN +    case otw_type_t::BO_BIG_ENDIAN:    pred |= $ph.nswap_p; break; +    case otw_type_t::BO_LITTLE_ENDIAN: pred |= $ph.bswap_p; break; +    \#else +    case otw_type_t::BO_BIG_ENDIAN:    pred |= $ph.bswap_p; break; +    case otw_type_t::BO_LITTLE_ENDIAN: pred |= $ph.nswap_p; break; +    \#endif +    case otw_type_t::BO_NATIVE:        pred |= $ph.nswap_p; break; +    default: throw std::runtime_error("unhandled otw byteorder type");      } -    switch(otw_type.width){ -    case 16: pred |= $ph.w16_p; break; -    default: throw std::runtime_error("unhandled bit width"); +    switch(otw_type.get_sample_size()){ +    case sizeof(boost::uint32_t): pred |= $ph.item32_p; break; +    default: throw std::runtime_error("unhandled otw sample size");      }      switch(io_type.tid){ -    case io_type_t::COMPLEX_INT16:   pred |= $ph.sc16_p; break;      case io_type_t::COMPLEX_FLOAT32: pred |= $ph.fc32_p; break; +    case io_type_t::COMPLEX_INT16:   pred |= $ph.sc16_p; break;      default: throw std::runtime_error("unhandled io type id");      }      return pred;  } +/*********************************************************************** + * Convert host type to device type + **********************************************************************/  void transport::convert_io_type_to_otw_type(      const void *io_buff, const io_type_t &io_type,      void *otw_buff, const otw_type_t &otw_type, @@ -123,16 +79,16 @@ void transport::convert_io_type_to_otw_type(      case $pred:          #set $out_type = $ph.get_dev_type($pred)          #set $in_type = $ph.get_host_type($pred) -        #set $converter = $in_type+"_to_"+$out_type -        #set $xe_macro = $ph.get_xe_macro($pred) -        for (size_t i = 0; i < num_samps; i++){ -            (($(out_type)_t *)otw_buff)[i] = $(xe_macro)($(converter)(((const $(in_type)_t *)io_buff)[i])); -        } +        #set $converter = '_'.join([$in_type, 'to', $out_type, $ph.get_swap_type($pred)]) +        $(converter)((const $(in_type)_t *)io_buff, ($(out_type)_t *)otw_buff, num_samps);          break;      #end for      }  } +/*********************************************************************** + * Convert device type to host type + **********************************************************************/  void transport::convert_otw_type_to_io_type(      const void *otw_buff, const otw_type_t &otw_type,      void *io_buff, const io_type_t &io_type, @@ -143,11 +99,8 @@ void transport::convert_otw_type_to_io_type(      case $pred:          #set $out_type = $ph.get_host_type($pred)          #set $in_type = $ph.get_dev_type($pred) -        #set $converter = $in_type+"_to_"+$out_type -        #set $xe_macro = $ph.get_xe_macro($pred) -        for (size_t i = 0; i < num_samps; i++){ -            (($(out_type)_t *)io_buff)[i] = $(converter)($(xe_macro)(((const $(in_type)_t *)otw_buff)[i])); -        } +        #set $converter = '_'.join([$in_type, 'to', $out_type, $ph.get_swap_type($pred)]) +        $(converter)((const $(in_type)_t *)otw_buff, ($(out_type)_t *)io_buff, num_samps);          break;      #end for      } @@ -160,29 +113,32 @@ def parse_tmpl(_tmpl_text, **kwargs):      return str(Template(_tmpl_text, kwargs))  class ph: -    be_p   = 0b00001 -    le_p   = 0b00000 -    w16_p  = 0b00000 -    sc16_p = 0b00010 -    fc32_p = 0b00000 +    bswap_p  = 0b00001 +    nswap_p  = 0b00000 +    item32_p = 0b00000 +    sc16_p   = 0b00010 +    fc32_p   = 0b00000      nbits = 2 #see above      @staticmethod -    def get_xe_macro(pred): -        if (pred & ph.be_p) == ph.be_p: return 'BE_MACRO' -        if (pred & ph.le_p) == ph.le_p: return 'LE_MACRO' +    def has(pred, flag): return (pred & flag) == flag + +    @staticmethod +    def get_swap_type(pred): +        if ph.has(pred, ph.bswap_p): return 'bswap' +        if ph.has(pred, ph.nswap_p): return 'nswap'          raise NotImplementedError      @staticmethod      def get_dev_type(pred): -        if (pred & ph.w16_p) == ph.w16_p: return 'item32' +        if ph.has(pred, ph.item32_p): return 'item32'          raise NotImplementedError      @staticmethod      def get_host_type(pred): -        if (pred & ph.sc16_p) == ph.sc16_p: return 'sc16' -        if (pred & ph.fc32_p) == ph.fc32_p: return 'fc32' +        if ph.has(pred, ph.sc16_p): return 'sc16' +        if ph.has(pred, ph.fc32_p): return 'fc32'          raise NotImplementedError  if __name__ == '__main__': diff --git a/host/lib/transport/gen_vrt.py b/host/lib/transport/gen_vrt_if_packet.py index 6cdd6645d..7910ff60d 100755 --- a/host/lib/transport/gen_vrt.py +++ b/host/lib/transport/gen_vrt_if_packet.py @@ -31,7 +31,7 @@ TMPL_TEXT = """   * This file was generated by $file on $time.strftime("%c")   **********************************************************************/ -\#include <uhd/transport/vrt.hpp> +\#include <uhd/transport/vrt_if_packet.hpp>  \#include <uhd/utils/byteswap.hpp>  \#include <boost/detail/endian.hpp>  \#include <stdexcept> @@ -61,20 +61,22 @@ using namespace uhd::transport;  #set $tsf_p = 0b01000  #set $tlr_p = 0b10000 -void vrt::pack_$(suffix)( -    const tx_metadata_t &metadata, //input -    boost::uint32_t *header_buff,  //output -    size_t &num_header_words32,    //output -    size_t num_payload_words32,    //input -    size_t &num_packet_words32,    //output -    size_t packet_count,           //input -    double tick_rate               //input +static UHD_INLINE void pack_uint64_$(suffix)(boost::uint64_t num, boost::uint32_t *mem){ +    *(reinterpret_cast<boost::uint64_t *>(mem)) = $(XE_MACRO)(num); +} + +void vrt::if_hdr_pack_$(suffix)( +    boost::uint32_t *packet_buff, +    if_packet_info_t &if_packet_info  ){      boost::uint32_t vrt_hdr_flags = 0;      boost::uint8_t pred = 0; -    if (metadata.has_stream_id) pred |= $hex($sid_p); -    if (metadata.has_time_spec) pred |= $hex($tsi_p | $tsf_p); +    if (if_packet_info.has_sid) pred |= $hex($sid_p); +    if (if_packet_info.has_cid) pred |= $hex($cid_p); +    if (if_packet_info.has_tsi) pred |= $hex($tsi_p); +    if (if_packet_info.has_tsf) pred |= $hex($tsf_p); +    if (if_packet_info.has_tlr) pred |= $hex($tlr_p);      switch(pred){      #for $pred in range(2**5) @@ -83,81 +85,75 @@ void vrt::pack_$(suffix)(          #set $flags = 0          ########## Stream ID ##########          #if $pred & $sid_p -            header_buff[$num_header_words] = $(XE_MACRO)(metadata.stream_id); +            packet_buff[$num_header_words] = $(XE_MACRO)(if_packet_info.sid);              #set $num_header_words += 1              #set $flags |= (0x1 << 28);          #end if          ########## Class ID ##########          #if $pred & $cid_p -            header_buff[$num_header_words] = 0; -            #set $num_header_words += 1 -            header_buff[$num_header_words] = 0; -            #set $num_header_words += 1 +            pack_uint64_$(suffix)(if_packet_info.cid, packet_buff+$num_header_words); +            #set $num_header_words += 2              #set $flags |= (0x1 << 27);          #end if          ########## Integer Time ##########          #if $pred & $tsi_p -            header_buff[$num_header_words] = $(XE_MACRO)(metadata.time_spec.secs); +            packet_buff[$num_header_words] = $(XE_MACRO)(if_packet_info.tsi);              #set $num_header_words += 1              #set $flags |= (0x3 << 22);          #end if          ########## Fractional Time ##########          #if $pred & $tsf_p -            header_buff[$num_header_words] = 0; -            #set $num_header_words += 1 -            header_buff[$num_header_words] = $(XE_MACRO)(metadata.time_spec.get_ticks(tick_rate)); -            #set $num_header_words += 1 +            pack_uint64_$(suffix)(if_packet_info.tsf, packet_buff+$num_header_words); +            #set $num_header_words += 2              #set $flags |= (0x1 << 20);          #end if          ########## Trailer ##########          #if $pred & $tlr_p +            //packet_buff[$num_header_words+if_packet_info.num_payload_words32] = $(XE_MACRO)(if_packet_info.tlr);              #set $flags |= (0x1 << 26);              #set $num_trailer_words = 1;          #else +            //packet_buff[$num_header_words+if_packet_info.num_payload_words32] = 0;              #set $num_trailer_words = 0;          #end if          ########## Variables ########## -            num_header_words32 = $num_header_words; -            num_packet_words32 = $($num_header_words + $num_trailer_words) + num_payload_words32; +            if_packet_info.num_header_words32 = $num_header_words; +            if_packet_info.num_packet_words32 = $($num_header_words + $num_trailer_words) + if_packet_info.num_payload_words32;              vrt_hdr_flags = $hex($flags);          break;      #end for      }      //set the burst flags -    if (metadata.start_of_burst) vrt_hdr_flags |= $hex(0x1 << 25); -    if (metadata.end_of_burst)   vrt_hdr_flags |= $hex(0x1 << 24); +    if (if_packet_info.sob) vrt_hdr_flags |= $hex(0x1 << 25); +    if (if_packet_info.eob) vrt_hdr_flags |= $hex(0x1 << 24);      //fill in complete header word -    header_buff[0] = $(XE_MACRO)(boost::uint32_t(0 +    packet_buff[0] = $(XE_MACRO)(boost::uint32_t(0          | vrt_hdr_flags -        | ((packet_count & 0xf) << 16) -        | (num_packet_words32 & 0xffff) +        | ((if_packet_info.packet_count & 0xf) << 16) +        | (if_packet_info.num_packet_words32 & 0xffff)      ));  } -void vrt::unpack_$(suffix)( -    rx_metadata_t &metadata,            //output -    const boost::uint32_t *header_buff, //input -    size_t &num_header_words32,         //output -    size_t &num_payload_words32,        //output -    size_t num_packet_words32,          //input -    size_t &packet_count,               //output -    double tick_rate                    //input -){ -    //clear the metadata -    metadata = rx_metadata_t(); +static UHD_INLINE void unpack_uint64_$(suffix)(boost::uint64_t &num, const boost::uint32_t *mem){ +    num = $(XE_MACRO)(*reinterpret_cast<const boost::uint64_t *>(mem)); +} +void vrt::if_hdr_unpack_$(suffix)( +    const boost::uint32_t *packet_buff, +    if_packet_info_t &if_packet_info +){      //extract vrt header -    boost::uint32_t vrt_hdr_word = $(XE_MACRO)(header_buff[0]); +    boost::uint32_t vrt_hdr_word = $(XE_MACRO)(packet_buff[0]);      size_t packet_words32 = vrt_hdr_word & 0xffff; -    packet_count = (vrt_hdr_word >> 16) & 0xf; +    if_packet_info.packet_count = (vrt_hdr_word >> 16) & 0xf;      //failure cases -    if (packet_words32 == 0 or num_packet_words32 < packet_words32) +    if (if_packet_info.num_packet_words32 < packet_words32)          throw std::runtime_error("bad vrt header or packet fragment");      if (vrt_hdr_word & (0x7 << 29)) -        throw std::runtime_error("unsupported vrt packet type"); +        throw std::runtime_error("bad vrt header or unsupported packet type");      boost::uint8_t pred = 0;      if(vrt_hdr_word & $hex(0x1 << 28)) pred |= $hex($sid_p); @@ -169,45 +165,55 @@ void vrt::unpack_$(suffix)(      switch(pred){      #for $pred in range(2**5)      case $pred: -        #set $set_has_time_spec = False +        #set $has_time_spec = False          #set $num_header_words = 1          ########## Stream ID ##########          #if $pred & $sid_p -            metadata.has_stream_id = true; -            metadata.stream_id = $(XE_MACRO)(header_buff[$num_header_words]); +            if_packet_info.has_sid = true; +            if_packet_info.sid = $(XE_MACRO)(packet_buff[$num_header_words]);              #set $num_header_words += 1 +        #else +            if_packet_info.has_sid = false;          #end if          ########## Class ID ##########          #if $pred & $cid_p -            #set $num_header_words += 1 -            #set $num_header_words += 1 +            if_packet_info.has_cid = true; +            unpack_uint64_$(suffix)(if_packet_info.cid, packet_buff+$num_header_words); +            #set $num_header_words += 2 +        #else +            if_packet_info.has_cid = false;          #end if          ########## Integer Time ##########          #if $pred & $tsi_p -            metadata.has_time_spec = true; -            #set $set_has_time_spec = True -            metadata.time_spec.secs = $(XE_MACRO)(header_buff[$num_header_words]); +            if_packet_info.has_tsi = true; +            if_packet_info.tsi = $(XE_MACRO)(packet_buff[$num_header_words]);              #set $num_header_words += 1 +        #else +            if_packet_info.has_tsi = false;          #end if          ########## Fractional Time ##########          #if $pred & $tsf_p -            #if not $set_has_time_spec -            metadata.has_time_spec = true; -                #set $set_has_time_spec = True -            #end if -            #set $num_header_words += 1 -            metadata.time_spec.set_ticks($(XE_MACRO)(header_buff[$num_header_words]), tick_rate); -            #set $num_header_words += 1 +            if_packet_info.has_tsf = true; +            unpack_uint64_$(suffix)(if_packet_info.tsf, packet_buff+$num_header_words); +            #set $num_header_words += 2 +        #else +            if_packet_info.has_tsf = false;          #end if          ########## Trailer ##########          #if $pred & $tlr_p +            if_packet_info.has_tlr = true; +            if_packet_info.tlr = $(XE_MACRO)(packet_buff[$num_header_words+packet_words32]);              #set $num_trailer_words = 1;          #else +            if_packet_info.has_tlr = false;              #set $num_trailer_words = 0;          #end if          ########## Variables ########## -            num_header_words32 = $num_header_words; -            num_payload_words32 = packet_words32 - $($num_header_words + $num_trailer_words); +            //another failure case +            if (packet_words32 < $($num_header_words + $num_trailer_words)) +                throw std::runtime_error("bad vrt header or invalid packet length"); +            if_packet_info.num_header_words32 = $num_header_words; +            if_packet_info.num_payload_words32 = packet_words32 - $($num_header_words + $num_trailer_words);          break;      #end for      } diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index c3c02707e..7f9292d24 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -27,7 +27,8 @@ using namespace uhd::transport;  /***********************************************************************   * Constants   **********************************************************************/ -static const size_t MIN_SOCK_BUFF_SIZE = size_t(100e3); +//enough buffering for half a second of samples at full rate on usrp2 +static const size_t MIN_SOCK_BUFF_SIZE = size_t(sizeof(boost::uint32_t) * 25e6 * 0.5);  static const size_t MAX_DGRAM_SIZE = 1500; //assume max size on send and recv  static const double RECV_TIMEOUT = 0.1; //100 ms @@ -159,6 +160,12 @@ template<typename Opt> static void resize_buff_helper(      //otherwise, ensure that the buffer is at least the minimum size      else if (udp_trans->get_buff_size<Opt>() < MIN_SOCK_BUFF_SIZE){          resize_buff_helper<Opt>(udp_trans, MIN_SOCK_BUFF_SIZE, name); +        if (udp_trans->get_buff_size<Opt>() < MIN_SOCK_BUFF_SIZE){ +            std::cerr << boost::format( +                "Warning: the %s buffer size is smaller than the recommended size of %d bytes.\n" +                "    See the USRP2 application notes on buffer resizing." +            ) % name % MIN_SOCK_BUFF_SIZE << std::endl; +        }      }  } diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp index 8dfc7b3d0..68edeb1e1 100644 --- a/host/lib/transport/vrt_packet_handler.hpp +++ b/host/lib/transport/vrt_packet_handler.hpp @@ -20,48 +20,51 @@  #include <uhd/config.hpp>  #include <uhd/device.hpp> +#include <uhd/utils/assert.hpp>  #include <uhd/types/io_type.hpp>  #include <uhd/types/otw_type.hpp>  #include <uhd/types/metadata.hpp> -#include <uhd/transport/vrt.hpp> +#include <uhd/transport/vrt_if_packet.hpp>  #include <uhd/transport/convert_types.hpp>  #include <uhd/transport/zero_copy.hpp> -#include <boost/asio/buffer.hpp>  #include <boost/function.hpp>  #include <stdexcept>  #include <iostream> +#include <vector>  namespace vrt_packet_handler{  /***********************************************************************   * vrt packet handler for recv   **********************************************************************/ +    typedef std::vector<uhd::transport::managed_recv_buffer::sptr> managed_recv_buffs_t; +    typedef boost::function<bool(managed_recv_buffs_t &)> get_recv_buffs_t; +      struct recv_state{ +        //width of the receiver in channels +        size_t width; +          //init the expected seq number -        size_t next_packet_seq; +        std::vector<size_t> next_packet_seq;          //state variables to handle fragments -        uhd::transport::managed_recv_buffer::sptr managed_buff; -        boost::asio::const_buffer copy_buff; +        managed_recv_buffs_t managed_buffs; +        std::vector<const boost::uint8_t *> copy_buffs; +        size_t size_of_copy_buffs;          size_t fragment_offset_in_samps; -        recv_state(void){ -            //first expected seq is zero -            next_packet_seq = 0; - -            //initially empty copy buffer -            copy_buff = boost::asio::buffer("", 0); +        recv_state(size_t width): +            width(width), +            next_packet_seq(width, 0), +            managed_buffs(width), +            copy_buffs(width, NULL), +            size_of_copy_buffs(0), +            fragment_offset_in_samps(0) +        { +            /* NOP */          }      }; -    typedef boost::function<uhd::transport::managed_recv_buffer::sptr(void)> get_recv_buff_t; - -    typedef boost::function<void(uhd::transport::managed_recv_buffer::sptr)> recv_cb_t; - -    static UHD_INLINE void recv_cb_nop(uhd::transport::managed_recv_buffer::sptr){ -        /* NOP */ -    } -      /*******************************************************************       * Unpack a received vrt header and set the copy buffer.       *  - helper function for vrt_packet_handler::_recv1 @@ -74,33 +77,39 @@ namespace vrt_packet_handler{          vrt_unpacker_type vrt_unpacker,          size_t vrt_header_offset_words32      ){ -        size_t num_packet_words32 = state.managed_buff->size()/sizeof(boost::uint32_t); +        size_t num_packet_words32 = state.managed_buffs[0]->size()/sizeof(boost::uint32_t);          if (num_packet_words32 <= vrt_header_offset_words32){ -            state.copy_buff = boost::asio::buffer("", 0); +            state.size_of_copy_buffs = 0;              return; //must exit here after setting the buffer          } -        const boost::uint32_t *vrt_hdr = state.managed_buff->cast<const boost::uint32_t *>() + vrt_header_offset_words32; -        size_t num_header_words32_out, num_payload_words32_out, packet_count_out; -        vrt_unpacker( -            metadata,                //output -            vrt_hdr,                 //input -            num_header_words32_out,  //output -            num_payload_words32_out, //output -            num_packet_words32,      //input -            packet_count_out,        //output -            tick_rate -        ); -        //handle the packet count / sequence number -        if (packet_count_out != state.next_packet_seq){ -            std::cerr << "S" << (packet_count_out - state.next_packet_seq)%16; +        //vrt unpack each managed buffer +        uhd::transport::vrt::if_packet_info_t if_packet_info; +        for (size_t i = 0; i < state.width; i++){ +            const boost::uint32_t *vrt_hdr = state.managed_buffs[i]->cast<const boost::uint32_t *>() + vrt_header_offset_words32; +            if_packet_info.num_packet_words32 = num_packet_words32 - vrt_header_offset_words32; +            vrt_unpacker(vrt_hdr, if_packet_info); + +            //handle the packet count / sequence number +            if (if_packet_info.packet_count != state.next_packet_seq[i]){ +                std::cerr << "S" << (if_packet_info.packet_count - state.next_packet_seq[i])%16; +            } +            state.next_packet_seq[i] = (if_packet_info.packet_count+1)%16; + +            //setup the buffer to point to the data +            state.copy_buffs[i] = reinterpret_cast<const boost::uint8_t *>(vrt_hdr + if_packet_info.num_header_words32); + +            //store the minimum payload length into the copy buffer length +            size_t num_payload_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t); +            if (i == 0 or state.size_of_copy_buffs > num_payload_bytes){ +                state.size_of_copy_buffs = num_payload_bytes; +            }          } -        state.next_packet_seq = (packet_count_out+1)%16; -        //setup the buffer to point to the data -        state.copy_buff = boost::asio::buffer( -            vrt_hdr + num_header_words32_out, -            num_payload_words32_out*sizeof(boost::uint32_t) +        //store the last vrt info into the metadata +        metadata.has_time_spec = if_packet_info.has_tsi and if_packet_info.has_tsf; +        metadata.time_spec = uhd::time_spec_t( +            time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), tick_rate          );      } @@ -111,24 +120,24 @@ namespace vrt_packet_handler{      template<typename vrt_unpacker_type>      static UHD_INLINE size_t _recv1(          recv_state &state, -        void *recv_mem, +        const std::vector<void *> &buffs, +        size_t offset_bytes,          size_t total_samps,          uhd::rx_metadata_t &metadata,          const uhd::io_type_t &io_type,          const uhd::otw_type_t &otw_type,          double tick_rate,          vrt_unpacker_type vrt_unpacker, -        const get_recv_buff_t &get_recv_buff, +        const get_recv_buffs_t &get_recv_buffs,          //use these two params to handle a layer above vrt -        size_t vrt_header_offset_words32, -        const recv_cb_t &recv_cb +        size_t vrt_header_offset_words32      ){ +        metadata.has_time_spec = false; //false unless set in the helper +          //perform a receive if no rx data is waiting to be copied -        if (boost::asio::buffer_size(state.copy_buff) == 0){ +        if (state.size_of_copy_buffs == 0){              state.fragment_offset_in_samps = 0; -            state.managed_buff = get_recv_buff(); -            if (state.managed_buff.get() == NULL) return 0; -            recv_cb(state.managed_buff); //callback before vrt unpack +            if (not get_recv_buffs(state.managed_buffs)) return 0;              try{                  _recv1_helper(                      state, metadata, tick_rate, vrt_unpacker, vrt_header_offset_words32 @@ -141,28 +150,30 @@ namespace vrt_packet_handler{          //extract the number of samples available to copy          size_t bytes_per_item = otw_type.get_sample_size(); -        size_t bytes_available = boost::asio::buffer_size(state.copy_buff); -        size_t num_samps = std::min(total_samps, bytes_available/bytes_per_item); +        size_t bytes_available = state.size_of_copy_buffs; +        size_t nsamps_to_copy = std::min(total_samps, bytes_available/bytes_per_item); +        size_t bytes_to_copy = nsamps_to_copy*bytes_per_item; + +        for (size_t i = 0; i < state.width; i++){ +            //copy-convert the samples from the recv buffer +            uhd::transport::convert_otw_type_to_io_type( +                state.copy_buffs[i], otw_type, +                reinterpret_cast<boost::uint8_t *>(buffs[i]) + offset_bytes, +                io_type, nsamps_to_copy +            ); + +            //update the rx copy buffer to reflect the bytes copied +            state.copy_buffs[i] += bytes_to_copy; +        } +        //update the copy buffer's availability +        state.size_of_copy_buffs -= bytes_to_copy;          //setup the fragment flags and offset -        metadata.more_fragments = total_samps < num_samps; +        metadata.more_fragments = state.size_of_copy_buffs != 0;          metadata.fragment_offset = state.fragment_offset_in_samps; -        state.fragment_offset_in_samps += num_samps; //set for next call - -        //copy-convert the samples from the recv buffer -        uhd::transport::convert_otw_type_to_io_type( -            boost::asio::buffer_cast<const void*>(state.copy_buff), otw_type, -            recv_mem, io_type, num_samps -        ); - -        //update the rx copy buffer to reflect the bytes copied -        size_t bytes_copied = num_samps*bytes_per_item; -        state.copy_buff = boost::asio::buffer( -            boost::asio::buffer_cast<const boost::uint8_t*>(state.copy_buff) + bytes_copied, -            bytes_available - bytes_copied -        ); +        state.fragment_offset_in_samps += nsamps_to_copy; //set for next call -        return num_samps; +        return nsamps_to_copy;      }      /******************************************************************* @@ -171,21 +182,18 @@ namespace vrt_packet_handler{      template<typename vrt_unpacker_type>      static UHD_INLINE size_t recv(          recv_state &state, -        const boost::asio::mutable_buffer &buff, +        const std::vector<void *> &buffs, +        const size_t total_num_samps,          uhd::rx_metadata_t &metadata,          uhd::device::recv_mode_t recv_mode,          const uhd::io_type_t &io_type,          const uhd::otw_type_t &otw_type,          double tick_rate,          vrt_unpacker_type vrt_unpacker, -        const get_recv_buff_t &get_recv_buff, +        const get_recv_buffs_t &get_recv_buffs,          //use these two params to handle a layer above vrt -        size_t vrt_header_offset_words32 = 0, -        const recv_cb_t& recv_cb = &recv_cb_nop +        size_t vrt_header_offset_words32 = 0      ){ -        metadata = uhd::rx_metadata_t(); //init the metadata -        const size_t total_num_samps = boost::asio::buffer_size(buff)/io_type.size; -          switch(recv_mode){          //////////////////////////////////////////////////////////////// @@ -193,15 +201,14 @@ namespace vrt_packet_handler{          ////////////////////////////////////////////////////////////////              return _recv1(                  state, -                boost::asio::buffer_cast<void *>(buff), +                buffs, 0,                  total_num_samps,                  metadata,                  io_type, otw_type,                  tick_rate,                  vrt_unpacker, -                get_recv_buff, -                vrt_header_offset_words32, -                recv_cb +                get_recv_buffs, +                vrt_header_offset_words32              );          } @@ -213,15 +220,14 @@ namespace vrt_packet_handler{              while(accum_num_samps < total_num_samps){                  size_t num_samps = _recv1(                      state, -                    boost::asio::buffer_cast<boost::uint8_t *>(buff) + (accum_num_samps*io_type.size), +                    buffs, accum_num_samps*io_type.size,                      total_num_samps - accum_num_samps,                      (accum_num_samps == 0)? metadata : tmp_md, //only the first metadata gets kept                      io_type, otw_type,                      tick_rate,                      vrt_unpacker, -                    get_recv_buff, -                    vrt_header_offset_words32, -                    recv_cb +                    get_recv_buffs, +                    vrt_header_offset_words32                  );                  if (num_samps == 0) break; //had a recv timeout or error, break loop                  accum_num_samps += num_samps; @@ -236,23 +242,18 @@ namespace vrt_packet_handler{  /***********************************************************************   * vrt packet handler for send   **********************************************************************/ +    typedef std::vector<uhd::transport::managed_send_buffer::sptr> managed_send_buffs_t; +    typedef boost::function<bool(managed_send_buffs_t &)> get_send_buffs_t; +      struct send_state{          //init the expected seq number          size_t next_packet_seq; -        send_state(void){ -            next_packet_seq = 0; +        send_state(void) : next_packet_seq(0){ +            /* NOP */          }      }; -    typedef boost::function<uhd::transport::managed_send_buffer::sptr(void)> get_send_buff_t; - -    typedef boost::function<void(uhd::transport::managed_send_buffer::sptr)> send_cb_t; - -    static UHD_INLINE void send_cb_nop(uhd::transport::managed_send_buffer::sptr){ -        /* NOP */ -    } -      /*******************************************************************       * Pack a vrt header, copy-convert the data, and send it.       *  - helper function for vrt_packet_handler::send @@ -260,46 +261,42 @@ namespace vrt_packet_handler{      template<typename vrt_packer_type>      static UHD_INLINE void _send1(          send_state &state, -        const void *send_mem, +        const std::vector<const void *> &buffs, +        size_t offset_bytes,          size_t num_samps, -        const uhd::tx_metadata_t &metadata, +        uhd::transport::vrt::if_packet_info_t &if_packet_info,          const uhd::io_type_t &io_type,          const uhd::otw_type_t &otw_type, -        double tick_rate,          vrt_packer_type vrt_packer, -        const get_send_buff_t &get_send_buff, -        size_t vrt_header_offset_words32, -        const send_cb_t& send_cb +        const get_send_buffs_t &get_send_buffs, +        size_t vrt_header_offset_words32      ){ -        //get a new managed send buffer -        uhd::transport::managed_send_buffer::sptr send_buff = get_send_buff(); -        boost::uint32_t *tx_mem = send_buff->cast<boost::uint32_t *>() + vrt_header_offset_words32; - -        size_t num_header_words32, num_packet_words32; -        size_t packet_count = state.next_packet_seq++; - -        //pack metadata into a vrt header -        vrt_packer( -            metadata,            //input -            tx_mem,              //output -            num_header_words32,  //output -            num_samps,           //input -            num_packet_words32,  //output -            packet_count,        //input -            tick_rate -        ); - -        //copy-convert the samples into the send buffer -        uhd::transport::convert_io_type_to_otw_type( -            send_mem, io_type, -            tx_mem + num_header_words32, otw_type, -            num_samps -        ); - -        send_cb(send_buff); //callback after memory filled +        //load the rest of the if_packet_info in here +        if_packet_info.num_payload_words32 = (num_samps*otw_type.get_sample_size())/sizeof(boost::uint32_t); +        if_packet_info.packet_count = state.next_packet_seq++; + +        //get send buffers for each channel +        managed_send_buffs_t send_buffs(buffs.size()); +        UHD_ASSERT_THROW(get_send_buffs(send_buffs)); + +        for (size_t i = 0; i < buffs.size(); i++){ +            //calculate pointers with offsets to io and otw memory +            const boost::uint8_t *io_mem = reinterpret_cast<const boost::uint8_t *>(buffs[i]) + offset_bytes; +            boost::uint32_t *otw_mem = send_buffs[i]->cast<boost::uint32_t *>() + vrt_header_offset_words32; + +            //pack metadata into a vrt header +            vrt_packer(otw_mem, if_packet_info); + +            //copy-convert the samples into the send buffer +            uhd::transport::convert_io_type_to_otw_type( +                io_mem, io_type, +                otw_mem + if_packet_info.num_header_words32, otw_type, +                num_samps +            ); -        //commit the samples to the zero-copy interface -        send_buff->commit(num_packet_words32*sizeof(boost::uint32_t)); +            //commit the samples to the zero-copy interface +            send_buffs[i]->commit(if_packet_info.num_packet_words32*sizeof(boost::uint32_t)); +        }      }      /******************************************************************* @@ -308,20 +305,27 @@ namespace vrt_packet_handler{      template<typename vrt_packer_type>      static UHD_INLINE size_t send(          send_state &state, -        const boost::asio::const_buffer &buff, +        const std::vector<const void *> &buffs, +        const size_t total_num_samps,          const uhd::tx_metadata_t &metadata,          uhd::device::send_mode_t send_mode,          const uhd::io_type_t &io_type,          const uhd::otw_type_t &otw_type,          double tick_rate,          vrt_packer_type vrt_packer, -        const get_send_buff_t &get_send_buff, +        const get_send_buffs_t &get_send_buffs,          size_t max_samples_per_packet,          //use these two params to handle a layer above vrt -        size_t vrt_header_offset_words32 = 0, -        const send_cb_t& send_cb = &send_cb_nop +        size_t vrt_header_offset_words32 = 0      ){ -        const size_t total_num_samps = boost::asio::buffer_size(buff)/io_type.size; +        //translate the metadata to vrt if packet info +        uhd::transport::vrt::if_packet_info_t if_packet_info; +        if_packet_info.has_sid = false; +        if_packet_info.has_cid = false; +        if_packet_info.has_tlr = false; +        if_packet_info.tsi = boost::uint32_t(metadata.time_spec.get_full_secs()); +        if_packet_info.tsf = boost::uint64_t(metadata.time_spec.get_tick_count(tick_rate)); +          if (total_num_samps <= max_samples_per_packet) send_mode = uhd::device::SEND_MODE_ONE_PACKET;          switch(send_mode){ @@ -329,17 +333,22 @@ namespace vrt_packet_handler{          case uhd::device::SEND_MODE_ONE_PACKET:{          ////////////////////////////////////////////////////////////////              size_t num_samps = std::min(total_num_samps, max_samples_per_packet); + +            //fill in parts of the packet info overwrote in full buff mode +            if_packet_info.has_tsi = metadata.has_time_spec; +            if_packet_info.has_tsf = metadata.has_time_spec; +            if_packet_info.sob = metadata.start_of_burst; +            if_packet_info.eob = metadata.end_of_burst; +              _send1(                  state, -                boost::asio::buffer_cast<const void *>(buff), +                buffs, 0,                  num_samps, -                metadata, +                if_packet_info,                  io_type, otw_type, -                tick_rate,                  vrt_packer, -                get_send_buff, -                vrt_header_offset_words32, -                send_cb +                get_send_buffs, +                vrt_header_offset_words32              );              return num_samps;          } @@ -352,29 +361,25 @@ namespace vrt_packet_handler{              static const size_t first_fragment_index = 0;              const size_t final_fragment_index = num_fragments-1; -            //make a rw copy of the metadata to re-flag below -            uhd::tx_metadata_t md(metadata); -              //loop through the following fragment indexes              for (size_t n = first_fragment_index; n <= final_fragment_index; n++){                  //calculate new flags for the fragments -                md.has_time_spec  = metadata.has_time_spec  and (n == first_fragment_index); -                md.start_of_burst = metadata.start_of_burst and (n == first_fragment_index); -                md.end_of_burst   = metadata.end_of_burst   and (n == final_fragment_index); +                if_packet_info.has_tsi = metadata.has_time_spec  and (n == first_fragment_index); +                if_packet_info.has_tsf = metadata.has_time_spec  and (n == first_fragment_index); +                if_packet_info.sob     = metadata.start_of_burst and (n == first_fragment_index); +                if_packet_info.eob     = metadata.end_of_burst   and (n == final_fragment_index);                  //send the fragment with the helper function                  _send1(                      state, -                    boost::asio::buffer_cast<const boost::uint8_t *>(buff) + (n*max_samples_per_packet*io_type.size), +                    buffs, n*max_samples_per_packet*io_type.size,                      (n == final_fragment_index)?(total_num_samps%max_samples_per_packet):max_samples_per_packet, -                    md, +                    if_packet_info,                      io_type, otw_type, -                    tick_rate,                      vrt_packer, -                    get_send_buff, -                    vrt_header_offset_words32, -                    send_cb +                    get_send_buffs, +                    vrt_header_offset_words32                  );              }              return total_num_samps; diff --git a/host/lib/types.cpp b/host/lib/types.cpp index daf3be7f7..9cf2a2220 100644 --- a/host/lib/types.cpp +++ b/host/lib/types.cpp @@ -96,8 +96,6 @@ stream_cmd_t::stream_cmd_t(const stream_mode_t &stream_mode):   * metadata   **********************************************************************/  rx_metadata_t::rx_metadata_t(void): -    has_stream_id(false), -    stream_id(0),      has_time_spec(false),      time_spec(time_spec_t()),      more_fragments(false), @@ -107,8 +105,6 @@ rx_metadata_t::rx_metadata_t(void):  }  tx_metadata_t::tx_metadata_t(void): -    has_stream_id(false), -    stream_id(0),      has_time_spec(false),      time_spec(time_spec_t()),      start_of_burst(false), @@ -120,19 +116,63 @@ tx_metadata_t::tx_metadata_t(void):  /***********************************************************************   * time spec   **********************************************************************/ -time_spec_t::time_spec_t(boost::uint32_t secs, double nsecs): -    secs(secs), -    nsecs(nsecs) +time_spec_t::time_spec_t(double secs): +    _full_secs(0), +    _frac_secs(secs)  {      /* NOP */  } -boost::uint32_t time_spec_t::get_ticks(double tick_rate) const{ -    return boost::math::iround(nsecs*tick_rate*1e-9); +time_spec_t::time_spec_t(time_t full_secs, double frac_secs): +    _full_secs(full_secs), +    _frac_secs(frac_secs) +{ +    /* NOP */ +} + +time_spec_t::time_spec_t(time_t full_secs, size_t tick_count, double tick_rate): +    _full_secs(full_secs), +    _frac_secs(double(tick_count)/tick_rate) +{ +    /* NOP */ +} + +size_t time_spec_t::get_tick_count(double tick_rate) const{ +    return boost::math::iround(this->get_frac_secs()*tick_rate); +} + +double time_spec_t::get_real_secs(void) const{ +    return this->_full_secs + this->_frac_secs; +} + +time_t time_spec_t::get_full_secs(void) const{ +    return this->_full_secs + time_t(std::floor(this->_frac_secs)); +} + +double time_spec_t::get_frac_secs(void) const{ +    return std::fmod(this->_frac_secs, 1.0); +} + +time_spec_t &time_spec_t::operator+=(const time_spec_t &rhs){ +    this->_full_secs += rhs.get_full_secs(); +    this->_frac_secs += rhs.get_frac_secs(); +    return *this; +} + +time_spec_t &time_spec_t::operator-=(const time_spec_t &rhs){ +    this->_full_secs -= rhs.get_full_secs(); +    this->_frac_secs -= rhs.get_frac_secs(); +    return *this; +} + +bool uhd::operator==(const time_spec_t &lhs, const time_spec_t &rhs){ +    return lhs.get_full_secs() == rhs.get_full_secs() and lhs.get_frac_secs() == rhs.get_frac_secs();  } -void time_spec_t::set_ticks(boost::uint32_t ticks, double tick_rate){ -    nsecs = double(ticks)*1e9/tick_rate; +bool uhd::operator<(const time_spec_t &lhs, const time_spec_t &rhs){ +    if (lhs.get_full_secs() < rhs.get_full_secs()) return true; +    if (lhs.get_full_secs() > rhs.get_full_secs()) return false; +    return lhs.get_frac_secs() < rhs.get_frac_secs();  }  /*********************************************************************** diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 3e12c087e..814affdd0 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -23,6 +23,7 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard_id.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/dboard_manager.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/dsp_utils.hpp +    ${CMAKE_SOURCE_DIR}/lib/usrp/mimo_usrp.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/simple_usrp.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/tune_helper.cpp  ) diff --git a/host/lib/usrp/mimo_usrp.cpp b/host/lib/usrp/mimo_usrp.cpp new file mode 100644 index 000000000..fd8225074 --- /dev/null +++ b/host/lib/usrp/mimo_usrp.cpp @@ -0,0 +1,291 @@ +// +// Copyright 2010 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/usrp/mimo_usrp.hpp> +#include <uhd/usrp/tune_helper.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <stdexcept> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * MIMO USRP Implementation + **********************************************************************/ +class mimo_usrp_impl : public mimo_usrp{ +public: +    mimo_usrp_impl(const device_addr_t &addr){ +        _dev = device::make(addr); + +        //extract each mboard and its sub-devices +        BOOST_FOREACH(const std::string &name, (*_dev)[DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>()){ +            _mboards.push_back((*_dev)[named_prop_t(DEVICE_PROP_MBOARD, name)]); +            _rx_dsps.push_back(_mboards.back()[MBOARD_PROP_RX_DSP]); +            _tx_dsps.push_back(_mboards.back()[MBOARD_PROP_TX_DSP]); + +            //extract rx subdevice +            _rx_dboards.push_back(_mboards.back()[MBOARD_PROP_RX_DBOARD]); +            std::string rx_subdev_in_use = _rx_dboards.back()[DBOARD_PROP_USED_SUBDEVS].as<prop_names_t>().at(0); +            _rx_subdevs.push_back(_rx_dboards.back()[named_prop_t(DBOARD_PROP_SUBDEV, rx_subdev_in_use)]); + +            //extract tx subdevice +            _tx_dboards.push_back(_mboards.back()[MBOARD_PROP_TX_DBOARD]); +            std::string tx_subdev_in_use = _tx_dboards.back()[DBOARD_PROP_USED_SUBDEVS].as<prop_names_t>().at(0); +            _tx_subdevs.push_back(_tx_dboards.back()[named_prop_t(DBOARD_PROP_SUBDEV, tx_subdev_in_use)]); +        } + +        //set the clock config across all mboards (TODO set through api) +        clock_config_t clock_config; +        clock_config.ref_source = clock_config_t::REF_SMA; +        clock_config.pps_source = clock_config_t::PPS_SMA; +        BOOST_FOREACH(wax::obj mboard, _mboards){ +            mboard[MBOARD_PROP_CLOCK_CONFIG] = clock_config; +        } +    } + +    ~mimo_usrp_impl(void){ +        /* NOP */ +    } + +    device::sptr get_device(void){ +        return _dev; +    } + +    std::string get_pp_string(void){ +        std::string buff = str(boost::format( +            "MIMO USRP:\n" +            "  Device: %s\n" +        ) +            % (*_dev)[DEVICE_PROP_NAME].as<std::string>() +        ); +        for (size_t i = 0; i < get_num_channels(); i++){ +            buff += str(boost::format( +                "  Channel: %u\n" +                "    Mboard: %s\n" +                "    RX DSP: %s\n" +                "    RX Dboard: %s\n" +                "    RX Subdev: %s\n" +                "    TX DSP: %s\n" +                "    TX Dboard: %s\n" +                "    TX Subdev: %s\n" +            ) % i +                % _mboards.at(i)[MBOARD_PROP_NAME].as<std::string>() +                % _rx_dsps.at(i)[DSP_PROP_NAME].as<std::string>() +                % _rx_dboards.at(i)[DBOARD_PROP_NAME].as<std::string>() +                % _rx_subdevs.at(i)[SUBDEV_PROP_NAME].as<std::string>() +                % _tx_dsps.at(i)[DSP_PROP_NAME].as<std::string>() +                % _tx_dboards.at(i)[DBOARD_PROP_NAME].as<std::string>() +                % _tx_subdevs.at(i)[SUBDEV_PROP_NAME].as<std::string>() +            ); +        } +        return buff; +    } + +    size_t get_num_channels(void){ +        return _mboards.size(); +    } + +    /******************************************************************* +     * Misc +     ******************************************************************/ +    time_spec_t get_time_now(void){ +        //the time on the first mboard better be the same on all +        return _mboards.front()[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +    } + +    void set_time_next_pps(const time_spec_t &time_spec){ +        BOOST_FOREACH(wax::obj mboard, _mboards){ +            mboard[MBOARD_PROP_TIME_NEXT_PPS] = time_spec; +        } +    } + +    void set_time_unknown_pps(const time_spec_t &time_spec){ +        std::cout << "Set time with unknown pps edge:" << std::endl; +        std::cout << "  1) set times next pps (race condition)" << std::endl; +        set_time_next_pps(time_spec); +        boost::this_thread::sleep(boost::posix_time::seconds(1)); + +        std::cout << "  2) catch seconds rollover at pps edge" << std::endl; +        time_t last_secs = 0, curr_secs = 0; +        while(curr_secs == last_secs){ +            last_secs = curr_secs; +            curr_secs = get_time_now().get_full_secs(); +        } + +        std::cout << "  3) set times next pps (synchronously)" << std::endl; +        set_time_next_pps(time_spec); +        boost::this_thread::sleep(boost::posix_time::seconds(1)); +    } + +    void issue_stream_cmd(const stream_cmd_t &stream_cmd){ +        BOOST_FOREACH(wax::obj mboard, _mboards){ +            mboard[MBOARD_PROP_STREAM_CMD] = stream_cmd; +        } +    } + +    /******************************************************************* +     * RX methods +     ******************************************************************/ +    void set_rx_rate_all(double rate){ +        std::vector<double> _actual_rates; +        BOOST_FOREACH(wax::obj rx_dsp, _rx_dsps){ +            rx_dsp[DSP_PROP_HOST_RATE] = rate; +            _actual_rates.push_back(rx_dsp[DSP_PROP_HOST_RATE].as<double>()); +        } +        _rx_rate = _actual_rates.front(); +        if (std::count(_actual_rates, _rx_rate) != _actual_rates.size()) throw std::runtime_error( +            "MIMO configuratio error: rx rate inconsistent across mboards" +        ); +    } + +    double get_rx_rate_all(void){ +        return _rx_rate; +    } + +    tune_result_t set_rx_freq(size_t chan, double target_freq){ +        return tune_rx_subdev_and_ddc(_rx_subdevs.at(chan), _rx_dsps.at(chan), target_freq); +    } + +    tune_result_t set_rx_freq(size_t chan, double target_freq, double lo_off){ +        return tune_rx_subdev_and_ddc(_rx_subdevs.at(chan), _rx_dsps.at(chan), target_freq, lo_off); +    } + +    freq_range_t get_rx_freq_range(size_t chan){ +        return _rx_subdevs.at(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(); +    } + +    void set_rx_gain(size_t chan, float gain){ +        _rx_subdevs.at(chan)[SUBDEV_PROP_GAIN] = gain; +    } + +    float get_rx_gain(size_t chan){ +        return _rx_subdevs.at(chan)[SUBDEV_PROP_GAIN].as<float>(); +    } + +    gain_range_t get_rx_gain_range(size_t chan){ +        return _rx_subdevs.at(chan)[SUBDEV_PROP_GAIN_RANGE].as<gain_range_t>(); +    } + +    void set_rx_antenna(size_t chan, const std::string &ant){ +        _rx_subdevs.at(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_rx_antenna(size_t chan){ +        return _rx_subdevs.at(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_rx_antennas(size_t chan){ +        return _rx_subdevs.at(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_rx_lo_locked(size_t chan){ +        return _rx_subdevs.at(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +    float read_rssi(size_t chan){ +        return _rx_subdevs.at(chan)[SUBDEV_PROP_RSSI].as<float>(); +    } + +    /******************************************************************* +     * TX methods +     ******************************************************************/ +    void set_tx_rate_all(double rate){ +        std::vector<double> _actual_rates; +        BOOST_FOREACH(wax::obj tx_dsp, _tx_dsps){ +            tx_dsp[DSP_PROP_HOST_RATE] = rate; +            _actual_rates.push_back(tx_dsp[DSP_PROP_HOST_RATE].as<double>()); +        } +        _tx_rate = _actual_rates.front(); +        if (std::count(_actual_rates, _tx_rate) != _actual_rates.size()) throw std::runtime_error( +            "MIMO configuratio error: tx rate inconsistent across mboards" +        ); +    } + +    double get_tx_rate_all(void){ +        return _tx_rate; +    } + +    tune_result_t set_tx_freq(size_t chan, double target_freq){ +        return tune_tx_subdev_and_duc(_tx_subdevs.at(chan), _tx_dsps.at(chan), target_freq); +    } + +    tune_result_t set_tx_freq(size_t chan, double target_freq, double lo_off){ +        return tune_tx_subdev_and_duc(_tx_subdevs.at(chan), _tx_dsps.at(chan), target_freq, lo_off); +    } + +    freq_range_t get_tx_freq_range(size_t chan){ +        return _tx_subdevs.at(chan)[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(); +    } + +    void set_tx_gain(size_t chan, float gain){ +        _tx_subdevs.at(chan)[SUBDEV_PROP_GAIN] = gain; +    } + +    float get_tx_gain(size_t chan){ +        return _tx_subdevs.at(chan)[SUBDEV_PROP_GAIN].as<float>(); +    } + +    gain_range_t get_tx_gain_range(size_t chan){ +        return _tx_subdevs.at(chan)[SUBDEV_PROP_GAIN_RANGE].as<gain_range_t>(); +    } + +    void set_tx_antenna(size_t chan, const std::string &ant){ +        _tx_subdevs.at(chan)[SUBDEV_PROP_ANTENNA] = ant; +    } + +    std::string get_tx_antenna(size_t chan){ +        return _tx_subdevs.at(chan)[SUBDEV_PROP_ANTENNA].as<std::string>(); +    } + +    std::vector<std::string> get_tx_antennas(size_t chan){ +        return _tx_subdevs.at(chan)[SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>(); +    } + +    bool get_tx_lo_locked(size_t chan){ +        return _tx_subdevs.at(chan)[SUBDEV_PROP_LO_LOCKED].as<bool>(); +    } + +private: +    device::sptr _dev; +    std::vector<wax::obj> _mboards; +    std::vector<wax::obj> _rx_dsps; +    std::vector<wax::obj> _tx_dsps; +    std::vector<wax::obj> _rx_dboards; +    std::vector<wax::obj> _tx_dboards; +    std::vector<wax::obj> _rx_subdevs; +    std::vector<wax::obj> _tx_subdevs; + +    //shadows +    double _rx_rate, _tx_rate; +}; + +/*********************************************************************** + * The Make Function + **********************************************************************/ +mimo_usrp::sptr mimo_usrp::make(const device_addr_t &dev_addr){ +    return sptr(new mimo_usrp_impl(dev_addr)); +} diff --git a/host/lib/usrp/simple_usrp.cpp b/host/lib/usrp/simple_usrp.cpp index f4aa82669..56e82d7ee 100644 --- a/host/lib/usrp/simple_usrp.cpp +++ b/host/lib/usrp/simple_usrp.cpp @@ -31,7 +31,7 @@ using namespace uhd;  using namespace uhd::usrp;  /*********************************************************************** - * Simple Device Implementation + * Simple USRP Implementation   **********************************************************************/  class simple_usrp_impl : public simple_usrp{  public: @@ -60,7 +60,7 @@ public:          return _dev;      } -    std::string get_name(void){ +    std::string get_pp_string(void){          return str(boost::format(              "Simple USRP:\n"              "  Device: %s\n" @@ -86,6 +86,10 @@ public:      /*******************************************************************       * Misc       ******************************************************************/ +    time_spec_t get_time_now(void){ +        return _mboard[MBOARD_PROP_TIME_NOW].as<time_spec_t>(); +    } +      void set_time_now(const time_spec_t &time_spec){          _mboard[MBOARD_PROP_TIME_NOW] = time_spec;      } @@ -102,10 +106,6 @@ public:          _mboard[MBOARD_PROP_CLOCK_CONFIG] = clock_config;      } -    float read_rssi(void){ -        return _rx_subdev[SUBDEV_PROP_RSSI].as<float>(); -    } -      /*******************************************************************       * RX methods       ******************************************************************/ @@ -157,6 +157,10 @@ public:          return _rx_subdev[SUBDEV_PROP_LO_LOCKED].as<bool>();      } +    float read_rssi(void){ +        return _rx_subdev[SUBDEV_PROP_RSSI].as<float>(); +    } +      /*******************************************************************       * TX methods       ******************************************************************/ diff --git a/host/lib/usrp/usrp2/dboard_impl.cpp b/host/lib/usrp/usrp2/dboard_impl.cpp index fef486771..fa8d1a674 100644 --- a/host/lib/usrp/usrp2/dboard_impl.cpp +++ b/host/lib/usrp/usrp2/dboard_impl.cpp @@ -33,7 +33,7 @@ using namespace uhd::usrp;  /***********************************************************************   * Helper Methods   **********************************************************************/ -void usrp2_impl::dboard_init(void){ +void usrp2_mboard_impl::dboard_init(void){      //read the dboard eeprom to extract the dboard ids      _rx_db_eeprom = dboard_eeprom_t(_iface->read_eeprom(I2C_ADDR_RX_DB, 0, dboard_eeprom_t::num_bytes()));      _tx_db_eeprom = dboard_eeprom_t(_iface->read_eeprom(I2C_ADDR_TX_DB, 0, dboard_eeprom_t::num_bytes())); @@ -46,12 +46,12 @@ void usrp2_impl::dboard_init(void){      //load dboards      _rx_dboard_proxy = wax_obj_proxy::make( -        boost::bind(&usrp2_impl::rx_dboard_get, this, _1, _2), -        boost::bind(&usrp2_impl::rx_dboard_set, this, _1, _2) +        boost::bind(&usrp2_mboard_impl::rx_dboard_get, this, _1, _2), +        boost::bind(&usrp2_mboard_impl::rx_dboard_set, this, _1, _2)      );      _tx_dboard_proxy = wax_obj_proxy::make( -        boost::bind(&usrp2_impl::tx_dboard_get, this, _1, _2), -        boost::bind(&usrp2_impl::tx_dboard_set, this, _1, _2) +        boost::bind(&usrp2_mboard_impl::tx_dboard_get, this, _1, _2), +        boost::bind(&usrp2_mboard_impl::tx_dboard_set, this, _1, _2)      );      //init the subdevs in use (use the first subdevice) @@ -62,7 +62,7 @@ void usrp2_impl::dboard_init(void){  /***********************************************************************   * RX DBoard Properties   **********************************************************************/ -void usrp2_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){ +void usrp2_mboard_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){      wax::obj key; std::string name;      boost::tie(key, name) = extract_named_prop(key_); @@ -96,7 +96,7 @@ void usrp2_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){      }  } -void usrp2_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val){ +void usrp2_mboard_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val){      switch(key.as<dboard_prop_t>()){      case DBOARD_PROP_USED_SUBDEVS:{              _rx_subdevs_in_use = val.as<prop_names_t>(); @@ -122,7 +122,7 @@ void usrp2_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val){  /***********************************************************************   * TX DBoard Properties   **********************************************************************/ -void usrp2_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){ +void usrp2_mboard_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){      wax::obj key; std::string name;      boost::tie(key, name) = extract_named_prop(key_); @@ -156,7 +156,7 @@ void usrp2_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){      }  } -void usrp2_impl::tx_dboard_set(const wax::obj &key, const wax::obj &val){ +void usrp2_mboard_impl::tx_dboard_set(const wax::obj &key, const wax::obj &val){      switch(key.as<dboard_prop_t>()){      case DBOARD_PROP_USED_SUBDEVS:{              _tx_subdevs_in_use = val.as<prop_names_t>(); diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index 367cde2e1..c315a2eec 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -41,11 +41,11 @@ pick_closest_rate(double exact_rate, const std::vector<rate_t> &rates){      return closest_match;  } -void usrp2_impl::init_ddc_config(void){ +void usrp2_mboard_impl::init_ddc_config(void){      //create the ddc in the rx dsp dict      _rx_dsp_proxy = wax_obj_proxy::make( -        boost::bind(&usrp2_impl::ddc_get, this, _1, _2), -        boost::bind(&usrp2_impl::ddc_set, this, _1, _2) +        boost::bind(&usrp2_mboard_impl::ddc_get, this, _1, _2), +        boost::bind(&usrp2_mboard_impl::ddc_set, this, _1, _2)      );      //initial config and update @@ -56,7 +56,7 @@ void usrp2_impl::init_ddc_config(void){  /***********************************************************************   * DDC Properties   **********************************************************************/ -void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ +void usrp2_mboard_impl::ddc_get(const wax::obj &key, wax::obj &val){      switch(key.as<dsp_prop_t>()){      case DSP_PROP_NAME:          val = std::string("usrp2 ddc0"); @@ -82,7 +82,7 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){      }  } -void usrp2_impl::ddc_set(const wax::obj &key, const wax::obj &val){ +void usrp2_mboard_impl::ddc_set(const wax::obj &key, const wax::obj &val){      switch(key.as<dsp_prop_t>()){      case DSP_PROP_FREQ_SHIFT:{ @@ -116,11 +116,11 @@ void usrp2_impl::ddc_set(const wax::obj &key, const wax::obj &val){  /***********************************************************************   * DUC Helper Methods   **********************************************************************/ -void usrp2_impl::init_duc_config(void){ +void usrp2_mboard_impl::init_duc_config(void){      //create the duc in the tx dsp dict      _tx_dsp_proxy = wax_obj_proxy::make( -        boost::bind(&usrp2_impl::duc_get, this, _1, _2), -        boost::bind(&usrp2_impl::duc_set, this, _1, _2) +        boost::bind(&usrp2_mboard_impl::duc_get, this, _1, _2), +        boost::bind(&usrp2_mboard_impl::duc_set, this, _1, _2)      );      //initial config and update @@ -131,7 +131,7 @@ void usrp2_impl::init_duc_config(void){  /***********************************************************************   * DUC Properties   **********************************************************************/ -void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ +void usrp2_mboard_impl::duc_get(const wax::obj &key, wax::obj &val){      switch(key.as<dsp_prop_t>()){      case DSP_PROP_NAME:          val = std::string("usrp2 duc0"); @@ -157,7 +157,7 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){      }  } -void usrp2_impl::duc_set(const wax::obj &key, const wax::obj &val){ +void usrp2_mboard_impl::duc_set(const wax::obj &key, const wax::obj &val){      switch(key.as<dsp_prop_t>()){      case DSP_PROP_FREQ_SHIFT:{ diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 242d268ec..4c66aa41e 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -38,7 +38,7 @@ extern "C" {  //defines the protocol version in this shared header  //increment this value when the protocol is changed -#define USRP2_PROTO_VERSION 4 +#define USRP2_PROTO_VERSION 5  //used to differentiate control packets over data port  #define USRP2_INVALID_VRT_HEADER 0 @@ -111,7 +111,9 @@ typedef struct{          struct {              _SINS_ uint32_t addr;              _SINS_ uint32_t data; -            _SINS_ uint8_t num_bytes; //1, 2, 4 +            _SINS_ uint32_t addrhi; +            _SINS_ uint32_t datahi; +            _SINS_ uint8_t num_bytes; //1, 2, 4, 8          } poke_args;      } data;  } usrp2_ctrl_data_t; diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index aa0f63321..c96528694 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -20,7 +20,7 @@  #include "usrp2_regs.hpp"  #include <uhd/utils/thread_priority.hpp>  #include <uhd/transport/convert_types.hpp> -#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/alignment_buffer.hpp>  #include <boost/format.hpp>  #include <boost/asio.hpp> //htonl and ntohl  #include <boost/bind.hpp> @@ -34,56 +34,81 @@ namespace asio = boost::asio;  /***********************************************************************   * io impl details (internal to this file) + * - pirate crew + * - alignment buffer + * - thread loop + * - vrt packet handler states   **********************************************************************/  struct usrp2_impl::io_impl{ +    typedef alignment_buffer<managed_recv_buffer::sptr, time_spec_t> alignment_buffer_type; -    io_impl(zero_copy_if::sptr zc_if); -    ~io_impl(void); +    io_impl(size_t num_frames, size_t width): +        packet_handler_recv_state(width), +        recv_pirate_booty(alignment_buffer_type::make(num_frames, width)) +    { +        /* NOP */ +    } + +    ~io_impl(void){ +        recv_pirate_crew_raiding = false; +        recv_pirate_crew.interrupt_all(); +        recv_pirate_crew.join_all(); +    } -    managed_recv_buffer::sptr get_recv_buff(void); +    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs){ +        boost::this_thread::disable_interruption di; //disable because the wait can throw +        return recv_pirate_booty->pop_elems_with_timed_wait(buffs, boost::posix_time::milliseconds(100)); +    }      //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; -    //methods and variables for the recv pirate -    void recv_pirate_loop(zero_copy_if::sptr zc_if); -    boost::thread *recv_pirate_thread; bool recv_pirate_running; -    bounded_buffer<managed_recv_buffer::sptr>::sptr recv_pirate_booty; +    //methods and variables for the pirate crew +    void recv_pirate_loop(zero_copy_if::sptr, usrp2_mboard_impl::sptr, size_t); +    boost::thread_group recv_pirate_crew; +    bool recv_pirate_crew_raiding; +    alignment_buffer_type::sptr recv_pirate_booty;  }; -usrp2_impl::io_impl::io_impl(zero_copy_if::sptr zc_if){ -    //create a large enough booty -    size_t num_frames = zc_if->get_num_recv_frames(); -    std::cout << "Recv pirate num frames: " << num_frames << std::endl; -    recv_pirate_booty = bounded_buffer<managed_recv_buffer::sptr>::make(num_frames); - -    //create a new pirate thread (yarr!!) -    recv_pirate_thread = new boost::thread( -        boost::bind(&usrp2_impl::io_impl::recv_pirate_loop, this, zc_if) -    ); -} - -usrp2_impl::io_impl::~io_impl(void){ -    recv_pirate_running = false; -    recv_pirate_thread->interrupt(); -    recv_pirate_thread->join(); -    delete recv_pirate_thread; -} - -managed_recv_buffer::sptr usrp2_impl::io_impl::get_recv_buff(void){ -    managed_recv_buffer::sptr buff; -    boost::this_thread::disable_interruption di; //disable because the wait can throw -    recv_pirate_booty->pop_with_timed_wait(buff, boost::posix_time::milliseconds(100)); -    return buff; //a timeout means that we return a null sptr... -} - -void usrp2_impl::io_impl::recv_pirate_loop(zero_copy_if::sptr zc_if){ +/*********************************************************************** + * Receive Pirate Loop + * - while raiding, loot for recv buffers + * - put booty into the alignment buffer + **********************************************************************/ +void usrp2_impl::io_impl::recv_pirate_loop( +    zero_copy_if::sptr zc_if, +    usrp2_mboard_impl::sptr mboard, +    size_t index +){      set_thread_priority_safe(); -    recv_pirate_running = true; -    while(recv_pirate_running){ +    recv_pirate_crew_raiding = true; +    while(recv_pirate_crew_raiding){          managed_recv_buffer::sptr buff = zc_if->get_recv_buff(); -        if (buff->size()) recv_pirate_booty->push_with_pop_on_full(buff); +        if (buff->size() == 0) continue; //ignore timeout buffers + +        try{ +            //extract the vrt header packet info +            vrt::if_packet_info_t if_packet_info; +            if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); +            vrt::if_hdr_unpack_be(buff->cast<const boost::uint32_t *>(), if_packet_info); + +            //extract the timespec and round to the nearest packet +            UHD_ASSERT_THROW(if_packet_info.has_tsi and if_packet_info.has_tsf); + +            //size_t pkt_dur_ticks = mboard->get_master_clock_freq() * 1; //TODO FIXME +            //size_t(if_packet_info.tsf) - size_t(if_packet_info.tsf)%pkt_dur_ticks +            //the idea is to round the time specs to packet boundaries to avoid the issue +            //of never getting alignment when things are not locked properly +            time_spec_t time( +                time_t(if_packet_info.tsi), size_t(if_packet_info.tsf), mboard->get_master_clock_freq() +            ); + +            //push the packet into the buffer with the new time +            recv_pirate_booty->push_with_pop_on_full(buff, time, index); +        }catch(const std::exception &e){ +            std::cerr << "Error (usrp2 recv pirate loop): " << e.what() << std::endl; +        }      }  } @@ -91,58 +116,64 @@ void usrp2_impl::io_impl::recv_pirate_loop(zero_copy_if::sptr zc_if){   * Helper Functions   **********************************************************************/  void usrp2_impl::io_init(void){ -    //setup rx otw type -    _rx_otw_type.width = 16; -    _rx_otw_type.shift = 0; -    _rx_otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; +    //send a small data packet so the usrp2 knows the udp source port +    for(size_t i = 0; i < _data_transports.size(); i++){ +        managed_send_buffer::sptr send_buff = _data_transports[i]->get_send_buff(); +        boost::uint32_t data = htonl(USRP2_INVALID_VRT_HEADER); +        memcpy(send_buff->cast<void*>(), &data, sizeof(data)); +        send_buff->commit(sizeof(data)); +    } -    //setup tx otw type -    _tx_otw_type.width = 16; -    _tx_otw_type.shift = 0; -    _tx_otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; +    //the number of recv frames is the number for the first transport +    //the assumption is that all data transports should be identical +    size_t num_frames = _data_transports.front()->get_num_recv_frames(); -    //send a small data packet so the usrp2 knows the udp source port -    managed_send_buffer::sptr send_buff = _data_transport->get_send_buff(); -    boost::uint32_t data = htonl(USRP2_INVALID_VRT_HEADER); -    memcpy(send_buff->cast<void*>(), &data, sizeof(data)); -    send_buff->commit(sizeof(data)); +    //create new io impl +    _io_impl = UHD_PIMPL_MAKE(io_impl, (num_frames, _data_transports.size())); + +    //create a new pirate thread for each zc if (yarr!!) +    for (size_t i = 0; i < _data_transports.size(); i++){ +        _io_impl->recv_pirate_crew.create_thread(boost::bind( +            &usrp2_impl::io_impl::recv_pirate_loop, +            _io_impl, _data_transports.at(i), +            _mboards.at(i), i +        )); +    } -    //setup RX DSP regs      std::cout << "RX samples per packet: " << get_max_recv_samps_per_packet() << std::endl; -    _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, get_max_recv_samps_per_packet()); -    _iface->poke32(U2_REG_RX_CTRL_NCHANNELS, 1); -    _iface->poke32(U2_REG_RX_CTRL_CLEAR_OVERRUN, 1); //reset -    _iface->poke32(U2_REG_RX_CTRL_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(U2_REG_RX_CTRL_VRT_STREAM_ID, 0); -    _iface->poke32(U2_REG_RX_CTRL_VRT_TRAILER, 0); -      std::cout << "TX samples per packet: " << get_max_send_samps_per_packet() << std::endl; - -    //create new io impl -    _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); +    std::cout << "Recv pirate num frames: " << num_frames << std::endl;  }  /***********************************************************************   * Send Data   **********************************************************************/ +bool get_send_buffs( +    const std::vector<udp_zero_copy::sptr> &trans, +    vrt_packet_handler::managed_send_buffs_t &buffs +){ +    UHD_ASSERT_THROW(trans.size() == buffs.size()); +    for (size_t i = 0; i < buffs.size(); i++){ +        buffs[i] = trans[i]->get_send_buff(); +    } +    return true; +} +  size_t usrp2_impl::send( -    const asio::const_buffer &buff, +    const std::vector<const void *> &buffs, +    size_t num_samps,      const tx_metadata_t &metadata,      const io_type_t &io_type,      send_mode_t send_mode  ){      return vrt_packet_handler::send( -        _io_impl->packet_handler_send_state, //last state of the send handler -        buff, metadata, send_mode,  //buffer to empty and samples metadata -        io_type, _tx_otw_type,      //input and output types to convert -        get_master_clock_freq(),    //master clock tick rate -        uhd::transport::vrt::pack_be, -        boost::bind(&zero_copy_if::get_send_buff, _data_transport), +        _io_impl->packet_handler_send_state,       //last state of the send handler +        buffs, num_samps,                          //buffer to fill +        metadata, send_mode,                       //samples metadata +        io_type, _io_helper.get_tx_otw_type(),     //input and output types to convert +        _mboards.front()->get_master_clock_freq(), //master clock tick rate +        uhd::transport::vrt::if_hdr_pack_be, +        boost::bind(&get_send_buffs, _data_transports, _1),          get_max_send_samps_per_packet()      );  } @@ -151,17 +182,19 @@ size_t usrp2_impl::send(   * Receive Data   **********************************************************************/  size_t usrp2_impl::recv( -    const asio::mutable_buffer &buff, +    const std::vector<void *> &buffs, +    size_t num_samps,      rx_metadata_t &metadata,      const io_type_t &io_type,      recv_mode_t recv_mode  ){      return vrt_packet_handler::recv( -        _io_impl->packet_handler_recv_state, //last state of the recv handler -        buff, metadata, recv_mode,  //buffer to fill and samples metadata -        io_type, _rx_otw_type,      //input and output types to convert -        get_master_clock_freq(),    //master clock tick rate -        uhd::transport::vrt::unpack_be, -        boost::bind(&usrp2_impl::io_impl::get_recv_buff, _io_impl) +        _io_impl->packet_handler_recv_state,       //last state of the recv handler +        buffs, num_samps,                          //buffer to fill +        metadata, recv_mode,                       //samples metadata +        io_type, _io_helper.get_rx_otw_type(),     //input and output types to convert +        _mboards.front()->get_master_clock_freq(), //master clock tick rate +        uhd::transport::vrt::if_hdr_unpack_be, +        boost::bind(&usrp2_impl::io_impl::get_recv_buffs, _io_impl, _1)      );  } diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index 78fc5dc23..952954286 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -25,21 +25,77 @@  #include <boost/bind.hpp>  #include <boost/asio/ip/address_v4.hpp>  #include <boost/assign/list_of.hpp> +#include <iostream>  using namespace uhd;  using namespace uhd::usrp;  /*********************************************************************** - * Helper Methods + * Structors   **********************************************************************/ -void usrp2_impl::mboard_init(void){ -    _mboard_proxy = wax_obj_proxy::make( -        boost::bind(&usrp2_impl::mboard_get, this, _1, _2), -        boost::bind(&usrp2_impl::mboard_set, this, _1, _2) +usrp2_mboard_impl::usrp2_mboard_impl( +    size_t index, +    transport::udp_simple::sptr ctrl_transport, +    const usrp2_io_helper &io_helper +): +    _index(index), +    _io_helper(io_helper) +{ +    //make a new interface for usrp2 stuff +    _iface = usrp2_iface::make(ctrl_transport); +    _clock_ctrl = usrp2_clock_ctrl::make(_iface); +    _codec_ctrl = usrp2_codec_ctrl::make(_iface); +    _serdes_ctrl = usrp2_serdes_ctrl::make(_iface); + +    //TODO move to dsp impl... +    //load the allowed decim/interp rates +    //_USRP2_RATES = range(4, 128+1, 1) + range(130, 256+1, 2) + range(260, 512+1, 4) +    _allowed_decim_and_interp_rates.clear(); +    for (size_t i = 4; i <= 128; i+=1){ +        _allowed_decim_and_interp_rates.push_back(i); +    } +    for (size_t i = 130; i <= 256; i+=2){ +        _allowed_decim_and_interp_rates.push_back(i); +    } +    for (size_t i = 260; i <= 512; i+=4){ +        _allowed_decim_and_interp_rates.push_back(i); +    } + +    //setup the vrt rx registers +    _iface->poke32(U2_REG_RX_CTRL_NSAMPS_PER_PKT, _io_helper.get_max_recv_samps_per_packet()); +    _iface->poke32(U2_REG_RX_CTRL_NCHANNELS, 1); +    _iface->poke32(U2_REG_RX_CTRL_CLEAR_OVERRUN, 1); //reset +    _iface->poke32(U2_REG_RX_CTRL_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(U2_REG_RX_CTRL_VRT_STREAM_ID, 0); +    _iface->poke32(U2_REG_RX_CTRL_VRT_TRAILER, 0); +    _iface->poke32(U2_REG_TIME64_TPS, size_t(get_master_clock_freq())); + +    //init the ddc +    init_ddc_config(); + +    //init the duc +    init_duc_config(); + +    //initialize the clock configuration +    init_clock_config(); + +    //init the tx and rx dboards (do last) +    dboard_init();  } -void usrp2_impl::init_clock_config(void){ +usrp2_mboard_impl::~usrp2_mboard_impl(void){ +    /* NOP */ +} + +/*********************************************************************** + * Helper Methods + **********************************************************************/ +void usrp2_mboard_impl::init_clock_config(void){      //setup the clock configuration settings      _clock_config.ref_source = clock_config_t::REF_INT;      _clock_config.pps_source = clock_config_t::PPS_SMA; @@ -49,7 +105,7 @@ void usrp2_impl::init_clock_config(void){      update_clock_config();  } -void usrp2_impl::update_clock_config(void){ +void usrp2_mboard_impl::update_clock_config(void){      boost::uint32_t pps_flags = 0;      //translate pps source enums @@ -82,19 +138,19 @@ void usrp2_impl::update_clock_config(void){      _clock_ctrl->enable_external_ref(use_external);  } -void usrp2_impl::set_time_spec(const time_spec_t &time_spec, bool now){ +void usrp2_mboard_impl::set_time_spec(const time_spec_t &time_spec, bool now){      //set the ticks -    _iface->poke32(U2_REG_TIME64_TICKS, time_spec.get_ticks(get_master_clock_freq())); +    _iface->poke32(U2_REG_TIME64_TICKS, time_spec.get_tick_count(get_master_clock_freq()));      //set the flags register      boost::uint32_t imm_flags = (now)? U2_FLAG_TIME64_LATCH_NOW : U2_FLAG_TIME64_LATCH_NEXT_PPS;      _iface->poke32(U2_REG_TIME64_IMM, imm_flags);      //set the seconds (latches in all 3 registers) -    _iface->poke32(U2_REG_TIME64_SECS, time_spec.secs); +    _iface->poke32(U2_REG_TIME64_SECS, boost::uint32_t(time_spec.get_full_secs()));  } -void usrp2_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){ +void usrp2_mboard_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){      UHD_ASSERT_THROW(stream_cmd.num_samps <= U2_REG_RX_CTRL_MAX_SAMPS_PER_CMD);      //setup the mode to instruction flags @@ -113,19 +169,19 @@ void usrp2_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){      //issue the stream command      _iface->poke32(U2_REG_RX_CTRL_STREAM_CMD, U2_REG_RX_CTRL_MAKE_CMD( -        (inst_samps)? stream_cmd.num_samps : ((inst_chain)? get_max_recv_samps_per_packet() : 1), +        (inst_samps)? stream_cmd.num_samps : ((inst_chain)? _io_helper.get_max_recv_samps_per_packet() : 1),          (stream_cmd.stream_now)? 1 : 0,          (inst_chain)? 1 : 0,          (inst_reload)? 1 : 0      )); -    _iface->poke32(U2_REG_RX_CTRL_TIME_SECS,  stream_cmd.time_spec.secs); -    _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_ticks(get_master_clock_freq())); +    _iface->poke32(U2_REG_RX_CTRL_TIME_SECS,  boost::uint32_t(stream_cmd.time_spec.get_full_secs())); +    _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_tick_count(get_master_clock_freq()));  }  /***********************************************************************   * MBoard Get Properties   **********************************************************************/ -void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ +void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){      wax::obj key; std::string name;      boost::tie(key, name) = extract_named_prop(key_); @@ -148,7 +204,7 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){      //handle the get request conditioned on the key      switch(key.as<mboard_prop_t>()){      case MBOARD_PROP_NAME: -        val = std::string("usrp2 mboard"); +        val = str(boost::format("usrp2 mboard %d") % _index);          return;      case MBOARD_PROP_OTHERS:{ @@ -200,6 +256,16 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){          val = _clock_config;          return; +    case MBOARD_PROP_TIME_NOW:{ +            usrp2_iface::pair64 time64( +                _iface->peek64(U2_REG_TIME64_SECS_RB, U2_REG_TIME64_TICKS_RB) +            ); +            val = time_spec_t( +                time64.first, time64.second, get_master_clock_freq() +            ); +        } +        return; +      default: UHD_THROW_PROP_GET_ERROR();      }  } @@ -207,7 +273,7 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){  /***********************************************************************   * MBoard Set Properties   **********************************************************************/ -void usrp2_impl::mboard_set(const wax::obj &key, const wax::obj &val){ +void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){      //handle the other props      if (key.type() == typeid(std::string)){          if (key.as<std::string>() == "mac-addr"){ diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 83e98904e..faf4a5c7e 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -59,6 +59,20 @@ public:          return this->peek<boost::uint16_t>(addr);      } +    pair64 peek64(boost::uint32_t addrlo, boost::uint32_t addrhi){ +        //setup the out data +        usrp2_ctrl_data_t out_data; +        out_data.id = htonl(USRP2_CTRL_ID_PEEK_AT_THIS_REGISTER_FOR_ME_BRO); +        out_data.data.poke_args.addr = htonl(addrlo); +        out_data.data.poke_args.addrhi = htonl(addrhi); +        out_data.data.poke_args.num_bytes = sizeof(boost::uint64_t); + +        //send and recv +        usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data); +        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_WOAH_I_DEFINITELY_PEEKED_IT_DUDE); +        return pair64(ntohl(in_data.data.poke_args.data), ntohl(in_data.data.poke_args.datahi)); +    } +  /***********************************************************************   * SPI   **********************************************************************/ @@ -86,7 +100,7 @@ public:          //send and recv          usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data); -        UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_OMG_TRANSACTED_SPI_DUDE); +        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_OMG_TRANSACTED_SPI_DUDE);          return ntohl(in_data.data.spi_args.data);      } @@ -109,7 +123,7 @@ public:          //send and recv          usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data); -        UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_COOL_IM_DONE_I2C_WRITE_DUDE); +        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_COOL_IM_DONE_I2C_WRITE_DUDE);      }      byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes){ @@ -124,7 +138,7 @@ public:          //send and recv          usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data); -        UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_HERES_THE_I2C_DATA_DUDE); +        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_HERES_THE_I2C_DATA_DUDE);          UHD_ASSERT_THROW(in_data.data.i2c_args.addr = num_bytes);          //copy out the data @@ -187,7 +201,7 @@ private:          //send and recv          usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data); -        UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_OMG_POKED_REGISTER_SO_BAD_DUDE); +        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_OMG_POKED_REGISTER_SO_BAD_DUDE);      }      template <class T> T peek(boost::uint32_t addr){ @@ -199,8 +213,8 @@ private:          //send and recv          usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data); -        UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_WOAH_I_DEFINITELY_PEEKED_IT_DUDE); -        return T(ntohl(out_data.data.poke_args.data)); +        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_WOAH_I_DEFINITELY_PEEKED_IT_DUDE); +        return T(ntohl(in_data.data.poke_args.data));      }  }; diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp index 7b2a3a89d..9cc32104e 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.hpp +++ b/host/lib/usrp/usrp2/usrp2_iface.hpp @@ -23,6 +23,7 @@  #include <boost/shared_ptr.hpp>  #include <boost/utility.hpp>  #include <boost/cstdint.hpp> +#include <utility>  #include "fw_common.h"  //////////////////////////////////////////////////////////////////////// @@ -49,6 +50,7 @@  class usrp2_iface : public uhd::i2c_iface, boost::noncopyable{  public:      typedef boost::shared_ptr<usrp2_iface> sptr; +    typedef std::pair<boost::uint32_t, boost::uint32_t> pair64;      /*!       * Make a new usrp2 interface with the control transport. @@ -65,6 +67,14 @@ public:      virtual usrp2_ctrl_data_t ctrl_send_and_recv(const usrp2_ctrl_data_t &data) = 0;      /*! +     * Read a dual register (64 bits) +     * \param addrlo the address for the low-32 bits +     * \param addrhi the address for the high-32 bits +     * \return a pair of 32 bit integers lo, hi +     */ +    virtual pair64 peek64(boost::uint32_t addrlo, boost::uint32_t addrhi) = 0; + +    /*!       * Write a register (32 bits)       * \param addr the address       * \param data the 32bit data diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 36c264c3c..3402c26b1 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -21,6 +21,7 @@  #include <uhd/usrp/device_props.hpp>  #include <uhd/utils/assert.hpp>  #include <uhd/utils/static.hpp> +#include <boost/algorithm/string.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/format.hpp>  #include <boost/foreach.hpp> @@ -34,14 +35,23 @@ using namespace uhd::usrp;  using namespace uhd::transport;  namespace asio = boost::asio; -UHD_STATIC_BLOCK(register_usrp2_device){ -    device::register_device(&usrp2::find, &usrp2::make); +/*********************************************************************** + * Helper Functions + **********************************************************************/ +std::vector<std::string> split_addrs(const std::string &addrs_str){ +    std::vector<std::string> addrs; +    boost::split(addrs, addrs_str, boost::is_any_of("\t ")); +    return addrs; +} + +template <class T> std::string num2str(T num){ +    return boost::lexical_cast<std::string>(num);  }  /***********************************************************************   * Discovery over the udp transport   **********************************************************************/ -uhd::device_addrs_t usrp2::find(const device_addr_t &hint){ +static uhd::device_addrs_t usrp2_find(const device_addr_t &hint){      device_addrs_t usrp2_addrs;      //return an empty list of addresses when type is set to non-usrp2 @@ -58,7 +68,7 @@ uhd::device_addrs_t usrp2::find(const device_addr_t &hint){              new_hint["addr"] = if_addrs.bcast;              //call discover with the new hint and append results -            device_addrs_t new_usrp2_addrs = usrp2::find(new_hint); +            device_addrs_t new_usrp2_addrs = usrp2_find(new_hint);              usrp2_addrs.insert(usrp2_addrs.begin(),                  new_usrp2_addrs.begin(), new_usrp2_addrs.end()              ); @@ -66,6 +76,16 @@ uhd::device_addrs_t usrp2::find(const device_addr_t &hint){          return usrp2_addrs;      } +    //if there are multiple addresses, just return good, dont test +    std::vector<std::string> addrs = split_addrs(hint["addr"]); +    if (addrs.size() > 1){ +        device_addr_t new_addr; +        new_addr["type"] = "usrp2"; +        new_addr["addr"] = hint["addr"]; +        usrp2_addrs.push_back(new_addr); +        return usrp2_addrs; +    } +      //create a udp transport to communicate      std::string ctrl_port = boost::lexical_cast<std::string>(USRP2_UDP_CTRL_PORT);      udp_simple::sptr udp_transport = udp_simple::make_broadcast( @@ -107,16 +127,7 @@ uhd::device_addrs_t usrp2::find(const device_addr_t &hint){  /***********************************************************************   * Make   **********************************************************************/ -template <class T> std::string num2str(T num){ -    return boost::lexical_cast<std::string>(num); -} - -device::sptr usrp2::make(const device_addr_t &device_addr){ -    //create a control transport -    udp_simple::sptr ctrl_transport = udp_simple::make_connected( -        device_addr["addr"], num2str(USRP2_UDP_CTRL_PORT) -    ); - +static device::sptr usrp2_make(const device_addr_t &device_addr){      //extract the receive and send buffer sizes      size_t recv_buff_size = 0, send_buff_size= 0 ;      if (device_addr.has_key("recv_buff_size")){ @@ -126,62 +137,48 @@ device::sptr usrp2::make(const device_addr_t &device_addr){          send_buff_size = size_t(boost::lexical_cast<double>(device_addr["send_buff_size"]));      } -    //create a data transport -    udp_zero_copy::sptr data_transport = udp_zero_copy::make( -        device_addr["addr"], -        num2str(USRP2_UDP_DATA_PORT), -        recv_buff_size, -        send_buff_size -    ); +    //create a ctrl and data transport for each address +    std::vector<udp_simple::sptr> ctrl_transports; +    std::vector<udp_zero_copy::sptr> data_transports; + +    BOOST_FOREACH(const std::string &addr, split_addrs(device_addr["addr"])){ +        ctrl_transports.push_back(udp_simple::make_connected( +            addr, num2str(USRP2_UDP_CTRL_PORT) +        )); +        data_transports.push_back(udp_zero_copy::make( +            addr, num2str(USRP2_UDP_DATA_PORT), +            recv_buff_size, send_buff_size +        )); +    }      //create the usrp2 implementation guts      return device::sptr( -        new usrp2_impl(ctrl_transport, data_transport) +        new usrp2_impl(ctrl_transports, data_transports)      );  } +UHD_STATIC_BLOCK(register_usrp2_device){ +    device::register_device(&usrp2_find, &usrp2_make); +} +  /***********************************************************************   * Structors   **********************************************************************/  usrp2_impl::usrp2_impl( -    udp_simple::sptr ctrl_transport, -    udp_zero_copy::sptr data_transport -){ -    _data_transport = data_transport; - -    //make a new interface for usrp2 stuff -    _iface = usrp2_iface::make(ctrl_transport); -    _clock_ctrl = usrp2_clock_ctrl::make(_iface); -    _codec_ctrl = usrp2_codec_ctrl::make(_iface); -    _serdes_ctrl = usrp2_serdes_ctrl::make(_iface); - -    //load the allowed decim/interp rates -    //_USRP2_RATES = range(4, 128+1, 1) + range(130, 256+1, 2) + range(260, 512+1, 4) -    _allowed_decim_and_interp_rates.clear(); -    for (size_t i = 4; i <= 128; i+=1){ -        _allowed_decim_and_interp_rates.push_back(i); +    std::vector<udp_simple::sptr> ctrl_transports, +    std::vector<udp_zero_copy::sptr> data_transports +): +    _data_transports(data_transports) +{ +    //create a new mboard handler for each control transport +    for(size_t i = 0; i < ctrl_transports.size(); i++){ +        _mboards.push_back(usrp2_mboard_impl::sptr( +            new usrp2_mboard_impl(i, ctrl_transports[i], _io_helper) +        )); +        //use an empty name when there is only one mboard +        std::string name = (ctrl_transports.size() > 1)? boost::lexical_cast<std::string>(i) : ""; +        _mboard_dict[name] = _mboards.back();      } -    for (size_t i = 130; i <= 256; i+=2){ -        _allowed_decim_and_interp_rates.push_back(i); -    } -    for (size_t i = 260; i <= 512; i+=4){ -        _allowed_decim_and_interp_rates.push_back(i); -    } - -    //init the mboard -    mboard_init(); - -    //init the ddc -    init_ddc_config(); - -    //init the duc -    init_duc_config(); - -    //initialize the clock configuration -    init_clock_config(); - -    //init the tx and rx dboards (do last) -    dboard_init();      //init the send and recv io      io_init(); @@ -202,16 +199,16 @@ void usrp2_impl::get(const wax::obj &key_, wax::obj &val){      //handle the get request conditioned on the key      switch(key.as<device_prop_t>()){      case DEVICE_PROP_NAME: -        val = std::string("usrp2 device"); +        if (_mboards.size() > 1) val = std::string("usrp2 mimo device"); +        else                     val = std::string("usrp2 device");          return;      case DEVICE_PROP_MBOARD: -        UHD_ASSERT_THROW(name == ""); -        val = _mboard_proxy->get_link(); +        val = _mboard_dict[name]->get_link();          return;      case DEVICE_PROP_MBOARD_NAMES: -        val = prop_names_t(1, ""); +        val = prop_names_t(_mboard_dict.keys());          return;      default: UHD_THROW_PROP_GET_ERROR(); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 2126b9565..42630a3e4 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -22,7 +22,7 @@  #include "clock_ctrl.hpp"  #include "codec_ctrl.hpp"  #include "serdes_ctrl.hpp" -#include <uhd/usrp/usrp2.hpp> +#include <uhd/device.hpp>  #include <uhd/utils/pimpl.hpp>  #include <uhd/types/dict.hpp>  #include <uhd/types/otw_type.hpp> @@ -31,7 +31,7 @@  #include <uhd/usrp/dboard_eeprom.hpp>  #include <boost/shared_ptr.hpp>  #include <boost/function.hpp> -#include <uhd/transport/vrt.hpp> +#include <uhd/transport/vrt_if_packet.hpp>  #include <uhd/transport/udp_zero_copy.hpp>  #include <uhd/usrp/dboard_manager.hpp> @@ -62,73 +62,84 @@ public:          return sptr(new wax_obj_proxy(get, set));      } -    ~wax_obj_proxy(void){ -        /* NOP */ +private: +    get_t _get; set_t _set; +    wax_obj_proxy(const get_t &get, const set_t &set): _get(get), _set(set){}; +    void get(const wax::obj &key, wax::obj &val){return _get(key, val);} +    void set(const wax::obj &key, const wax::obj &val){return _set(key, val);} +}; + +/*! + * The io helper class encapculates the max packet sizes and otw types. + * The otw types are read-only for now, this will be reimplemented + * when it becomes possible to change the otw type in the usrp2. + */ +class usrp2_io_helper{ +public: +    usrp2_io_helper(void){ +        //setup rx otw type +        _rx_otw_type.width = 16; +        _rx_otw_type.shift = 0; +        _rx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; + +        //setup tx otw type +        _tx_otw_type.width = 16; +        _tx_otw_type.shift = 0; +        _tx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN;      } -private: -    get_t _get; -    set_t _set; +    inline size_t get_max_send_samps_per_packet(void) const{ +        return _max_tx_bytes_per_packet/_tx_otw_type.get_sample_size(); +    } -    wax_obj_proxy(const get_t &get, const set_t &set){ -        _get = get; -        _set = set; -    }; +    inline size_t get_max_recv_samps_per_packet(void) const{ +        return _max_rx_bytes_per_packet/_rx_otw_type.get_sample_size(); +    } -    void get(const wax::obj &key, wax::obj &val){ -        return _get(key, val); +    inline const uhd::otw_type_t &get_rx_otw_type(void) const{ +        return _rx_otw_type;      } -    void set(const wax::obj &key, const wax::obj &val){ -        return _set(key, val); +    inline const uhd::otw_type_t &get_tx_otw_type(void) const{ +        return _tx_otw_type;      } + +private: +    uhd::otw_type_t _rx_otw_type, _tx_otw_type; +    static const size_t _max_rx_bytes_per_packet = +        USRP2_UDP_BYTES - +        USRP2_HOST_RX_VRT_HEADER_WORDS32*sizeof(boost::uint32_t) - +        USRP2_HOST_RX_VRT_TRAILER_WORDS32*sizeof(boost::uint32_t) +    ; +    static const size_t _max_tx_bytes_per_packet = +        USRP2_UDP_BYTES - +        uhd::transport::vrt::max_if_hdr_words32*sizeof(boost::uint32_t) - +        sizeof(uhd::transport::vrt::if_packet_info_t().cid) //no class id ever used +    ;  };  /*! - * USRP2 implementation guts: + * USRP2 mboard implementation guts:   * The implementation details are encapsulated here.   * Handles properties on the mboard, dboard, dsps...   */ -class usrp2_impl : public uhd::device{ +class usrp2_mboard_impl : public wax::obj{  public: -    /*! -     * Create a new usrp2 impl base. -     * \param ctrl_transport the udp transport for control -     * \param data_transport the udp transport for data -     */ -    usrp2_impl( -        uhd::transport::udp_simple::sptr ctrl_transport, -        uhd::transport::udp_zero_copy::sptr data_transport -    ); +    typedef boost::shared_ptr<usrp2_mboard_impl> sptr; -    ~usrp2_impl(void); +    //structors +    usrp2_mboard_impl(size_t index, uhd::transport::udp_simple::sptr, const usrp2_io_helper &); +    ~usrp2_mboard_impl(void); -    //the io interface -    size_t get_max_send_samps_per_packet(void) const{ -        return _max_tx_bytes_per_packet/_tx_otw_type.get_sample_size(); -    } -    size_t send( -        const boost::asio::const_buffer &, -        const uhd::tx_metadata_t &, -        const uhd::io_type_t &, -        uhd::device::send_mode_t -    ); -    size_t get_max_recv_samps_per_packet(void) const{ -        return _max_rx_bytes_per_packet/_rx_otw_type.get_sample_size(); -    } -    size_t recv( -        const boost::asio::mutable_buffer &, -        uhd::rx_metadata_t &, -        const uhd::io_type_t &, -        uhd::device::recv_mode_t -    ); - -private:      inline double get_master_clock_freq(void){          return _clock_ctrl->get_master_clock_rate();      } -    //device properties interface +private: +    size_t _index; +    const usrp2_io_helper &_io_helper; + +    //properties for this mboard      void get(const wax::obj &, wax::obj &);      void set(const wax::obj &, const wax::obj &); @@ -138,25 +149,10 @@ private:      usrp2_codec_ctrl::sptr _codec_ctrl;      usrp2_serdes_ctrl::sptr _serdes_ctrl; -    /******************************************************************* -     * Deal with the rx and tx packet sizes -     ******************************************************************/ -    static const size_t _max_rx_bytes_per_packet = -        USRP2_UDP_BYTES - -        USRP2_HOST_RX_VRT_HEADER_WORDS32*sizeof(boost::uint32_t) - -        USRP2_HOST_RX_VRT_TRAILER_WORDS32*sizeof(boost::uint32_t) -    ; -    static const size_t _max_tx_bytes_per_packet = -        USRP2_UDP_BYTES - -        uhd::transport::vrt::max_header_words32*sizeof(boost::uint32_t) -    ; - -    uhd::otw_type_t _rx_otw_type, _tx_otw_type; -    UHD_PIMPL_DECL(io_impl) _io_impl; -    void io_init(void); - -    //udp transports for control and data -    uhd::transport::udp_zero_copy::sptr _data_transport; +    //rx and tx dboard methods and objects +    uhd::usrp::dboard_manager::sptr _dboard_manager; +    uhd::usrp::dboard_iface::sptr _dboard_iface; +    void dboard_init(void);      //methods and shadows for clock configuration      uhd::clock_config_t _clock_config; @@ -164,17 +160,6 @@ private:      void update_clock_config(void);      void set_time_spec(const uhd::time_spec_t &time_spec, bool now); -    //rx and tx dboard methods and objects -    uhd::usrp::dboard_manager::sptr _dboard_manager; -    uhd::usrp::dboard_iface::sptr _dboard_iface; -    void dboard_init(void); - -    //properties for the mboard -    void mboard_init(void); -    void mboard_get(const wax::obj &, wax::obj &); -    void mboard_set(const wax::obj &, const wax::obj &); -    wax_obj_proxy::sptr _mboard_proxy; -      //properties interface for rx dboard      void rx_dboard_get(const wax::obj &, wax::obj &);      void rx_dboard_set(const wax::obj &, const wax::obj &); @@ -213,4 +198,59 @@ private:  }; +/*! + * USRP2 implementation guts: + * The implementation details are encapsulated here. + * Handles device properties and streaming... + */ +class usrp2_impl : public uhd::device{ +public: +    /*! +     * Create a new usrp2 impl base. +     * \param ctrl_transports the udp transports for control +     * \param data_transports the udp transports for data +     */ +    usrp2_impl( +        std::vector<uhd::transport::udp_simple::sptr> ctrl_transports, +        std::vector<uhd::transport::udp_zero_copy::sptr> data_transports +    ); + +    ~usrp2_impl(void); + +    //the io interface +    size_t get_max_send_samps_per_packet(void) const{ +        return _io_helper.get_max_send_samps_per_packet(); +    } +    size_t send( +        const std::vector<const void *> &, size_t, +        const uhd::tx_metadata_t &, +        const uhd::io_type_t &, +        uhd::device::send_mode_t +    ); +    size_t get_max_recv_samps_per_packet(void) const{ +        return _io_helper.get_max_recv_samps_per_packet(); +    } +    size_t recv( +        const std::vector<void *> &, size_t, +        uhd::rx_metadata_t &, +        const uhd::io_type_t &, +        uhd::device::recv_mode_t +    ); + +private: +    //device properties interface +    void get(const wax::obj &, wax::obj &); +    void set(const wax::obj &, const wax::obj &); + +    //pointers to mboards on this device (think mimo setup) +    std::vector<usrp2_mboard_impl::sptr> _mboards; +    uhd::dict<std::string, usrp2_mboard_impl::sptr> _mboard_dict; + +    //io impl methods and members +    std::vector<uhd::transport::udp_zero_copy::sptr> _data_transports; +    const usrp2_io_helper _io_helper; +    UHD_PIMPL_DECL(io_impl) _io_impl; +    void io_init(void); +}; +  #endif /* INCLUDED_USRP2_IMPL_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index 589fa71a3..c859d3603 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -18,8 +18,6 @@  #ifndef INCLUDED_USRP2_REGS_HPP  #define INCLUDED_USRP2_REGS_HPP -#include <boost/cstdint.hpp> -  ////////////////////////////////////////////////////  // Settings Bus, Slave #7, Not Byte Addressable!  // @@ -46,7 +44,7 @@  #define SR_SIMTIMER 198  #define SR_LAST 255 -#define _SR_ADDR(sr)    (MISC_OUTPUT_BASE + (sr) * sizeof(boost::uint32_t)) +#define _SR_ADDR(sr)    ((MISC_OUTPUT_BASE) + (4*(sr)))  /////////////////////////////////////////////////  // SPI Slave Constants @@ -104,7 +102,11 @@  #define U2_REG_TIME64_SECS  _SR_ADDR(SR_TIME64 + 0)  // value to set absolute secs to on next PPS  #define U2_REG_TIME64_TICKS _SR_ADDR(SR_TIME64 + 1)  // value to set absolute ticks to on next PPS  #define U2_REG_TIME64_FLAGS _SR_ADDR(SR_TIME64 + 2)  // flags - see chart above -#define U2_REG_TIME64_IMM   _SR_ADDR(SR_TIME64 + 3) // set immediate (0=latch on next pps, 1=latch immediate, default=0) +#define U2_REG_TIME64_IMM   _SR_ADDR(SR_TIME64 + 3)  // set immediate (0=latch on next pps, 1=latch immediate, default=0) +#define U2_REG_TIME64_TPS   _SR_ADDR(SR_TIME64 + 4)  // the ticks per second rollover count + +#define U2_REG_TIME64_SECS_RB  (0xCC00 + 4*10) +#define U2_REG_TIME64_TICKS_RB (0xCC00 + 4*11)  //pps flags (see above)  #define U2_FLAG_TIME64_PPS_NEGEDGE (0 << 0) diff --git a/host/test/vrt_test.cpp b/host/test/vrt_test.cpp index 3e596164c..b90b2fc15 100644 --- a/host/test/vrt_test.cpp +++ b/host/test/vrt_test.cpp @@ -16,87 +16,124 @@  //  #include <boost/test/unit_test.hpp> -#include <uhd/transport/vrt.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <cstdlib>  using namespace uhd::transport;  static void pack_and_unpack( -    const uhd::tx_metadata_t &metadata, -    size_t num_payload_words32, -    size_t packet_count +    vrt::if_packet_info_t &if_packet_info_in  ){ -    boost::uint32_t header_buff[vrt::max_header_words32]; -    size_t num_header_words32; -    size_t num_packet_words32; +    boost::uint32_t header_buff[vrt::max_if_hdr_words32];      //pack metadata into a vrt header -    vrt::pack_be( -        metadata,            //input -        header_buff,         //output -        num_header_words32,  //output -        num_payload_words32, //input -        num_packet_words32,  //output -        packet_count,        //input -        100e6 +    vrt::if_hdr_pack_be( +        header_buff, if_packet_info_in      ); -    uhd::rx_metadata_t metadata_out; -    size_t num_header_words32_out; -    size_t num_payload_words32_out; -    size_t packet_count_out; +    vrt::if_packet_info_t if_packet_info_out; +    if_packet_info_out.num_packet_words32 = if_packet_info_in.num_packet_words32;      //unpack the vrt header back into metadata -    vrt::unpack_be( -        metadata_out,            //output -        header_buff,             //input -        num_header_words32_out,  //output -        num_payload_words32_out, //output -        num_packet_words32,      //input -        packet_count_out,        //output -        100e6 +    vrt::if_hdr_unpack_be( +        header_buff, if_packet_info_out      );      //check the the unpacked metadata is the same -    BOOST_CHECK_EQUAL(packet_count, packet_count_out); -    BOOST_CHECK_EQUAL(num_header_words32, num_header_words32_out); -    BOOST_CHECK_EQUAL(num_payload_words32, num_payload_words32_out); -    BOOST_CHECK_EQUAL(metadata.has_stream_id, metadata_out.has_stream_id); -    if (metadata.has_stream_id and metadata_out.has_stream_id){ -        BOOST_CHECK_EQUAL(metadata.stream_id, metadata_out.stream_id); +    BOOST_CHECK_EQUAL(if_packet_info_in.packet_count, if_packet_info_out.packet_count); +    BOOST_CHECK_EQUAL(if_packet_info_in.num_header_words32, if_packet_info_out.num_header_words32); +    BOOST_CHECK_EQUAL(if_packet_info_in.num_payload_words32, if_packet_info_out.num_payload_words32); +    BOOST_CHECK_EQUAL(if_packet_info_in.has_sid, if_packet_info_out.has_sid); +    if (if_packet_info_in.has_sid and if_packet_info_out.has_sid){ +        BOOST_CHECK_EQUAL(if_packet_info_in.sid, if_packet_info_out.sid);      } -    BOOST_CHECK_EQUAL(metadata.has_time_spec, metadata_out.has_time_spec); -    if (metadata.has_time_spec and metadata_out.has_time_spec){ -        BOOST_CHECK_EQUAL(metadata.time_spec.secs, metadata_out.time_spec.secs); -        BOOST_CHECK_EQUAL(metadata.time_spec.nsecs, metadata_out.time_spec.nsecs); +    BOOST_CHECK_EQUAL(if_packet_info_in.has_cid, if_packet_info_out.has_cid); +    if (if_packet_info_in.has_cid and if_packet_info_out.has_cid){ +        BOOST_CHECK_EQUAL(if_packet_info_in.cid, if_packet_info_out.cid); +    } +    BOOST_CHECK_EQUAL(if_packet_info_in.has_tsi, if_packet_info_out.has_tsi); +    if (if_packet_info_in.has_tsi and if_packet_info_out.has_tsi){ +        BOOST_CHECK_EQUAL(if_packet_info_in.tsi, if_packet_info_out.tsi); +    } +    BOOST_CHECK_EQUAL(if_packet_info_in.has_tsf, if_packet_info_out.has_tsf); +    if (if_packet_info_in.has_tsf and if_packet_info_out.has_tsf){ +        BOOST_CHECK_EQUAL(if_packet_info_in.tsf, if_packet_info_out.tsf); +    } +    BOOST_CHECK_EQUAL(if_packet_info_in.has_tlr, if_packet_info_out.has_tlr); +    if (if_packet_info_in.has_tlr and if_packet_info_out.has_tlr){ +        BOOST_CHECK_EQUAL(if_packet_info_in.tlr, if_packet_info_out.tlr);      }  } +/*********************************************************************** + * Loopback test the vrt packer/unpacker with various packet info combos + * The trailer is not tested as it is not convenient to do so. + **********************************************************************/ +  BOOST_AUTO_TEST_CASE(test_with_none){ -    uhd::tx_metadata_t metadata; -    pack_and_unpack(metadata, 300, 1); +    vrt::if_packet_info_t if_packet_info; +    if_packet_info.packet_count = 0; +    if_packet_info.has_sid = false; +    if_packet_info.has_cid = false; +    if_packet_info.has_tsi = false; +    if_packet_info.has_tsf = false; +    if_packet_info.has_tlr = false; +    if_packet_info.num_payload_words32 = 0; +    pack_and_unpack(if_packet_info);  }  BOOST_AUTO_TEST_CASE(test_with_sid){ -    uhd::tx_metadata_t metadata; -    metadata.has_stream_id = true; -    metadata.stream_id = 6; -    pack_and_unpack(metadata, 400, 2); +    vrt::if_packet_info_t if_packet_info; +    if_packet_info.packet_count = 1; +    if_packet_info.has_sid = true; +    if_packet_info.has_cid = false; +    if_packet_info.has_tsi = false; +    if_packet_info.has_tsf = false; +    if_packet_info.has_tlr = false; +    if_packet_info.sid = std::rand(); +    if_packet_info.num_payload_words32 = 1111; +    pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_cid){ +    vrt::if_packet_info_t if_packet_info; +    if_packet_info.packet_count = 2; +    if_packet_info.has_sid = false; +    if_packet_info.has_cid = true; +    if_packet_info.has_tsi = false; +    if_packet_info.has_tsf = false; +    if_packet_info.has_tlr = false; +    if_packet_info.cid = std::rand(); +    if_packet_info.num_payload_words32 = 2222; +    pack_and_unpack(if_packet_info);  } -BOOST_AUTO_TEST_CASE(test_with_time_spec){ -    uhd::tx_metadata_t metadata; -    metadata.has_time_spec = true; -    metadata.time_spec.secs = 7; -    metadata.time_spec.nsecs = 2000; -    pack_and_unpack(metadata, 500, 3); +BOOST_AUTO_TEST_CASE(test_with_time){ +    vrt::if_packet_info_t if_packet_info; +    if_packet_info.packet_count = 3; +    if_packet_info.has_sid = false; +    if_packet_info.has_cid = false; +    if_packet_info.has_tsi = true; +    if_packet_info.has_tsf = true; +    if_packet_info.has_tlr = false; +    if_packet_info.tsi = std::rand(); +    if_packet_info.tsf = std::rand(); +    if_packet_info.num_payload_words32 = 33333; +    pack_and_unpack(if_packet_info);  } -BOOST_AUTO_TEST_CASE(test_with_sid_and_time_spec){ -    uhd::tx_metadata_t metadata; -    metadata.has_stream_id = true; -    metadata.stream_id = 2; -    metadata.has_time_spec = true; -    metadata.time_spec.secs = 5; -    metadata.time_spec.nsecs = 1000; -    pack_and_unpack(metadata, 600, 4); +BOOST_AUTO_TEST_CASE(test_with_all){ +    vrt::if_packet_info_t if_packet_info; +    if_packet_info.packet_count = 4; +    if_packet_info.has_sid = true; +    if_packet_info.has_cid = true; +    if_packet_info.has_tsi = true; +    if_packet_info.has_tsf = true; +    if_packet_info.has_tlr = false; +    if_packet_info.sid = std::rand(); +    if_packet_info.cid = std::rand(); +    if_packet_info.tsi = std::rand(); +    if_packet_info.tsf = std::rand(); +    if_packet_info.num_payload_words32 = 44444; +    pack_and_unpack(if_packet_info);  } diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 8d260c06c..c349a9018 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -15,21 +15,35 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  # +######################################################################## +# Utilities that get installed into the runtime path +########################################################################  ADD_EXECUTABLE(uhd_find_devices uhd_find_devices.cpp)  TARGET_LINK_LIBRARIES(uhd_find_devices uhd) -INSTALL(TARGETS uhd_find_devices RUNTIME DESTINATION ${RUNTIME_DIR})  ADD_EXECUTABLE(uhd_usrp_probe uhd_usrp_probe.cpp)  TARGET_LINK_LIBRARIES(uhd_usrp_probe uhd) -INSTALL(TARGETS uhd_usrp_probe RUNTIME DESTINATION ${RUNTIME_DIR}) +INSTALL(TARGETS +    uhd_find_devices +    uhd_usrp_probe +    RUNTIME DESTINATION ${RUNTIME_DIR} +) + +######################################################################## +# Utilities that get installed into the share path +########################################################################  ADD_EXECUTABLE(usrp2_addr_burner usrp2_addr_burner.cpp)  TARGET_LINK_LIBRARIES(usrp2_addr_burner uhd) -INSTALL(TARGETS usrp2_addr_burner RUNTIME DESTINATION ${PKG_DATA_DIR}/utils) -ADD_EXECUTABLE(uhd_burn_db_eeprom uhd_burn_db_eeprom.cpp) -TARGET_LINK_LIBRARIES(uhd_burn_db_eeprom uhd) -INSTALL(TARGETS uhd_burn_db_eeprom RUNTIME DESTINATION ${PKG_DATA_DIR}/utils) +ADD_EXECUTABLE(usrp_burn_db_eeprom usrp_burn_db_eeprom.cpp) +TARGET_LINK_LIBRARIES(usrp_burn_db_eeprom uhd) + +INSTALL(TARGETS +    usrp2_addr_burner +    usrp_burn_db_eeprom +    RUNTIME DESTINATION ${PKG_DATA_DIR}/utils +)  INSTALL(PROGRAMS      usrp2_recovery.py diff --git a/host/utils/usrp2_addr_burner.cpp b/host/utils/usrp2_addr_burner.cpp index 08fc1e218..f0e3434b7 100644 --- a/host/utils/usrp2_addr_burner.cpp +++ b/host/utils/usrp2_addr_burner.cpp @@ -16,7 +16,7 @@  //  #include <uhd/utils/safe_main.hpp> -#include <uhd/usrp/usrp2.hpp> +#include <uhd/device.hpp>  #include <uhd/usrp/device_props.hpp>  #include <boost/program_options.hpp>  #include <boost/format.hpp> @@ -45,6 +45,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //load the options into the address      uhd::device_addr_t device_addr; +    device_addr["type"] = "usrp2";      if (vm.count("addr")){          device_addr["addr"] = vm["addr"].as<std::string>();      } @@ -54,7 +55,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      }      //create a usrp2 device -    uhd::device::sptr u2_dev = uhd::usrp::usrp2::make(device_addr); +    uhd::device::sptr u2_dev = uhd::device::make(device_addr);      //FIXME usees the default mboard for now (until the mimo link is supported)      wax::obj u2_mb = (*u2_dev)[uhd::usrp::DEVICE_PROP_MBOARD];      std::cout << std::endl; diff --git a/host/utils/uhd_burn_db_eeprom.cpp b/host/utils/usrp_burn_db_eeprom.cpp index ba7aa6cec..db2981e87 100644 --- a/host/utils/uhd_burn_db_eeprom.cpp +++ b/host/utils/usrp_burn_db_eeprom.cpp @@ -54,7 +54,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      //print the help message      if (vm.count("help")){ -        std::cout << boost::format("UHD Burn DB EEPROM %s") % desc << std::endl; +        std::cout << boost::format("USRP Burn DB EEPROM %s") % desc << std::endl;          std::cout << boost::format(              "Omit the id argument to perform readback,\n"              "Or specify a new id to burn into the eeprom.\n" | 
