summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--firmware/microblaze/apps/txrx_uhd.c16
-rw-r--r--fpga/usrp2/serdes/serdes_rx.v9
-rw-r--r--fpga/usrp2/top/Makefile.common3
-rw-r--r--host/CMakeLists.txt16
-rw-r--r--host/docs/build.rst6
-rw-r--r--host/docs/coding.rst37
-rw-r--r--host/docs/dboards.rst8
-rw-r--r--host/docs/general.rst2
-rw-r--r--host/docs/usrp2.rst67
-rw-r--r--host/examples/CMakeLists.txt12
-rw-r--r--host/examples/benchmark_rx_rate.cpp143
-rw-r--r--host/examples/rx_timed_samples.cpp33
-rw-r--r--host/examples/tx_timed_samples.cpp12
-rw-r--r--host/include/uhd/CMakeLists.txt1
-rw-r--r--host/include/uhd/config.hpp11
-rw-r--r--host/include/uhd/device.hpp59
-rw-r--r--host/include/uhd/device.ipp79
-rw-r--r--host/include/uhd/transport/CMakeLists.txt4
-rw-r--r--host/include/uhd/transport/alignment_buffer.hpp84
-rw-r--r--host/include/uhd/transport/alignment_buffer.ipp140
-rw-r--r--host/include/uhd/transport/bounded_buffer.hpp82
-rw-r--r--host/include/uhd/transport/bounded_buffer.ipp112
-rw-r--r--host/include/uhd/transport/vrt.hpp115
-rw-r--r--host/include/uhd/transport/vrt_if_packet.hpp99
-rw-r--r--host/include/uhd/transport/zero_copy.hpp8
-rw-r--r--host/include/uhd/types/metadata.hpp14
-rw-r--r--host/include/uhd/types/time_spec.hpp86
-rw-r--r--host/include/uhd/usrp/CMakeLists.txt3
-rw-r--r--host/include/uhd/usrp/dboard_id.hpp2
-rw-r--r--host/include/uhd/usrp/mboard_props.hpp2
-rw-r--r--host/include/uhd/usrp/mimo_usrp.hpp171
-rw-r--r--host/include/uhd/usrp/simple_usrp.hpp22
-rw-r--r--host/include/uhd/usrp/usrp2.hpp62
-rw-r--r--host/include/uhd/utils/CMakeLists.txt1
-rw-r--r--host/include/uhd/utils/algorithm.hpp29
-rw-r--r--host/include/uhd/utils/byteswap.hpp82
-rw-r--r--host/include/uhd/utils/byteswap.ipp120
-rw-r--r--host/lib/transport/CMakeLists.txt24
-rw-r--r--host/lib/transport/convert_types_impl.hpp201
-rwxr-xr-xhost/lib/transport/gen_convert_types.py122
-rwxr-xr-xhost/lib/transport/gen_vrt_if_packet.py (renamed from host/lib/transport/gen_vrt.py)126
-rw-r--r--host/lib/transport/udp_zero_copy_asio.cpp9
-rw-r--r--host/lib/transport/vrt_packet_handler.hpp301
-rw-r--r--host/lib/types.cpp62
-rw-r--r--host/lib/usrp/CMakeLists.txt1
-rw-r--r--host/lib/usrp/mimo_usrp.cpp291
-rw-r--r--host/lib/usrp/simple_usrp.cpp16
-rw-r--r--host/lib/usrp/usrp2/dboard_impl.cpp18
-rw-r--r--host/lib/usrp/usrp2/dsp_impl.cpp20
-rw-r--r--host/lib/usrp/usrp2/fw_common.h6
-rw-r--r--host/lib/usrp/usrp2/io_impl.cpp195
-rw-r--r--host/lib/usrp/usrp2/mboard_impl.cpp100
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.cpp26
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.hpp10
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.cpp123
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp198
-rw-r--r--host/lib/usrp/usrp2/usrp2_regs.hpp10
-rw-r--r--host/test/vrt_test.cpp151
-rw-r--r--host/utils/CMakeLists.txt26
-rw-r--r--host/utils/usrp2_addr_burner.cpp5
-rw-r--r--host/utils/usrp_burn_db_eeprom.cpp (renamed from host/utils/uhd_burn_db_eeprom.cpp)2
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"